/** * Add custom link to element - must be uneditable for link to be added * * @param string &$v value * @param array $data row data * @param int $repeatCounter repeat counter * * @return string */ protected function addCustomLink(&$v, $data, $repeatCounter = 0) { if ($this->isEditable()) { return $v; } $params = $this->getParams(); $customLink = $params->get('custom_link', ''); if ($customLink !== '' && $this->getElement()->link_to_detail == '1' && $params->get('custom_link_indetails', true)) { $w = new FabrikWorker(); /** * $$$ hugh - this should really happen elsewhere, but I needed a quick fix for handling * {slug} in detail view links, which for some reason are not 'stringURLSafe' at this point, * so they are like "4:A Page Title" instead of 4-a-page-title. */ if (strstr($customLink, '{slug}') && array_key_exists('slug', $data)) { $slug = str_replace(':', '-', $data['slug']); $slug = JApplicationHelper::stringURLSafe($slug); $customLink = str_replace('{slug}', $slug, $customLink); } /** * Testing new parseMessageForRepeats(), see comments on the function itself. */ $customLink = $w->parseMessageForRepeats($customLink, $data, $this, $repeatCounter); $customLink = $w->parseMessageForPlaceHolder($customLink, $data); $customLink = $this->getListModel()->parseMessageForRowHolder($customLink, $data); if (trim($customLink) !== '') { $v = '<a href="' . $customLink . '">' . $v . '</a>'; } } return $v; }
/** * Get the full server file path for the upload, including the file name * * @param int $repeatCounter Repeat group counter * * @return string Path */ protected function _getFilePath($repeatCounter = 0) { $params = $this->getParams(); if (!isset($this->_filePaths)) { $this->_filePaths = array(); } if (array_key_exists($repeatCounter, $this->_filePaths)) { /* * $$$ hugh - if it uses element placeholders, there's a likelihood the element * data may have changed since we cached the path during validation, so we need * to rebuild it. For instance, if the element data is changed by a onBeforeProcess * submission plugin, or by a 'replace' validation. */ if (!FabrikString::usesElementPlaceholders($params->get('ul_directory'))) { return $this->_filePaths[$repeatCounter]; } } $filter = JFilterInput::getInstance(); $aData = $filter->clean($_POST, 'array'); $elName = $this->getFullName(true, false); $elNameRaw = $elName . '_raw'; /** * $$$ hugh - if we use the @ way of doing this, and one of the array keys doesn't exist, * PHP still sets an error, even though it doesn't toss it. So if we then have some eval'd * code, like a PHP validation, and do the logError() thing, that will pick up and report this error, * and fail the validation. Which is VERY hard to track. So we'll have to do it long hand. */ // $myFileName = array_key_exists($elName, $_FILES) ? @$_FILES[$elName]['name'] : @$_FILES['file']['name']; $myFileName = ''; if (array_key_exists($elName, $_FILES) && is_array($_FILES[$elName])) { $myFileName = FArrayHelper::getValue($_FILES[$elName], 'name', ''); } else { if (array_key_exists('file', $_FILES) && is_array($_FILES['file'])) { $myFileName = FArrayHelper::getValue($_FILES['file'], 'name', ''); } } if (is_array($myFileName)) { $myFileName = FArrayHelper::getValue($myFileName, $repeatCounter, ''); } $myFileDir = array_key_exists($elNameRaw, $aData) && is_array($aData[$elNameRaw]) ? @$aData[$elNameRaw]['ul_end_dir'] : ''; if (is_array($myFileDir)) { $myFileDir = FArrayHelper::getValue($myFileDir, $repeatCounter, ''); } $storage = $this->getStorage(); // $$$ hugh - check if we need to blow away the cached filepath, set in validation $myFileName = $storage->cleanName($myFileName, $repeatCounter); $folder = $params->get('ul_directory'); $folder = $folder . '/' . $myFileDir; if ($storage->appendServerPath()) { $folder = JPATH_SITE . '/' . $folder; } $folder = JPath::clean($folder); $w = new FabrikWorker(); $formModel = $this->getFormModel(); $folder = $w->parseMessageForRepeats($folder, $formModel->formData, $this, $repeatCounter); $folder = $w->parseMessageForPlaceHolder($folder); if ($storage->appendServerPath()) { JPath::check($folder); } $storage->makeRecursiveFolders($folder); $p = $folder . '/' . $myFileName; $this->_filePaths[$repeatCounter] = JPath::clean($p); return $this->_filePaths[$repeatCounter]; }
/** * Create the sql query used to get the possible selectable value/labels used to create * the drop-down/checkboxes * * @param array $data data * @param bool $incWhere include where * @param array $opts query options * * @return mixed JDatabaseQuery or false if query can't be built */ protected function buildQuery($data = array(), $incWhere = true, $opts = array()) { $input = $this->app->input; $sig = isset($this->autocomplete_where) ? $this->autocomplete_where . '.' . $incWhere : $incWhere; $sig .= '.' . serialize($opts); $repeatCounter = FArrayHelper::getValue($opts, 'repeatCounter', 0); $db = FabrikWorker::getDbo(); if (isset($this->sql[$sig])) { return $this->sql[$sig]; } $params = $this->getParams(); $watch = $this->getWatchFullName(); $whereVal = null; $groups = $this->getFormModel()->getGroupsHiarachy(); $formModel = $this->getFormModel(); $watchElement = $this->getWatchElement(); // Test for ajax update if ($input->get('fabrik_cascade_ajax_update') == 1) { // Allow for multiple values - e.g. when observing a db join rendered as a checkbox $whereVal = $input->get('v', array(), 'array'); } else { if (isset($formModel->data) || isset($formModel->formData)) { $watchOpts = array('raw' => 1); if (isset($formModel->data)) { if ($watchElement->isJoin()) { $id = $watchElement->getFullName(true, false) . '_id'; $whereVal = FArrayHelper::getValue($formModel->data, $id); } else { $whereVal = $watchElement->getValue($formModel->data, $repeatCounter, $watchOpts); } } else { /* * If we're running onAfterProcess, formData will have short names in it, which means getValue() * won't find the watch element, as it's looking for full names. So if it exists, use formDataWithTableName. */ if (is_array($formModel->formDataWithTableName) && array_key_exists($watch, $formModel->formDataWithTableName)) { $whereVal = $watchElement->getValue($formModel->formDataWithTableName, $repeatCounter, $watchOpts); } else { $whereVal = $watchElement->getValue($formModel->formData, $repeatCounter, $watchOpts); } } // $$$ hugh - if not set, set to '' to avoid selecting entire table if (!isset($whereVal)) { $whereVal = ''; } } else { // $$$ hugh - probably rendering table view ... $watchRaw = $watch . '_raw'; if (isset($data[$watchRaw])) { $whereVal = $data[$watchRaw]; } else { // $$$ hugh ::sigh:: might be coming in via swapLabelsForvalues in pre_process phase // and join array in data will have been flattened. So try regular element name for watch. $noJoinWatchRaw = $watchElement->getFullName(true, false) . '_raw'; if (isset($data[$noJoinWatchRaw])) { $whereVal = $data[$noJoinWatchRaw]; } else { // $$$ hugh - if watched element has no value, we have been selecting all rows from CDD table // but should probably select none. // Unless its a cdd autocomplete list filter - seems sensible to populate that with the values matching the search term if ($this->app->input->get('method') !== 'autocomplete_options') { $whereVal = ''; } } } } } $where = ''; $whereKey = $params->get('cascadingdropdown_key'); if (!is_null($whereVal) && $whereKey != '') { $whereBits = strstr($whereKey, '___') ? explode('___', $whereKey) : explode('.', $whereKey); $whereKey = array_pop($whereBits); if (is_array($whereVal)) { foreach ($whereVal as &$v) { // Jaanus: Solving bug: imploded arrays when chbx in repeated group if (is_array($v)) { foreach ($v as &$vchild) { $vchild = FabrikString::safeQuote($vchild); } $v = implode(',', $v); } else { $v = FabrikString::safeQuote($v); } } // Jaanus: if count of where values is 0 or if there are no letters or numbers, only commas in imploded array $where .= count($whereVal) == 0 || !preg_match('/\\w/', implode(',', $whereVal)) ? '4 = -4' : $whereKey . ' IN ' . '(' . str_replace(',,', ',\'\',', implode(',', $whereVal)) . ')'; } else { $where .= $whereKey . ' = ' . $db->quote($whereVal); } } $filter = $params->get('cascadingdropdown_filter'); if (!empty($this->autocomplete_where)) { $where .= $where !== '' ? ' AND ' . $this->autocomplete_where : $this->autocomplete_where; } /* $$$ hugh - temporary hack to work around this issue: * http://fabrikar.com/forums/showthread.php?p=71288#post71288 * ... which is basically that if they are using {placeholders} in their * filter query, there's no point trying to apply that filter if we * aren't in form view, for instance when building a search filter * or in table view when the cdd is in a repeat group, 'cos there won't * be any {placeholder} data to use. * So ... for now, if the filter contains {...}, and view!=form ... skip it * $$$ testing fix for the bandaid, ccd JS should not be submitting data from form */ if (trim($filter) != '') { $where .= $where == '' ? ' ' : ' AND '; $where .= $filter; } $w = new FabrikWorker(); // $$$ hugh - add some useful stuff to search data $placeholders = is_null($whereVal) ? array() : array('whereval' => $whereVal, 'wherekey' => $whereKey); $join = $this->getJoin(); $where = $this->parseThisTable($where, $join); $data = array_merge($data, $placeholders); $where = $w->parseMessageForRepeats($where, $data, $this, $repeatCounter); $where = $w->parseMessageForPlaceHolder($where, $data); $table = $this->getDbName(); $key = $this->queryKey(); $orderBy = 'text'; $tables = $this->getFormModel()->getLinkedFabrikLists($params->get('join_db_name')); $listModel = JModelLegacy::getInstance('List', 'FabrikFEModel'); $val = $params->get('cascadingdropdown_label_concat'); if (!empty($val)) { $val = $this->parseThisTable($val, $join); $val = $w->parseMessageForPlaceHolder($val, $data); $val = 'CONCAT_WS(\'\', ' . $val . ')'; $orderBy = $val; } else { $val = FabrikString::safeColName($params->get($this->labelParam)); $val = preg_replace("#^`({$table})`\\.#", $db->qn($join->table_join_alias) . '.', $val); foreach ($tables as $tid) { $listModel->setId($tid); $listModel->getTable(); $formModel = $this->getFormModel(); $formModel->getGroupsHiarachy(); $orderBy = $val; // See if any of the tables elements match the db joins val/text foreach ($groups as $groupModel) { $elementModels = $groupModel->getPublishedElements(); foreach ($elementModels as $elementModel) { $element = $elementModel->element; if ($element->name == $val) { $val = $elementModel->modifyJoinQuery($val); } } } } } $val = str_replace($db->qn($table), $db->qn($join->table_join_alias), $val); $query = $db->getQuery(true); $query->select('DISTINCT(' . $key . ') AS value, ' . $val . 'AS text'); $desc = $params->get('cdd_desc_column', ''); if ($desc !== '') { $query->select(FabrikString::safeColName($desc) . ' AS description'); } $query->from($db->qn($table) . ' AS ' . $db->qn($join->table_join_alias)); $query = $this->buildQueryJoin($query); $where = FabrikString::rtrimword($where); if ($where !== '') { $query->where($where); } if (!JString::stristr($where, 'order by')) { $query->order($orderBy . ' ASC'); } $this->sql[$sig] = $query; FabrikHelperHTML::debug((string) $this->sql[$sig]); return $this->sql[$sig]; }