/** * Method to get a DatabaseQuery object for retrieving the data set from a database. * * @return DatabaseQuery A DatabaseQuery object to retrieve the data set. * * @since 1.0 */ protected function getListQuery() { $db = $this->getDb(); $query = $db->getQuery(true); $query->select(array('id', 'username')); $query->from('#__users'); $filter = $this->state->get('filter.search-user'); if ($filter) { // Clean filter variable $filter = $db->quote('%' . $db->escape(StringHelper::strtolower($filter), true) . '%', false); $query->where($db->quoteName('username') . ' LIKE ' . $filter); } return $query; }
/** * This method processes a string and replaces all accented UTF-8 characters by unaccented * ASCII-7 "equivalents", whitespaces are replaced by hyphens and the string is lowercase. * * @param string $string String to process * @param string $language Language to transilterate to * * @return string Processed string * * @since 11.1 */ public static function stringURLSafe($string, $language = '') { // Remove any '-' from the string since they will be used as concatenaters $str = str_replace('-', ' ', $string); // Transliterate on the language requested (fallback to current language if not specified) $lang = $language == '' || $language == '*' ? JFactory::getLanguage() : JLanguage::getInstance($language); $str = $lang->transliterate($str); // Trim white spaces at beginning and end of alias and make lowercase $str = trim(StringHelper::strtolower($str)); // Remove any duplicate whitespace, and ensure all characters are alphanumeric $str = preg_replace('/(\\s|[^A-Za-z0-9\\-])+/', '-', $str); // Trim dashes at beginning and end of alias $str = trim($str, '-'); return $str; }
/** * Sets an inflected word in the cache. * * @param string $singular The singular form of the word. * @param string $plural The plural form of the word. If omitted, it is assumed the singular and plural are identical. * * @return void * * @since 1.0 */ private function setCache($singular, $plural = null) { $singular = StringHelper::strtolower($singular); if ($plural === null) { $plural = $singular; } else { $plural = StringHelper::strtolower($plural); } $this->cache[$singular] = $plural; }
/** * Method to tokenize a text string. * * @param string $input The input to tokenize. * @param string $lang The language of the input. * @param boolean $phrase Flag to indicate whether input could be a phrase. [optional] * * @return array An array of FinderIndexerToken objects. * * @since 2.5 */ public static function tokenize($input, $lang, $phrase = false) { static $cache; $store = StringHelper::strlen($input) < 128 ? md5($input . '::' . $lang . '::' . $phrase) : null; // Check if the string has been tokenized already. if ($store && isset($cache[$store])) { return $cache[$store]; } $tokens = array(); $quotes = html_entity_decode('‘’'', ENT_QUOTES, 'UTF-8'); // Get the simple language key. $lang = static::getPrimaryLanguage($lang); /* * Parsing the string input into terms is a multi-step process. * * Regexes: * 1. Remove everything except letters, numbers, quotes, apostrophe, plus, dash, period, and comma. * 2. Remove plus, dash, period, and comma characters located before letter characters. * 3. Remove plus, dash, period, and comma characters located after other characters. * 4. Remove plus, period, and comma characters enclosed in alphabetical characters. Ungreedy. * 5. Remove orphaned apostrophe, plus, dash, period, and comma characters. * 6. Remove orphaned quote characters. * 7. Replace the assorted single quotation marks with the ASCII standard single quotation. * 8. Remove multiple space characters and replaces with a single space. */ $input = StringHelper::strtolower($input); $input = preg_replace('#[^\\pL\\pM\\pN\\p{Pi}\\p{Pf}\'+-.,]+#mui', ' ', $input); $input = preg_replace('#(^|\\s)[+-.,]+([\\pL\\pM]+)#mui', ' $1', $input); $input = preg_replace('#([\\pL\\pM\\pN]+)[+-.,]+(\\s|$)#mui', '$1 ', $input); $input = preg_replace('#([\\pL\\pM]+)[+.,]+([\\pL\\pM]+)#muiU', '$1 $2', $input); $input = preg_replace('#(^|\\s)[\'+-.,]+(\\s|$)#mui', ' ', $input); $input = preg_replace('#(^|\\s)[\\p{Pi}\\p{Pf}]+(\\s|$)#mui', ' ', $input); $input = preg_replace('#[' . $quotes . ']+#mui', '\'', $input); $input = preg_replace('#\\s+#mui', ' ', $input); $input = StringHelper::trim($input); // Explode the normalized string to get the terms. $terms = explode(' ', $input); /* * If we have Unicode support and are dealing with Chinese text, Chinese * has to be handled specially because there are not necessarily any spaces * between the "words". So, we have to test if the words belong to the Chinese * character set and if so, explode them into single glyphs or "words". */ if ($lang === 'zh') { // Iterate through the terms and test if they contain Chinese. for ($i = 0, $n = count($terms); $i < $n; $i++) { $charMatches = array(); $charCount = preg_match_all('#[\\p{Han}]#mui', $terms[$i], $charMatches); // Split apart any groups of Chinese characters. for ($j = 0; $j < $charCount; $j++) { $tSplit = StringHelper::str_ireplace($charMatches[0][$j], '', $terms[$i], false); if (!empty($tSplit)) { $terms[$i] = $tSplit; } else { unset($terms[$i]); } $terms[] = $charMatches[0][$j]; } } // Reset array keys. $terms = array_values($terms); } /* * If we have to handle the input as a phrase, that means we don't * tokenize the individual terms and we do not create the two and three * term combinations. The phrase must contain more than one word! */ if ($phrase === true && count($terms) > 1) { // Create tokens from the phrase. $tokens[] = new FinderIndexerToken($terms, $lang); } else { // Create tokens from the terms. for ($i = 0, $n = count($terms); $i < $n; $i++) { $tokens[] = new FinderIndexerToken($terms[$i], $lang); } // Create two and three word phrase tokens from the individual words. for ($i = 0, $n = count($tokens); $i < $n; $i++) { // Setup the phrase positions. $i2 = $i + 1; $i3 = $i + 2; // Create the two word phrase. if ($i2 < $n && isset($tokens[$i2])) { // Tokenize the two word phrase. $token = new FinderIndexerToken(array($tokens[$i]->term, $tokens[$i2]->term), $lang, $lang === 'zh' ? '' : ' '); $token->derived = true; // Add the token to the stack. $tokens[] = $token; } // Create the three word phrase. if ($i3 < $n && isset($tokens[$i3])) { // Tokenize the three word phrase. $token = new FinderIndexerToken(array($tokens[$i]->term, $tokens[$i2]->term, $tokens[$i3]->term), $lang, $lang === 'zh' ? '' : ' '); $token->derived = true; // Add the token to the stack. $tokens[] = $token; } } } if ($store) { $cache[$store] = count($tokens) > 1 ? $tokens : array_shift($tokens); return $cache[$store]; } else { return count($tokens) > 1 ? $tokens : array_shift($tokens); } }
/** * This method implements unicode slugs instead of transliteration. * * @param string $string String to process * * @return string Processed string * * @since 1.0 */ public static function stringUrlUnicodeSlug($string) { // Replace double byte whitespaces by single byte (East Asian languages) $str = preg_replace('/\\xE3\\x80\\x80/', ' ', $string); // Remove any '-' from the string as they will be used as concatenator. // Would be great to let the spaces in but only Firefox is friendly with this $str = str_replace('-', ' ', $str); // Replace forbidden characters by whitespaces $str = preg_replace('#[:\\#\\*"@+=;!><&\\.%()\\]\\/\'\\\\|\\[]#', " ", $str); // Delete all '?' $str = str_replace('?', '', $str); // Trim white spaces at beginning and end of alias and make lowercase $str = trim(StringHelper::strtolower($str)); // Remove any duplicate whitespace and replace whitespaces by hyphens $str = preg_replace('#\\x20+#', '-', $str); return $str; }
/** * Function to convert a sef route to an internal URI * * @param JUri &$uri The sef URI * * @return string Internal URI * * @since 3.2 * @deprecated 4.0 Attach your logic as rule to the main parse stage */ protected function parseSefRoute(&$uri) { $route = $uri->getPath(); // Remove the suffix if ($this->app->get('sef_suffix')) { if ($suffix = pathinfo($route, PATHINFO_EXTENSION)) { $route = str_replace('.' . $suffix, '', $route); } } // Get the variables from the uri $vars = $uri->getQuery(true); // Handle an empty URL (special case) if (empty($route)) { // If route is empty AND option is set in the query, assume it's non-sef url, and parse apropriately if (isset($vars['option']) || isset($vars['Itemid'])) { return $this->parseRawRoute($uri); } $item = $this->menu->getDefault($this->app->getLanguage()->getTag()); // If user not allowed to see default menu item then avoid notices if (is_object($item)) { // Set the information in the request $vars = $item->query; // Get the itemid $vars['Itemid'] = $item->id; // Set the active menu item $this->menu->setActive($vars['Itemid']); $this->setVars($vars); } return $vars; } // Parse the application route $segments = explode('/', $route); if (count($segments) > 1 && $segments[0] == 'component') { $vars['option'] = 'com_' . $segments[1]; $vars['Itemid'] = null; $route = implode('/', array_slice($segments, 2)); } else { // Get menu items. $items = $this->menu->getMenu(); $found = false; $route_lowercase = StringHelper::strtolower($route); $lang_tag = $this->app->getLanguage()->getTag(); // Iterate through all items and check route matches. foreach ($items as $item) { if ($item->route && StringHelper::strpos($route_lowercase . '/', $item->route . '/') === 0 && $item->type != 'menulink') { // Usual method for non-multilingual site. if (!$this->app->getLanguageFilter()) { // Exact route match. We can break iteration because exact item was found. if ($item->route == $route_lowercase) { $found = $item; break; } // Partial route match. Item with highest level takes priority. if (!$found || $found->level < $item->level) { $found = $item; } } elseif ($item->language == '*' || $item->language == $lang_tag) { // Exact route match. if ($item->route == $route_lowercase) { $found = $item; // Break iteration only if language is matched. if ($item->language == $lang_tag) { break; } } // Partial route match. Item with highest level or same language takes priority. if (!$found || $found->level < $item->level || $item->language == $lang_tag) { $found = $item; } } } } if (!$found) { $found = $this->menu->getDefault($lang_tag); } else { $route = substr($route, strlen($found->route)); if ($route) { $route = substr($route, 1); } } if ($found) { $vars['Itemid'] = $found->id; $vars['option'] = $found->component; } } // Set the active menu item if (isset($vars['Itemid'])) { $this->menu->setActive($vars['Itemid']); } // Set the variables $this->setVars($vars); // Parse the component route if (!empty($route) && isset($this->_vars['option'])) { $segments = explode('/', $route); if (empty($segments[0])) { array_shift($segments); } // Handle component route $component = preg_replace('/[^A-Z0-9_\\.-]/i', '', $this->_vars['option']); if (count($segments)) { $crouter = $this->getComponentRouter($component); $vars = $crouter->parse($segments); $this->setVars($vars); } } else { // Set active menu item if ($item = $this->menu->getActive()) { $vars = $item->query; } } return $vars; }
/** * Transliterate function * * This method processes a string and replaces all accented UTF-8 characters by unaccented * ASCII-7 "equivalents". * * @param string $string The string to transliterate. * * @return string The transliteration of the string. * * @since 11.1 */ public function transliterate($string) { if ($this->transliterator !== null) { return call_user_func($this->transliterator, $string); } $string = JLanguageTransliterate::utf8_latin_to_ascii($string); $string = StringHelper::strtolower($string); return $string; }
/** * Method to get the table of contents * * @return array Table of contents */ public function &getToc() { if (!is_null($this->toc)) { return $this->toc; } // Get vars $lang_tag = $this->getLangTag(); $help_search = $this->getHelpSearch(); // New style - Check for a TOC JSON file if (file_exists(JPATH_BASE . '/help/' . $lang_tag . '/toc.json')) { $data = json_decode(file_get_contents(JPATH_BASE . '/help/' . $lang_tag . '/toc.json')); // Loop through the data array foreach ($data as $key => $value) { $this->toc[$key] = JText::_('COM_ADMIN_HELP_' . $value); } // Sort the Table of Contents asort($this->toc); return $this->toc; } // Get Help files jimport('joomla.filesystem.folder'); $files = JFolder::files(JPATH_BASE . '/help/' . $lang_tag, '\\.xml$|\\.html$'); $this->toc = array(); foreach ($files as $file) { $buffer = file_get_contents(JPATH_BASE . '/help/' . $lang_tag . '/' . $file); if (!preg_match('#<title>(.*?)</title>#', $buffer, $m)) { continue; } $title = trim($m[1]); if (!$title) { continue; } // Translate the page title $title = JText::_($title); // Strip the extension $file = preg_replace('#\\.xml$|\\.html$#', '', $file); if ($help_search && StringHelper::strpos(StringHelper::strtolower(strip_tags($buffer)), StringHelper::strtolower($help_search)) === false) { continue; } // Add an item in the Table of Contents $this->toc[$file] = $title; } // Sort the Table of Contents asort($this->toc); return $this->toc; }
/** * Common function to process the filters for a query based on the model state * * @param DatabaseQuery $query DatabaseQuery object * * @return DatabaseQuery * * @since 1.0 */ private function processStateFilter(DatabaseQuery $query) { $db = $this->getDb(); $filter = $this->getProject()->project_id; if ($filter) { $query->where($db->quoteName('a.project_id') . ' = ' . (int) $filter); } $filter = $this->state->get('filter.search'); if ($filter) { $query = $this->processSearchFilter($query, $filter); } $filter = $this->state->get('filter.status'); if ($filter) { $query->where($db->quoteName('a.status') . ' = ' . (int) $filter); } $filter = $this->state->get('filter.state'); // State == 2 means "all". if (is_numeric($filter) && 2 != $filter) { $query->where($db->quoteName('s.closed') . ' = ' . (int) $filter); } $filter = $this->state->get('filter.priority'); if ($filter) { $query->where($db->quoteName('a.priority') . ' = ' . (int) $filter); } $filter = $this->state->get('filter.user'); if ($filter && is_numeric($filter)) { $username = $this->state->get('username'); switch ($filter) { case 1: $query->where($db->quoteName('a.opened_by') . ' = ' . $db->quote($username)); break; case 2: // Join over the activities. $query->join('LEFT', '#__activities AS ac ON a.issue_number = ac.issue_number'); $query->where($db->quoteName('ac.user') . ' = ' . $db->quote($username)); $query->where($db->quoteName('ac.project_id') . ' = ' . (int) $this->getProject()->project_id); $query->group('a.issue_number'); break; } } $filter = $this->state->get('filter.created_by'); if ($filter) { // Clean filter variable $filter = $db->quote('%' . $db->escape(StringHelper::strtolower($filter), true) . '%', false); $query->where($db->quoteName('a.opened_by') . ' LIKE ' . $filter); } $filter = $this->state->get('filter.category'); if ($filter && is_numeric($filter)) { $categoryModel = new CategoryModel($db); // If the category filter equals -1, that means we want issues without category. if ($filter == -1) { $issues = $categoryModel->getIssueIdsWithCategory(); } else { $issues = $categoryModel->getIssueIdsByCategory($filter); } if ($issues != null) { $issueId = array(); foreach ($issues as $issue) { $issueId[] = $issue->issue_id; } $issueId = implode(', ', $issueId); } else { $issueId = 0; } // Handle the no category filter if ($filter == -1) { $query->where($db->quoteName('a.id') . ' NOT IN (' . $issueId . ')'); } else { $query->where($db->quoteName('a.id') . ' IN (' . $issueId . ')'); } } $filter = $this->state->get('filter.label'); if ($filter && is_numeric($filter)) { $query->where('FIND_IN_SET(' . $filter . ', ' . $db->quoteName('a.labels') . ')'); } $filter = $this->state->get('filter.tests'); if ($filter && is_numeric($filter)) { // Common query elements $query->leftJoin($db->quoteName('#__issues_tests', 'it') . 'ON a.id = it.item_id')->where($db->quoteName('a.has_code') . ' = 1')->group('a.issue_number'); switch ($filter) { case 1: $query->where($db->quoteName('it.result') . ' = 1')->having('COUNT(it.item_id) = 1'); break; case 2: $query->where($db->quoteName('it.result') . ' = 1')->having('COUNT(it.item_id) > 1'); break; case 3: $query->having('COUNT(it.item_id) = 0'); break; } } $filter = $this->state->get('filter.easytest'); if ($filter && is_numeric($filter)) { $query->where($db->quoteName('a.easy') . ' = ' . (int) $filter); } return $query; }
/** * Get an item by alias * * @param string $alias The alias of the category * * @return object * * @since 1.0 */ public function getByAlias($alias = '') { $db = $this->getDb(); $query = $db->getQuery(true); $projectId = $this->getProject()->project_id; $query->select('*')->from('#__issues_categories')->where($db->quoteName('project_id') . '=' . $projectId); $filter = new InputFilter(); $alias = $filter->clean($alias, 'cmd'); if ($alias) { $alias = $db->quote('%' . $db->escape(StringHelper::strtolower($alias), true) . '%', false); $query->where($db->quoteName('alias') . ' LIKE ' . $alias); } $item = $db->setQuery($query)->loadObject(); return $item; }
/** * Returns substring of characters around a searchword. * * @param string $text The source string. * @param integer $searchword Number of chars to return. * * @return string * * @since 1.5 */ public static function _smartSubstr($text, $searchword) { $lang = JFactory::getLanguage(); $length = $lang->getSearchDisplayedCharactersNumber(); $ltext = self::remove_accents($text); $textlen = StringHelper::strlen($ltext); $lsearchword = StringHelper::strtolower(self::remove_accents($searchword)); $wordfound = false; $pos = 0; while ($wordfound === false && $pos < $textlen) { if (($wordpos = @StringHelper::strpos($ltext, ' ', $pos + $length)) !== false) { $chunk_size = $wordpos - $pos; } else { $chunk_size = $length; } $chunk = StringHelper::substr($ltext, $pos, $chunk_size); $wordfound = StringHelper::strpos(StringHelper::strtolower($chunk), $lsearchword); if ($wordfound === false) { $pos += $chunk_size + 1; } } if ($wordfound !== false) { return ($pos > 0 ? '... ' : '') . StringHelper::substr($text, $pos, $chunk_size) . ' ...'; } else { if (($wordpos = @StringHelper::strpos($text, ' ', $length)) !== false) { return StringHelper::substr($text, 0, $wordpos) . ' ...'; } else { return StringHelper::substr($text, 0, $length); } } }
/** * 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(); $filter = JFilterInput::getInstance(); $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 // 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 process the query input string and extract required, optional, * and excluded tokens; taxonomy filters; and date filters. * * @param string $input The query input string. * @param string $lang The query input language. * @param string $mode The query matching mode. * * @return boolean True on success. * * @since 2.5 * @throws Exception on database error. */ protected function processString($input, $lang, $mode) { // Clean up the input string. $input = html_entity_decode($input, ENT_QUOTES, 'UTF-8'); $input = StringHelper::strtolower($input); $input = preg_replace('#\\s+#mi', ' ', $input); $input = StringHelper::trim($input); $debug = JFactory::getConfig()->get('debug_lang'); /* * First, we need to handle string based modifiers. String based * modifiers could potentially include things like "category:blah" or * "before:2009-10-21" or "type:article", etc. */ $patterns = array('before' => JText::_('COM_FINDER_FILTER_WHEN_BEFORE'), 'after' => JText::_('COM_FINDER_FILTER_WHEN_AFTER')); // Add the taxonomy branch titles to the possible patterns. foreach (FinderIndexerTaxonomy::getBranchTitles() as $branch) { // Add the pattern. $patterns[$branch] = StringHelper::strtolower(JText::_(FinderHelperLanguage::branchSingular($branch))); } // Container for search terms and phrases. $terms = array(); $phrases = array(); // Cleared filter branches. $cleared = array(); /* * Compile the suffix pattern. This is used to match the values of the * filter input string. Single words can be input directly, multi-word * values have to be wrapped in double quotes. */ $quotes = html_entity_decode('‘’'', ENT_QUOTES, 'UTF-8'); $suffix = '(([\\w\\d' . $quotes . '-]+)|\\"([\\w\\d\\s' . $quotes . '-]+)\\")'; /* * Iterate through the possible filter patterns and search for matches. * We need to match the key, colon, and a value pattern for the match * to be valid. */ foreach ($patterns as $modifier => $pattern) { $matches = array(); if ($debug) { $pattern = substr($pattern, 2, -2); } // Check if the filter pattern is in the input string. if (preg_match('#' . $pattern . '\\s*:\\s*' . $suffix . '#mi', $input, $matches)) { // Get the value given to the modifier. $value = isset($matches[3]) ? $matches[3] : $matches[1]; // Now we have to handle the filter string. switch ($modifier) { // Handle a before and after date filters. case 'before': case 'after': // Get the time offset. $offset = JFactory::getApplication()->get('offset'); // Array of allowed when values. $whens = array('before', 'after', 'exact'); // The value of 'today' is a special case that we need to handle. if ($value === StringHelper::strtolower(JText::_('COM_FINDER_QUERY_FILTER_TODAY'))) { $value = JFactory::getDate('now', $offset)->format('%Y-%m-%d'); } // Try to parse the date string. $date = JFactory::getDate($value, $offset); // Check if the date was parsed successfully. if ($date->toUnix() !== null) { // Set the date filter. $this->date1 = $date->toSql(); $this->when1 = in_array($modifier, $whens) ? $modifier : 'before'; } break; // Handle a taxonomy branch filter. // Handle a taxonomy branch filter. default: // Try to find the node id. $return = FinderIndexerTaxonomy::getNodeByTitle($modifier, $value); // Check if the node id was found. if ($return) { // Check if the branch has been cleared. if (!in_array($modifier, $cleared)) { // Clear the branch. $this->filters[$modifier] = array(); // Add the branch to the cleared list. $cleared[] = $modifier; } // Add the filter to the list. $this->filters[$modifier][$return->title] = (int) $return->id; } break; } // Clean up the input string again. $input = str_replace($matches[0], '', $input); $input = preg_replace('#\\s+#mi', ' ', $input); $input = StringHelper::trim($input); } } /* * Extract the tokens enclosed in double quotes so that we can handle * them as phrases. */ if (StringHelper::strpos($input, '"') !== false) { $matches = array(); // Extract the tokens enclosed in double quotes. if (preg_match_all('#\\"([^"]+)\\"#mi', $input, $matches)) { /* * One or more phrases were found so we need to iterate through * them, tokenize them as phrases, and remove them from the raw * input string before we move on to the next processing step. */ foreach ($matches[1] as $key => $match) { // Find the complete phrase in the input string. $pos = StringHelper::strpos($input, $matches[0][$key]); $len = StringHelper::strlen($matches[0][$key]); // Add any terms that are before this phrase to the stack. if (StringHelper::trim(StringHelper::substr($input, 0, $pos))) { $terms = array_merge($terms, explode(' ', StringHelper::trim(StringHelper::substr($input, 0, $pos)))); } // Strip out everything up to and including the phrase. $input = StringHelper::substr($input, $pos + $len); // Clean up the input string again. $input = preg_replace('#\\s+#mi', ' ', $input); $input = StringHelper::trim($input); // Get the number of words in the phrase. $parts = explode(' ', $match); // Check if the phrase is longer than three words. if (count($parts) > 3) { /* * If the phrase is longer than three words, we need to * break it down into smaller chunks of phrases that * are less than or equal to three words. We overlap * the chunks so that we can ensure that a match is * found for the complete phrase and not just portions * of it. */ for ($i = 0, $c = count($parts); $i < $c; $i += 2) { // Set up the chunk. $chunk = array(); // The chunk has to be assembled based on how many // pieces are available to use. switch ($c - $i) { /* * If only one word is left, we can break from * the switch and loop because the last word * was already used at the end of the last * chunk. */ case 1: break 2; // If there words are left, we use them both as // the last chunk of the phrase and we're done. // If there words are left, we use them both as // the last chunk of the phrase and we're done. case 2: $chunk[] = $parts[$i]; $chunk[] = $parts[$i + 1]; break; // If there are three or more words left, we // build a three word chunk and continue on. // If there are three or more words left, we // build a three word chunk and continue on. default: $chunk[] = $parts[$i]; $chunk[] = $parts[$i + 1]; $chunk[] = $parts[$i + 2]; break; } // If the chunk is not empty, add it as a phrase. if (count($chunk)) { $phrases[] = implode(' ', $chunk); $terms[] = implode(' ', $chunk); } } } else { // The phrase is <= 3 words so we can use it as is. $phrases[] = $match; $terms[] = $match; } } } } // Add the remaining terms if present. if (!empty($input)) { $terms = array_merge($terms, explode(' ', $input)); } // An array of our boolean operators. $operator => $translation $operators = array('AND' => StringHelper::strtolower(JText::_('COM_FINDER_QUERY_OPERATOR_AND')), 'OR' => StringHelper::strtolower(JText::_('COM_FINDER_QUERY_OPERATOR_OR')), 'NOT' => StringHelper::strtolower(JText::_('COM_FINDER_QUERY_OPERATOR_NOT'))); // If language debugging is enabled you need to ignore the debug strings in matching. if (JDEBUG) { $debugStrings = array('**', '??'); $operators = str_replace($debugStrings, '', $operators); } /* * Iterate through the terms and perform any sorting that needs to be * done based on boolean search operators. Terms that are before an * and/or/not modifier have to be handled in relation to their operator. */ for ($i = 0, $c = count($terms); $i < $c; $i++) { // Check if the term is followed by an operator that we understand. if (isset($terms[$i + 1]) && in_array($terms[$i + 1], $operators)) { // Get the operator mode. $op = array_search($terms[$i + 1], $operators); // Handle the AND operator. if ($op === 'AND' && isset($terms[$i + 2])) { // Tokenize the current term. $token = FinderIndexerHelper::tokenize($terms[$i], $lang, true); $token = $this->getTokenData($token); // Set the required flag. $token->required = true; // Add the current token to the stack. $this->included[] = $token; $this->highlight = array_merge($this->highlight, array_keys($token->matches)); // Skip the next token (the mode operator). $this->operators[] = $terms[$i + 1]; // Tokenize the term after the next term (current plus two). $other = FinderIndexerHelper::tokenize($terms[$i + 2], $lang, true); $other = $this->getTokenData($other); // Set the required flag. $other->required = true; // Add the token after the next token to the stack. $this->included[] = $other; $this->highlight = array_merge($this->highlight, array_keys($other->matches)); // Remove the processed phrases if possible. if (($pk = array_search($terms[$i], $phrases)) !== false) { unset($phrases[$pk]); } if (($pk = array_search($terms[$i + 2], $phrases)) !== false) { unset($phrases[$pk]); } // Remove the processed terms. unset($terms[$i]); unset($terms[$i + 1]); unset($terms[$i + 2]); // Adjust the loop. $i += 2; continue; } elseif ($op === 'OR' && isset($terms[$i + 2])) { // Tokenize the current term. $token = FinderIndexerHelper::tokenize($terms[$i], $lang, true); $token = $this->getTokenData($token); // Set the required flag. $token->required = false; // Add the current token to the stack. if (count($token->matches)) { $this->included[] = $token; $this->highlight = array_merge($this->highlight, array_keys($token->matches)); } else { $this->ignored[] = $token; } // Skip the next token (the mode operator). $this->operators[] = $terms[$i + 1]; // Tokenize the term after the next term (current plus two). $other = FinderIndexerHelper::tokenize($terms[$i + 2], $lang, true); $other = $this->getTokenData($other); // Set the required flag. $other->required = false; // Add the token after the next token to the stack. if (count($other->matches)) { $this->included[] = $other; $this->highlight = array_merge($this->highlight, array_keys($other->matches)); } else { $this->ignored[] = $other; } // Remove the processed phrases if possible. if (($pk = array_search($terms[$i], $phrases)) !== false) { unset($phrases[$pk]); } if (($pk = array_search($terms[$i + 2], $phrases)) !== false) { unset($phrases[$pk]); } // Remove the processed terms. unset($terms[$i]); unset($terms[$i + 1]); unset($terms[$i + 2]); // Adjust the loop. $i += 2; continue; } } elseif (isset($terms[$i + 1]) && array_search($terms[$i], $operators) === 'OR') { // Skip the next token (the mode operator). $this->operators[] = $terms[$i]; // Tokenize the next term (current plus one). $other = FinderIndexerHelper::tokenize($terms[$i + 1], $lang, true); $other = $this->getTokenData($other); // Set the required flag. $other->required = false; // Add the token after the next token to the stack. if (count($other->matches)) { $this->included[] = $other; $this->highlight = array_merge($this->highlight, array_keys($other->matches)); } else { $this->ignored[] = $other; } // Remove the processed phrase if possible. if (($pk = array_search($terms[$i + 1], $phrases)) !== false) { unset($phrases[$pk]); } // Remove the processed terms. unset($terms[$i]); unset($terms[$i + 1]); // Adjust the loop. $i++; continue; } elseif (isset($terms[$i + 1]) && array_search($terms[$i], $operators) === 'NOT') { // Skip the next token (the mode operator). $this->operators[] = $terms[$i]; // Tokenize the next term (current plus one). $other = FinderIndexerHelper::tokenize($terms[$i + 1], $lang, true); $other = $this->getTokenData($other); // Set the required flag. $other->required = false; // Add the next token to the stack. if (count($other->matches)) { $this->excluded[] = $other; } else { $this->ignored[] = $other; } // Remove the processed phrase if possible. if (($pk = array_search($terms[$i + 1], $phrases)) !== false) { unset($phrases[$pk]); } // Remove the processed terms. unset($terms[$i]); unset($terms[$i + 1]); // Adjust the loop. $i++; continue; } } /* * Iterate through any search phrases and tokenize them. We handle * phrases as autonomous units and do not break them down into two and * three word combinations. */ for ($i = 0, $c = count($phrases); $i < $c; $i++) { // Tokenize the phrase. $token = FinderIndexerHelper::tokenize($phrases[$i], $lang, true); $token = $this->getTokenData($token); // Set the required flag. $token->required = true; // Add the current token to the stack. $this->included[] = $token; $this->highlight = array_merge($this->highlight, array_keys($token->matches)); // Remove the processed term if possible. if (($pk = array_search($phrases[$i], $terms)) !== false) { unset($terms[$pk]); } // Remove the processed phrase. unset($phrases[$i]); } /* * Handle any remaining tokens using the standard processing mechanism. */ if (!empty($terms)) { // Tokenize the terms. $terms = implode(' ', $terms); $tokens = FinderIndexerHelper::tokenize($terms, $lang, false); // Make sure we are working with an array. $tokens = is_array($tokens) ? $tokens : array($tokens); // Get the token data and required state for all the tokens. foreach ($tokens as $token) { // Get the token data. $token = $this->getTokenData($token); // Set the required flag for the token. $token->required = $mode === 'AND' ? $token->phrase ? false : true : false; // Add the token to the appropriate stack. if (count($token->matches) || $token->required) { $this->included[] = $token; $this->highlight = array_merge($this->highlight, array_keys($token->matches)); } else { $this->ignored[] = $token; } } } return true; }
/** * Get the master query for retrieving a list of articles subject to the model state. * * @return JDatabaseQuery * * @since 1.6 */ protected function getListQuery() { // Get the current user for authorisation checks $user = JFactory::getUser(); // Create a new query object. $db = $this->getDbo(); $query = $db->getQuery(true); // Select the required fields from the table. $query->select($this->getState('list.select', 'a.id, a.title, a.alias, a.introtext, a.fulltext, ' . 'a.checked_out, a.checked_out_time, ' . 'a.catid, a.created, a.created_by, a.created_by_alias, ' . 'CASE WHEN a.modified = ' . $db->quote($db->getNullDate()) . ' THEN a.created ELSE a.modified END as modified, ' . 'a.modified_by, uam.name as modified_by_name,' . 'CASE WHEN a.publish_up = ' . $db->quote($db->getNullDate()) . ' THEN a.created ELSE a.publish_up END as publish_up,' . 'a.publish_down, a.images, a.urls, a.attribs, a.metadata, a.metakey, a.metadesc, a.access, ' . 'a.hits, a.xreference, a.featured, a.language, ' . ' ' . $query->length('a.fulltext') . ' AS readmore')); // Process an Archived Article layout if ($this->getState('filter.published') == 2) { // If badcats is not null, this means that the article is inside an archived category // In this case, the state is set to 2 to indicate Archived (even if the article state is Published) $query->select($this->getState('list.select', 'CASE WHEN badcats.id is null THEN a.state ELSE 2 END AS state')); } else { /* Process non-archived layout If badcats is not null, this means that the article is inside an unpublished category In this case, the state is set to 0 to indicate Unpublished (even if the article state is Published) */ $query->select($this->getState('list.select', 'CASE WHEN badcats.id is not null THEN 0 ELSE a.state END AS state')); } $query->from('#__content AS a'); $params = $this->getState('params'); $orderby_sec = $params->get('orderby_sec'); // Join over the frontpage articles if required. if ($this->getState('filter.frontpage')) { if ($orderby_sec == 'front') { $query->join('INNER', '#__content_frontpage AS fp ON fp.content_id = a.id'); } else { $query->where('a.featured = 1'); } } elseif ($orderby_sec == 'front' || $this->getState('list.ordering') == 'fp.ordering') { $query->join('LEFT', '#__content_frontpage AS fp ON fp.content_id = a.id'); } // Join over the categories. $query->select('c.title AS category_title, c.path AS category_route, c.access AS category_access, c.alias AS category_alias')->join('LEFT', '#__categories AS c ON c.id = a.catid'); // Join over the users for the author and modified_by names. $query->select("CASE WHEN a.created_by_alias > ' ' THEN a.created_by_alias ELSE ua.name END AS author")->select("ua.email AS author_email")->join('LEFT', '#__users AS ua ON ua.id = a.created_by')->join('LEFT', '#__users AS uam ON uam.id = a.modified_by'); // Join over the categories to get parent category titles $query->select('parent.title as parent_title, parent.id as parent_id, parent.path as parent_route, parent.alias as parent_alias')->join('LEFT', '#__categories as parent ON parent.id = c.parent_id'); if (JPluginHelper::isEnabled('content', 'vote')) { // Join on voting table $query->select('COALESCE(NULLIF(ROUND(v.rating_sum / v.rating_count, 0), 0), 0) AS rating, COALESCE(NULLIF(v.rating_count, 0), 0) as rating_count')->join('LEFT', '#__content_rating AS v ON a.id = v.content_id'); } // Join to check for category published state in parent categories up the tree $query->select('c.published, CASE WHEN badcats.id is null THEN c.published ELSE 0 END AS parents_published'); $subquery = 'SELECT cat.id as id FROM #__categories AS cat JOIN #__categories AS parent '; $subquery .= 'ON cat.lft BETWEEN parent.lft AND parent.rgt '; $subquery .= 'WHERE parent.extension = ' . $db->quote('com_content'); if ($this->getState('filter.published') == 2) { // Find any up-path categories that are archived // If any up-path categories are archived, include all children in archived layout $subquery .= ' AND parent.published = 2 GROUP BY cat.id '; // Set effective state to archived if up-path category is archived $publishedWhere = 'CASE WHEN badcats.id is null THEN a.state ELSE 2 END'; } else { // Find any up-path categories that are not published // If all categories are published, badcats.id will be null, and we just use the article state $subquery .= ' AND parent.published != 1 GROUP BY cat.id '; // Select state to unpublished if up-path category is unpublished $publishedWhere = 'CASE WHEN badcats.id is null THEN a.state ELSE 0 END'; } $query->join('LEFT OUTER', '(' . $subquery . ') AS badcats ON badcats.id = c.id'); // Filter by access level. if ($access = $this->getState('filter.access')) { $groups = implode(',', $user->getAuthorisedViewLevels()); $query->where('a.access IN (' . $groups . ')')->where('c.access IN (' . $groups . ')'); } // Filter by published state $published = $this->getState('filter.published'); if (is_numeric($published)) { // Use article state if badcats.id is null, otherwise, force 0 for unpublished $query->where($publishedWhere . ' = ' . (int) $published); } elseif (is_array($published)) { $published = ArrayHelper::toInteger($published); $published = implode(',', $published); // Use article state if badcats.id is null, otherwise, force 0 for unpublished $query->where($publishedWhere . ' IN (' . $published . ')'); } // Filter by featured state $featured = $this->getState('filter.featured'); switch ($featured) { case 'hide': $query->where('a.featured = 0'); break; case 'only': $query->where('a.featured = 1'); break; case 'show': default: // Normally we do not discriminate // between featured/unfeatured items. break; } // Filter by a single or group of articles. $articleId = $this->getState('filter.article_id'); if (is_numeric($articleId)) { $type = $this->getState('filter.article_id.include', true) ? '= ' : '<> '; $query->where('a.id ' . $type . (int) $articleId); } elseif (is_array($articleId)) { $articleId = ArrayHelper::toInteger($articleId); $articleId = implode(',', $articleId); $type = $this->getState('filter.article_id.include', true) ? 'IN' : 'NOT IN'; $query->where('a.id ' . $type . ' (' . $articleId . ')'); } // Filter by a single or group of categories $categoryId = $this->getState('filter.category_id'); if (is_numeric($categoryId)) { $type = $this->getState('filter.category_id.include', true) ? '= ' : '<> '; // Add subcategory check $includeSubcategories = $this->getState('filter.subcategories', false); $categoryEquals = 'a.catid ' . $type . (int) $categoryId; if ($includeSubcategories) { $levels = (int) $this->getState('filter.max_category_levels', '1'); // Create a subquery for the subcategory list $subQuery = $db->getQuery(true)->select('sub.id')->from('#__categories as sub')->join('INNER', '#__categories as this ON sub.lft > this.lft AND sub.rgt < this.rgt')->where('this.id = ' . (int) $categoryId); if ($levels >= 0) { $subQuery->where('sub.level <= this.level + ' . $levels); } // Add the subquery to the main query $query->where('(' . $categoryEquals . ' OR a.catid IN (' . (string) $subQuery . '))'); } else { $query->where($categoryEquals); } } elseif (is_array($categoryId) && count($categoryId) > 0) { $categoryId = ArrayHelper::toInteger($categoryId); $categoryId = implode(',', $categoryId); if (!empty($categoryId)) { $type = $this->getState('filter.category_id.include', true) ? 'IN' : 'NOT IN'; $query->where('a.catid ' . $type . ' (' . $categoryId . ')'); } } // Filter by author $authorId = $this->getState('filter.author_id'); $authorWhere = ''; if (is_numeric($authorId)) { $type = $this->getState('filter.author_id.include', true) ? '= ' : '<> '; $authorWhere = 'a.created_by ' . $type . (int) $authorId; } elseif (is_array($authorId)) { $authorId = ArrayHelper::toInteger($authorId); $authorId = implode(',', $authorId); if ($authorId) { $type = $this->getState('filter.author_id.include', true) ? 'IN' : 'NOT IN'; $authorWhere = 'a.created_by ' . $type . ' (' . $authorId . ')'; } } // Filter by author alias $authorAlias = $this->getState('filter.author_alias'); $authorAliasWhere = ''; if (is_string($authorAlias)) { $type = $this->getState('filter.author_alias.include', true) ? '= ' : '<> '; $authorAliasWhere = 'a.created_by_alias ' . $type . $db->quote($authorAlias); } elseif (is_array($authorAlias)) { $first = current($authorAlias); if (!empty($first)) { foreach ($authorAlias as $key => $alias) { $authorAlias[$key] = $db->quote($alias); } $authorAlias = implode(',', $authorAlias); if ($authorAlias) { $type = $this->getState('filter.author_alias.include', true) ? 'IN' : 'NOT IN'; $authorAliasWhere = 'a.created_by_alias ' . $type . ' (' . $authorAlias . ')'; } } } if (!empty($authorWhere) && !empty($authorAliasWhere)) { $query->where('(' . $authorWhere . ' OR ' . $authorAliasWhere . ')'); } elseif (empty($authorWhere) && empty($authorAliasWhere)) { // If both are empty we don't want to add to the query } else { // One of these is empty, the other is not so we just add both $query->where($authorWhere . $authorAliasWhere); } // Define null and now dates $nullDate = $db->quote($db->getNullDate()); $nowDate = $db->quote(JFactory::getDate()->toSql()); // Filter by start and end dates. if (!$user->authorise('core.edit.state', 'com_content') && !$user->authorise('core.edit', 'com_content')) { $query->where('(a.publish_up = ' . $nullDate . ' OR a.publish_up <= ' . $nowDate . ')')->where('(a.publish_down = ' . $nullDate . ' OR a.publish_down >= ' . $nowDate . ')'); } // Filter by Date Range or Relative Date $dateFiltering = $this->getState('filter.date_filtering', 'off'); $dateField = $this->getState('filter.date_field', 'a.created'); switch ($dateFiltering) { case 'range': $startDateRange = $db->quote($this->getState('filter.start_date_range', $nullDate)); $endDateRange = $db->quote($this->getState('filter.end_date_range', $nullDate)); $query->where('(' . $dateField . ' >= ' . $startDateRange . ' AND ' . $dateField . ' <= ' . $endDateRange . ')'); break; case 'relative': $relativeDate = (int) $this->getState('filter.relative_date', 0); $query->where($dateField . ' >= DATE_SUB(' . $nowDate . ', INTERVAL ' . $relativeDate . ' DAY)'); break; case 'off': default: break; } // Process the filter for list views with user-entered filters if (is_object($params) && $params->get('filter_field') != 'hide' && ($filter = $this->getState('list.filter'))) { // Clean filter variable $filter = StringHelper::strtolower($filter); $hitsFilter = (int) $filter; $filter = $db->quote('%' . $db->escape($filter, true) . '%', false); switch ($params->get('filter_field')) { case 'author': $query->where('LOWER( CASE WHEN a.created_by_alias > ' . $db->quote(' ') . ' THEN a.created_by_alias ELSE ua.name END ) LIKE ' . $filter . ' '); break; case 'hits': $query->where('a.hits >= ' . $hitsFilter . ' '); break; case 'title': default: // Default to 'title' if parameter is not valid $query->where('LOWER( a.title ) LIKE ' . $filter); break; } } // Filter by language if ($this->getState('filter.language')) { $query->where('a.language in (' . $db->quote(JFactory::getLanguage()->getTag()) . ',' . $db->quote('*') . ')'); } // Filter by a single tag. $tagId = $this->getState('filter.tag'); if (!empty($tagId) && is_numeric($tagId)) { $query->where($db->quoteName('tagmap.tag_id') . ' = ' . (int) $tagId)->join('LEFT', $db->quoteName('#__contentitem_tag_map', 'tagmap') . ' ON ' . $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') . ' AND ' . $db->quoteName('tagmap.type_alias') . ' = ' . $db->quote('com_content.article')); } // Add the list ordering clause. $query->order($this->getState('list.ordering', 'a.ordering') . ' ' . $this->getState('list.direction', 'ASC')); return $query; }
/** * Import locations from TXT or XML file. * The TXT file comes from geodata.org * The XML file is generated by the current extension ( Crowdfunding ) * * @param string $file A path to file * @param array $options */ public function importLocations($file, array $options) { $ext = StringHelper::strtolower(JFile::getExt($file)); if (strcmp($ext, 'xml') === 0) { $this->importLocationsXml($file, $options); } else { // Import from file. $this->importLocationsTxt($file, $options); } }
* * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Joomla\String\StringHelper; // Get the mime type class. $mime = !empty($this->result->mime) ? 'mime-' . $this->result->mime : null; $show_description = $this->params->get('show_description', 1); if ($show_description) { // Calculate number of characters to display around the result $term_length = StringHelper::strlen($this->query->input); $desc_length = $this->params->get('description_length', 255); $pad_length = $term_length < $desc_length ? (int) floor(($desc_length - $term_length) / 2) : 0; // Find the position of the search term $pos = $term_length ? StringHelper::strpos(StringHelper::strtolower($this->result->description), StringHelper::strtolower($this->query->input)) : false; // Find a potential start point $start = $pos && $pos > $pad_length ? $pos - $pad_length : 0; // Find a space between $start and $pos, start right after it. $space = StringHelper::strpos($this->result->description, ' ', $start > 0 ? $start - 1 : 0); $start = $space && $space < $pos ? $space + 1 : $start; $description = JHtml::_('string.truncate', StringHelper::substr($this->result->description, $start), $desc_length, true); } $route = $this->result->route; // Get the route with highlighting information. if (!empty($this->query->highlight) && empty($this->result->mime) && $this->params->get('highlight_terms', 1) && JPluginHelper::isEnabled('system', 'highlight')) { $route .= '&highlight=' . base64_encode(json_encode($this->query->highlight)); } ?> <li>