/** * Draws the html form element * * @param array $data To pre-populate element with * @param int $repeatCounter Repeat group counter * * @return string Elements html */ public function render($data, $repeatCounter = 0) { $name = $this->getHTMLName($repeatCounter); $app = JFactory::getApplication(); $input = $app->input; $id = $this->getHTMLId($repeatCounter); $params = $this->getParams(); $values = $this->getSubOptionValues(); $labels = $this->getSubOptionLabels(); /** * $$$ hugh -- working on issue with radio and checkbox, where extra blank subitem gets added * if nothing selected. this fix assumes that 'value' cannot be empty string for sub-options, * and I'm not sure if we enforce that. Problem being that if we just cast directly to * an array, the array isn't "empty()", as it has a single, empty string entry. So then * the array_diff() we're about to do sees that as a diff. * * $$$ rob - Need more logic that the previous test, as we weren't applying default value/label if set and data empty */ $selected = (array) $this->getValue($data, $repeatCounter); if (FArrayHelper::emptyIsh($selected)) { $selected = array(); // Nothing previously selected, and not editable, set selected to default value, which later on is replaced with default label if (!$this->isEditable() && $params->get('sub_default_value', '') !== '') { $selected[] = $params->get('sub_default_value'); } } // $$$ rob 06/10/2011 if front end add option on, but added option not saved we should add in the selected value to the // values and labels. $diff = array_diff($selected, $values); if (!empty($diff)) { $values = array_merge($values, $diff); // Swap over the default value to the default label if (!$this->isEditable()) { foreach ($diff as &$di) { if ($di === $params->get('sub_default_value')) { $di = $params->get('sub_default_label'); } } } $labels = array_merge($labels, $diff); } if (!$this->isEditable()) { $aRoValues = array(); for ($i = 0; $i < count($values); $i++) { if (in_array($values[$i], $selected)) { $aRoValues[] = $this->getReadOnlyOutput($values[$i], $labels[$i]); } } $splitter = $params->get('icon_folder') != -1 && $params->get('icon_folder') != '' ? ' ' : ', '; if (empty($aRoValues)) { return ''; } return $this->isMultiple() && $this->renderWithHTML ? '<ul class="fabrikRepeatData"><li>' . implode('</li><li>', $aRoValues) . '</li></ul>' : implode($splitter, $aRoValues); } // Remove the default value $key = array_search($params->get('sub_default_value'), $values); if ($key) { unset($values[$key]); } $optionsPerRow = (int) $this->getParams()->get('options_per_row', 4); $elBeforeLabel = (bool) $this->getParams()->get('element_before_label', true); // Element_before_label if ($input->get('format') == 'raw') { $optionsPerRow = 1; } $classes = $this->labelClasses(); $buttonGroup = $this->buttonGroup(); $grid = FabrikHelperHTML::grid($values, $labels, $selected, $name, $this->inputType, $elBeforeLabel, $optionsPerRow, $classes, $buttonGroup); array_unshift($grid, '<div class="fabrikSubElementContainer" id="' . $id . '">'); $grid[] = '</div><!-- close subElementContainer -->'; if ($params->get('allow_frontend_addto', false)) { $onlylabel = $params->get('allowadd-onlylabel'); $grid[] = $this->getAddOptionFields($repeatCounter, $onlylabel); } return implode("\n", $grid); }
/** * the function called from the preg_replace_callback - replace the {} with the correct HTML * * @param string $match plug-in match * * @return void */ protected function replace($match) { $app = JFactory::getApplication(); $input = $app->input; $match = $match[0]; $match = trim($match, "{"); $match = trim($match, "}"); $ref = preg_replace('/[^A-Z|a-z|0-9]/', '_', $match); $match = $this->parse(array($match)); $match = explode(" ", $match); array_shift($match); $user = JFactory::getUser(); $usersConfig = JComponentHelper::getParams('com_fabrik'); $unused = array(); // Special case if we are wanting to write in an element's data $element = false; $repeatCounter = 0; // Was defaulting to 1 but that forced filters to show in cal viz even with showfilter=no option turned on $showFilters = $input->get('showfilters', null); $clearFilters = $input->get('clearfilters', 0); $resetFilters = $input->get('resetfilters', 0); $this->origRequestVars = array(); $id = 0; $origLayout = $input->get('layout'); $origFLayout = $input->get('flayout'); $layoutFound = false; $rowId = ''; $useKey = ''; $limit = false; $session = JFactory::getSession(); $usersConfig->set('rowid', 0); $viewName = ''; foreach ($match as $m) { $m = explode('=', $m); // $$$ hugh - deal with %20 as space in arguments $m[1] = urldecode(FArrayHelper::getValue($m, 1)); switch ($m[0]) { case 'view': $viewName = JString::strtolower($m[1]); break; case 'id': case 'formid': case 'listid': // Cast to int in case there are two spaces after value. $id = (int) $m[1]; break; case 'layout': $layoutFound = true; $layout = $m[1]; $input->set('layout', $layout); break; case 'row': case 'rowid': $row = $m[1]; // When printing the content the rowid can't be passed in the querystring so don't set here if ($row !== '{rowid}') { if ($row == -1) { $row = $user->get('id'); } $usersConfig->set('rowid', $row); // Set the rowid in the session so that print pages can grab it again $session->set('fabrik.plgcontent.rowid', $row); $rowId = $row; } break; case 'element': // {fabrik view=element list=3 rowid=364 element=fielddatatwo} $viewName = 'list'; $element = $m[1]; break; case 'table': case 'list': $listId = $m[1]; break; case 'limit': $limit = $m[1]; case 'usekey': $useKey = $m[1]; break; case 'repeatcounter': $repeatCounter = $m[1]; break; case 'showfilters': $showFilters = $m[1]; break; // $$$ rob for these 2 grab the qs var in priority over the plugin settings // $$$ rob for these 2 grab the qs var in priority over the plugin settings case 'clearfilters': $clearFilters = $input->get('clearfilters', $m[1]); break; case 'resetfilters': $resetFilters = $input->get('resetfilters', $m[1]); break; default: if (array_key_exists(1, $m)) { /** * These are later set as app->input vars if present in list view * html_entity_decode to allow for content plugin values to contain * Urlencode the value for plugin statements such as: asylum_events___start_date[condition]=> */ $unused[] = trim($m[0]) . '=' . urlencode(html_entity_decode($m[1], ENT_NOQUOTES)); } } } // Get the rowid in the session so that print pages can use it // Commented out - was messing with element rendering and seems to general to be correct. IE what if more than one content plugin being used // $rowId = $session->get('fabrik.plgcontent.rowid', $rowId); if ($viewName == 'table') { // Some backwards compat with fabrik 2 $viewName = 'list'; } // Moved out of switch as otherwise first plugin to use this will effect all subsequent plugins $input->set('usekey', $useKey); // If no layout defined - set it if (!$layoutFound) { $thisLayout = $input->get('layout'); // Also test for saving an article if ($input->get('option') === 'com_content' && ($thisLayout === 'blog' || $app->isAdmin() && $thisLayout === 'edit')) { $layout = 'default'; $input->set('layout', $layout); } } /* $$$ hugh - added this so the fabrik2article plugin can arrange to have form CSS * included when the article is rendered by com_content, by inserting ... * {fabrik view=form_css id=X layout=foo} * ... at the top of the article. */ if ($viewName == 'form_css') { // The getFormCss() call blows up if we don't do this jimport('joomla.filesystem.file'); $this->generalIncludes('form'); $controller = $this->getController('form', $id); $view = $this->getView($controller, 'form', $id); $model = $this->getModel($controller, 'form', $id); if (!$model) { return; } $model->setId($id); $model->setEditable(false); $layout = !empty($layout) ? $layout : 'default'; $view->setModel($model, true); $model->getFormCss($layout); return ''; } $this->generalIncludes($viewName); if ($element !== false) { // Special case for rendering element data $controller = $this->getController('list', $listId); $model = $this->getModel($controller, 'list', $listId); if (!$model) { return; } $model->setId($listId); $formModel = $model->getFormModel(); $groups = $formModel->getGroupsHiarachy(); foreach ($groups as $groupModel) { $elements = $groupModel->getMyElements(); foreach ($elements as &$elementModel) { // $$$ rob 26/05/2011 changed it so that you can pick up joined elements without specifying plugin // param 'element' as joinx[x][fullname] but simply 'fullname' if ($element == $elementModel->getFullName(true, false)) { $activeEl = $elementModel; continue 2; } } } // $$$ hugh in case they have a typo in their elementname if (empty($activeEl) || !$activeEl->isPublished()) { throw new RuntimeException('You are trying to embed an element called ' . $element . ' which is not present in the list or has been unpublished'); } if ($rowId === '') { $rows = $model->getData(); $group = array_shift($rows); $row = array_shift($group); $res = $row->{$element}; } else { $this->_setRequest($unused); $row = $model->getRow($rowId, false, true); if (substr($element, JString::strlen($element) - 4, JString::strlen($element)) !== '_raw') { $element = $element . '_raw'; } // $$$ hugh - need to pass all row data, or calc elements that use {placeholders} won't work $defaultData = is_object($row) ? get_object_vars($row) : $row; /* $$$ hugh - if we don't do this, our passed data gets blown away when render() merges the form data * not sure why, but apparently if you do $foo =& $bar and $bar is NULL ... $foo ends up NULL */ $activeEl->getFormModel()->data = $defaultData; $activeEl->editable = false; // Set row id for things like user element $origRowId = $input->get('rowid'); $input->set('rowid', $rowId); $defaultData = (array) $defaultData; unset($activeEl->defaults); if ($repeatCounter === 'all') { $repeat = $activeEl->getGroupModel()->repeatCount(); $res = array(); for ($j = 0; $j < $repeat; $j++) { $res[] = $activeEl->render($defaultData, $j); } $res = count($res) > 1 ? '<ul><li>' . implode('</li><li>', $res) . '</li></ul>' : $res[0]; } else { $res = $activeEl->render($defaultData, $repeatCounter); $ref = $activeEl->elementJavascript($repeatCounter); } $input->set('rowid', $origRowId); $this->resetRequest(); } return $res; } if (!isset($viewName)) { return; } $origId = $input->get('id', '', 'string'); $origView = $input->get('view'); // Allow for random=1 (which has to map to fabrik_random for list views) $origRandom = $input->get('fabrik_random'); // For fabble $input->set('origid', $origId); $input->set('origview', $origView); $input->set('id', $id); $input->set('view', $viewName); /* * $$$ hugh - at least make the $origId available for certain corner cases, like ... * http://fabrikar.com/forums/showthread.php?p=42960#post42960 */ $input->set('origid', $origId, 'GET', false); $cacheKey = $id; if ($rowId !== '') { $cacheKey .= '.' . $rowId; } $controller = $this->getController($viewName, $cacheKey); $view = $this->getView($controller, $viewName, $cacheKey); if ($model = $this->getModel($controller, $viewName, $cacheKey)) { $view->setModel($model, true); } // Display the view $view->error = $controller->getError(); $view->isMambot = true; $displayed = false; // Do some view specific code switch ($viewName) { case 'form_css': $model->getFormCss(); break; case 'form': case 'details': if ($id === 0) { $app->enqueueMessage('No id set in fabrik plugin declaration', 'warning'); return; } $model->ajax = true; $model->setId($id); unset($model->groups); // Set default values set in plugin declaration // - note cant check if the form model has the key' as its not yet loaded $this->_setRequest($unused); // $$$ rob - flayout is used in form/details view when _isMamot = true $input->set('flayout', $input->get('layout')); $input->set('rowid', $rowId); break; case 'csv': case 'table': case 'list': /* $$$ rob 15/02/2011 added this as otherwise when you filtered on a table * with multiple filter set up subsequent tables were showing * the first tables data */ if ($input->get('activelistid') == '') { $input->set('activelistid', $input->getId('listid')); } $input->set('listid', $id); // Allow for simple limit=2 in plugin declaration if ($limit) { $limitKey = 'limit' . $id; $this->origRequestVars[$limitKey] = $input->get($limitKey); $input->set($limitKey, $limit); } $this->_setRequest($unused); $input->set('fabrik_random', $input->get('random', $origRandom)); $input->set('showfilters', $showFilters); $input->set('clearfilters', $clearFilters); $input->set('resetfilters', $resetFilters); if ($id === 0) { $app->enqueueMessage('No id set in fabrik plugin declaration', 'warning'); return; } $model->setId($id); $model->isMambot = true; /** * * Reset this otherwise embedding a list in a list menu page, the embedded list takes the show in list fields from the menu list * * $$$ hugh - nasty little hack to reduce 'emptyish' array, 'cos if no 'elements' in the request, the following ends up setting * returning an array with a single empty string. This ends up meaning that we render a list with no * elements in it. We've run across this before, so we have a FArrayHelper:;emptyish() to detect it. */ $show_in_list = explode('|', $input->getString('elements', '')); if (FArrayHelper::emptyIsh($show_in_list, true)) { $show_in_list = array(); } $input->set('fabrik_show_in_list', $show_in_list); $model->ajax = 1; $task = $input->get('task'); if (method_exists($controller, $task) && $input->getInt('activetableid') == $id) { /* * Enable delete() of rows * list controller deals with display after tasks is called * set $displayed to true to stop controller running twice */ $displayed = true; ob_start(); $controller->{$task}(); $result = ob_get_contents(); ob_end_clean(); } $model->setOrderByAndDir(); $formModel = $model->getFormModel(); break; case 'visualization': $input->set('showfilters', $showFilters); $input->set('clearfilters', $clearFilters); $input->set('resetfilters', $resetFilters); $this->_setRequest($unused); break; } // Hack for gallery viz as it may not use the default view $controller->isMambot = true; if (!$displayed) { ob_start(); if (method_exists($model, 'reset')) { $model->reset(); } $controller->display($model); $result = ob_get_contents(); ob_end_clean(); } $input->set('id', $origId); $input->set('view', $origView); if ($origLayout != '') { $input->set('layout', $origLayout); } if ($origFLayout != '') { $input->set('flayout', $origFLayout); } if ($origRandom) { $input->set('fabrik_random', $origRandom); } $this->resetRequest(); return $result; }
/** * For related table links get the record count for each of the table's rows * * @param object &$element element * @param array $pks primary keys to count on * * @return array counts key'd on element primary key */ public function getRecordCounts(&$element, $pks = array()) { if (!isset($this->recordCounts)) { $this->recordCounts = array(); } $input = $this->app->input; $k = $element->element_id; if (array_key_exists($k, $this->recordCounts)) { return $this->recordCounts[$k]; } $listModel = JModelLegacy::getInstance('List', 'FabrikFEModel'); $listModel->setId($element->list_id); $db = $listModel->getDb(); $elementModel = $listModel->getFormModel()->getElement($element->element_id, true); $key = $elementModel->getFullName(false, false); $linkKey = FabrikString::safeColName($key); $fparams = $listModel->getParams(); // Ensure that the faceted list's "require filters" option is set to false $fparams->set('require-filter', false); // Ignore faceted lists session filters $origIncSesssionFilters = $input->get('fabrik_incsessionfilters', true); $input->set('fabrik_incsessionfilters', false); $query = $db->getQuery(true); $query = $listModel->buildQueryWhere($input->getInt('incfilters', 0), $query); if (!FArrayHelper::emptyIsh($pks)) { // Only load the current record sets record counts $query->where($linkKey . ' IN (' . implode(',', $pks) . ')'); } // Force reload of join sql $listModel->set('joinsSQL', null); // Trigger load of joins without cdd elements - seems to mess up count otherwise $listModel->set('includeCddInJoin', false); $k2 = $db->q(FabrikString::safeColNameToArrayKey($key)); // $$$ Jannus - see http://fabrikar.com/forums/showthread.php?t=20751 $distinct = $listModel->mergeJoinedData() ? 'DISTINCT ' : ''; $item = $listModel->getTable(); $query->select($k2 . ' AS linkKey, ' . $linkKey . ' AS id, COUNT(' . $distinct . $item->db_primary_key . ') AS total')->from($item->db_table_name); $query = $listModel->buildQueryJoin($query); $listModel->set('includeCddInJoin', true); $query->group($linkKey); $db->setQuery($query); $this->recordCounts[$k] = $db->loadObjectList('id'); $this->recordCounts[$k]['linkKey'] = FabrikString::safeColNameToArrayKey($key); FabrikHelperHTML::debug($query->dump(), 'getRecordCounts query: ' . $linkKey); FabrikHelperHTML::debug($this->recordCounts[$k], 'getRecordCounts data: ' . $linkKey); $input->set('fabrik_incsessionfilters', $origIncSesssionFilters); return $this->recordCounts[$k]; }
/** * Build 'slide-show' / carousel. What gets built will depend on content type, * using the first file in the data array as the type. So if the first file is * an image, a Bootstrap carousel will be built. * * @param string $id Widget HTML id * @param array $data Array of file paths * @param object $thisRow Row data * * @return string HTML */ public function buildCarousel($id = 'carousel', $data = array(), $thisRow = null) { $rendered = ''; if (!FArrayHelper::emptyIsh($data)) { $render = $this->loadElement($data[0]); $params = $this->getParams(); $rendered = $render->renderCarousel($id, $data, $this, $params, $thisRow); } return $rendered; }
/** * Create the where part for the query that selects the list options * * @param array $data Current row data to use in placeholder replacements * @param bool $incWhere Should the additional user defined WHERE statement be included * @param string $thisTableAlias Db table alias * @param array $opts Options * @param JDatabaseQuery $query Append where to JDatabaseQuery object or return string (false) * * @return string|JDatabaseQuery */ protected function buildQueryWhere($data = array(), $incWhere = true, $thisTableAlias = null, $opts = array(), $query = false) { $rowId = $this->getFormModel()->getRowId(); $db = $this->getDb(); $join = $this->getJoin(); $fk = $db->qn($join->table_join_alias . '.' . $join->table_join_key); $params = $this->getParams(); $formModel = $this->getFormModel(); // Always filter on the current records tags (return no records if new row) $params->set('database_join_where_access', 1); if ($formModel->failedValidation()) { $pk = $db->qn($join->table_join_alias . '.' . $join->table_key); $name = $this->getFullName(true, false) . '_raw'; $tagIds = FArrayHelper::getValue($data, $name, array()); $tagIds = ArrayHelper::toInteger($tagIds); $where = FArrayHelper::emptyIsh($tagIds) ? '6 = -6' : $pk . ' IN (' . implode(', ', $tagIds) . ')'; } else { // $$$ hugh - erm ... surely we don't want to select ALL tags on a new form? /* if (!empty($rowId)) { $where = $fk . ' = ' . $db->quote($rowId); } else { $where = ''; } */ if (FArrayHelper::getValue($opts, 'mode', '') !== 'filter') { $where = $fk . ' = ' . $db->quote($rowId); } else { $where = ''; } } $params->set('database_join_where_sql', $where); $where = parent::buildQueryWhere($data, $incWhere, $thisTableAlias, $opts, $query); return $where; }
/** * Draws the html form element * * @param array $data to pre-populate element with * @param int $repeatCounter repeat group counter * * @return string elements html */ public function render($data, $repeatCounter = 0) { // For repeating groups we need to unset this where each time the element is rendered unset($this->autocomplete_where); if ($this->isJoin()) { $this->hasSubElements = true; } $params = $this->getParams(); $formModel = $this->getFormModel(); $displayType = $this->getDisplayType(); $default = (array) $this->getValue($data, $repeatCounter, array('raw' => true)); $defaultLabels = (array) $this->getValue($data, $repeatCounter, array('raw' => false)); $defaultLabels = array_values($defaultLabels); $tmp = $this->_getOptions($data, $repeatCounter); $w = new FabrikWorker(); $default = $w->parseMessageForPlaceHolder($default); $id = $this->getHTMLId($repeatCounter); $html = array(); if (!$formModel->isEditable() || !$this->isEditable()) { // Read only element formatting... if (FArrayHelper::getValue($defaultLabels, 0) === $params->get('database_join_noselectionlabel', FText::_('COM_FABRIK_PLEASE_SELECT'))) { // No point showing 'please select' for read only unset($defaultLabels[0]); } // Encrypted failed validations - only the raw value is retrieved, swap it with the option text if ($formModel->failedValidation()) { $newLabels = array(); foreach ($tmp as $t) { if (in_array($t->value, $defaultLabels)) { $newLabels[] = $t->text; } } if (!empty($newLabels)) { $defaultLabels = $newLabels; } } /* * if it's a new form, labels won't be set for any defaults. */ if ($formModel->getRowId() == '') { foreach ($defaultLabels as $key => $val) { /* * Calling getLabelForValue works, but it generates a database query for each one. * We should already have what we need in $tmp (the result of _getOptions), so lets * grab it from there. */ // $defaultLabels[$key] = $this->getLabelForValue($default[$key], $default[$key], true); if (!empty($val)) { foreach ($tmp as $t) { if ($t->value == $val) { $defaultLabels[$key] = $t->text; break; } } } } } $targetIds = $this->multiOptionTargetIds($data, $repeatCounter); // $$$ hugh - no selection, and not new row, so Nothing To See Here, Move Along. if ($this->isJoin() && FArrayHelper::emptyIsh($targetIds, true) && $formModel->getRowId() != '') { return ''; } $targetIds = $targetIds === false ? $default : $targetIds; // $$$ hugh - trying to fix issue with read only multiselects submitting wrong values $formModel->tmplData[$id . '_raw'] = $targetIds; $formModel->data[$id . '_raw'] = $targetIds; // Add icons // $$$ hugh - $targetId's isn't sequential for multiselect joins, but $defaultLabels is! //for ($i = 0; $i < count($targetIds); $i++) $i = 0; foreach ($targetIds as $tkey => $targetId) { $tmpLabel = FArrayHelper::getValue($defaultLabels, $i, 'unknown label'); if ($this->emptyConcatString($tmpLabel)) { $tmpLabel = ''; } $defaultLabels[$i] = $this->getReadOnlyOutput($targetId, $tmpLabel); $i++; } $this->addReadOnlyLinks($defaultLabels, $targetIds); $html[] = count($defaultLabels) < 2 ? implode(' ', $defaultLabels) : '<ul><li>' . implode('<li>', $defaultLabels) . '</li></ul>'; } else { // $$$rob should be canUse() otherwise if user set to view but not use the dd was shown if ($this->canUse()) { // If user can access the drop down switch ($displayType) { case 'dropdown': default: $this->renderDropdownList($data, $repeatCounter, $html, $tmp, $default); break; case 'radio': $this->renderRadioList($data, $repeatCounter, $html, $tmp, FArrayHelper::getValue($default, 0)); break; case 'checkbox': $this->renderCheckBoxList($data, $repeatCounter, $html, $tmp, $default); break; case 'multilist': $this->renderMultiSelectList($data, $repeatCounter, $html, $tmp, $default); break; case 'auto-complete': $this->renderAutoComplete($data, $repeatCounter, $html, $default); break; } $html[] = $this->renderFrontEndSelect($html); $html[] = $displayType == 'radio' ? '</div>' : ''; } elseif ($this->canView()) { $html[] = $this->renderListData($default, JArrayHelper::toObject($data)); } } $html[] = $this->renderDescription($tmp, $default); return implode("\n", $html); }
/** * Does the element consider the data to be empty * Used in isempty validation rule * * @param array $data data to test against * @param int $repeatCounter repeat group # * * @return bool */ public function dataConsideredEmpty($data, $repeatCounter) { // $$$ hugh on validations (at least), we're getting arrays if (is_array($data)) { // Check if it's an array because we are a multiselect join if ($this->isJoin()) { return FArrayHelper::emptyIsh($data); } else { return empty($data[0]); } } if ($data == '' || $data == '-1') { return true; } return false; }
/** * Get creator ids * * @param int $listId int list id * @param int $formId int form id * @param int $rowId int row id * @param array $ids all row ids * * @return int user id */ protected function getCreatorId($listId, $formId, $rowId, $ids = array()) { if (!isset($this->creatorIds)) { if (empty($ids)) { $ids[] = $rowId; } $ids = ArrayHelper::toInteger($ids); $db = FabrikWorker::getDbo(true); $elementId = $this->getElement()->id; $query = $db->getQuery(true); $query->select('row_id, user_id')->from('#__{package}_ratings')->where(array('rating <> -1', 'listid = ' . (int) $listId, 'formid = ' . (int) $formId, 'element_id = ' . (int) $elementId)); if (FArrayHelper::emptyIsh($ids)) { $query->where('6 = -6'); } else { $query->where('row_id IN (' . implode(',', $ids) . ')'); } $query->group('row_id'); // Do this query so that table view only needs one query to load up all ratings $db->setQuery($query); $this->creatorIds = $db->loadObjectList('row_id'); } if (empty($this->creatorIds)) { return JFactory::getUser()->get('id'); } else { return array_key_exists($rowId, $this->creatorIds) ? $this->creatorIds[$rowId]->user_id : 0; } }