/** * Remove a special page from the list * Formerly used to disable expensive or dangerous special pages. The * preferred method is now to add a SpecialPage_initList hook. * @deprecated since 1.18 * * @param $name String the page to remove */ static function removePage($name) { wfDeprecated(__METHOD__, '1.18'); unset(SpecialPageFactory::getList()->{$name}); }
protected function appendSpecialPageAliases($property) { global $wgContLang; $data = array(); $aliases = $wgContLang->getSpecialPageAliases(); foreach (SpecialPageFactory::getList() as $specialpage => $stuff) { if (isset($aliases[$specialpage])) { $arr = array('realname' => $specialpage, 'aliases' => $aliases[$specialpage]); $this->getResult()->setIndexedTagName($arr['aliases'], 'alias'); $data[] = $arr; } } $this->getResult()->setIndexedTagName($data, 'specialpage'); return $this->getResult()->addValue('query', $property, $data); }
/** * Prefix search special-case for Special: namespace. * * @param $search String: term * @param $limit Integer: max number of items to return * @return Array */ protected static function specialSearch($search, $limit) { global $wgContLang; # normalize searchKey, so aliases with spaces can be found - bug 25675 $search = str_replace(' ', '_', $search); $searchKey = $wgContLang->caseFold($search); // Unlike SpecialPage itself, we want the canonical forms of both // canonical and alias title forms... $keys = array(); foreach (SpecialPageFactory::getList() as $page => $class) { $keys[$wgContLang->caseFold($page)] = $page; } foreach ($wgContLang->getSpecialPageAliases() as $page => $aliases) { if (!array_key_exists($page, SpecialPageFactory::getList())) { # bug 20885 continue; } foreach ($aliases as $alias) { $keys[$wgContLang->caseFold($alias)] = $alias; } } ksort($keys); $srchres = array(); foreach ($keys as $pageKey => $page) { if ($searchKey === '' || strpos($pageKey, $searchKey) === 0) { wfSuppressWarnings(); // bug 27671: Don't use SpecialPage::getTitleFor() here because it // localizes its input leading to searches for e.g. Special:All // returning Spezial:MediaWiki-Systemnachrichten and returning // Spezial:Alle_Seiten twice when $wgLanguageCode == 'de' $srchres[] = Title::makeTitleSafe(NS_SPECIAL, $page)->getPrefixedText(); wfRestoreWarnings(); } if (count($srchres) >= $limit) { break; } } return $srchres; }
function getLinkSuggest() { global $wgRequest, $wgContLang, $wgCityId, $wgExternalDatawareDB, $wgContentNamespaces, $wgMemc, $wgLinkSuggestLimit; wfProfileIn(__METHOD__); $isMobile = F::app()->checkSkin('wikiamobile'); // trim passed query and replace spaces by underscores // - this is how MediaWiki store article titles in database $query = urldecode(trim($wgRequest->getText('query'))); $query = str_replace(' ', '_', $query); if ($isMobile) { $key = wfMemcKey(__METHOD__, md5($query . '_' . $wgRequest->getText('format') . $wgRequest->getText('nospecial', '')), 'WikiaMobile'); } else { $key = wfMemcKey(__METHOD__, md5($query . '_' . $wgRequest->getText('format') . $wgRequest->getText('nospecial', ''))); } if (strlen($query) < 3) { // enforce minimum character limit on server side $out = $wgRequest->getText('format') == 'json' ? json_encode(array('suggestions' => array(), 'redirects' => array())) : ''; } else { if (false && ($cached = $wgMemc->get($key))) { $out = $cached; } } if (isset($out)) { return linkSuggestAjaxResponse($out); } // Allow the calling-code to specify a namespace to search in (which at the moment, could be overridden by having prefixed text in the input field). // NOTE: This extension does parse titles to try to find things in other namespaces, but that actually doesn't work in practice because jQuery // Autocomplete will stop making requests after it finds 0 results. So if you start to type "Category" and there is no page beginning // with "Cate", it will not even make the call to LinkSuggest. $namespace = $wgRequest->getVal('ns'); // explode passed query by ':' to get namespace and article title $queryParts = explode(':', $query, 2); if (count($queryParts) == 2) { $query = $queryParts[1]; $namespaceName = $queryParts[0]; // try to get the index by canonical name first $namespace = MWNamespace::getCanonicalIndex(strtolower($namespaceName)); if ($namespace == null) { // if we failed, try looking through localized namespace names $namespace = array_search(ucfirst($namespaceName), $wgContLang->getNamespaces()); if (empty($namespace)) { // getting here means our "namespace" is not real and can only be part of the title $query = $namespaceName . ':' . $query; } } if ($namespace !== null && $query === '') { $out = $wgRequest->getText('format') == 'json' ? json_encode(array('suggestions' => array(), 'redirects' => array())) : ''; return linkSuggestAjaxResponse($out); } } // which namespaces to search in? if (empty($namespace)) { // search only within content namespaces (BugId:4625) - default behaviour $namespaces = $wgContentNamespaces; } else { // search only within a given namespace $namespaces = array($namespace); } $query = addslashes($query); $db = wfGetDB(DB_SLAVE, 'search'); $redirects = array(); $results = array(); $exactMatchRow = null; $queryLower = strtolower($query); $res = $db->select(array('querycache', 'page'), array('page_namespace', 'page_title', 'page_is_redirect'), array('qc_title = page_title', 'qc_namespace = page_namespace', 'page_is_redirect = 0', 'qc_type' => 'Mostlinked', "(qc_title LIKE '{$query}%' or LOWER(qc_title) LIKE '{$queryLower}%')", 'qc_namespace' => $namespaces), __METHOD__, array('ORDER BY' => 'qc_value DESC', 'LIMIT' => $wgLinkSuggestLimit)); linkSuggestFormatResults($db, $res, $query, $redirects, $results, $exactMatchRow); if (count($namespaces) > 0) { $commaJoinedNamespaces = count($namespaces) > 1 ? array_shift($namespaces) . ', ' . implode(', ', $namespaces) : $namespaces[0]; } $pageNamespaceClause = isset($commaJoinedNamespaces) ? 'page_namespace IN (' . $commaJoinedNamespaces . ') AND ' : ''; if (count($results) < $wgLinkSuggestLimit) { $sql = "SELECT page_len, page_id, page_title, rd_title, page_namespace, page_is_redirect\n\n\t\t\t\tFROM page IGNORE INDEX (`name_title`)\n\n\t\t\t\tLEFT JOIN redirect ON page_is_redirect = 1 AND page_id = rd_from\n\n\t\t\t\tWHERE {$pageNamespaceClause} (page_title LIKE '{$query}%' or LOWER(page_title) LIKE '{$queryLower}%')\n\n\t\t\t\tLIMIT " . $wgLinkSuggestLimit * 3; $res = $db->query($sql); linkSuggestFormatResults($db, $res, $query, $redirects, $results, $exactMatchRow); } if ($exactMatchRow !== null) { $row = $exactMatchRow; $titleFormatted = wfLinkSuggestFormatTitle($row->page_namespace, $row->page_title); if ($row->page_is_redirect == 0) { // remove any instances of original array's value $resultsFlipped = array_flip($results); unset($resultsFlipped[$titleFormatted]); $results = array_flip($resultsFlipped); array_unshift($results, $titleFormatted); $flippedRedirs = array_flip($redirects); if (isset($flippedRedirs[$titleFormatted])) { unset($redirects[$flippedRedirs[$titleFormatted]]); } } else { $redirTitleFormatted = wfLinkSuggestFormatTitle($row->page_namespace, $row->rd_title); // remove any instances of original array's value $resultsFlipped = array_flip($results); unset($resultsFlipped[$redirTitleFormatted]); $results = array_flip($resultsFlipped); array_unshift($results, $redirTitleFormatted); $redirects[$redirTitleFormatted] = $titleFormatted; } } $db->freeResult($res); if ($wgRequest->getText('nospecial', 0) != 1) { // bugid 29988: include special pages // (registered in SpecialPage::$mList, not in the DB like a normal page) if ($namespaces == array('-1') && strlen($query) > 0) { $specialPagesByAlpha = SpecialPageFactory::getList(); $specialPagesByAlpha = get_object_vars($specialPagesByAlpha); ksort($specialPagesByAlpha, SORT_STRING); array_walk($specialPagesByAlpha, function ($val, $key) use(&$results, $query) { if (strtolower(substr($key, 0, strlen($query))) === strtolower($query)) { $results[] = wfLinkSuggestFormatTitle('-1', $key); } }); } } $format = $wgRequest->getText('format'); if ($format == 'json') { $result_values = array_values($results); if ($isMobile) { $out = json_encode(array(array_splice($result_values, 0, 10), array_splice($redirects, -1, 1))); } else { $out = json_encode(array('query' => $wgRequest->getText('query'), 'suggestions' => $result_values, 'redirects' => $redirects)); } // legacy: LinkSuggest.js uses this } else { // Overwrite canonical title with redirect title for ($i = 0; $i < count($results); $i++) { if (isset($redirects[$results[$i]])) { $results[$i] = $redirects[$results[$i]]; } } $out = implode("\n", $results); } // 15 minutes times four (one hour, but easier to slice and dice) $wgMemc->set($key, $out, 4 * 900); wfProfileOut(__METHOD__); return linkSuggestAjaxResponse($out); }
/** * Remove a special page from the list * Formerly used to disable expensive or dangerous special pages. The * preferred method is now to add a SpecialPage_initList hook. * @deprecated since 1.18 * * @param $name String the page to remove */ static function removePage($name) { unset(SpecialPageFactory::getList()->{$name}); }
/** * Prefix search special-case for Special: namespace. * * @param string $search Term * @param int $limit Max number of items to return * @return array */ protected function specialSearch($search, $limit) { global $wgContLang; $searchParts = explode('/', $search, 2); $searchKey = $searchParts[0]; $subpageSearch = isset($searchParts[1]) ? $searchParts[1] : null; // Handle subpage search separately. if ($subpageSearch !== null) { // Try matching the full search string as a page name $specialTitle = Title::makeTitleSafe(NS_SPECIAL, $searchKey); if (!$specialTitle) { return array(); } $special = SpecialPageFactory::getPage($specialTitle->getText()); if ($special) { $subpages = $special->prefixSearchSubpages($subpageSearch, $limit); return array_map(function ($sub) use($specialTitle) { return $specialTitle->getSubpage($sub); }, $subpages); } else { return array(); } } # normalize searchKey, so aliases with spaces can be found - bug 25675 $searchKey = str_replace(' ', '_', $searchKey); $searchKey = $wgContLang->caseFold($searchKey); // Unlike SpecialPage itself, we want the canonical forms of both // canonical and alias title forms... $keys = array(); foreach (SpecialPageFactory::getList() as $page => $class) { $keys[$wgContLang->caseFold($page)] = $page; } foreach ($wgContLang->getSpecialPageAliases() as $page => $aliases) { if (!array_key_exists($page, SpecialPageFactory::getList())) { # bug 20885 continue; } foreach ($aliases as $alias) { $keys[$wgContLang->caseFold($alias)] = $alias; } } ksort($keys); $srchres = array(); foreach ($keys as $pageKey => $page) { if ($searchKey === '' || strpos($pageKey, $searchKey) === 0) { // bug 27671: Don't use SpecialPage::getTitleFor() here because it // localizes its input leading to searches for e.g. Special:All // returning Spezial:MediaWiki-Systemnachrichten and returning // Spezial:Alle_Seiten twice when $wgLanguageCode == 'de' $srchres[] = Title::makeTitleSafe(NS_SPECIAL, $page); } if (count($srchres) >= $limit) { break; } } return $srchres; }
/** * Get a list of suggested titles * * @param WebRequest $request * @return bool|Object|string * * @author Inez Korczyński <*****@*****.**> * @author Robert Elwell <*****@*****.**> */ static function getLinkSuggest(WebRequest $request) { global $wgContLang, $wgContentNamespaces, $wgMemc, $wgLinkSuggestLimit; $measurement = T::start(__FUNCTION__); wfProfileIn(__METHOD__); $isMobile = F::app()->checkSkin('wikiamobile'); // trim passed query and replace spaces by underscores // - this is how MediaWiki store article titles in database $query = urldecode(trim($request->getText('query'))); $query = str_replace(' ', '_', $query); if ($isMobile) { $key = wfMemcKey(__METHOD__, md5($query . '_' . $request->getText('format') . $request->getText('nospecial', '')), 'WikiaMobile'); } else { $key = wfMemcKey(__METHOD__, md5($query . '_' . $request->getText('format') . $request->getText('nospecial', ''))); } if (strlen($query) < 3) { // enforce minimum character limit on server side $out = self::getEmptyResponse($request->getText('format')); } else { if ($cached = $wgMemc->get($key)) { $out = $cached; } } if (isset($out)) { wfProfileOut(__METHOD__); return $out; } // Allow the calling-code to specify a namespace to search in (which at the moment, could be overridden by having prefixed text in the input field). // NOTE: This extension does parse titles to try to find things in other namespaces, but that actually doesn't work in practice because jQuery // Autocomplete will stop making requests after it finds 0 results. So if you start to type "Category" and there is no page beginning // with "Cate", it will not even make the call to LinkSuggest. $namespace = $request->getVal('ns'); // explode passed query by ':' to get namespace and article title $queryParts = explode(':', $query, 2); if (count($queryParts) == 2) { $query = $queryParts[1]; $namespaceName = $queryParts[0]; // try to get the index by canonical name first $namespace = MWNamespace::getCanonicalIndex(strtolower($namespaceName)); if ($namespace == null) { // if we failed, try looking through localized namespace names $namespace = array_search(ucfirst($namespaceName), $wgContLang->getNamespaces()); if (empty($namespace)) { // getting here means our "namespace" is not real and can only be part of the title $query = $namespaceName . ':' . $query; } } if ($namespace !== null && $query === '') { $out = self::getEmptyResponse($request->getText('format')); wfProfileOut(__METHOD__); return $out; } } // which namespaces to search in? if (empty($namespace)) { // search only within content namespaces (BugId:4625) - default behaviour $namespaces = $wgContentNamespaces; } else { // search only within a given namespace $namespaces = array($namespace); } //limit the result only to this namespace $namespaceFilter = $request->getVal('nsfilter'); if (strlen($namespaceFilter) > 0) { $namespaces = array($namespaceFilter); } if (!empty($namespaceFilter) && $namespace != $namespaceFilter) { $out = self::getEmptyResponse($request->getText('format')); wfProfileOut(__METHOD__); return $out; } $query = addslashes($query); $db = wfGetDB(DB_SLAVE, 'search'); $redirects = array(); $results = array(); $exactMatchRow = null; $queryLower = strtolower($query); $sql1Measurement = T::start([__FUNCTION__, "sql-1"]); $res = $db->select(array('querycache', 'page'), array('page_namespace', 'page_title', 'page_is_redirect'), array('qc_title = page_title', 'qc_namespace = page_namespace', 'page_is_redirect = 0', 'qc_type' => 'Mostlinked', "(qc_title LIKE '{$query}%' or LOWER(qc_title) LIKE '{$queryLower}%')", 'qc_namespace' => $namespaces), __METHOD__, array('ORDER BY' => 'qc_value DESC', 'LIMIT' => $wgLinkSuggestLimit)); self::formatResults($db, $res, $query, $redirects, $results, $exactMatchRow); $sql1Measurement->stop(); if (count($namespaces) > 0) { $commaJoinedNamespaces = count($namespaces) > 1 ? array_shift($namespaces) . ', ' . implode(', ', $namespaces) : $namespaces[0]; } $pageNamespaceClause = isset($commaJoinedNamespaces) ? 'page_namespace IN (' . $commaJoinedNamespaces . ') AND ' : ''; if (count($results) < $wgLinkSuggestLimit) { /** * @var string $pageTitlePrefilter this condition is able to use name_title index. It's added only for performance reasons. * It uses fact that page titles can't start with lowercase letter. */ $pageTitlePrefilter = ""; if (strlen($queryLower) >= 2) { $pageTitlePrefilter = "(\n\t\t\t\t\t\t\t( page_title " . $db->buildLike(strtoupper($queryLower[0]) . strtolower($queryLower[1]), $db->anyString()) . " ) OR\n\t\t\t\t\t\t\t( page_title " . $db->buildLike(strtoupper($queryLower[0]) . strtoupper($queryLower[1]), $db->anyString()) . " ) ) AND "; } else { if (strlen($queryLower) >= 1) { $pageTitlePrefilter = "( page_title " . $db->buildLike(strtoupper($queryLower[0]), $db->anyString()) . " ) AND "; } } // TODO: use $db->select helper method $sql = "SELECT page_len, page_id, page_title, rd_title, page_namespace, rd_namespace, page_is_redirect\n\t\t\t\t\t\tFROM page\n\t\t\t\t\t\tLEFT JOIN redirect ON page_is_redirect = 1 AND page_id = rd_from\n\t\t\t\t\t\tLEFT JOIN querycache ON qc_title = page_title AND qc_type = 'BrokenRedirects'\n\t\t\t\t\t\tWHERE {$pageTitlePrefilter} {$pageNamespaceClause} (LOWER(page_title) LIKE '{$queryLower}%')\n\t\t\t\t\t\t\tAND qc_type IS NULL\n\t\t\t\t\t\tLIMIT " . $wgLinkSuggestLimit * 3; // we fetch 3 times more results to leave out redirects to the same page $sql2Measurement = T::start([__FUNCTION__, "sql-2"]); $res = $db->query($sql, __METHOD__); self::formatResults($db, $res, $query, $redirects, $results, $exactMatchRow); $sql2Measurement->stop(); } if ($exactMatchRow !== null) { /* @var StdClass $exactMatchRow */ $row = $exactMatchRow; $titleFormatted = self::formatTitle($row->page_namespace, $row->page_title); if ($row->page_is_redirect == 0) { // remove any instances of original array's value $resultsFlipped = array_flip($results); unset($resultsFlipped[$titleFormatted]); $results = array_flip($resultsFlipped); array_unshift($results, $titleFormatted); $flippedRedirs = array_flip($redirects); if (isset($flippedRedirs[$titleFormatted])) { unset($redirects[$flippedRedirs[$titleFormatted]]); } } else { $redirTitleFormatted = self::formatTitle($row->page_namespace, $row->rd_title); // remove any instances of original array's value $resultsFlipped = array_flip($results); unset($resultsFlipped[$redirTitleFormatted]); $results = array_flip($resultsFlipped); array_unshift($results, $redirTitleFormatted); $redirects[$redirTitleFormatted] = $titleFormatted; } } $db->freeResult($res); if ($request->getText('nospecial', 0) != 1) { // bugid 29988: include special pages // (registered in SpecialPage::$mList, not in the DB like a normal page) if ($namespaces == array('-1') && strlen($query) > 0) { $specialPagesByAlpha = SpecialPageFactory::getList(); $specialPagesByAlpha = get_object_vars($specialPagesByAlpha); ksort($specialPagesByAlpha, SORT_STRING); array_walk($specialPagesByAlpha, function ($val, $key) use(&$results, $query) { if (strtolower(substr($key, 0, strlen($query))) === strtolower($query)) { $results[] = self::formatTitle('-1', $key); } }); } } // Overwrite canonical title with redirect title for all formats self::replaceResultIfRedirected($results, $redirects); $format = $request->getText('format'); if ($format == 'json') { $result_values = array_values($results); if ($isMobile) { $out = json_encode(array(array_splice($result_values, 0, 10), array_splice($redirects, -1, 1))); } else { $out = json_encode(array('query' => $request->getText('query'), 'suggestions' => $result_values, 'redirects' => $redirects)); } } elseif ($format == 'array') { $out = $results; } else { // legacy: LinkSuggest.js uses plain text $out = implode("\n", $results); } // 15 minutes times four (one hour, but easier to slice and dice) $wgMemc->set($key, $out, 4 * 900); wfProfileOut(__METHOD__); return $out; }