Файловый менеджер - Редактировать - /home/wuectly/www/03cbe/com_finder.zip
Назад
PK rq!]w�/] controller.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR . '/components/com_finder/helpers/language.php'); /** * Finder Component Controller. * * @since 2.5 */ class FinderController extends JControllerLegacy { /** * Method to display a view. * * @param boolean $cachable If true, the view output will be cached. [optional] * @param array $urlparams An array of safe URL parameters and their variable types, * for valid values see {@link JFilterInput::clean()}. [optional] * * @return JControllerLegacy This object is to support chaining. * * @since 2.5 */ public function display($cachable = false, $urlparams = array()) { $input = JFactory::getApplication()->input; $cachable = true; // Load plugin language files. FinderHelperLanguage::loadPluginLanguage(); // Set the default view name and format from the Request. $viewName = $input->get('view', 'search', 'word'); $input->set('view', $viewName); // Don't cache view for search queries if ($input->get('q', null, 'string') || $input->get('f', null, 'int') || $input->get('t', null, 'array')) { $cachable = false; } $safeurlparams = array( 'f' => 'INT', 'lang' => 'CMD' ); return parent::display($cachable, $safeurlparams); } } PK rq!]!f� helpers/route.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Finder route helper class. * * @since 2.5 */ class FinderHelperRoute { /** * Method to get the route for a search page. * * @param integer $f The search filter id. [optional] * @param string $q The search query string. [optional] * * @return string The search route. * * @since 2.5 */ public static function getSearchRoute($f = null, $q = null) { // Get the menu item id. $query = array('view' => 'search', 'q' => $q, 'f' => $f); $item = self::getItemid($query); // Get the base route. $uri = clone JUri::getInstance('index.php?option=com_finder&view=search'); // Add the pre-defined search filter if present. if ($f !== null) { $uri->setVar('f', $f); } // Add the search query string if present. if ($q !== null) { $uri->setVar('q', $q); } // Add the menu item id if present. if ($item !== null) { $uri->setVar('Itemid', $item); } return $uri->toString(array('path', 'query')); } /** * Method to get the route for an advanced search page. * * @param integer $f The search filter id. [optional] * @param string $q The search query string. [optional] * * @return string The advanced search route. * * @since 2.5 */ public static function getAdvancedRoute($f = null, $q = null) { // Get the menu item id. $query = array('view' => 'advanced', 'q' => $q, 'f' => $f); $item = self::getItemid($query); // Get the base route. $uri = clone JUri::getInstance('index.php?option=com_finder&view=advanced'); // Add the pre-defined search filter if present. if ($q !== null) { $uri->setVar('f', $f); } // Add the search query string if present. if ($q !== null) { $uri->setVar('q', $q); } // Add the menu item id if present. if ($item !== null) { $uri->setVar('Itemid', $item); } return $uri->toString(array('path', 'query')); } /** * Method to get the most appropriate menu item for the route based on the * supplied query needles. * * @param array $query An array of URL parameters. * * @return mixed An integer on success, null otherwise. * * @since 2.5 */ public static function getItemid($query) { static $items, $active; // Get the menu items for com_finder. if (!$items || !$active) { $app = JFactory::getApplication('site'); $com = JComponentHelper::getComponent('com_finder'); $menu = $app->getMenu(); $active = $menu->getActive(); $items = $menu->getItems('component_id', $com->id); $items = is_array($items) ? $items : array(); } // Try to match the active view and filter. if ($active && @$active->query['view'] == @$query['view'] && @$active->query['f'] == @$query['f']) { return $active->id; } // Try to match the view, query, and filter. foreach ($items as $item) { if (@$item->query['view'] == @$query['view'] && @$item->query['q'] == @$query['q'] && @$item->query['f'] == @$query['f']) { return $item->id; } } // Try to match the view and filter. foreach ($items as $item) { if (@$item->query['view'] == @$query['view'] && @$item->query['f'] == @$query['f']) { return $item->id; } } // Try to match the view. foreach ($items as $item) { if (@$item->query['view'] == @$query['view']) { return $item->id; } } return null; } } PK rq!]X�1�7 �7 helpers/html/filter.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\Registry\Registry; JLoader::register('FinderHelperLanguage', JPATH_ADMINISTRATOR . '/components/com_finder/helpers/language.php'); /** * Filter HTML Behaviors for Finder. * * @since 2.5 */ abstract class JHtmlFilter { /** * Method to generate filters using the slider widget and decorated * with the FinderFilter JavaScript behaviors. * * @param array $options An array of configuration options. [optional] * * @return mixed A rendered HTML widget on success, null otherwise. * * @since 2.5 */ public static function slider($options = array()) { $db = JFactory::getDbo(); $query = $db->getQuery(true); $user = JFactory::getUser(); $groups = implode(',', $user->getAuthorisedViewLevels()); $html = ''; $filter = null; // Get the configuration options. $filterId = array_key_exists('filter_id', $options) ? $options['filter_id'] : null; $activeNodes = array_key_exists('selected_nodes', $options) ? $options['selected_nodes'] : array(); $classSuffix = array_key_exists('class_suffix', $options) ? $options['class_suffix'] : ''; // Load the predefined filter if specified. if (!empty($filterId)) { $query->select('f.data, f.params') ->from($db->quoteName('#__finder_filters') . ' AS f') ->where('f.filter_id = ' . (int) $filterId); // Load the filter data. $db->setQuery($query); try { $filter = $db->loadObject(); } catch (RuntimeException $e) { return null; } // Initialize the filter parameters. if ($filter) { $filter->params = new Registry($filter->params); } } // Build the query to get the branch data and the number of child nodes. $query->clear() ->select('t.*, count(c.id) AS children') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->join('INNER', $db->quoteName('#__finder_taxonomy') . ' AS c ON c.parent_id = t.id') ->where('t.parent_id = 1') ->where('t.state = 1') ->where('t.access IN (' . $groups . ')') ->group('t.id, t.parent_id, t.state, t.access, t.ordering, t.title, c.parent_id') ->order('t.ordering, t.title'); // Limit the branch children to a predefined filter. if ($filter) { $query->where('c.id IN(' . $filter->data . ')'); } // Load the branches. $db->setQuery($query); try { $branches = $db->loadObjectList('id'); } catch (RuntimeException $e) { return null; } // Check that we have at least one branch. if (count($branches) === 0) { return null; } $branch_keys = array_keys($branches); $html .= JHtml::_('bootstrap.startAccordion', 'accordion', array('parent' => true, 'active' => 'accordion-' . $branch_keys[0]) ); // Load plugin language files. FinderHelperLanguage::loadPluginLanguage(); // Iterate through the branches and build the branch groups. foreach ($branches as $bk => $bv) { // If the multi-lang plugin is enabled then drop the language branch. if ($bv->title === 'Language' && JLanguageMultilang::isEnabled()) { continue; } // Build the query to get the child nodes for this branch. $query->clear() ->select('t.*') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->where('t.parent_id = ' . (int) $bk) ->where('t.state = 1') ->where('t.access IN (' . $groups . ')') ->order('t.ordering, t.title'); // Self-join to get the parent title. $query->select('e.title AS parent_title') ->join('LEFT', $db->quoteName('#__finder_taxonomy', 'e') . ' ON ' . $db->quoteName('e.id') . ' = ' . $db->quoteName('t.parent_id')); // Load the branches. $db->setQuery($query); try { $nodes = $db->loadObjectList('id'); } catch (RuntimeException $e) { return null; } // Translate node titles if possible. $lang = JFactory::getLanguage(); foreach ($nodes as $nk => $nv) { if (trim($nv->parent_title, '**') === 'Language') { $title = FinderHelperLanguage::branchLanguageTitle($nv->title); } else { $key = FinderHelperLanguage::branchPlural($nv->title); $title = $lang->hasKey($key) ? JText::_($key) : $nv->title; } $nodes[$nk]->title = $title; } // Adding slides $html .= JHtml::_('bootstrap.addSlide', 'accordion', JText::sprintf('COM_FINDER_FILTER_BRANCH_LABEL', JText::_(FinderHelperLanguage::branchSingular($bv->title)) . ' - ' . count($nodes) ), 'accordion-' . $bk ); // Populate the toggle button. $html .= '<button class="btn jform-rightbtn" type="button" onclick="jQuery(\'[id="tax-' . $bk . '"]\').each(function(){this.click();});"><span class="icon-checkbox-partial"></span> ' . JText::_('JGLOBAL_SELECTION_INVERT') . '</button><hr/>'; // Populate the group with nodes. foreach ($nodes as $nk => $nv) { // Determine if the node should be checked. $checked = in_array($nk, $activeNodes) ? ' checked="checked"' : ''; // Build a node. $html .= '<div class="control-group">'; $html .= '<div class="controls">'; $html .= '<label class="checkbox">'; $html .= '<input type="checkbox" class="selector filter-node' . $classSuffix . '" value="' . $nk . '" name="t[]" id="tax-' . $bk . '"' . $checked . ' />'; $html .= $nv->title; $html .= '</label>'; $html .= '</div>'; $html .= '</div>'; } $html .= JHtml::_('bootstrap.endSlide'); } $html .= JHtml::_('bootstrap.endAccordion'); return $html; } /** * Method to generate filters using select box dropdown controls. * * @param FinderIndexerQuery $idxQuery A FinderIndexerQuery object. * @param array $options An array of options. * * @return mixed A rendered HTML widget on success, null otherwise. * * @since 2.5 */ public static function select($idxQuery, $options) { $user = JFactory::getUser(); $groups = implode(',', $user->getAuthorisedViewLevels()); $filter = null; // Get the configuration options. $classSuffix = $options->get('class_suffix', null); $showDates = $options->get('show_date_filters', false); // Try to load the results from cache. $cache = JFactory::getCache('com_finder', ''); $cacheId = 'filter_select_' . serialize(array($idxQuery->filter, $options, $groups, JFactory::getLanguage()->getTag())); // Check the cached results. if ($cache->contains($cacheId)) { $branches = $cache->get($cacheId); } else { $db = JFactory::getDbo(); $query = $db->getQuery(true); // Load the predefined filter if specified. if (!empty($idxQuery->filter)) { $query->select('f.data, ' . $db->quoteName('f.params')) ->from($db->quoteName('#__finder_filters') . ' AS f') ->where('f.filter_id = ' . (int) $idxQuery->filter); // Load the filter data. $db->setQuery($query); try { $filter = $db->loadObject(); } catch (RuntimeException $e) { return null; } // Initialize the filter parameters. if ($filter) { $filter->params = new Registry($filter->params); } } // Build the query to get the branch data and the number of child nodes. $query->clear() ->select('t.*, count(c.id) AS children') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->join('INNER', $db->quoteName('#__finder_taxonomy') . ' AS c ON c.parent_id = t.id') ->where('t.parent_id = 1') ->where('t.state = 1') ->where('t.access IN (' . $groups . ')') ->where('c.state = 1') ->where('c.access IN (' . $groups . ')') ->group($db->quoteName('t.id')) ->order('t.ordering, t.title'); // Limit the branch children to a predefined filter. if (!empty($filter->data)) { $query->where('c.id IN(' . $filter->data . ')'); } // Load the branches. $db->setQuery($query); try { $branches = $db->loadObjectList('id'); } catch (RuntimeException $e) { return null; } // Check that we have at least one branch. if (count($branches) === 0) { return null; } // Iterate through the branches and build the branch groups. foreach ($branches as $bk => $bv) { // If the multi-lang plugin is enabled then drop the language branch. if ($bv->title === 'Language' && JLanguageMultilang::isEnabled()) { continue; } // Build the query to get the child nodes for this branch. $query->clear() ->select('t.*') ->from($db->quoteName('#__finder_taxonomy') . ' AS t') ->where('t.parent_id = ' . (int) $bk) ->where('t.state = 1') ->where('t.access IN (' . $groups . ')') ->order('t.ordering, t.title'); // Self-join to get the parent title. $query->select('e.title AS parent_title') ->join('LEFT', $db->quoteName('#__finder_taxonomy', 'e') . ' ON ' . $db->quoteName('e.id') . ' = ' . $db->quoteName('t.parent_id')); // Limit the nodes to a predefined filter. if (!empty($filter->data)) { $query->where('t.id IN(' . $filter->data . ')'); } // Load the branches. $db->setQuery($query); try { $branches[$bk]->nodes = $db->loadObjectList('id'); } catch (RuntimeException $e) { return null; } // Translate branch nodes if possible. $language = JFactory::getLanguage(); foreach ($branches[$bk]->nodes as $node_id => $node) { if (trim($node->parent_title, '**') === 'Language') { $title = FinderHelperLanguage::branchLanguageTitle($node->title); } else { $key = FinderHelperLanguage::branchPlural($node->title); $title = $language->hasKey($key) ? JText::_($key) : $node->title; } $branches[$bk]->nodes[$node_id]->title = $title; } // Add the Search All option to the branch. array_unshift($branches[$bk]->nodes, array('id' => null, 'title' => JText::_('COM_FINDER_FILTER_SELECT_ALL_LABEL'))); } // Store the data in cache. $cache->store($branches, $cacheId); } $html = ''; // Add the dates if enabled. if ($showDates) { $html .= JHtml::_('filter.dates', $idxQuery, $options); } $html .= '<div class="filter-branch' . $classSuffix . ' control-group clearfix">'; // Iterate through all branches and build code. foreach ($branches as $bk => $bv) { // If the multi-lang plugin is enabled then drop the language branch. if ($bv->title === 'Language' && JLanguageMultilang::isEnabled()) { continue; } $active = null; // Check if the branch is in the filter. if (array_key_exists($bv->title, $idxQuery->filters)) { // Get the request filters. $temp = JFactory::getApplication()->input->request->get('t', array(), 'array'); // Search for active nodes in the branch and get the active node. $active = array_intersect($temp, $idxQuery->filters[$bv->title]); $active = count($active) === 1 ? array_shift($active) : null; } // Build a node. $html .= '<div class="controls finder-selects">'; $html .= '<label for="tax-' . JFilterOutput::stringURLSafe($bv->title) . '" class="control-label">'; $html .= JText::sprintf('COM_FINDER_FILTER_BRANCH_LABEL', JText::_(FinderHelperLanguage::branchSingular($bv->title))); $html .= '</label>'; $html .= '<br />'; $html .= JHtml::_( 'select.genericlist', $branches[$bk]->nodes, 't[]', 'class="inputbox advancedSelect"', 'id', 'title', $active, 'tax-' . JFilterOutput::stringURLSafe($bv->title) ); $html .= '</div>'; } $html .= '</div>'; return $html; } /** * Method to generate fields for filtering dates * * @param FinderIndexerQuery $idxQuery A FinderIndexerQuery object. * @param array $options An array of options. * * @return mixed A rendered HTML widget on success, null otherwise. * * @since 2.5 */ public static function dates($idxQuery, $options) { $html = ''; // Get the configuration options. $classSuffix = $options->get('class_suffix', null); $loadMedia = $options->get('load_media', true); $showDates = $options->get('show_date_filters', false); if (!empty($showDates)) { // Build the date operators options. $operators = array(); $operators[] = JHtml::_('select.option', 'before', JText::_('COM_FINDER_FILTER_DATE_BEFORE')); $operators[] = JHtml::_('select.option', 'exact', JText::_('COM_FINDER_FILTER_DATE_EXACTLY')); $operators[] = JHtml::_('select.option', 'after', JText::_('COM_FINDER_FILTER_DATE_AFTER')); // Load the CSS/JS resources. if ($loadMedia) { JHtml::_('stylesheet', 'com_finder/dates.css', array('version' => 'auto', 'relative' => true)); } // Open the widget. $html .= '<ul id="finder-filter-select-dates">'; // Start date filter. $attribs['class'] = 'input-medium'; $html .= '<li class="filter-date' . $classSuffix . '">'; $html .= '<label for="filter_date1" class="hasTooltip" title ="' . JText::_('COM_FINDER_FILTER_DATE1_DESC') . '">'; $html .= JText::_('COM_FINDER_FILTER_DATE1'); $html .= '</label>'; $html .= '<br />'; $html .= JHtml::_( 'select.genericlist', $operators, 'w1', 'class="inputbox filter-date-operator advancedSelect"', 'value', 'text', $idxQuery->when1, 'finder-filter-w1' ); $html .= JHtml::_('calendar', $idxQuery->date1, 'd1', 'filter_date1', '%Y-%m-%d', $attribs); $html .= '</li>'; // End date filter. $html .= '<li class="filter-date' . $classSuffix . '">'; $html .= '<label for="filter_date2" class="hasTooltip" title ="' . JText::_('COM_FINDER_FILTER_DATE2_DESC') . '">'; $html .= JText::_('COM_FINDER_FILTER_DATE2'); $html .= '</label>'; $html .= '<br />'; $html .= JHtml::_( 'select.genericlist', $operators, 'w2', 'class="inputbox filter-date-operator advancedSelect"', 'value', 'text', $idxQuery->when2, 'finder-filter-w2' ); $html .= JHtml::_('calendar', $idxQuery->date2, 'd2', 'filter_date2', '%Y-%m-%d', $attribs); $html .= '</li>'; // Close the widget. $html .= '</ul>'; } return $html; } } PK rq!]�ش�� � helpers/html/query.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Query HTML behavior class for Finder. * * @since 2.5 */ abstract class JHtmlQuery { /** * Method to get the explained (human-readable) search query. * * @param FinderIndexerQuery $query A FinderIndexerQuery object to explain. * * @return mixed String if there is data to explain, null otherwise. * * @since 2.5 */ public static function explained(FinderIndexerQuery $query) { $parts = array(); // Process the required tokens. foreach ($query->included as $token) { if ($token->required && (!isset($token->derived) || $token->derived == false)) { $parts[] = '<span class="query-required">' . JText::sprintf('COM_FINDER_QUERY_TOKEN_REQUIRED', $token->term) . '</span>'; } } // Process the optional tokens. foreach ($query->included as $token) { if (!$token->required && (!isset($token->derived) || $token->derived == false)) { $parts[] = '<span class="query-optional">' . JText::sprintf('COM_FINDER_QUERY_TOKEN_OPTIONAL', $token->term) . '</span>'; } } // Process the excluded tokens. foreach ($query->excluded as $token) { if (!isset($token->derived) || $token->derived === false) { $parts[] = '<span class="query-excluded">' . JText::sprintf('COM_FINDER_QUERY_TOKEN_EXCLUDED', $token->term) . '</span>'; } } // Process the start date. if ($query->date1) { $date = JFactory::getDate($query->date1)->format(JText::_('DATE_FORMAT_LC')); $datecondition = JText::_('COM_FINDER_QUERY_DATE_CONDITION_' . strtoupper($query->when1)); $parts[] = '<span class="query-start-date">' . JText::sprintf('COM_FINDER_QUERY_START_DATE', $datecondition, $date) . '</span>'; } // Process the end date. if ($query->date2) { $date = JFactory::getDate($query->date2)->format(JText::_('DATE_FORMAT_LC')); $datecondition = JText::_('COM_FINDER_QUERY_DATE_CONDITION_' . strtoupper($query->when2)); $parts[] = '<span class="query-end-date">' . JText::sprintf('COM_FINDER_QUERY_END_DATE', $datecondition, $date) . '</span>'; } // Process the taxonomy filters. if (!empty($query->filters)) { // Get the filters in the request. $t = JFactory::getApplication()->input->request->get('t', array(), 'array'); // Process the taxonomy branches. foreach ($query->filters as $branch => $nodes) { // Process the taxonomy nodes. $lang = JFactory::getLanguage(); foreach ($nodes as $title => $id) { // Translate the title for Types $key = FinderHelperLanguage::branchPlural($title); if ($lang->hasKey($key)) { $title = JText::_($key); } // Don't include the node if it is not in the request. if (!in_array($id, $t)) { continue; } // Add the node to the explanation. $parts[] = '<span class="query-taxonomy">' . JText::sprintf('COM_FINDER_QUERY_TAXONOMY_NODE', $title, JText::_(FinderHelperLanguage::branchSingular($branch))) . '</span>'; } } } // Build the interpreted query. return count($parts) ? JText::sprintf('COM_FINDER_QUERY_TOKEN_INTERPRETED', implode(JText::_('COM_FINDER_QUERY_TOKEN_GLUE'), $parts)) : null; } /** * Method to get the suggested search query. * * @param FinderIndexerQuery $query A FinderIndexerQuery object. * * @return mixed String if there is a suggestion, false otherwise. * * @since 2.5 */ public static function suggested(FinderIndexerQuery $query) { $suggested = false; // Check if the query input is empty. if (empty($query->input)) { return $suggested; } // Check if there were any ignored or included keywords. if (count($query->ignored) || count($query->included)) { $suggested = $query->input; // Replace the ignored keyword suggestions. foreach (array_reverse($query->ignored) as $token) { if (isset($token->suggestion)) { $suggested = str_ireplace($token->term, $token->suggestion, $suggested); } } // Replace the included keyword suggestions. foreach (array_reverse($query->included) as $token) { if (isset($token->suggestion)) { $suggested = str_ireplace($token->term, $token->suggestion, $suggested); } } // Check if we made any changes. if ($suggested == $query->input) { $suggested = false; } } return $suggested; } } PK rq!] 9P�� � models/suggestions.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\String\StringHelper; define('FINDER_PATH_INDEXER', JPATH_ADMINISTRATOR . '/components/com_finder/helpers/indexer'); JLoader::register('FinderIndexerHelper', FINDER_PATH_INDEXER . '/helper.php'); /** * Suggestions model class for the Finder package. * * @since 2.5 */ class FinderModelSuggestions extends JModelList { /** * Context string for the model type. * * @var string * @since 2.5 */ protected $context = 'com_finder.suggestions'; /** * Method to get an array of data items. * * @return array An array of data items. * * @since 2.5 */ public function getItems() { // Get the items. $items = parent::getItems(); // Convert them to a simple array. foreach ($items as $k => $v) { $items[$k] = $v->term; } return $items; } /** * Method to build a database query to load the list data. * * @return JDatabaseQuery A database query * * @since 2.5 */ protected function getListQuery() { $user = JFactory::getUser(); $groups = \Joomla\Utilities\ArrayHelper::toInteger($user->getAuthorisedViewLevels()); // Create a new query object. $db = $this->getDbo(); $termIdQuery = $db->getQuery(true); $termQuery = $db->getQuery(true); // Limit term count to a reasonable number of results to reduce main query join size $termIdQuery->select('ti.term_id') ->from($db->quoteName('#__finder_terms', 'ti')) ->where('ti.term LIKE ' . $db->quote($db->escape(StringHelper::strtolower($this->getState('input')), true) . '%', false)) ->where('ti.common = 0') ->where('ti.language IN (' . $db->quote($this->getState('language')) . ', ' . $db->quote('*') . ')') ->order('ti.links DESC') ->order('ti.weight DESC'); $termIds = $db->setQuery($termIdQuery, 0, 100)->loadColumn(); // Early return on term mismatch if (!count($termIds)) { return $termIdQuery; } $termIdString = implode(',', $termIds); // Select required fields $termQuery->select('DISTINCT(t.term)') ->select('t.links') ->select('t.weight') ->from($db->quoteName('#__finder_terms') . ' AS t') ->where('t.term_id IN (' . $termIdString . ')') ->order('t.links DESC') ->order('t.weight DESC'); // Determine the relevant mapping table suffix by inverting the logic from drivers $mappingTableSuffix = StringHelper::substr(md5(StringHelper::substr(StringHelper::strtolower($this->getState('input')), 0, 1)), 0, 1); // Join mapping table for term <-> link relation $mappingTable = $db->quoteName('#__finder_links_terms' . $mappingTableSuffix); $termQuery->join('INNER', $mappingTable . ' AS tm ON tm.term_id = t.term_id'); // Join links table $termQuery->join('INNER', $db->quoteName('#__finder_links') . ' AS l ON (tm.link_id = l.link_id)') ->where('l.access IN (' . implode(',', $groups) . ')') ->where('l.state = 1') ->where('l.published = 1'); return $termQuery; } /** * Method to get a store id based on model the configuration state. * * This is necessary because the model is used by the component and * different modules that might need different sets of data or different * ordering requirements. * * @param string $id An identifier string to generate the store id. [optional] * * @return string A store id. * * @since 2.5 */ protected function getStoreId($id = '') { // Add the search query state. $id .= ':' . $this->getState('input'); $id .= ':' . $this->getState('language'); // Add the list state. $id .= ':' . $this->getState('list.start'); $id .= ':' . $this->getState('list.limit'); return parent::getStoreId($id); } /** * Method to auto-populate the model state. Calling getState in this method will result in recursion. * * @param string $ordering An optional ordering field. * @param string $direction An optional direction (asc|desc). * * @return void * * @since 2.5 */ protected function populateState($ordering = null, $direction = null) { // Get the configuration options. $app = JFactory::getApplication(); $input = $app->input; $params = JComponentHelper::getParams('com_finder'); $user = JFactory::getUser(); // Get the query input. $this->setState('input', $input->request->get('q', '', 'string')); // Set the query language if (JLanguageMultilang::isEnabled()) { $lang = JFactory::getLanguage()->getTag(); } else { $lang = FinderIndexerHelper::getDefaultLanguage(); } $lang = FinderIndexerHelper::getPrimaryLanguage($lang); $this->setState('language', $lang); // Load the list state. $this->setState('list.start', 0); $this->setState('list.limit', 10); // Load the parameters. $this->setState('params', $params); // Load the user state. $this->setState('user.id', (int) $user->get('id')); } } PK rq!]Lf6܍� �� models/search.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\String\StringHelper; use Joomla\Utilities\ArrayHelper; // Register dependent classes. define('FINDER_PATH_INDEXER', JPATH_ADMINISTRATOR . '/components/com_finder/helpers/indexer'); JLoader::register('FinderIndexerHelper', FINDER_PATH_INDEXER . '/helper.php'); JLoader::register('FinderIndexerQuery', FINDER_PATH_INDEXER . '/query.php'); JLoader::register('FinderIndexerResult', FINDER_PATH_INDEXER . '/result.php'); JLoader::register('FinderIndexerStemmer', FINDER_PATH_INDEXER . '/stemmer.php'); /** * Search model class for the Finder package. * * @since 2.5 */ class FinderModelSearch extends JModelList { /** * Context string for the model type * * @var string * @since 2.5 */ protected $context = 'com_finder.search'; /** * The query object is an instance of FinderIndexerQuery which contains and * models the entire search query including the text input; static and * dynamic taxonomy filters; date filters; etc. * * @var FinderIndexerQuery * @since 2.5 */ protected $query; /** * An array of all excluded terms ids. * * @var array * @since 2.5 */ protected $excludedTerms = array(); /** * An array of all included terms ids. * * @var array * @since 2.5 */ protected $includedTerms = array(); /** * An array of all required terms ids. * * @var array * @since 2.5 */ protected $requiredTerms = array(); /** * Method to get the results of the query. * * @return array An array of FinderIndexerResult objects. * * @since 2.5 * @throws Exception on database error. */ public function getResults() { // Check if the search query is valid. if (empty($this->query->search)) { return null; } // Check if we should return results. if (empty($this->includedTerms) && (empty($this->query->filters) || !$this->query->empty)) { return null; } // Get the store id. $store = $this->getStoreId('getResults'); // Use the cached data if possible. if ($this->retrieve($store)) { return $this->retrieve($store); } // Get the row data. $items = $this->getResultsData(); // Check the data. if (empty($items)) { return null; } // Create the query to get the search results. $db = $this->getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('link_id') . ', ' . $db->quoteName('object')) ->from($db->quoteName('#__finder_links')) ->where($db->quoteName('link_id') . ' IN (' . implode(',', array_keys($items)) . ')'); // Load the results from the database. $db->setQuery($query); $rows = $db->loadObjectList('link_id'); // Set up our results container. $results = $items; // Convert the rows to result objects. foreach ($rows as $rk => $row) { // Build the result object. if (is_resource($row->object)) { $object = pg_unescape_bytea(stream_get_contents($row->object)); $result = unserialize(str_replace("''", "'", $object)); } else { $result = unserialize($row->object); } $result->weight = $results[$rk]; $result->link_id = $rk; // Add the result back to the stack. $results[$rk] = $result; } // Switch to a non-associative array. $results = array_values($results); // Push the results into cache. $this->store($store, $results); // Return the results. return $this->retrieve($store); } /** * Method to get the total number of results. * * @return integer The total number of results. * * @since 2.5 * @throws Exception on database error. */ public function getTotal() { // Check if the search query is valid. if (empty($this->query->search)) { return null; } // Check if we should return results. if (empty($this->includedTerms) && (empty($this->query->filters) || !$this->query->empty)) { return null; } // Get the store id. $store = $this->getStoreId('getTotal'); // Use the cached data if possible. if ($this->retrieve($store)) { return $this->retrieve($store); } // Get the results total. $total = $this->getResultsTotal(); // Push the total into cache. $this->store($store, $total); // Return the total. return $this->retrieve($store); } /** * Method to get the query object. * * @return FinderIndexerQuery A query object. * * @since 2.5 */ public function getQuery() { // Return the query object. return $this->query; } /** * Method to build a database query to load the list data. * * @return JDatabaseQuery A database query. * * @since 2.5 */ protected function getListQuery() { // Get the store id. $store = $this->getStoreId('getListQuery'); // Use the cached data if possible. if ($this->retrieve($store, false)) { return clone $this->retrieve($store, false); } // Set variables $user = JFactory::getUser(); $groups = implode(',', $user->getAuthorisedViewLevels()); // Create a new query object. $db = $this->getDbo(); $query = $db->getQuery(true) ->select('l.link_id') ->from($db->quoteName('#__finder_links') . ' AS l') ->where('l.access IN (' . $groups . ')') ->where('l.state = 1') ->where('l.published = 1'); // Get the null date and the current date, minus seconds. $nullDate = $db->quote($db->getNullDate()); $nowDate = $db->quote(substr_replace(JFactory::getDate()->toSql(), '00', -2)); // Add the publish up and publish down filters. $query->where('(l.publish_start_date = ' . $nullDate . ' OR l.publish_start_date <= ' . $nowDate . ')') ->where('(l.publish_end_date = ' . $nullDate . ' OR l.publish_end_date >= ' . $nowDate . ')'); /* * Add the taxonomy filters to the query. We have to join the taxonomy * map table for each group so that we can use AND clauses across * groups. Within each group there can be an array of values that will * use OR clauses. */ if (!empty($this->query->filters)) { // Convert the associative array to a numerically indexed array. $groups = array_values($this->query->filters); // Iterate through each taxonomy group and add the join and where. for ($i = 0, $c = count($groups); $i < $c; $i++) { // We use the offset because each join needs a unique alias. $query->join('INNER', $db->quoteName('#__finder_taxonomy_map') . ' AS t' . $i . ' ON t' . $i . '.link_id = l.link_id') ->where('t' . $i . '.node_id IN (' . implode(',', $groups[$i]) . ')'); } } // Add the start date filter to the query. if (!empty($this->query->date1)) { // Escape the date. $date1 = $db->quote($this->query->date1); // Add the appropriate WHERE condition. if ($this->query->when1 === 'before') { $query->where($db->quoteName('l.start_date') . ' <= ' . $date1); } elseif ($this->query->when1 === 'after') { $query->where($db->quoteName('l.start_date') . ' >= ' . $date1); } else { $query->where($db->quoteName('l.start_date') . ' = ' . $date1); } } // Add the end date filter to the query. if (!empty($this->query->date2)) { // Escape the date. $date2 = $db->quote($this->query->date2); // Add the appropriate WHERE condition. if ($this->query->when2 === 'before') { $query->where($db->quoteName('l.start_date') . ' <= ' . $date2); } elseif ($this->query->when2 === 'after') { $query->where($db->quoteName('l.start_date') . ' >= ' . $date2); } else { $query->where($db->quoteName('l.start_date') . ' = ' . $date2); } } // Filter by language if ($this->getState('filter.language')) { $query->where('l.language IN (' . $db->quote(JFactory::getLanguage()->getTag()) . ', ' . $db->quote('*') . ')'); } // Push the data into cache. $this->store($store, $query, false); // Return a copy of the query object. return clone $this->retrieve($store, false); } /** * Method to get the total number of results for the search query. * * @return integer The results total. * * @since 2.5 * @throws Exception on database error. */ protected function getResultsTotal() { // Get the store id. $store = $this->getStoreId('getResultsTotal', false); // Use the cached data if possible. if ($this->retrieve($store)) { return $this->retrieve($store); } // Get the base query and add the ordering information. $base = $this->getListQuery(); $base->select('0 AS ordering'); // Get the maximum number of results. $limit = (int) $this->getState('match.limit'); /* * If there are no optional or required search terms in the query, * we can get the result total in one relatively simple database query. */ if (empty($this->includedTerms)) { // Adjust the query to join on the appropriate mapping table. $query = clone $base; $query->clear('select') ->select('COUNT(DISTINCT l.link_id)'); // Get the total from the database. $this->_db->setQuery($query); $total = $this->_db->loadResult(); // Push the total into cache. $this->store($store, min($total, $limit)); // Return the total. return $this->retrieve($store); } /* * If there are optional or required search terms in the query, the * process of getting the result total is more complicated. */ $start = 0; $items = array(); $sorted = array(); $maps = array(); $excluded = $this->getExcludedLinkIds(); /* * Iterate through the included search terms and group them by mapping * table suffix. This ensures that we never have to do more than 16 * queries to get a batch. This may seem like a lot but it is rarely * anywhere near 16 because of the improved mapping algorithm. */ foreach ($this->includedTerms as $token => $ids) { // Get the mapping table suffix. $suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)), 0, 1); // Initialize the mapping group. if (!array_key_exists($suffix, $maps)) { $maps[$suffix] = array(); } // Add the terms to the mapping group. $maps[$suffix] = array_merge($maps[$suffix], $ids); } /* * When the query contains search terms we need to find and process the * result total iteratively using a do-while loop. */ do { // Create a container for the fetched results. $results = array(); $more = false; /* * Iterate through the mapping groups and load the total from each * mapping table. */ foreach ($maps as $suffix => $ids) { // Create a storage key for this set. $setId = $this->getStoreId('getResultsTotal:' . serialize(array_values($ids)) . ':' . $start . ':' . $limit); // Use the cached data if possible. if ($this->retrieve($setId)) { $temp = $this->retrieve($setId); } // Load the data from the database. else { // Adjust the query to join on the appropriate mapping table. $query = clone $base; $query->join('INNER', '#__finder_links_terms' . $suffix . ' AS m ON m.link_id = l.link_id') ->where('m.term_id IN (' . implode(',', $ids) . ')'); // Load the results from the database. $this->_db->setQuery($query, $start, $limit); $temp = $this->_db->loadObjectList(); // Set the more flag to true if any of the sets equal the limit. $more = count($temp) === $limit; // We loaded the data unkeyed but we need it to be keyed for later. $junk = $temp; $temp = array(); // Convert to an associative array. for ($i = 0, $c = count($junk); $i < $c; $i++) { $temp[$junk[$i]->link_id] = $junk[$i]; } // Store this set in cache. $this->store($setId, $temp); } // Merge the results. $results = array_merge($results, $temp); } // Check if there are any excluded terms to deal with. if (count($excluded)) { // Remove any results that match excluded terms. for ($i = 0, $c = count($results); $i < $c; $i++) { if (in_array($results[$i]->link_id, $excluded)) { unset($results[$i]); } } // Reset the array keys. $results = array_values($results); } // Iterate through the set to extract the unique items. for ($i = 0, $c = count($results); $i < $c; $i++) { if (!isset($sorted[$results[$i]->link_id])) { $sorted[$results[$i]->link_id] = $results[$i]->ordering; } } /* * If the query contains just optional search terms and we have * enough items for the page, we can stop here. */ if (empty($this->requiredTerms)) { // If we need more items and they're available, make another pass. if ($more && count($sorted) < $limit) { // Increment the batch starting point and continue. $start += $limit; continue; } // Push the total into cache. $this->store($store, min(count($sorted), $limit)); // Return the total. return $this->retrieve($store); } /* * The query contains required search terms so we have to iterate * over the items and remove any items that do not match all of the * required search terms. This is one of the most expensive steps * because a required token could theoretically eliminate all of * current terms which means we would have to loop through all of * the possibilities. */ foreach ($this->requiredTerms as $token => $required) { // Create a storage key for this set. $setId = $this->getStoreId('getResultsTotal:required:' . serialize(array_values($required)) . ':' . $start . ':' . $limit); // Use the cached data if possible. if ($this->retrieve($setId)) { $reqTemp = $this->retrieve($setId); } // Check if the token was matched. elseif (empty($required)) { return null; } // Load the data from the database. else { // Setup containers in case we have to make multiple passes. $reqStart = 0; $reqTemp = array(); do { // Get the map table suffix. $suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)), 0, 1); // Adjust the query to join on the appropriate mapping table. $query = clone $base; $query->join('INNER', '#__finder_links_terms' . $suffix . ' AS m ON m.link_id = l.link_id') ->where('m.term_id IN (' . implode(',', $required) . ')'); // Load the results from the database. $this->_db->setQuery($query, $reqStart, $limit); $temp = $this->_db->loadObjectList('link_id'); // Set the required token more flag to true if the set equal the limit. $reqMore = count($temp) === $limit; // Merge the matching set for this token. $reqTemp += $temp; // Increment the term offset. $reqStart += $limit; } while ($reqMore === true); // Store this set in cache. $this->store($setId, $reqTemp); } // Remove any items that do not match the required term. $sorted = array_intersect_key($sorted, $reqTemp); } // If we need more items and they're available, make another pass. if ($more && count($sorted) < $limit) { // Increment the batch starting point. $start += $limit; // Merge the found items. $items += $sorted; continue; } // Otherwise, end the loop. { // Merge the found items. $items += $sorted; $more = false; } // End do-while loop. } while ($more === true); // Set the total. $total = count($items); $total = min($total, $limit); // Push the total into cache. $this->store($store, $total); // Return the total. return $this->retrieve($store); } /** * Method to get the results for the search query. * * @return array An array of result data objects. * * @since 2.5 * @throws Exception on database error. */ protected function getResultsData() { // Get the store id. $store = $this->getStoreId('getResultsData', false); // Use the cached data if possible. if ($this->retrieve($store)) { return $this->retrieve($store); } // Get the result ordering and direction. $ordering = $this->getState('list.ordering', 'l.start_date'); $direction = $this->getState('list.direction', 'DESC'); // Get the base query and add the ordering information. $base = $this->getListQuery(); $base->select($this->_db->escape($ordering) . ' AS ordering'); $base->order($this->_db->escape($ordering) . ' ' . $this->_db->escape($direction)); /* * If there are no optional or required search terms in the query, we * can get the results in one relatively simple database query. */ if (empty($this->includedTerms)) { // Get the results from the database. $this->_db->setQuery($base, (int) $this->getState('list.start'), (int) $this->getState('list.limit')); $return = $this->_db->loadObjectList('link_id'); // Get a new store id because this data is page specific. $store = $this->getStoreId('getResultsData', true); // Push the results into cache. $this->store($store, $return); // Return the results. return $this->retrieve($store); } /* * If there are optional or required search terms in the query, the * process of getting the results is more complicated. */ $start = 0; $limit = (int) $this->getState('match.limit'); $items = array(); $sorted = array(); $maps = array(); $excluded = $this->getExcludedLinkIds(); /* * Iterate through the included search terms and group them by mapping * table suffix. This ensures that we never have to do more than 16 * queries to get a batch. This may seem like a lot but it is rarely * anywhere near 16 because of the improved mapping algorithm. */ foreach ($this->includedTerms as $token => $ids) { // Get the mapping table suffix. $suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)), 0, 1); // Initialize the mapping group. if (!array_key_exists($suffix, $maps)) { $maps[$suffix] = array(); } // Add the terms to the mapping group. $maps[$suffix] = array_merge($maps[$suffix], $ids); } /* * When the query contains search terms we need to find and process the * results iteratively using a do-while loop. */ do { // Create a container for the fetched results. $results = array(); $more = false; /* * Iterate through the mapping groups and load the results from each * mapping table. */ foreach ($maps as $suffix => $ids) { // Create a storage key for this set. $setId = $this->getStoreId('getResultsData:' . serialize(array_values($ids)) . ':' . $start . ':' . $limit); // Use the cached data if possible. if ($this->retrieve($setId)) { $temp = $this->retrieve($setId); } // Load the data from the database. else { // Adjust the query to join on the appropriate mapping table. $query = clone $base; $query->join('INNER', $this->_db->quoteName('#__finder_links_terms' . $suffix) . ' AS m ON m.link_id = l.link_id') ->where('m.term_id IN (' . implode(',', $ids) . ')'); // Load the results from the database. $this->_db->setQuery($query, $start, $limit); $temp = $this->_db->loadObjectList('link_id'); // Store this set in cache. $this->store($setId, $temp); // The data is keyed by link_id to ease caching, we don't need it till later. $temp = array_values($temp); } // Set the more flag to true if any of the sets equal the limit. $more = count($temp) === $limit; // Merge the results. $results = array_merge($results, $temp); } // Check if there are any excluded terms to deal with. if (count($excluded)) { // Remove any results that match excluded terms. for ($i = 0, $c = count($results); $i < $c; $i++) { if (in_array($results[$i]->link_id, $excluded)) { unset($results[$i]); } } // Reset the array keys. $results = array_values($results); } /* * If we are ordering by relevance we have to add up the relevance * scores that are contained in the ordering field. */ if ($ordering === 'm.weight') { // Iterate through the set to extract the unique items. for ($i = 0, $c = count($results); $i < $c; $i++) { // Add the total weights for all included search terms. if (isset($sorted[$results[$i]->link_id])) { $sorted[$results[$i]->link_id] += (float) $results[$i]->ordering; } else { $sorted[$results[$i]->link_id] = (float) $results[$i]->ordering; } } } /* * If we are ordering by start date we have to add convert the * dates to unix timestamps. */ elseif ($ordering === 'l.start_date') { // Iterate through the set to extract the unique items. for ($i = 0, $c = count($results); $i < $c; $i++) { if (!isset($sorted[$results[$i]->link_id])) { $sorted[$results[$i]->link_id] = strtotime($results[$i]->ordering); } } } /* * If we are not ordering by relevance or date, we just have to add * the unique items to the set. */ else { // Iterate through the set to extract the unique items. for ($i = 0, $c = count($results); $i < $c; $i++) { if (!isset($sorted[$results[$i]->link_id])) { $sorted[$results[$i]->link_id] = $results[$i]->ordering; } } } // Sort the results. natcasesort($items); if ($direction === 'DESC') { $items = array_reverse($items, true); } /* * If the query contains just optional search terms and we have * enough items for the page, we can stop here. */ if (empty($this->requiredTerms)) { // If we need more items and they're available, make another pass. if ($more && count($sorted) < ($this->getState('list.start') + $this->getState('list.limit'))) { // Increment the batch starting point and continue. $start += $limit; continue; } // Push the results into cache. $this->store($store, $sorted); // Return the requested set. return array_slice($this->retrieve($store), (int) $this->getState('list.start'), (int) $this->getState('list.limit'), true); } /* * The query contains required search terms so we have to iterate * over the items and remove any items that do not match all of the * required search terms. This is one of the most expensive steps * because a required token could theoretically eliminate all of * current terms which means we would have to loop through all of * the possibilities. */ foreach ($this->requiredTerms as $token => $required) { // Create a storage key for this set. $setId = $this->getStoreId('getResultsData:required:' . serialize(array_values($required)) . ':' . $start . ':' . $limit); // Use the cached data if possible. if ($this->retrieve($setId)) { $reqTemp = $this->retrieve($setId); } // Check if the token was matched. elseif (empty($required)) { return null; } // Load the data from the database. else { // Setup containers in case we have to make multiple passes. $reqStart = 0; $reqTemp = array(); do { // Get the map table suffix. $suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)), 0, 1); // Adjust the query to join on the appropriate mapping table. $query = clone $base; $query->join('INNER', $this->_db->quoteName('#__finder_links_terms' . $suffix) . ' AS m ON m.link_id = l.link_id') ->where('m.term_id IN (' . implode(',', $required) . ')'); // Load the results from the database. $this->_db->setQuery($query, $reqStart, $limit); $temp = $this->_db->loadObjectList('link_id'); // Set the required token more flag to true if the set equal the limit. $reqMore = count($temp) === $limit; // Merge the matching set for this token. $reqTemp += $temp; // Increment the term offset. $reqStart += $limit; } while ($reqMore === true); // Store this set in cache. $this->store($setId, $reqTemp); } // Remove any items that do not match the required term. $sorted = array_intersect_key($sorted, $reqTemp); } // If we need more items and they're available, make another pass. if ($more && count($sorted) < ($this->getState('list.start') + $this->getState('list.limit'))) { // Increment the batch starting point. $start += $limit; // Merge the found items. $items = array_merge($items, $sorted); continue; } // Otherwise, end the loop. else { // Set the found items. $items = $sorted; $more = false; } // End do-while loop. } while ($more === true); // Push the results into cache. $this->store($store, $items); // Return the requested set. return array_slice($this->retrieve($store), (int) $this->getState('list.start'), (int) $this->getState('list.limit'), true); } /** * Method to get an array of link ids that match excluded terms. * * @return array An array of links ids. * * @since 2.5 * @throws Exception on database error. */ protected function getExcludedLinkIds() { // Check if the search query has excluded terms. if (empty($this->excludedTerms)) { return array(); } // Get the store id. $store = $this->getStoreId('getExcludedLinkIds', false); // Use the cached data if possible. if ($this->retrieve($store)) { return $this->retrieve($store); } // Initialize containers. $links = array(); $maps = array(); /* * Iterate through the excluded search terms and group them by mapping * table suffix. This ensures that we never have to do more than 16 * queries to get a batch. This may seem like a lot but it is rarely * anywhere near 16 because of the improved mapping algorithm. */ foreach ($this->excludedTerms as $token => $id) { // Get the mapping table suffix. $suffix = StringHelper::substr(md5(StringHelper::substr($token, 0, 1)), 0, 1); // Initialize the mapping group. if (!array_key_exists($suffix, $maps)) { $maps[$suffix] = array(); } // Add the terms to the mapping group. $maps[$suffix][] = (int) $id; } /* * Iterate through the mapping groups and load the excluded links ids * from each mapping table. */ // Create a new query object. $db = $this->getDbo(); $query = $db->getQuery(true); foreach ($maps as $suffix => $ids) { // Create the query to get the links ids. $query->clear() ->select('link_id') ->from($db->quoteName('#__finder_links_terms' . $suffix)) ->where($db->quoteName('term_id') . ' IN (' . implode(',', $ids) . ')') ->group($db->quoteName('link_id')); // Load the link ids from the database. $db->setQuery($query); $temp = $db->loadColumn(); // Merge the link ids. $links = array_merge($links, $temp); } // Sanitize the link ids. $links = array_unique($links); $links = ArrayHelper::toInteger($links); // Push the link ids into cache. $this->store($store, $links); return $links; } /** * Method to get a store id based on model the configuration state. * * This is necessary because the model is used by the component and * different modules that might need different sets of data or different * ordering requirements. * * @param string $id An identifier string to generate the store id. [optional] * @param boolean $page True to store the data paged, false to store all data. [optional] * * @return string A store id. * * @since 2.5 */ protected function getStoreId($id = '', $page = true) { // Get the query object. $query = $this->getQuery(); // Add the search query state. $id .= ':' . $query->input; $id .= ':' . $query->language; $id .= ':' . $query->filter; $id .= ':' . serialize($query->filters); $id .= ':' . $query->date1; $id .= ':' . $query->date2; $id .= ':' . $query->when1; $id .= ':' . $query->when2; if ($page) { // Add the list state for page specific data. $id .= ':' . $this->getState('list.start'); $id .= ':' . $this->getState('list.limit'); $id .= ':' . $this->getState('list.ordering'); $id .= ':' . $this->getState('list.direction'); } return parent::getStoreId($id); } /** * Method to auto-populate the model state. Calling getState in this method will result in recursion. * * @param string $ordering An optional ordering field. [optional] * @param string $direction An optional direction. [optional] * * @return void * * @since 2.5 */ protected function populateState($ordering = null, $direction = null) { // Get the configuration options. $app = JFactory::getApplication(); $input = $app->input; $params = $app->getParams(); $user = JFactory::getUser(); $this->setState('filter.language', JLanguageMultilang::isEnabled()); // Setup the stemmer. if ($params->get('stem', 1) && $params->get('stemmer', 'porter_en')) { FinderIndexerHelper::$stemmer = FinderIndexerStemmer::getInstance($params->get('stemmer', 'porter_en')); } $request = $input->request; $options = array(); // Get the empty query setting. $options['empty'] = $params->get('allow_empty_query', 0); // Get the static taxonomy filters. $options['filter'] = $request->getInt('f', $params->get('f', '')); // Get the dynamic taxonomy filters. $options['filters'] = $request->get('t', $params->get('t', array()), '', 'array'); // Get the query string. $options['input'] = $request->getString('q', $params->get('q', '')); // Get the query language. $options['language'] = $request->getCmd('l', $params->get('l', '')); // Get the start date and start date modifier filters. $options['date1'] = $request->getString('d1', $params->get('d1', '')); $options['when1'] = $request->getString('w1', $params->get('w1', '')); // Get the end date and end date modifier filters. $options['date2'] = $request->getString('d2', $params->get('d2', '')); $options['when2'] = $request->getString('w2', $params->get('w2', '')); // Load the query object. $this->query = new FinderIndexerQuery($options); // Load the query token data. $this->excludedTerms = $this->query->getExcludedTermIds(); $this->includedTerms = $this->query->getIncludedTermIds(); $this->requiredTerms = $this->query->getRequiredTermIds(); // Load the list state. $this->setState('list.start', $input->get('limitstart', 0, 'uint')); $this->setState('list.limit', $input->get('limit', $app->get('list_limit', 20), 'uint')); /** * Load the sort ordering. * Currently this is 'hard' coded via menu item parameter but may not satisfy a users need. * More flexibility was way more user friendly. So we allow the user to pass a custom value * from the pool of fields that are indexed like the 'title' field. * Also, we allow this parameter to be passed in either case (lower/upper). */ $order = $input->getWord('filter_order', $params->get('sort_order', 'relevance')); $order = StringHelper::strtolower($order); switch ($order) { case 'date': $this->setState('list.ordering', 'l.start_date'); break; case 'price': $this->setState('list.ordering', 'l.list_price'); break; case ($order === 'relevance' && !empty($this->includedTerms)) : $this->setState('list.ordering', 'm.weight'); break; // Custom field that is indexed and might be required for ordering case 'title': $this->setState('list.ordering', 'l.title'); break; default: $this->setState('list.ordering', 'l.link_id'); break; } /** * Load the sort direction. * Currently this is 'hard' coded via menu item parameter but may not satisfy a users need. * More flexibility was way more user friendly. So we allow to be inverted. * Also, we allow this parameter to be passed in either case (lower/upper). */ $dirn = $input->getWord('filter_order_Dir', $params->get('sort_direction', 'desc')); $dirn = StringHelper::strtolower($dirn); switch ($dirn) { case 'asc': $this->setState('list.direction', 'ASC'); break; default: case 'desc': $this->setState('list.direction', 'DESC'); break; } // Set the match limit. $this->setState('match.limit', 1000); // Load the parameters. $this->setState('params', $params); // Load the user state. $this->setState('user.id', (int) $user->get('id')); $this->setState('user.groups', $user->getAuthorisedViewLevels()); } /** * Method to retrieve data from cache. * * @param string $id The cache store id. * @param boolean $persistent Flag to enable the use of external cache. [optional] * * @return mixed The cached data if found, null otherwise. * * @since 2.5 */ protected function retrieve($id, $persistent = true) { $data = null; // Use the internal cache if possible. if (isset($this->cache[$id])) { return $this->cache[$id]; } // Use the external cache if data is persistent. if ($persistent) { $data = JFactory::getCache($this->context, 'output')->get($id); $data = $data ? unserialize($data) : null; } // Store the data in internal cache. if ($data) { $this->cache[$id] = $data; } return $data; } /** * Method to store data in cache. * * @param string $id The cache store id. * @param mixed $data The data to cache. * @param boolean $persistent Flag to enable the use of external cache. [optional] * * @return boolean True on success, false on failure. * * @since 2.5 */ protected function store($id, $data, $persistent = true) { // Store the data in internal cache. $this->cache[$id] = $data; // Store the data in external cache if data is persistent. if ($persistent) { return JFactory::getCache($this->context, 'output')->store(serialize($data), $id); } return true; } } PK rq!]��� � controllers/suggestions.json.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Suggestions JSON controller for Finder. * * @since 2.5 */ class FinderControllerSuggestions extends JControllerLegacy { /** * Method to find search query suggestions. Uses jQuery and autocompleter.js * * @return void * * @since 3.4 */ public function suggest() { /** @var \Joomla\CMS\Application\CMSApplication $app */ $app = JFactory::getApplication(); $app->mimeType = 'application/json'; // Ensure caching is disabled as it depends on the query param in the model $app->allowCache(false); $suggestions = $this->getSuggestions(); // Send the response. $app->setHeader('Content-Type', $app->mimeType . '; charset=' . $app->charSet); $app->sendHeaders(); echo '{ "suggestions": ' . json_encode($suggestions) . ' }'; $app->close(); } /** * Method to find search query suggestions. Uses Mootools and autocompleter.js * * @param boolean $cachable If true, the view output will be cached * @param array $urlparams An array of safe URL parameters and their variable types, for valid values see {@link JFilterInput::clean()}. * * @return void * * @since 2.5 * @deprecated 3.4 */ public function display($cachable = false, $urlparams = false) { /** @var \Joomla\CMS\Application\CMSApplication $app */ $app = JFactory::getApplication(); $app->mimeType = 'application/json'; // Ensure caching is disabled as it depends on the query param in the model $app->allowCache(false); $suggestions = $this->getSuggestions(); // Send the response. $app->setHeader('Content-Type', $app->mimeType . '; charset=' . $app->charSet); $app->sendHeaders(); echo json_encode($suggestions); $app->close(); } /** * Method to retrieve the data from the database * * @return array The suggested words * * @since 3.4 */ protected function getSuggestions() { $return = array(); $params = JComponentHelper::getParams('com_finder'); if ($params->get('show_autosuggest', 1)) { // Get the suggestions. $model = $this->getModel('Suggestions', 'FinderModel'); $return = $model->getItems(); } // Check the data. if (empty($return)) { $return = array(); } return $return; } } PK rq!]�&hp p router.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; /** * Routing class from com_finder * * @since 3.3 */ class FinderRouter extends JComponentRouterBase { /** * Build the route for the com_finder component * * @param array &$query An array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @since 3.3 */ public function build(&$query) { $segments = array(); /* * First, handle menu item routes first. When the menu system builds a * route, it only provides the option and the menu item id. We don't have * to do anything to these routes. */ if (count($query) === 2 && isset($query['Itemid'], $query['option'])) { return $segments; } /* * Next, handle a route with a supplied menu item id. All system generated * routes should fall into this group. We can assume that the menu item id * is the best possible match for the query but we need to go through and * see which variables we can eliminate from the route query string because * they are present in the menu item route already. */ if (!empty($query['Itemid'])) { // Get the menu item. $item = $this->menu->getItem($query['Itemid']); // Check if the view matches. if ($item && isset($item->query['view']) && isset($query['view']) && $item->query['view'] === $query['view']) { unset($query['view']); } // Check if the search query filter matches. if ($item && isset($item->query['f']) && isset($query['f']) && $item->query['f'] === $query['f']) { unset($query['f']); } // Check if the search query string matches. if ($item && isset($item->query['q']) && isset($query['q']) && $item->query['q'] === $query['q']) { unset($query['q']); } return $segments; } /* * Lastly, handle a route with no menu item id. Fortunately, we only need * to deal with the view as the other route variables are supposed to stay * in the query string. */ if (isset($query['view'])) { // Add the view to the segments. $segments[] = $query['view']; unset($query['view']); } $total = count($segments); for ($i = 0; $i < $total; $i++) { $segments[$i] = str_replace(':', '-', $segments[$i]); } return $segments; } /** * Parse the segments of a URL. * * @param array &$segments The segments of the URL to parse. * * @return array The URL attributes to be used by the application. * * @since 3.3 */ public function parse(&$segments) { $total = count($segments); $vars = array(); for ($i = 0; $i < $total; $i++) { $segments[$i] = preg_replace('/-/', ':', $segments[$i], 1); } // Check if the view segment is set and it equals search or advanced. if (isset($segments[0]) && ($segments[0] === 'search' || $segments[0] === 'advanced')) { $vars['view'] = $segments[0]; } return $vars; } } /** * Finder router functions * * These functions are proxys for the new router interface * for old SEF extensions. * * @param array &$query An array of URL arguments * * @return array The URL arguments to use to assemble the subsequent URL. * * @deprecated 4.0 Use Class based routers instead */ function FinderBuildRoute(&$query) { $router = new FinderRouter; return $router->build($query); } /** * Finder router functions * * These functions are proxys for the new router interface * for old SEF extensions. * * @param array $segments The segments of the URL to parse. * * @return array The URL attributes to be used by the application. * * @deprecated 4.0 Use Class based routers instead */ function FinderParseRoute($segments) { $router = new FinderRouter; return $router->parse($segments); } PK rq!]g)4�" " views/search/tmpl/default.xmlnu &1i� <?xml version="1.0" encoding="utf-8"?> <metadata> <layout title="COM_FINDER_MENU_SEARCH_VIEW_DEFAULT_TITLE"> <help key = "JHELP_MENUS_MENU_ITEM_FINDER_SEARCH" /> <message> <![CDATA[COM_FINDER_MENU_SEARCH_VIEW_DEFAULT_TEXT]]> </message> </layout> <fields name="request" addfieldpath="/administrator/components/com_finder/models/fields"> <fieldset name="request"> <field name="q" type="text" label="COM_FINDER_SEARCH_SEARCH_QUERY_LABEL" description="COM_FINDER_SEARCH_SEARCH_QUERY_DESC" size="30" /> <field name="f" type="searchfilter" label="COM_FINDER_SEARCH_FILTER_SEARCH_LABEL" description="COM_FINDER_SEARCH_FILTER_SEARCH_DESC" default="" /> </fieldset> </fields> <fields name="params" addfieldpath="/administrator/components/com_finder/models/fields"> <fieldset name="basic"> <field name="show_date_filters" type="list" label="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_LABEL" description="COM_FINDER_CONFIG_SHOW_DATE_FILTERS_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field name="show_advanced" type="list" label="COM_FINDER_CONFIG_SHOW_ADVANCED_LABEL" description="COM_FINDER_CONFIG_SHOW_ADVANCED_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field name="expand_advanced" type="list" label="COM_FINDER_CONFIG_EXPAND_ADVANCED_LABEL" description="COM_FINDER_CONFIG_EXPAND_ADVANCED_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field type="spacer" /> <field name="show_description" type="list" label="COM_FINDER_CONFIG_SHOW_DESCRIPTION_LABEL" description="COM_FINDER_CONFIG_SHOW_DESCRIPTION_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field name="description_length" type="number" label="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_LABEL" description="COM_FINDER_CONFIG_DESCRIPTION_LENGTH_DESC" default="" size="5" useglobal="true" /> <field name="show_url" type="list" label="COM_FINDER_CONFIG_SHOW_URL_LABEL" description="COM_FINDER_CONFIG_SHOW_URL_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JSHOW</option> <option value="0">JHIDE</option> </field> <field type="spacer" /> </fieldset> <fieldset name="advanced"> <field name="show_pagination_limit" type="list" label="JGLOBAL_DISPLAY_SELECT_LABEL" description="JGLOBAL_DISPLAY_SELECT_DESC" validate="options" class="chzn-color" > <option value="">JGLOBAL_USE_GLOBAL</option> <option value="0">JHIDE</option> <option value="1">JSHOW</option> </field> <field name="show_pagination" type="list" label="JGLOBAL_PAGINATION_LABEL" description="JGLOBAL_PAGINATION_DESC" validate="options" class="chzn-color" > <option value="">JGLOBAL_USE_GLOBAL</option> <option value="0">JHIDE</option> <option value="1">JSHOW</option> <option value="2">JGLOBAL_AUTO</option> </field> <field name="show_pagination_results" type="list" label="JGLOBAL_PAGINATION_RESULTS_LABEL" description="JGLOBAL_PAGINATION_RESULTS_DESC" validate="options" class="chzn-color" > <option value="">JGLOBAL_USE_GLOBAL</option> <option value="0">JHIDE</option> <option value="1">JSHOW</option> </field> <field name="allow_empty_query" type="list" label="COM_FINDER_ALLOW_EMPTY_QUERY_LABEL" description="COM_FINDER_ALLOW_EMPTY_QUERY_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="show_suggested_query" type="list" label="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_LABEL" description="COM_FINDER_CONFIG_SHOW_SUGGESTED_QUERY_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="show_explained_query" type="list" label="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_LABEL" description="COM_FINDER_CONFIG_SHOW_EXPLAINED_QUERY_DESC" default="" useglobal="true" class="chzn-color" > <option value="1">JYES</option> <option value="0">JNO</option> </field> <field name="sort_order" type="list" label="COM_FINDER_CONFIG_SORT_ORDER_LABEL" description="COM_FINDER_CONFIG_SORT_ORDER_DESC" default="" useglobal="true" > <option value="relevance">COM_FINDER_CONFIG_SORT_OPTION_RELEVANCE</option> <option value="date">COM_FINDER_CONFIG_SORT_OPTION_START_DATE</option> <option value="price">COM_FINDER_CONFIG_SORT_OPTION_LIST_PRICE</option> </field> <field name="sort_direction" type="list" label="COM_FINDER_CONFIG_SORT_DIRECTION_LABEL" description="COM_FINDER_CONFIG_SORT_DIRECTION_DESC" default="" useglobal="true" > <option value="desc">COM_FINDER_CONFIG_SORT_OPTION_DESCENDING</option> <option value="asc">COM_FINDER_CONFIG_SORT_OPTION_ASCENDING</option> </field> </fieldset> <fieldset name="integration"> <field name="show_feed_link" type="list" label="JGLOBAL_SHOW_FEED_LINK_LABEL" description="JGLOBAL_SHOW_FEED_LINK_DESC" validate="options" class="chzn-color" > <option value="">JGLOBAL_USE_GLOBAL</option> <option value="0">JHIDE</option> <option value="1">JSHOW</option> </field> </fieldset> </fields> </metadata> PK rq!]�7� � views/search/tmpl/default.phpnu &1i� <?php /** * @package Joomla.Site * @subpackage com_finder * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; JHtml::_('behavior.core'); JHtml::_('formbehavior.chosen'); JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html'); JHtml::_('stylesheet', 'com_finder/finder.css', array('version' => 'auto', 'relative' => true)); ?> <div class="finder<?php echo $this->pageclass_sfx; ?>"> <?php if ($this->params->get('show_page_heading')) : ?> <h1> <?php if ($this->escape($this->params->get('page_heading'))) : ?> <?php echo $this->escape($this->params->get('page_heading')); ?> <?php else : ?> <?php echo $this->escape($this->params->get('page_title')); ?> <?php endif; ?> </h1> <?php endif; ?> <?php if ($this->params->get('show_search_form', 1)) : ?> <div id="search-form"> <?php echo $this->loadTemplate('form'); ?> </div> <?php endif; ?> <?php // Load the search results layout if we are performing a search. ?> <?php if ($this->query->search === true) : ?> <div id="search-results"> <?php echo $this->loadTemplate('results'); ?> </div> <?php endif; ?> </div> PK rq!]��� � "