function getQueryInfo()
 {
     $dbr = wfGetDB(DB_SLAVE);
     $dMsgText = wfMsgForContent('disambiguationspage');
     $linkBatch = new LinkBatch();
     # If the text can be treated as a title, use it verbatim.
     # Otherwise, pull the titles from the links table
     $dp = Title::newFromText($dMsgText);
     if ($dp) {
         if ($dp->getNamespace() != NS_TEMPLATE) {
             # @todo FIXME: We assume the disambiguation message is a template but
             # the page can potentially be from another namespace :/
             wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
         }
         $linkBatch->addObj($dp);
     } else {
         # Get all the templates linked from the Mediawiki:Disambiguationspage
         $disPageObj = Title::makeTitleSafe(NS_MEDIAWIKI, 'disambiguationspage');
         $res = $dbr->select(array('pagelinks', 'page'), 'pl_title', array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE, 'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()), __METHOD__);
         foreach ($res as $row) {
             $linkBatch->addObj(Title::makeTitle(NS_TEMPLATE, $row->pl_title));
         }
     }
     $set = $linkBatch->constructSet('tl', $dbr);
     if ($set === false) {
         # We must always return a valid SQL query, but this way
         # the DB will always quickly return an empty result
         $set = 'FALSE';
         wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
     }
     // @todo FIXME: What are pagelinks and p2 doing here?
     return array('tables' => array('templatelinks', 'p1' => 'page', 'pagelinks', 'p2' => 'page'), 'fields' => array('p1.page_namespace AS namespace', 'p1.page_title AS title', 'pl_from AS value'), 'conds' => array($set, 'p1.page_id = tl_from', 'pl_namespace = p1.page_namespace', 'pl_title = p1.page_title', 'p2.page_id = pl_from', 'p2.page_namespace' => MWNamespace::getContentNamespaces()));
 }
Exemplo n.º 2
0
 function getSQL()
 {
     $dbr =& wfGetDB(DB_SLAVE);
     list($page, $pagelinks, $templatelinks) = $dbr->tableNamesN('page', 'pagelinks', 'templatelinks');
     $dMsgText = wfMsgForContent('disambiguationspage');
     $linkBatch = new LinkBatch();
     # If the text can be treated as a title, use it verbatim.
     # Otherwise, pull the titles from the links table
     $dp = Title::newFromText($dMsgText);
     if ($dp) {
         if ($dp->getNamespace() != NS_TEMPLATE) {
             # FIXME we assume the disambiguation message is a template but
             # the page can potentially be from another namespace :/
             wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
         }
         $linkBatch->addObj($dp);
     } else {
         # Get all the templates linked from the Mediawiki:Disambiguationspage
         $disPageObj = $this->getDisambiguationPageObj();
         $res = $dbr->select(array('pagelinks', 'page'), 'pl_title', array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE, 'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()), 'DisambiguationsPage::getSQL');
         while ($row = $dbr->fetchObject($res)) {
             $linkBatch->addObj(Title::makeTitle(NS_TEMPLATE, $row->pl_title));
         }
         $dbr->freeResult($res);
     }
     $set = $linkBatch->constructSet('lb.tl', $dbr);
     if ($set === false) {
         $set = 'FALSE';
         # We must always return a valid sql query, but this way DB will always quicly return an empty result
         wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
     }
     $sql = "SELECT 'Disambiguations' AS \"type\", pb.page_namespace AS namespace," . " pb.page_title AS title, la.pl_from AS value" . " FROM {$templatelinks} AS lb, {$page} AS pb, {$pagelinks} AS la, {$page} AS pa" . " WHERE {$set}" . ' AND pa.page_id = la.pl_from' . ' AND pa.page_namespace = ' . NS_MAIN . ' AND pb.page_id = lb.tl_from' . ' AND pb.page_namespace = la.pl_namespace' . ' AND pb.page_title = la.pl_title' . ' ORDER BY lb.tl_namespace, lb.tl_title';
     return $sql;
 }
	/**
	 * Return a clause with the list of disambiguation templates.
	 * This function was copied verbatim from specials/SpecialDisambiguations.php
	 */
	function disambiguation_templates( $dbr ) {
		$dMsgText = wfMsgForContent('disambiguationspage');

		$linkBatch = new LinkBatch;

		# If the text can be treated as a title, use it verbatim.
		# Otherwise, pull the titles from the links table
		$dp = Title::newFromText($dMsgText);
		if( $dp ) {
			if($dp->getNamespace() != NS_TEMPLATE) {
				# FIXME we assume the disambiguation message is a template but
				# the page can potentially be from another namespace :/
				wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
			}
			$linkBatch->addObj( $dp );
		} else {
			# Get all the templates linked from the Mediawiki:Disambiguationspage
			$disPageObj = Title::makeTitleSafe( NS_MEDIAWIKI, 'disambiguationspage' );
			$res = $dbr->select(
				array('pagelinks', 'page'),
				'pl_title',
				array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE,
					'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()),
				__METHOD__ );

			foreach ( $res as $row ) {
				$linkBatch->addObj( Title::makeTitle( NS_TEMPLATE, $row->pl_title ));
			}
		}
		return $linkBatch->constructSet( 'tl', $dbr );
	}
 protected function reallyGetTitleMtimes(ResourceLoaderContext $context)
 {
     wfProfileIn(__METHOD__);
     $dbr = $this->getDB();
     if (!$dbr) {
         // We're dealing with a subclass that doesn't have a DB
         wfProfileOut(__METHOD__);
         return array();
     }
     $mtimes = array();
     $local = array();
     $byWiki = array();
     $pages = $this->getPages($context);
     foreach ($pages as $titleText => $options) {
         $title = $this->createTitle($titleText, $options);
         if ($title instanceof GlobalTitle) {
             $byWiki[$title->getCityId()][] = array($title, $titleText, $options);
         } else {
             $local[] = array($title, $titleText, $options);
         }
     }
     if (!empty($local)) {
         $batch = new LinkBatch();
         foreach ($local as $page) {
             list($title, $titleText, $options) = $page;
             $batch->addObj($title);
         }
         if (!$batch->isEmpty()) {
             $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_touched'), $batch->constructSet('page', $dbr), __METHOD__);
             foreach ($res as $row) {
                 $title = Title::makeTitle($row->page_namespace, $row->page_title);
                 $mtimes[$title->getPrefixedDBkey()] = wfTimestamp(TS_UNIX, $row->page_touched);
             }
         }
     }
     foreach ($byWiki as $cityId => $pages) {
         // $pages[0][0] has to be GlobalTitle
         $dbName = $pages[0][0]->getDatabaseName();
         $dbr = wfGetDB(DB_SLAVE, array(), $dbName);
         $pagesData = array();
         foreach ($pages as $page) {
             list($title, $titleText, $options) = $page;
             /** @var $title GlobalTitle */
             $pagesData[$title->getNamespace()][$title->getDBkey()] = true;
         }
         $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_touched'), $dbr->makeWhereFrom2d($pagesData, 'page_namespace', 'page_title'), __METHOD__);
         foreach ($res as $row) {
             $title = GlobalTitle::newFromTextCached($row->page_title, $row->page_namespace, $cityId);
             $mtimes[$dbName . '::' . $title->getPrefixedDBkey()] = wfTimestamp(TS_UNIX, $row->page_touched);
         }
     }
     wfProfileOut(__METHOD__);
     return $mtimes;
 }
 /**
  * Get the modification times of all titles that would be loaded for
  * a given context.
  * @param $context ResourceLoaderContext: Context object
  * @return array( prefixed DB key => UNIX timestamp ), nonexistent titles are dropped
  */
 protected function getTitleMtimes(ResourceLoaderContext $context)
 {
     $dbr = $this->getDB();
     if (!$dbr) {
         // We're dealing with a subclass that doesn't have a DB
         return array();
     }
     $hash = $context->getHash();
     if (isset($this->titleMtimes[$hash])) {
         return $this->titleMtimes[$hash];
     }
     $this->titleMtimes[$hash] = array();
     $batch = new LinkBatch();
     foreach ($this->getPages($context) as $titleText => $options) {
         $batch->addObj(Title::newFromText($titleText));
     }
     if (!$batch->isEmpty()) {
         $res = $dbr->select('page', array('page_namespace', 'page_title', 'page_touched'), $batch->constructSet('page', $dbr), __METHOD__);
         foreach ($res as $row) {
             $title = Title::makeTitle($row->page_namespace, $row->page_title);
             $this->titleMtimes[$hash][$title->getPrefixedDBkey()] = wfTimestamp(TS_UNIX, $row->page_touched);
         }
     }
     return $this->titleMtimes[$hash];
 }
Exemplo n.º 6
0
 /**
  * Get information about watched status and put it in $this->watched
  */
 private function getWatchedInfo()
 {
     global $wgUser;
     if ($wgUser->isAnon() || count($this->titles) == 0) {
         return;
     }
     $this->watched = array();
     $db = $this->getDB();
     $lb = new LinkBatch($this->titles);
     $this->resetQueryParams();
     $this->addTables(array('page', 'watchlist'));
     $this->addFields(array('page_title', 'page_namespace'));
     $this->addWhere(array($lb->constructSet('page', $db), 'wl_namespace=page_namespace', 'wl_title=page_title', 'wl_user' => $wgUser->getID()));
     $res = $this->select(__METHOD__);
     while ($row = $db->fetchObject($res)) {
         $this->watched[$row->page_namespace][$row->page_title] = true;
     }
 }
Exemplo n.º 7
0
 public function execute()
 {
     global $wgUser;
     // Before doing anything at all, let's check permissions
     if (!$wgUser->isAllowed('deletedhistory')) {
         $this->dieUsage('You don\'t have permission to view deleted revision information', 'permissiondenied');
     }
     $db = $this->getDB();
     $params = $this->extractRequestParams(false);
     $prop = array_flip($params['prop']);
     $fld_revid = isset($prop['revid']);
     $fld_user = isset($prop['user']);
     $fld_comment = isset($prop['comment']);
     $fld_minor = isset($prop['minor']);
     $fld_len = isset($prop['len']);
     $fld_content = isset($prop['content']);
     $fld_token = isset($prop['token']);
     $result = $this->getResult();
     $pageSet = $this->getPageSet();
     $titles = $pageSet->getTitles();
     $data = array();
     $this->addTables('archive');
     $this->addFields(array('ar_title', 'ar_namespace', 'ar_timestamp'));
     if ($fld_revid) {
         $this->addFields('ar_rev_id');
     }
     if ($fld_user) {
         $this->addFields('ar_user_text');
     }
     if ($fld_comment) {
         $this->addFields('ar_comment');
     }
     if ($fld_minor) {
         $this->addFields('ar_minor_edit');
     }
     if ($fld_len) {
         $this->addFields('ar_len');
     }
     if ($fld_content) {
         $this->addTables('text');
         $this->addFields(array('ar_text', 'ar_text_id', 'old_text', 'old_flags'));
         $this->addWhere('ar_text_id = old_id');
         // This also means stricter restrictions
         if (!$wgUser->isAllowed('undelete')) {
             $this->dieUsage('You don\'t have permission to view deleted revision content', 'permissiondenied');
         }
     }
     // Check limits
     $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1;
     $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2;
     if ($limit == 'max') {
         $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
         $this->getResult()->addValue('limits', 'limit', $limit);
     }
     $this->validateLimit('limit', $params['limit'], 1, $userMax, $botMax);
     if ($fld_token) {
         // Undelete tokens are identical for all pages, so we cache one here
         $token = $wgUser->editToken();
     }
     // We need a custom WHERE clause that matches all titles.
     if (count($titles) > 0) {
         $lb = new LinkBatch($titles);
         $where = $lb->constructSet('ar', $db);
         $this->addWhere($where);
     }
     $this->addOption('LIMIT', $params['limit'] + 1);
     $this->addWhereRange('ar_timestamp', $params['dir'], $params['start'], $params['end']);
     if (isset($params['namespace'])) {
         $this->addWhereFld('ar_namespace', $params['namespace']);
     }
     $res = $this->select(__METHOD__);
     $pages = array();
     $count = 0;
     // First populate the $pages array
     while ($row = $db->fetchObject($res)) {
         if ($count++ == $params['limit']) {
             // We've had enough
             $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp));
             break;
         }
         $rev = array();
         $rev['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ar_timestamp);
         if ($fld_revid) {
             $rev['revid'] = $row->ar_rev_id;
         }
         if ($fld_user) {
             $rev['user'] = $row->ar_user_text;
         }
         if ($fld_comment) {
             $rev['comment'] = $row->ar_comment;
         }
         if ($fld_minor) {
             if ($row->ar_minor_edit == 1) {
                 $rev['minor'] = '';
             }
         }
         if ($fld_len) {
             $rev['len'] = $row->ar_len;
         }
         if ($fld_content) {
             ApiResult::setContent($rev, Revision::getRevisionText($row));
         }
         $t = Title::makeTitle($row->ar_namespace, $row->ar_title);
         if (!isset($pages[$t->getPrefixedText()])) {
             $pages[$t->getPrefixedText()] = array('title' => $t->getPrefixedText(), 'ns' => intval($row->ar_namespace), 'revisions' => array($rev));
             if ($fld_token) {
                 $pages[$t->getPrefixedText()]['token'] = $token;
             }
         } else {
             $pages[$t->getPrefixedText()]['revisions'][] = $rev;
         }
     }
     $db->freeResult($res);
     // We don't want entire pagenames as keys, so let's make this array indexed
     foreach ($pages as $page) {
         $result->setIndexedTagName($page['revisions'], 'rev');
         $data[] = $page;
     }
     $result->setIndexedTagName($data, 'page');
     $result->addValue('query', $this->getModuleName(), $data);
 }
Exemplo n.º 8
0
 protected function run(ApiPageSet $resultPageSet = null)
 {
     $user = $this->getUser();
     // Before doing anything at all, let's check permissions
     if (!$user->isAllowed('deletedhistory')) {
         $this->dieUsage('You don\'t have permission to view deleted revision information', 'permissiondenied');
     }
     $result = $this->getResult();
     $pageSet = $this->getPageSet();
     $pageMap = $pageSet->getGoodAndMissingTitlesByNamespace();
     $pageCount = count($pageSet->getGoodAndMissingTitles());
     $revCount = $pageSet->getRevisionCount();
     if ($revCount === 0 && $pageCount === 0) {
         // Nothing to do
         return;
     }
     if ($revCount !== 0 && count($pageSet->getDeletedRevisionIDs()) === 0) {
         // Nothing to do, revisions were supplied but none are deleted
         return;
     }
     $params = $this->extractRequestParams(false);
     $db = $this->getDB();
     if (!is_null($params['user']) && !is_null($params['excludeuser'])) {
         $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
     }
     $this->addTables('archive');
     if ($resultPageSet === null) {
         $this->parseParameters($params);
         $this->addFields(Revision::selectArchiveFields());
         $this->addFields(array('ar_title', 'ar_namespace'));
     } else {
         $this->limit = $this->getParameter('limit') ?: 10;
         $this->addFields(array('ar_title', 'ar_namespace', 'ar_timestamp', 'ar_rev_id', 'ar_id'));
     }
     if ($this->fld_tags) {
         $this->addTables('tag_summary');
         $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('ar_rev_id=ts_rev_id'))));
         $this->addFields('ts_tags');
     }
     if (!is_null($params['tag'])) {
         $this->addTables('change_tag');
         $this->addJoinConds(array('change_tag' => array('INNER JOIN', array('ar_rev_id=ct_rev_id'))));
         $this->addWhereFld('ct_tag', $params['tag']);
     }
     if ($this->fetchContent) {
         // Modern MediaWiki has the content for deleted revs in the 'text'
         // table using fields old_text and old_flags. But revisions deleted
         // pre-1.5 store the content in the 'archive' table directly using
         // fields ar_text and ar_flags, and no corresponding 'text' row. So
         // we have to LEFT JOIN and fetch all four fields.
         $this->addTables('text');
         $this->addJoinConds(array('text' => array('LEFT JOIN', array('ar_text_id=old_id'))));
         $this->addFields(array('ar_text', 'ar_flags', 'old_text', 'old_flags'));
         // This also means stricter restrictions
         if (!$user->isAllowedAny('undelete', 'deletedtext')) {
             $this->dieUsage('You don\'t have permission to view deleted revision content', 'permissiondenied');
         }
     }
     $dir = $params['dir'];
     if ($revCount !== 0) {
         $this->addWhere(array('ar_rev_id' => array_keys($pageSet->getDeletedRevisionIDs())));
     } else {
         // We need a custom WHERE clause that matches all titles.
         $lb = new LinkBatch($pageSet->getGoodAndMissingTitles());
         $where = $lb->constructSet('ar', $db);
         $this->addWhere($where);
     }
     if (!is_null($params['user'])) {
         $this->addWhereFld('ar_user_text', $params['user']);
     } elseif (!is_null($params['excludeuser'])) {
         $this->addWhere('ar_user_text != ' . $db->addQuotes($params['excludeuser']));
     }
     if (!is_null($params['user']) || !is_null($params['excludeuser'])) {
         // Paranoia: avoid brute force searches (bug 17342)
         // (shouldn't be able to get here without 'deletedhistory', but
         // check it again just in case)
         if (!$user->isAllowed('deletedhistory')) {
             $bitmask = Revision::DELETED_USER;
         } elseif (!$user->isAllowedAny('suppressrevision', 'viewsuppressed')) {
             $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
         } else {
             $bitmask = 0;
         }
         if ($bitmask) {
             $this->addWhere($db->bitAnd('ar_deleted', $bitmask) . " != {$bitmask}");
         }
     }
     if (!is_null($params['continue'])) {
         $cont = explode('|', $params['continue']);
         $op = $dir == 'newer' ? '>' : '<';
         if ($revCount !== 0) {
             $this->dieContinueUsageIf(count($cont) != 2);
             $rev = intval($cont[0]);
             $this->dieContinueUsageIf(strval($rev) !== $cont[0]);
             $ar_id = (int) $cont[1];
             $this->dieContinueUsageIf(strval($ar_id) !== $cont[1]);
             $this->addWhere("ar_rev_id {$op} {$rev} OR " . "(ar_rev_id = {$rev} AND " . "ar_id {$op}= {$ar_id})");
         } else {
             $this->dieContinueUsageIf(count($cont) != 4);
             $ns = intval($cont[0]);
             $this->dieContinueUsageIf(strval($ns) !== $cont[0]);
             $title = $db->addQuotes($cont[1]);
             $ts = $db->addQuotes($db->timestamp($cont[2]));
             $ar_id = (int) $cont[3];
             $this->dieContinueUsageIf(strval($ar_id) !== $cont[3]);
             $this->addWhere("ar_namespace {$op} {$ns} OR " . "(ar_namespace = {$ns} AND " . "(ar_title {$op} {$title} OR " . "(ar_title = {$title} AND " . "(ar_timestamp {$op} {$ts} OR " . "(ar_timestamp = {$ts} AND " . "ar_id {$op}= {$ar_id})))))");
         }
     }
     $this->addOption('LIMIT', $this->limit + 1);
     if ($revCount !== 0) {
         // Sort by ar_rev_id when querying by ar_rev_id
         $this->addWhereRange('ar_rev_id', $dir, null, null);
     } else {
         // Sort by ns and title in the same order as timestamp for efficiency
         // But only when not already unique in the query
         if (count($pageMap) > 1) {
             $this->addWhereRange('ar_namespace', $dir, null, null);
         }
         $oneTitle = key(reset($pageMap));
         foreach ($pageMap as $pages) {
             if (count($pages) > 1 || key($pages) !== $oneTitle) {
                 $this->addWhereRange('ar_title', $dir, null, null);
                 break;
             }
         }
         $this->addTimestampWhereRange('ar_timestamp', $dir, $params['start'], $params['end']);
     }
     // Include in ORDER BY for uniqueness
     $this->addWhereRange('ar_id', $dir, null, null);
     $res = $this->select(__METHOD__);
     $count = 0;
     $generated = array();
     foreach ($res as $row) {
         if (++$count > $this->limit) {
             // We've had enough
             $this->setContinueEnumParameter('continue', $revCount ? "{$row->ar_rev_id}|{$row->ar_id}" : "{$row->ar_namespace}|{$row->ar_title}|{$row->ar_timestamp}|{$row->ar_id}");
             break;
         }
         if ($resultPageSet !== null) {
             $generated[] = $row->ar_rev_id;
         } else {
             if (!isset($pageMap[$row->ar_namespace][$row->ar_title])) {
                 // Was it converted?
                 $title = Title::makeTitle($row->ar_namespace, $row->ar_title);
                 $converted = $pageSet->getConvertedTitles();
                 if ($title && isset($converted[$title->getPrefixedText()])) {
                     $title = Title::newFromText($converted[$title->getPrefixedText()]);
                     if ($title && isset($pageMap[$title->getNamespace()][$title->getDBkey()])) {
                         $pageMap[$row->ar_namespace][$row->ar_title] = $pageMap[$title->getNamespace()][$title->getDBkey()];
                     }
                 }
             }
             if (!isset($pageMap[$row->ar_namespace][$row->ar_title])) {
                 ApiBase::dieDebug("Found row in archive (ar_id={$row->ar_id}) that didn't " . "get processed by ApiPageSet");
             }
             $fit = $this->addPageSubItem($pageMap[$row->ar_namespace][$row->ar_title], $this->extractRevisionInfo(Revision::newFromArchiveRow($row), $row), 'rev');
             if (!$fit) {
                 $this->setContinueEnumParameter('continue', $revCount ? "{$row->ar_rev_id}|{$row->ar_id}" : "{$row->ar_namespace}|{$row->ar_title}|{$row->ar_timestamp}|{$row->ar_id}");
                 break;
             }
         }
     }
     if ($resultPageSet !== null) {
         $resultPageSet->populateFromRevisionIDs($generated);
     }
 }
Exemplo n.º 9
0
 /**
  * Add an array of categories, with names in the keys
  */
 public function addCategoryLinks($categories)
 {
     global $wgUser, $wgContLang;
     if (!is_array($categories) || count($categories) == 0) {
         return;
     }
     # Add the links to a LinkBatch
     $arr = array(NS_CATEGORY => $categories);
     $lb = new LinkBatch();
     $lb->setArray($arr);
     # Fetch existence plus the hiddencat property
     $dbr = wfGetDB(DB_SLAVE);
     $pageTable = $dbr->tableName('page');
     $where = $lb->constructSet('page', $dbr);
     $propsTable = $dbr->tableName('page_props');
     $sql = "SELECT page_id, page_namespace, page_title, page_len, page_is_redirect, pp_value\n\t\t\tFROM {$pageTable} LEFT JOIN {$propsTable} ON pp_propname='hiddencat' AND pp_page=page_id WHERE {$where}";
     $res = $dbr->query($sql, __METHOD__);
     # Add the results to the link cache
     $lb->addResultToCache(LinkCache::singleton(), $res);
     # Set all the values to 'normal'. This can be done with array_fill_keys in PHP 5.2.0+
     $categories = array_combine(array_keys($categories), array_fill(0, count($categories), 'normal'));
     # Mark hidden categories
     foreach ($res as $row) {
         if (isset($row->pp_value)) {
             $categories[$row->page_title] = 'hidden';
         }
     }
     # Add the remaining categories to the skin
     if (wfRunHooks('OutputPageMakeCategoryLinks', array(&$this, $categories, &$this->mCategoryLinks))) {
         $sk = $wgUser->getSkin();
         foreach ($categories as $category => $type) {
             $title = Title::makeTitleSafe(NS_CATEGORY, $category);
             $text = $wgContLang->convertHtml($title->getText());
             $this->mCategoryLinks[$type][] = $sk->makeLinkObj($title, $text);
         }
     }
 }
Exemplo n.º 10
0
 /**
  * Replace <!--LINK--> link placeholders with actual links, in the buffer
  * Placeholders created in Skin::makeLinkObj()
  * Returns an array of link CSS classes, indexed by PDBK.
  * $options is a bit field, RLH_FOR_UPDATE to select for update
  */
 function replaceLinkHolders(&$text, $options = 0)
 {
     global $wgUser;
     global $wgContLang;
     $fname = 'Parser::replaceLinkHolders';
     wfProfileIn($fname);
     $pdbks = array();
     $colours = array();
     $linkcolour_ids = array();
     $sk = $this->mOptions->getSkin();
     $linkCache =& LinkCache::singleton();
     if (!empty($this->mLinkHolders['namespaces'])) {
         wfProfileIn($fname . '-check');
         $dbr = wfGetDB(DB_SLAVE);
         $page = $dbr->tableName('page');
         $threshold = $wgUser->getOption('stubthreshold');
         # Sort by namespace
         asort($this->mLinkHolders['namespaces']);
         # Generate query
         $query = false;
         $current = null;
         foreach ($this->mLinkHolders['namespaces'] as $key => $ns) {
             # Make title object
             $title = $this->mLinkHolders['titles'][$key];
             # Skip invalid entries.
             # Result will be ugly, but prevents crash.
             if (is_null($title)) {
                 continue;
             }
             $pdbk = $pdbks[$key] = $title->getPrefixedDBkey();
             # Check if it's a static known link, e.g. interwiki
             if ($title->isAlwaysKnown()) {
                 $colours[$pdbk] = '';
             } elseif (($id = $linkCache->getGoodLinkID($pdbk)) != 0) {
                 $colours[$pdbk] = '';
                 $this->mOutput->addLink($title, $id);
             } elseif ($linkCache->isBadLink($pdbk)) {
                 $colours[$pdbk] = 'new';
             } elseif ($title->getNamespace() == NS_SPECIAL && !SpecialPage::exists($pdbk)) {
                 $colours[$pdbk] = 'new';
             } else {
                 # Not in the link cache, add it to the query
                 if (!isset($current)) {
                     $current = $ns;
                     $query = "SELECT page_id, page_namespace, page_title, page_is_redirect";
                     if ($threshold > 0) {
                         $query .= ', page_len';
                     }
                     $query .= " FROM {$page} WHERE (page_namespace={$ns} AND page_title IN(";
                 } elseif ($current != $ns) {
                     $current = $ns;
                     $query .= ")) OR (page_namespace={$ns} AND page_title IN(";
                 } else {
                     $query .= ', ';
                 }
                 $query .= $dbr->addQuotes($this->mLinkHolders['dbkeys'][$key]);
             }
         }
         if ($query) {
             $query .= '))';
             if ($options & RLH_FOR_UPDATE) {
                 $query .= ' FOR UPDATE';
             }
             $res = $dbr->query($query, $fname);
             # Fetch data and form into an associative array
             # non-existent = broken
             while ($s = $dbr->fetchObject($res)) {
                 $title = Title::makeTitle($s->page_namespace, $s->page_title);
                 $pdbk = $title->getPrefixedDBkey();
                 $linkCache->addGoodLinkObj($s->page_id, $title);
                 $this->mOutput->addLink($title, $s->page_id);
                 $colours[$pdbk] = $sk->getLinkColour($s, $threshold);
                 //add id to the extension todolist
                 $linkcolour_ids[$s->page_id] = $pdbk;
             }
             //pass an array of page_ids to an extension
             wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours));
         }
         wfProfileOut($fname . '-check');
         # Do a second query for different language variants of links and categories
         if ($wgContLang->hasVariants()) {
             $linkBatch = new LinkBatch();
             $variantMap = array();
             // maps $pdbkey_Variant => $keys (of link holders)
             $categoryMap = array();
             // maps $category_variant => $category (dbkeys)
             $varCategories = array();
             // category replacements oldDBkey => newDBkey
             $categories = $this->mOutput->getCategoryLinks();
             // Add variants of links to link batch
             foreach ($this->mLinkHolders['namespaces'] as $key => $ns) {
                 $title = $this->mLinkHolders['titles'][$key];
                 if (is_null($title)) {
                     continue;
                 }
                 $pdbk = $title->getPrefixedDBkey();
                 $titleText = $title->getText();
                 // generate all variants of the link title text
                 $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText);
                 // if link was not found (in first query), add all variants to query
                 if (!isset($colours[$pdbk])) {
                     foreach ($allTextVariants as $textVariant) {
                         if ($textVariant != $titleText) {
                             $variantTitle = Title::makeTitle($ns, $textVariant);
                             if (is_null($variantTitle)) {
                                 continue;
                             }
                             $linkBatch->addObj($variantTitle);
                             $variantMap[$variantTitle->getPrefixedDBkey()][] = $key;
                         }
                     }
                 }
             }
             // process categories, check if a category exists in some variant
             foreach ($categories as $category) {
                 $variants = $wgContLang->convertLinkToAllVariants($category);
                 foreach ($variants as $variant) {
                     if ($variant != $category) {
                         $variantTitle = Title::newFromDBkey(Title::makeName(NS_CATEGORY, $variant));
                         if (is_null($variantTitle)) {
                             continue;
                         }
                         $linkBatch->addObj($variantTitle);
                         $categoryMap[$variant] = $category;
                     }
                 }
             }
             if (!$linkBatch->isEmpty()) {
                 // construct query
                 $titleClause = $linkBatch->constructSet('page', $dbr);
                 $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect";
                 if ($threshold > 0) {
                     $variantQuery .= ', page_len';
                 }
                 $variantQuery .= " FROM {$page} WHERE {$titleClause}";
                 if ($options & RLH_FOR_UPDATE) {
                     $variantQuery .= ' FOR UPDATE';
                 }
                 $varRes = $dbr->query($variantQuery, $fname);
                 // for each found variants, figure out link holders and replace
                 while ($s = $dbr->fetchObject($varRes)) {
                     $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title);
                     $varPdbk = $variantTitle->getPrefixedDBkey();
                     $vardbk = $variantTitle->getDBkey();
                     $holderKeys = array();
                     if (isset($variantMap[$varPdbk])) {
                         $holderKeys = $variantMap[$varPdbk];
                         $linkCache->addGoodLinkObj($s->page_id, $variantTitle);
                         $this->mOutput->addLink($variantTitle, $s->page_id);
                     }
                     // loop over link holders
                     foreach ($holderKeys as $key) {
                         $title = $this->mLinkHolders['titles'][$key];
                         if (is_null($title)) {
                             continue;
                         }
                         $pdbk = $title->getPrefixedDBkey();
                         if (!isset($colours[$pdbk])) {
                             // found link in some of the variants, replace the link holder data
                             $this->mLinkHolders['titles'][$key] = $variantTitle;
                             $this->mLinkHolders['dbkeys'][$key] = $variantTitle->getDBkey();
                             // set pdbk and colour
                             $pdbks[$key] = $varPdbk;
                             $colours[$varPdbk] = $sk->getLinkColour($s, $threshold);
                             $linkcolour_ids[$s->page_id] = $pdbk;
                         }
                         wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours));
                     }
                     // check if the object is a variant of a category
                     if (isset($categoryMap[$vardbk])) {
                         $oldkey = $categoryMap[$vardbk];
                         if ($oldkey != $vardbk) {
                             $varCategories[$oldkey] = $vardbk;
                         }
                     }
                 }
                 // rebuild the categories in original order (if there are replacements)
                 if (count($varCategories) > 0) {
                     $newCats = array();
                     $originalCats = $this->mOutput->getCategories();
                     foreach ($originalCats as $cat => $sortkey) {
                         // make the replacement
                         if (array_key_exists($cat, $varCategories)) {
                             $newCats[$varCategories[$cat]] = $sortkey;
                         } else {
                             $newCats[$cat] = $sortkey;
                         }
                     }
                     $this->mOutput->setCategoryLinks($newCats);
                 }
             }
         }
         # Construct search and replace arrays
         wfProfileIn($fname . '-construct');
         $replacePairs = array();
         foreach ($this->mLinkHolders['namespaces'] as $key => $ns) {
             $pdbk = $pdbks[$key];
             $searchkey = "<!--LINK {$key}-->";
             $title = $this->mLinkHolders['titles'][$key];
             if (!isset($colours[$pdbk]) || $colours[$pdbk] == 'new') {
                 $linkCache->addBadLinkObj($title);
                 $colours[$pdbk] = 'new';
                 $this->mOutput->addLink($title, 0);
                 $replacePairs[$searchkey] = $sk->makeBrokenLinkObj($title, $this->mLinkHolders['texts'][$key], $this->mLinkHolders['queries'][$key]);
             } else {
                 $replacePairs[$searchkey] = $sk->makeColouredLinkObj($title, $colours[$pdbk], $this->mLinkHolders['texts'][$key], $this->mLinkHolders['queries'][$key]);
             }
         }
         $replacer = new HashtableReplacer($replacePairs, 1);
         wfProfileOut($fname . '-construct');
         # Do the thing
         wfProfileIn($fname . '-replace');
         $text = preg_replace_callback('/(<!--LINK .*?-->)/', $replacer->cb(), $text);
         wfProfileOut($fname . '-replace');
     }
     # Now process interwiki link holders
     # This is quite a bit simpler than internal links
     if (!empty($this->mInterwikiLinkHolders['texts'])) {
         wfProfileIn($fname . '-interwiki');
         # Make interwiki link HTML
         $replacePairs = array();
         foreach ($this->mInterwikiLinkHolders['texts'] as $key => $link) {
             $title = $this->mInterwikiLinkHolders['titles'][$key];
             $replacePairs[$key] = $sk->makeLinkObj($title, $link);
         }
         $replacer = new HashtableReplacer($replacePairs, 1);
         $text = preg_replace_callback('/<!--IWLINK (.*?)-->/', $replacer->cb(), $text);
         wfProfileOut($fname . '-interwiki');
     }
     wfProfileOut($fname);
     return $colours;
 }
Exemplo n.º 11
0
 /**
  * Get the count of watchers who have visited recent edits and put it in
  * $this->visitingwatchers
  *
  * Based on InfoAction::pageCounts
  */
 private function getVisitingWatcherInfo()
 {
     $config = $this->getConfig();
     $user = $this->getUser();
     $db = $this->getDB();
     $canUnwatchedpages = $user->isAllowed('unwatchedpages');
     $unwatchedPageThreshold = $this->getConfig()->get('UnwatchedPageThreshold');
     if (!$canUnwatchedpages && !is_int($unwatchedPageThreshold)) {
         return;
     }
     $this->showZeroWatchers = $canUnwatchedpages;
     $titlesWithThresholds = [];
     if ($this->titles) {
         $lb = new LinkBatch($this->titles);
         // Fetch last edit timestamps for pages
         $this->resetQueryParams();
         $this->addTables(['page', 'revision']);
         $this->addFields(['page_namespace', 'page_title', 'rev_timestamp']);
         $this->addWhere(['page_latest = rev_id', $lb->constructSet('page', $db)]);
         $this->addOption('GROUP BY', ['page_namespace', 'page_title']);
         $timestampRes = $this->select(__METHOD__);
         $age = $config->get('WatchersMaxAge');
         $timestamps = [];
         foreach ($timestampRes as $row) {
             $revTimestamp = wfTimestamp(TS_UNIX, (int) $row->rev_timestamp);
             $timestamps[$row->page_namespace][$row->page_title] = $revTimestamp - $age;
         }
         $titlesWithThresholds = array_map(function (LinkTarget $target) use($timestamps) {
             return [$target, $timestamps[$target->getNamespace()][$target->getDBkey()]];
         }, $this->titles);
     }
     if ($this->missing) {
         $titlesWithThresholds = array_merge($titlesWithThresholds, array_map(function (LinkTarget $target) {
             return [$target, null];
         }, $this->missing));
     }
     $store = MediaWikiServices::getInstance()->getWatchedItemStore();
     $this->visitingwatchers = $store->countVisitingWatchersMultiple($titlesWithThresholds, !$canUnwatchedpages ? $unwatchedPageThreshold : null);
 }
 /**
  * Get the information about the wiki pages for a given context.
  * @param ResourceLoaderContext $context
  * @return array Keyed by page name. Contains arrays with 'rev_len' and 'rev_sha1' keys
  */
 protected function getTitleInfo(ResourceLoaderContext $context)
 {
     $dbr = $this->getDB();
     if (!$dbr) {
         // We're dealing with a subclass that doesn't have a DB
         return array();
     }
     $pages = $this->getPages($context);
     $key = implode('|', array_keys($pages));
     if (!isset($this->titleInfo[$key])) {
         $this->titleInfo[$key] = array();
         $batch = new LinkBatch();
         foreach ($pages as $titleText => $options) {
             $batch->addObj(Title::newFromText($titleText));
         }
         if (!$batch->isEmpty()) {
             $res = $dbr->select(array('page', 'revision'), array('page_namespace', 'page_title', 'rev_len', 'rev_sha1'), $batch->constructSet('page', $dbr), __METHOD__, array(), array('revision' => array('INNER JOIN', array('page_latest=rev_id'))));
             foreach ($res as $row) {
                 // Avoid including ids or timestamps of revision/page tables so
                 // that versions are not wasted
                 $title = Title::makeTitle($row->page_namespace, $row->page_title);
                 $this->titleInfo[$key][$title->getPrefixedText()] = array('rev_len' => $row->rev_len, 'rev_sha1' => $row->rev_sha1);
             }
         }
     }
     return $this->titleInfo[$key];
 }
 public function execute()
 {
     $user = $this->getUser();
     // Before doing anything at all, let's check permissions
     if (!$user->isAllowed('deletedhistory')) {
         $this->dieUsage('You don\'t have permission to view deleted revision information', 'permissiondenied');
     }
     $this->setWarning('list=deletedrevs has been deprecated. Please use prop=deletedrevisions or ' . 'list=alldeletedrevisions instead.');
     $this->logFeatureUsage('action=query&list=deletedrevs');
     $db = $this->getDB();
     $params = $this->extractRequestParams(false);
     $prop = array_flip($params['prop']);
     $fld_parentid = isset($prop['parentid']);
     $fld_revid = isset($prop['revid']);
     $fld_user = isset($prop['user']);
     $fld_userid = isset($prop['userid']);
     $fld_comment = isset($prop['comment']);
     $fld_parsedcomment = isset($prop['parsedcomment']);
     $fld_minor = isset($prop['minor']);
     $fld_len = isset($prop['len']);
     $fld_sha1 = isset($prop['sha1']);
     $fld_content = isset($prop['content']);
     $fld_token = isset($prop['token']);
     $fld_tags = isset($prop['tags']);
     if (isset($prop['token'])) {
         $p = $this->getModulePrefix();
         $this->setWarning("{$p}prop=token has been deprecated. Please use action=query&meta=tokens instead.");
     }
     // If we're in a mode that breaks the same-origin policy, no tokens can
     // be obtained
     if ($this->lacksSameOriginSecurity()) {
         $fld_token = false;
     }
     // If user can't undelete, no tokens
     if (!$user->isAllowed('undelete')) {
         $fld_token = false;
     }
     $result = $this->getResult();
     $pageSet = $this->getPageSet();
     $titles = $pageSet->getTitles();
     // This module operates in three modes:
     // 'revs': List deleted revs for certain titles (1)
     // 'user': List deleted revs by a certain user (2)
     // 'all': List all deleted revs in NS (3)
     $mode = 'all';
     if (count($titles) > 0) {
         $mode = 'revs';
     } elseif (!is_null($params['user'])) {
         $mode = 'user';
     }
     if ($mode == 'revs' || $mode == 'user') {
         // Ignore namespace and unique due to inability to know whether they were purposely set
         foreach (array('from', 'to', 'prefix') as $p) {
             if (!is_null($params[$p])) {
                 $this->dieUsage("The '{$p}' parameter cannot be used in modes 1 or 2", 'badparams');
             }
         }
     } else {
         foreach (array('start', 'end') as $p) {
             if (!is_null($params[$p])) {
                 $this->dieUsage("The {$p} parameter cannot be used in mode 3", 'badparams');
             }
         }
     }
     if (!is_null($params['user']) && !is_null($params['excludeuser'])) {
         $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
     }
     $this->addTables('archive');
     $this->addFields(array('ar_title', 'ar_namespace', 'ar_timestamp', 'ar_deleted', 'ar_id'));
     $this->addFieldsIf('ar_parent_id', $fld_parentid);
     $this->addFieldsIf('ar_rev_id', $fld_revid);
     $this->addFieldsIf('ar_user_text', $fld_user);
     $this->addFieldsIf('ar_user', $fld_userid);
     $this->addFieldsIf('ar_comment', $fld_comment || $fld_parsedcomment);
     $this->addFieldsIf('ar_minor_edit', $fld_minor);
     $this->addFieldsIf('ar_len', $fld_len);
     $this->addFieldsIf('ar_sha1', $fld_sha1);
     if ($fld_tags) {
         $this->addTables('tag_summary');
         $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', array('ar_rev_id=ts_rev_id'))));
         $this->addFields('ts_tags');
     }
     if (!is_null($params['tag'])) {
         $this->addTables('change_tag');
         $this->addJoinConds(array('change_tag' => array('INNER JOIN', array('ar_rev_id=ct_rev_id'))));
         $this->addWhereFld('ct_tag', $params['tag']);
     }
     if ($fld_content) {
         // Modern MediaWiki has the content for deleted revs in the 'text'
         // table using fields old_text and old_flags. But revisions deleted
         // pre-1.5 store the content in the 'archive' table directly using
         // fields ar_text and ar_flags, and no corresponding 'text' row. So
         // we have to LEFT JOIN and fetch all four fields, plus ar_text_id
         // to be able to tell the difference.
         $this->addTables('text');
         $this->addJoinConds(array('text' => array('LEFT JOIN', array('ar_text_id=old_id'))));
         $this->addFields(array('ar_text', 'ar_flags', 'ar_text_id', 'old_text', 'old_flags'));
         // This also means stricter restrictions
         if (!$user->isAllowedAny('undelete', 'deletedtext')) {
             $this->dieUsage('You don\'t have permission to view deleted revision content', 'permissiondenied');
         }
     }
     // Check limits
     $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1;
     $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2;
     $limit = $params['limit'];
     if ($limit == 'max') {
         $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
         $this->getResult()->addParsedLimit($this->getModuleName(), $limit);
     }
     $this->validateLimit('limit', $limit, 1, $userMax, $botMax);
     if ($fld_token) {
         // Undelete tokens are identical for all pages, so we cache one here
         $token = $user->getEditToken('', $this->getMain()->getRequest());
     }
     $dir = $params['dir'];
     // We need a custom WHERE clause that matches all titles.
     if ($mode == 'revs') {
         $lb = new LinkBatch($titles);
         $where = $lb->constructSet('ar', $db);
         $this->addWhere($where);
     } elseif ($mode == 'all') {
         $this->addWhereFld('ar_namespace', $params['namespace']);
         $from = $params['from'] === null ? null : $this->titlePartToKey($params['from'], $params['namespace']);
         $to = $params['to'] === null ? null : $this->titlePartToKey($params['to'], $params['namespace']);
         $this->addWhereRange('ar_title', $dir, $from, $to);
         if (isset($params['prefix'])) {
             $this->addWhere('ar_title' . $db->buildLike($this->titlePartToKey($params['prefix'], $params['namespace']), $db->anyString()));
         }
     }
     if (!is_null($params['user'])) {
         $this->addWhereFld('ar_user_text', $params['user']);
     } elseif (!is_null($params['excludeuser'])) {
         $this->addWhere('ar_user_text != ' . $db->addQuotes($params['excludeuser']));
     }
     if (!is_null($params['user']) || !is_null($params['excludeuser'])) {
         // Paranoia: avoid brute force searches (bug 17342)
         // (shouldn't be able to get here without 'deletedhistory', but
         // check it again just in case)
         if (!$user->isAllowed('deletedhistory')) {
             $bitmask = Revision::DELETED_USER;
         } elseif (!$user->isAllowedAny('suppressrevision', 'viewsuppressed')) {
             $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
         } else {
             $bitmask = 0;
         }
         if ($bitmask) {
             $this->addWhere($db->bitAnd('ar_deleted', $bitmask) . " != {$bitmask}");
         }
     }
     if (!is_null($params['continue'])) {
         $cont = explode('|', $params['continue']);
         $op = $dir == 'newer' ? '>' : '<';
         if ($mode == 'all' || $mode == 'revs') {
             $this->dieContinueUsageIf(count($cont) != 4);
             $ns = intval($cont[0]);
             $this->dieContinueUsageIf(strval($ns) !== $cont[0]);
             $title = $db->addQuotes($cont[1]);
             $ts = $db->addQuotes($db->timestamp($cont[2]));
             $ar_id = (int) $cont[3];
             $this->dieContinueUsageIf(strval($ar_id) !== $cont[3]);
             $this->addWhere("ar_namespace {$op} {$ns} OR " . "(ar_namespace = {$ns} AND " . "(ar_title {$op} {$title} OR " . "(ar_title = {$title} AND " . "(ar_timestamp {$op} {$ts} OR " . "(ar_timestamp = {$ts} AND " . "ar_id {$op}= {$ar_id})))))");
         } else {
             $this->dieContinueUsageIf(count($cont) != 2);
             $ts = $db->addQuotes($db->timestamp($cont[0]));
             $ar_id = (int) $cont[1];
             $this->dieContinueUsageIf(strval($ar_id) !== $cont[1]);
             $this->addWhere("ar_timestamp {$op} {$ts} OR " . "(ar_timestamp = {$ts} AND " . "ar_id {$op}= {$ar_id})");
         }
     }
     $this->addOption('LIMIT', $limit + 1);
     $this->addOption('USE INDEX', array('archive' => $mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp'));
     if ($mode == 'all') {
         if ($params['unique']) {
             // @todo Does this work on non-MySQL?
             $this->addOption('GROUP BY', 'ar_title');
         } else {
             $sort = $dir == 'newer' ? '' : ' DESC';
             $this->addOption('ORDER BY', array('ar_title' . $sort, 'ar_timestamp' . $sort, 'ar_id' . $sort));
         }
     } else {
         if ($mode == 'revs') {
             // Sort by ns and title in the same order as timestamp for efficiency
             $this->addWhereRange('ar_namespace', $dir, null, null);
             $this->addWhereRange('ar_title', $dir, null, null);
         }
         $this->addTimestampWhereRange('ar_timestamp', $dir, $params['start'], $params['end']);
         // Include in ORDER BY for uniqueness
         $this->addWhereRange('ar_id', $dir, null, null);
     }
     $res = $this->select(__METHOD__);
     $pageMap = array();
     // Maps ns&title to (fake) pageid
     $count = 0;
     $newPageID = 0;
     foreach ($res as $row) {
         if (++$count > $limit) {
             // We've had enough
             if ($mode == 'all' || $mode == 'revs') {
                 $this->setContinueEnumParameter('continue', "{$row->ar_namespace}|{$row->ar_title}|{$row->ar_timestamp}|{$row->ar_id}");
             } else {
                 $this->setContinueEnumParameter('continue', "{$row->ar_timestamp}|{$row->ar_id}");
             }
             break;
         }
         $rev = array();
         $anyHidden = false;
         $rev['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ar_timestamp);
         if ($fld_revid) {
             $rev['revid'] = intval($row->ar_rev_id);
         }
         if ($fld_parentid && !is_null($row->ar_parent_id)) {
             $rev['parentid'] = intval($row->ar_parent_id);
         }
         if ($fld_user || $fld_userid) {
             if ($row->ar_deleted & Revision::DELETED_USER) {
                 $rev['userhidden'] = true;
                 $anyHidden = true;
             }
             if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_USER, $user)) {
                 if ($fld_user) {
                     $rev['user'] = $row->ar_user_text;
                 }
                 if ($fld_userid) {
                     $rev['userid'] = $row->ar_user;
                 }
             }
         }
         if ($fld_comment || $fld_parsedcomment) {
             if ($row->ar_deleted & Revision::DELETED_COMMENT) {
                 $rev['commenthidden'] = true;
                 $anyHidden = true;
             }
             if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_COMMENT, $user)) {
                 if ($fld_comment) {
                     $rev['comment'] = $row->ar_comment;
                 }
                 if ($fld_parsedcomment) {
                     $title = Title::makeTitle($row->ar_namespace, $row->ar_title);
                     $rev['parsedcomment'] = Linker::formatComment($row->ar_comment, $title);
                 }
             }
         }
         if ($fld_minor) {
             $rev['minor'] = $row->ar_minor_edit == 1;
         }
         if ($fld_len) {
             $rev['len'] = $row->ar_len;
         }
         if ($fld_sha1) {
             if ($row->ar_deleted & Revision::DELETED_TEXT) {
                 $rev['sha1hidden'] = true;
                 $anyHidden = true;
             }
             if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_TEXT, $user)) {
                 if ($row->ar_sha1 != '') {
                     $rev['sha1'] = wfBaseConvert($row->ar_sha1, 36, 16, 40);
                 } else {
                     $rev['sha1'] = '';
                 }
             }
         }
         if ($fld_content) {
             if ($row->ar_deleted & Revision::DELETED_TEXT) {
                 $rev['texthidden'] = true;
                 $anyHidden = true;
             }
             if (Revision::userCanBitfield($row->ar_deleted, Revision::DELETED_TEXT, $user)) {
                 if (isset($row->ar_text) && !$row->ar_text_id) {
                     // Pre-1.5 ar_text row (if condition from Revision::newFromArchiveRow)
                     ApiResult::setContentValue($rev, 'text', Revision::getRevisionText($row, 'ar_'));
                 } else {
                     ApiResult::setContentValue($rev, 'text', Revision::getRevisionText($row));
                 }
             }
         }
         if ($fld_tags) {
             if ($row->ts_tags) {
                 $tags = explode(',', $row->ts_tags);
                 ApiResult::setIndexedTagName($tags, 'tag');
                 $rev['tags'] = $tags;
             } else {
                 $rev['tags'] = array();
             }
         }
         if ($anyHidden && $row->ar_deleted & Revision::DELETED_RESTRICTED) {
             $rev['suppressed'] = true;
         }
         if (!isset($pageMap[$row->ar_namespace][$row->ar_title])) {
             $pageID = $newPageID++;
             $pageMap[$row->ar_namespace][$row->ar_title] = $pageID;
             $a['revisions'] = array($rev);
             ApiResult::setIndexedTagName($a['revisions'], 'rev');
             $title = Title::makeTitle($row->ar_namespace, $row->ar_title);
             ApiQueryBase::addTitleInfo($a, $title);
             if ($fld_token) {
                 $a['token'] = $token;
             }
             $fit = $result->addValue(array('query', $this->getModuleName()), $pageID, $a);
         } else {
             $pageID = $pageMap[$row->ar_namespace][$row->ar_title];
             $fit = $result->addValue(array('query', $this->getModuleName(), $pageID, 'revisions'), null, $rev);
         }
         if (!$fit) {
             if ($mode == 'all' || $mode == 'revs') {
                 $this->setContinueEnumParameter('continue', "{$row->ar_namespace}|{$row->ar_title}|{$row->ar_timestamp}|{$row->ar_id}");
             } else {
                 $this->setContinueEnumParameter('continue', "{$row->ar_timestamp}|{$row->ar_id}");
             }
             break;
         }
     }
     $result->addIndexedTagName(array('query', $this->getModuleName()), 'page');
 }
 public function execute()
 {
     $user = $this->getUser();
     // Before doing anything at all, let's check permissions
     if (!$user->isAllowed('deletedhistory')) {
         $this->dieUsage('You don\'t have permission to view deleted revision information', 'permissiondenied');
     }
     $db = $this->getDB();
     $params = $this->extractRequestParams(false);
     $prop = array_flip($params['prop']);
     $fld_parentid = isset($prop['parentid']);
     $fld_revid = isset($prop['revid']);
     $fld_user = isset($prop['user']);
     $fld_userid = isset($prop['userid']);
     $fld_comment = isset($prop['comment']);
     $fld_parsedcomment = isset($prop['parsedcomment']);
     $fld_minor = isset($prop['minor']);
     $fld_len = isset($prop['len']);
     $fld_sha1 = isset($prop['sha1']);
     $fld_content = isset($prop['content']);
     $fld_token = isset($prop['token']);
     $result = $this->getResult();
     $pageSet = $this->getPageSet();
     $titles = $pageSet->getTitles();
     // This module operates in three modes:
     // 'revs': List deleted revs for certain titles (1)
     // 'user': List deleted revs by a certain user (2)
     // 'all': List all deleted revs in NS (3)
     $mode = 'all';
     if (count($titles) > 0) {
         $mode = 'revs';
     } elseif (!is_null($params['user'])) {
         $mode = 'user';
     }
     if ($mode == 'revs' || $mode == 'user') {
         // Ignore namespace and unique due to inability to know whether they were purposely set
         foreach (array('from', 'to', 'prefix') as $p) {
             if (!is_null($params[$p])) {
                 $this->dieUsage("The '{$p}' parameter cannot be used in modes 1 or 2", 'badparams');
             }
         }
     } else {
         foreach (array('start', 'end') as $p) {
             if (!is_null($params[$p])) {
                 $this->dieUsage("The {$p} parameter cannot be used in mode 3", 'badparams');
             }
         }
     }
     if (!is_null($params['user']) && !is_null($params['excludeuser'])) {
         $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
     }
     $this->addTables('archive');
     $this->addWhere('ar_deleted = 0');
     $this->addFields(array('ar_title', 'ar_namespace', 'ar_timestamp'));
     $this->addFieldsIf('ar_parent_id', $fld_parentid);
     $this->addFieldsIf('ar_rev_id', $fld_revid);
     $this->addFieldsIf('ar_user_text', $fld_user);
     $this->addFieldsIf('ar_user', $fld_userid);
     $this->addFieldsIf('ar_comment', $fld_comment || $fld_parsedcomment);
     $this->addFieldsIf('ar_minor_edit', $fld_minor);
     $this->addFieldsIf('ar_len', $fld_len);
     $this->addFieldsIf('ar_sha1', $fld_sha1);
     if ($fld_content) {
         $this->addTables('text');
         $this->addFields(array('ar_text', 'ar_text_id', 'old_text', 'old_flags'));
         $this->addWhere('ar_text_id = old_id');
         // This also means stricter restrictions
         if (!$user->isAllowed('undelete')) {
             $this->dieUsage('You don\'t have permission to view deleted revision content', 'permissiondenied');
         }
     }
     // Check limits
     $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1;
     $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2;
     $limit = $params['limit'];
     if ($limit == 'max') {
         $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
         $this->getResult()->setParsedLimit($this->getModuleName(), $limit);
     }
     $this->validateLimit('limit', $limit, 1, $userMax, $botMax);
     if ($fld_token) {
         // Undelete tokens are identical for all pages, so we cache one here
         $token = $user->getEditToken('', $this->getMain()->getRequest());
     }
     $dir = $params['dir'];
     // We need a custom WHERE clause that matches all titles.
     if ($mode == 'revs') {
         $lb = new LinkBatch($titles);
         $where = $lb->constructSet('ar', $db);
         $this->addWhere($where);
     } elseif ($mode == 'all') {
         $this->addWhereFld('ar_namespace', $params['namespace']);
         $from = is_null($params['from']) ? null : $this->titleToKey($params['from']);
         $to = is_null($params['to']) ? null : $this->titleToKey($params['to']);
         $this->addWhereRange('ar_title', $dir, $from, $to);
         if (isset($params['prefix'])) {
             $this->addWhere('ar_title' . $db->buildLike($this->titlePartToKey($params['prefix']), $db->anyString()));
         }
     }
     if (!is_null($params['user'])) {
         $this->addWhereFld('ar_user_text', $params['user']);
     } elseif (!is_null($params['excludeuser'])) {
         $this->addWhere('ar_user_text != ' . $db->addQuotes($params['excludeuser']));
     }
     if (!is_null($params['continue']) && ($mode == 'all' || $mode == 'revs')) {
         $cont = explode('|', $params['continue']);
         $this->dieContinueUsageIf(count($cont) != 3);
         $ns = intval($cont[0]);
         $this->dieContinueUsageIf(strval($ns) !== $cont[0]);
         $title = $db->addQuotes($cont[1]);
         $ts = $db->addQuotes($db->timestamp($cont[2]));
         $op = $dir == 'newer' ? '>' : '<';
         $this->addWhere("ar_namespace {$op} {$ns} OR " . "(ar_namespace = {$ns} AND " . "(ar_title {$op} {$title} OR " . "(ar_title = {$title} AND " . "ar_timestamp {$op}= {$ts})))");
     }
     $this->addOption('LIMIT', $limit + 1);
     $this->addOption('USE INDEX', array('archive' => $mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp'));
     if ($mode == 'all') {
         if ($params['unique']) {
             $this->addOption('GROUP BY', 'ar_title');
         } else {
             $sort = $dir == 'newer' ? '' : ' DESC';
             $this->addOption('ORDER BY', array('ar_title' . $sort, 'ar_timestamp' . $sort));
         }
     } else {
         if ($mode == 'revs') {
             // Sort by ns and title in the same order as timestamp for efficiency
             $this->addWhereRange('ar_namespace', $dir, null, null);
             $this->addWhereRange('ar_title', $dir, null, null);
         }
         $this->addTimestampWhereRange('ar_timestamp', $dir, $params['start'], $params['end']);
     }
     $res = $this->select(__METHOD__);
     $pageMap = array();
     // Maps ns&title to (fake) pageid
     $count = 0;
     $newPageID = 0;
     foreach ($res as $row) {
         if (++$count > $limit) {
             // We've had enough
             if ($mode == 'all' || $mode == 'revs') {
                 $this->setContinueEnumParameter('continue', intval($row->ar_namespace) . '|' . $row->ar_title . '|' . $row->ar_timestamp);
             } else {
                 $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp));
             }
             break;
         }
         $rev = array();
         $rev['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ar_timestamp);
         if ($fld_revid) {
             $rev['revid'] = intval($row->ar_rev_id);
         }
         if ($fld_parentid && !is_null($row->ar_parent_id)) {
             $rev['parentid'] = intval($row->ar_parent_id);
         }
         if ($fld_user) {
             $rev['user'] = $row->ar_user_text;
         }
         if ($fld_userid) {
             $rev['userid'] = $row->ar_user;
         }
         if ($fld_comment) {
             $rev['comment'] = $row->ar_comment;
         }
         $title = Title::makeTitle($row->ar_namespace, $row->ar_title);
         if ($fld_parsedcomment) {
             $rev['parsedcomment'] = Linker::formatComment($row->ar_comment, $title);
         }
         if ($fld_minor && $row->ar_minor_edit == 1) {
             $rev['minor'] = '';
         }
         if ($fld_len) {
             $rev['len'] = $row->ar_len;
         }
         if ($fld_sha1) {
             if ($row->ar_sha1 != '') {
                 $rev['sha1'] = wfBaseConvert($row->ar_sha1, 36, 16, 40);
             } else {
                 $rev['sha1'] = '';
             }
         }
         if ($fld_content) {
             ApiResult::setContent($rev, Revision::getRevisionText($row));
         }
         if (!isset($pageMap[$row->ar_namespace][$row->ar_title])) {
             $pageID = $newPageID++;
             $pageMap[$row->ar_namespace][$row->ar_title] = $pageID;
             $a['revisions'] = array($rev);
             $result->setIndexedTagName($a['revisions'], 'rev');
             ApiQueryBase::addTitleInfo($a, $title);
             if ($fld_token) {
                 $a['token'] = $token;
             }
             $fit = $result->addValue(array('query', $this->getModuleName()), $pageID, $a);
         } else {
             $pageID = $pageMap[$row->ar_namespace][$row->ar_title];
             $fit = $result->addValue(array('query', $this->getModuleName(), $pageID, 'revisions'), null, $rev);
         }
         if (!$fit) {
             if ($mode == 'all' || $mode == 'revs') {
                 $this->setContinueEnumParameter('continue', intval($row->ar_namespace) . '|' . $row->ar_title . '|' . $row->ar_timestamp);
             } else {
                 $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp));
             }
             break;
         }
     }
     $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'page');
 }
Exemplo n.º 15
0
 /**
  * Modify $this->internals and $colours according to language variant linking rules
  */
 protected function doVariants(&$colours)
 {
     global $wgContLang;
     $linkBatch = new LinkBatch();
     $variantMap = array();
     // maps $pdbkey_Variant => $keys (of link holders)
     $output = $this->parent->getOutput();
     $linkCache = LinkCache::singleton();
     $sk = $this->parent->getOptions()->getSkin();
     $threshold = $this->getStubThreshold();
     // Add variants of links to link batch
     foreach ($this->internals as $ns => $entries) {
         foreach ($entries as $index => $entry) {
             $key = "{$ns}:{$index}";
             $pdbk = $entry['pdbk'];
             $title = $entry['title'];
             $titleText = $title->getText();
             // generate all variants of the link title text
             $allTextVariants = $wgContLang->convertLinkToAllVariants($titleText);
             // if link was not found (in first query), add all variants to query
             if (!isset($colours[$pdbk])) {
                 foreach ($allTextVariants as $textVariant) {
                     if ($textVariant != $titleText) {
                         $variantTitle = Title::makeTitle($ns, $textVariant);
                         if (is_null($variantTitle)) {
                             continue;
                         }
                         $linkBatch->addObj($variantTitle);
                         $variantMap[$variantTitle->getPrefixedDBkey()][] = $key;
                     }
                 }
             }
         }
     }
     // process categories, check if a category exists in some variant
     $categoryMap = array();
     // maps $category_variant => $category (dbkeys)
     $varCategories = array();
     // category replacements oldDBkey => newDBkey
     foreach ($output->getCategoryLinks() as $category) {
         $variants = $wgContLang->convertLinkToAllVariants($category);
         foreach ($variants as $variant) {
             if ($variant != $category) {
                 $variantTitle = Title::newFromDBkey(Title::makeName(NS_CATEGORY, $variant));
                 if (is_null($variantTitle)) {
                     continue;
                 }
                 $linkBatch->addObj($variantTitle);
                 $categoryMap[$variant] = $category;
             }
         }
     }
     if (!$linkBatch->isEmpty()) {
         // construct query
         $dbr = wfGetDB(DB_SLAVE);
         $page = $dbr->tableName('page');
         $titleClause = $linkBatch->constructSet('page', $dbr);
         $variantQuery = "SELECT page_id, page_namespace, page_title, page_is_redirect, page_len";
         $variantQuery .= " FROM {$page} WHERE {$titleClause}";
         $varRes = $dbr->query($variantQuery, __METHOD__);
         $linkcolour_ids = array();
         // for each found variants, figure out link holders and replace
         while ($s = $dbr->fetchObject($varRes)) {
             $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title);
             $varPdbk = $variantTitle->getPrefixedDBkey();
             $vardbk = $variantTitle->getDBkey();
             $holderKeys = array();
             if (isset($variantMap[$varPdbk])) {
                 $holderKeys = $variantMap[$varPdbk];
                 $linkCache->addGoodLinkObj($s->page_id, $variantTitle, $s->page_len, $s->page_is_redirect);
                 $output->addLink($variantTitle, $s->page_id);
             }
             // loop over link holders
             foreach ($holderKeys as $key) {
                 list($ns, $index) = explode(':', $key, 2);
                 $entry =& $this->internals[$ns][$index];
                 $pdbk = $entry['pdbk'];
                 if (!isset($colours[$pdbk])) {
                     // found link in some of the variants, replace the link holder data
                     $entry['title'] = $variantTitle;
                     $entry['pdbk'] = $varPdbk;
                     // set pdbk and colour
                     # FIXME: convoluted data flow
                     # The redirect status and length is passed to getLinkColour via the LinkCache
                     # Use formal parameters instead
                     $colours[$varPdbk] = $sk->getLinkColour($variantTitle, $threshold);
                     $linkcolour_ids[$s->page_id] = $pdbk;
                 }
             }
             // check if the object is a variant of a category
             if (isset($categoryMap[$vardbk])) {
                 $oldkey = $categoryMap[$vardbk];
                 if ($oldkey != $vardbk) {
                     $varCategories[$oldkey] = $vardbk;
                 }
             }
         }
         wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours));
         // rebuild the categories in original order (if there are replacements)
         if (count($varCategories) > 0) {
             $newCats = array();
             $originalCats = $output->getCategories();
             foreach ($originalCats as $cat => $sortkey) {
                 // make the replacement
                 if (array_key_exists($cat, $varCategories)) {
                     $newCats[$varCategories[$cat]] = $sortkey;
                 } else {
                     $newCats[$cat] = $sortkey;
                 }
             }
             $output->setCategoryLinks($newCats);
         }
     }
 }
Exemplo n.º 16
0
 /**
  * Modify $this->internals and $colours according to language variant linking rules
  * @param array $colours
  */
 protected function doVariants(&$colours)
 {
     global $wgContLang, $wgContentHandlerUseDB;
     $linkBatch = new LinkBatch();
     $variantMap = array();
     // maps $pdbkey_Variant => $keys (of link holders)
     $output = $this->parent->getOutput();
     $linkCache = LinkCache::singleton();
     $threshold = $this->parent->getOptions()->getStubThreshold();
     $titlesToBeConverted = '';
     $titlesAttrs = array();
     // Concatenate titles to a single string, thus we only need auto convert the
     // single string to all variants. This would improve parser's performance
     // significantly.
     foreach ($this->internals as $ns => $entries) {
         if ($ns == NS_SPECIAL) {
             continue;
         }
         foreach ($entries as $index => $entry) {
             $pdbk = $entry['pdbk'];
             // we only deal with new links (in its first query)
             if (!isset($colours[$pdbk]) || $colours[$pdbk] === 'new') {
                 $titlesAttrs[] = array($index, $entry['title']);
                 // separate titles with \0 because it would never appears
                 // in a valid title
                 $titlesToBeConverted .= $entry['title']->getText() . "";
             }
         }
     }
     // Now do the conversion and explode string to text of titles
     $titlesAllVariants = $wgContLang->autoConvertToAllVariants(rtrim($titlesToBeConverted, ""));
     $allVariantsName = array_keys($titlesAllVariants);
     foreach ($titlesAllVariants as &$titlesVariant) {
         $titlesVariant = explode("", $titlesVariant);
     }
     // Then add variants of links to link batch
     $parentTitle = $this->parent->getTitle();
     foreach ($titlesAttrs as $i => $attrs) {
         /** @var Title $title */
         list($index, $title) = $attrs;
         $ns = $title->getNamespace();
         $text = $title->getText();
         foreach ($allVariantsName as $variantName) {
             $textVariant = $titlesAllVariants[$variantName][$i];
             if ($textVariant === $text) {
                 continue;
             }
             $variantTitle = Title::makeTitle($ns, $textVariant);
             if (is_null($variantTitle)) {
                 continue;
             }
             // Self-link checking for mixed/different variant titles. At this point, we
             // already know the exact title does not exist, so the link cannot be to a
             // variant of the current title that exists as a separate page.
             if ($variantTitle->equals($parentTitle) && !$title->hasFragment()) {
                 $this->internals[$ns][$index]['selflink'] = true;
                 continue 2;
             }
             $linkBatch->addObj($variantTitle);
             $variantMap[$variantTitle->getPrefixedDBkey()][] = "{$ns}:{$index}";
         }
     }
     // process categories, check if a category exists in some variant
     $categoryMap = array();
     // maps $category_variant => $category (dbkeys)
     $varCategories = array();
     // category replacements oldDBkey => newDBkey
     foreach ($output->getCategoryLinks() as $category) {
         $categoryTitle = Title::makeTitleSafe(NS_CATEGORY, $category);
         $linkBatch->addObj($categoryTitle);
         $variants = $wgContLang->autoConvertToAllVariants($category);
         foreach ($variants as $variant) {
             if ($variant !== $category) {
                 $variantTitle = Title::makeTitleSafe(NS_CATEGORY, $variant);
                 if (is_null($variantTitle)) {
                     continue;
                 }
                 $linkBatch->addObj($variantTitle);
                 $categoryMap[$variant] = array($category, $categoryTitle);
             }
         }
     }
     if (!$linkBatch->isEmpty()) {
         // construct query
         $dbr = wfGetDB(DB_SLAVE);
         $fields = array('page_id', 'page_namespace', 'page_title', 'page_is_redirect', 'page_len', 'page_latest');
         if ($wgContentHandlerUseDB) {
             $fields[] = 'page_content_model';
         }
         $varRes = $dbr->select('page', $fields, $linkBatch->constructSet('page', $dbr), __METHOD__);
         $linkcolour_ids = array();
         // for each found variants, figure out link holders and replace
         foreach ($varRes as $s) {
             $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title);
             $varPdbk = $variantTitle->getPrefixedDBkey();
             $vardbk = $variantTitle->getDBkey();
             $holderKeys = array();
             if (isset($variantMap[$varPdbk])) {
                 $holderKeys = $variantMap[$varPdbk];
                 $linkCache->addGoodLinkObjFromRow($variantTitle, $s);
                 $output->addLink($variantTitle, $s->page_id);
             }
             // loop over link holders
             foreach ($holderKeys as $key) {
                 list($ns, $index) = explode(':', $key, 2);
                 $entry =& $this->internals[$ns][$index];
                 $pdbk = $entry['pdbk'];
                 if (!isset($colours[$pdbk]) || $colours[$pdbk] === 'new') {
                     // found link in some of the variants, replace the link holder data
                     $entry['title'] = $variantTitle;
                     $entry['pdbk'] = $varPdbk;
                     // set pdbk and colour
                     # @todo FIXME: Convoluted data flow
                     # The redirect status and length is passed to getLinkColour via the LinkCache
                     # Use formal parameters instead
                     $colours[$varPdbk] = Linker::getLinkColour($variantTitle, $threshold);
                     $linkcolour_ids[$s->page_id] = $pdbk;
                 }
             }
             // check if the object is a variant of a category
             if (isset($categoryMap[$vardbk])) {
                 list($oldkey, $oldtitle) = $categoryMap[$vardbk];
                 if (!isset($varCategories[$oldkey]) && !$oldtitle->exists()) {
                     $varCategories[$oldkey] = $vardbk;
                 }
             }
         }
         Hooks::run('GetLinkColours', array($linkcolour_ids, &$colours));
         // rebuild the categories in original order (if there are replacements)
         if (count($varCategories) > 0) {
             $newCats = array();
             $originalCats = $output->getCategories();
             foreach ($originalCats as $cat => $sortkey) {
                 // make the replacement
                 if (array_key_exists($cat, $varCategories)) {
                     $newCats[$varCategories[$cat]] = $sortkey;
                 } else {
                     $newCats[$cat] = $sortkey;
                 }
             }
             $output->setCategoryLinks($newCats);
         }
     }
 }
Exemplo n.º 17
0
 private function formatCategoryLinks($links)
 {
     $result = [];
     if (!$links) {
         return $result;
     }
     // Fetch hiddencat property
     $lb = new LinkBatch();
     $lb->setArray([NS_CATEGORY => $links]);
     $db = $this->getDB();
     $res = $db->select(['page', 'page_props'], ['page_title', 'pp_propname'], $lb->constructSet('page', $db), __METHOD__, [], ['page_props' => ['LEFT JOIN', ['pp_propname' => 'hiddencat', 'pp_page = page_id']]]);
     $hiddencats = [];
     foreach ($res as $row) {
         $hiddencats[$row->page_title] = isset($row->pp_propname);
     }
     $linkCache = LinkCache::singleton();
     foreach ($links as $link => $sortkey) {
         $entry = [];
         $entry['sortkey'] = $sortkey;
         // array keys will cast numeric category names to ints, so cast back to string
         ApiResult::setContentValue($entry, 'category', (string) $link);
         if (!isset($hiddencats[$link])) {
             $entry['missing'] = true;
             // We already know the link doesn't exist in the database, so
             // tell LinkCache that before calling $title->isKnown().
             $title = Title::makeTitle(NS_CATEGORY, $link);
             $linkCache->addBadLinkObj($title);
             if ($title->isKnown()) {
                 $entry['known'] = true;
             }
         } elseif ($hiddencats[$link]) {
             $entry['hidden'] = true;
         }
         $result[] = $entry;
     }
     return $result;
 }
 public function execute()
 {
     $user = $this->getUser();
     if ($user->isAnon()) {
         $this->dieUsage('Anonymous users cannot use watchlist change notifications', 'notloggedin');
     }
     if (!$user->isAllowed('editmywatchlist')) {
         $this->dieUsage('You don\'t have permission to edit your watchlist', 'permissiondenied');
     }
     $params = $this->extractRequestParams();
     $this->requireMaxOneParameter($params, 'timestamp', 'torevid', 'newerthanrevid');
     $continuationManager = new ApiContinuationManager($this, array(), array());
     $this->setContinuationManager($continuationManager);
     $pageSet = $this->getPageSet();
     if ($params['entirewatchlist'] && $pageSet->getDataSource() !== null) {
         $this->dieUsage("Cannot use 'entirewatchlist' at the same time as '{$pageSet->getDataSource()}'", 'multisource');
     }
     $dbw = wfGetDB(DB_MASTER, 'api');
     $timestamp = null;
     if (isset($params['timestamp'])) {
         $timestamp = $dbw->timestamp($params['timestamp']);
     }
     if (!$params['entirewatchlist']) {
         $pageSet->execute();
     }
     if (isset($params['torevid'])) {
         if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) {
             $this->dieUsage('torevid may only be used with a single page', 'multpages');
         }
         $title = reset($pageSet->getGoodTitles());
         if ($title) {
             $timestamp = Revision::getTimestampFromId($title, $params['torevid'], Revision::READ_LATEST);
             if ($timestamp) {
                 $timestamp = $dbw->timestamp($timestamp);
             } else {
                 $timestamp = null;
             }
         }
     } elseif (isset($params['newerthanrevid'])) {
         if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) {
             $this->dieUsage('newerthanrevid may only be used with a single page', 'multpages');
         }
         $title = reset($pageSet->getGoodTitles());
         if ($title) {
             $revid = $title->getNextRevisionID($params['newerthanrevid'], Title::GAID_FOR_UPDATE);
             if ($revid) {
                 $timestamp = $dbw->timestamp(Revision::getTimestampFromId($title, $revid));
             } else {
                 $timestamp = null;
             }
         }
     }
     $apiResult = $this->getResult();
     $result = array();
     if ($params['entirewatchlist']) {
         // Entire watchlist mode: Just update the thing and return a success indicator
         $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID()), __METHOD__);
         $result['notificationtimestamp'] = is_null($timestamp) ? '' : wfTimestamp(TS_ISO_8601, $timestamp);
     } else {
         // First, log the invalid titles
         foreach ($pageSet->getInvalidTitles() as $title) {
             $r = array();
             $r['title'] = $title;
             $r['invalid'] = true;
             $result[] = $r;
         }
         foreach ($pageSet->getMissingPageIDs() as $p) {
             $page = array();
             $page['pageid'] = $p;
             $page['missing'] = true;
             $page['notwatched'] = true;
             $result[] = $page;
         }
         foreach ($pageSet->getMissingRevisionIDs() as $r) {
             $rev = array();
             $rev['revid'] = $r;
             $rev['missing'] = true;
             $rev['notwatched'] = true;
             $result[] = $rev;
         }
         if ($pageSet->getTitles()) {
             // Now process the valid titles
             $lb = new LinkBatch($pageSet->getTitles());
             $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__);
             // Query the results of our update
             $timestamps = array();
             $res = $dbw->select('watchlist', array('wl_namespace', 'wl_title', 'wl_notificationtimestamp'), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__);
             foreach ($res as $row) {
                 $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
             }
             // Now, put the valid titles into the result
             /** @var $title Title */
             foreach ($pageSet->getTitles() as $title) {
                 $ns = $title->getNamespace();
                 $dbkey = $title->getDBkey();
                 $r = array('ns' => intval($ns), 'title' => $title->getPrefixedText());
                 if (!$title->exists()) {
                     $r['missing'] = true;
                 }
                 if (isset($timestamps[$ns]) && array_key_exists($dbkey, $timestamps[$ns])) {
                     $r['notificationtimestamp'] = '';
                     if ($timestamps[$ns][$dbkey] !== null) {
                         $r['notificationtimestamp'] = wfTimestamp(TS_ISO_8601, $timestamps[$ns][$dbkey]);
                     }
                 } else {
                     $r['notwatched'] = true;
                 }
                 $result[] = $r;
             }
         }
         ApiResult::setIndexedTagName($result, 'page');
     }
     $apiResult->addValue(null, $this->getModuleName(), $result);
     $this->setContinuationManager(null);
     $continuationManager->setContinuationIntoResult($apiResult);
 }
Exemplo n.º 19
0
 public function execute()
 {
     global $wgUser;
     // Before doing anything at all, let's check permissions
     if (!$wgUser->isAllowed('deletedhistory')) {
         $this->dieUsage('You don\'t have permission to view deleted revision information', 'permissiondenied');
     }
     $db = $this->getDB();
     $params = $this->extractRequestParams(false);
     $prop = array_flip($params['prop']);
     $fld_revid = isset($prop['revid']);
     $fld_user = isset($prop['user']);
     $fld_comment = isset($prop['comment']);
     $fld_minor = isset($prop['minor']);
     $fld_len = isset($prop['len']);
     $fld_content = isset($prop['content']);
     $fld_token = isset($prop['token']);
     $result = $this->getResult();
     $pageSet = $this->getPageSet();
     $titles = $pageSet->getTitles();
     $data = array();
     // This module operates in three modes:
     // 'revs': List deleted revs for certain titles
     // 'user': List deleted revs by a certain user
     // 'all': List all deleted revs
     $mode = 'all';
     if (count($titles) > 0) {
         $mode = 'revs';
     } else {
         if (!is_null($params['user'])) {
             $mode = 'user';
         }
     }
     if (!is_null($params['user']) && !is_null($params['excludeuser'])) {
         $this->dieUsage('user and excludeuser cannot be used together', 'badparams');
     }
     $this->addTables('archive');
     $this->addWhere('ar_deleted = 0');
     $this->addFields(array('ar_title', 'ar_namespace', 'ar_timestamp'));
     if ($fld_revid) {
         $this->addFields('ar_rev_id');
     }
     if ($fld_user) {
         $this->addFields('ar_user_text');
     }
     if ($fld_comment) {
         $this->addFields('ar_comment');
     }
     if ($fld_minor) {
         $this->addFields('ar_minor_edit');
     }
     if ($fld_len) {
         $this->addFields('ar_len');
     }
     if ($fld_content) {
         $this->addTables('text');
         $this->addFields(array('ar_text', 'ar_text_id', 'old_text', 'old_flags'));
         $this->addWhere('ar_text_id = old_id');
         // This also means stricter restrictions
         if (!$wgUser->isAllowed('undelete')) {
             $this->dieUsage('You don\'t have permission to view deleted revision content', 'permissiondenied');
         }
     }
     // Check limits
     $userMax = $fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1;
     $botMax = $fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2;
     $limit = $params['limit'];
     if ($limit == 'max') {
         $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
         $this->getResult()->addValue('limits', $this->getModuleName(), $limit);
     }
     $this->validateLimit('limit', $limit, 1, $userMax, $botMax);
     if ($fld_token) {
         // Undelete tokens are identical for all pages, so we cache one here
         $token = $wgUser->editToken();
     }
     // We need a custom WHERE clause that matches all titles.
     if ($mode == 'revs') {
         $lb = new LinkBatch($titles);
         $where = $lb->constructSet('ar', $db);
         $this->addWhere($where);
     } elseif ($mode == 'all') {
         $this->addWhereFld('ar_namespace', $params['namespace']);
         if (!is_null($params['from'])) {
             $from = $this->getDB()->strencode($this->titleToKey($params['from']));
             $this->addWhere("ar_title >= '{$from}'");
         }
     }
     if (!is_null($params['user'])) {
         $this->addWhereFld('ar_user_text', $params['user']);
     } elseif (!is_null($params['excludeuser'])) {
         $this->addWhere('ar_user_text != ' . $this->getDB()->addQuotes($params['excludeuser']));
     }
     if (!is_null($params['continue']) && ($mode == 'all' || $mode == 'revs')) {
         $cont = explode('|', $params['continue']);
         if (count($cont) != 3) {
             $this->dieUsage("Invalid continue param. You should pass the original value returned by the previous query", "badcontinue");
         }
         $ns = intval($cont[0]);
         $title = $this->getDB()->strencode($this->titleToKey($cont[1]));
         $ts = $this->getDB()->strencode($cont[2]);
         $op = $params['dir'] == 'newer' ? '>' : '<';
         $this->addWhere("ar_namespace {$op} {$ns} OR " . "(ar_namespace = {$ns} AND " . "(ar_title {$op} '{$title}' OR " . "(ar_title = '{$title}' AND " . "ar_timestamp = '{$ts}')))");
     }
     $this->addOption('LIMIT', $limit + 1);
     $this->addOption('USE INDEX', array('archive' => $mode == 'user' ? 'usertext_timestamp' : 'name_title_timestamp'));
     if ($mode == 'all') {
         if ($params['unique']) {
             $this->addOption('GROUP BY', 'ar_title');
             $this->addOption('ORDER BY', 'ar_title');
         } else {
             $this->addOption('ORDER BY', 'ar_title, ar_timestamp');
         }
     } else {
         if ($mode == 'revs') {
             // Sort by ns and title in the same order as timestamp for efficiency
             $this->addWhereRange('ar_namespace', $params['dir'], null, null);
             $this->addWhereRange('ar_title', $params['dir'], null, null);
         }
         $this->addWhereRange('ar_timestamp', $params['dir'], $params['start'], $params['end']);
     }
     $res = $this->select(__METHOD__);
     $pageMap = array();
     // Maps ns&title to (fake) pageid
     $count = 0;
     $newPageID = 0;
     while ($row = $db->fetchObject($res)) {
         if (++$count > $limit) {
             // We've had enough
             if ($mode == 'all' || $mode == 'revs') {
                 $this->setContinueEnumParameter('continue', intval($row->ar_namespace) . '|' . $this->keyToTitle($row->ar_title) . '|' . $row->ar_timestamp);
             } else {
                 $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp));
             }
             break;
         }
         $rev = array();
         $rev['timestamp'] = wfTimestamp(TS_ISO_8601, $row->ar_timestamp);
         if ($fld_revid) {
             $rev['revid'] = intval($row->ar_rev_id);
         }
         if ($fld_user) {
             $rev['user'] = $row->ar_user_text;
         }
         if ($fld_comment) {
             $rev['comment'] = $row->ar_comment;
         }
         if ($fld_minor) {
             if ($row->ar_minor_edit == 1) {
                 $rev['minor'] = '';
             }
         }
         if ($fld_len) {
             $rev['len'] = $row->ar_len;
         }
         if ($fld_content) {
             ApiResult::setContent($rev, Revision::getRevisionText($row));
         }
         if (!isset($pageMap[$row->ar_namespace][$row->ar_title])) {
             $pageID = $newPageID++;
             $pageMap[$row->ar_namespace][$row->ar_title] = $pageID;
             $t = Title::makeTitle($row->ar_namespace, $row->ar_title);
             $a['revisions'] = array($rev);
             $result->setIndexedTagName($a['revisions'], 'rev');
             ApiQueryBase::addTitleInfo($a, $t);
             if ($fld_token) {
                 $a['token'] = $token;
             }
             $fit = $result->addValue(array('query', $this->getModuleName()), $pageID, $a);
         } else {
             $pageID = $pageMap[$row->ar_namespace][$row->ar_title];
             $fit = $result->addValue(array('query', $this->getModuleName(), $pageID, 'revisions'), null, $rev);
         }
         if (!$fit) {
             if ($mode == 'all' || $mode == 'revs') {
                 $this->setContinueEnumParameter('continue', intval($row->ar_namespace) . '|' . $this->keyToTitle($row->ar_title) . '|' . $row->ar_timestamp);
             } else {
                 $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->ar_timestamp));
             }
             break;
         }
     }
     $db->freeResult($res);
     $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'page');
 }
Exemplo n.º 20
0
 /**
  * Modify $this->internals and $colours according to language variant linking rules
  */
 protected function doVariants(&$colours)
 {
     global $wgContLang;
     $linkBatch = new LinkBatch();
     $variantMap = array();
     // maps $pdbkey_Variant => $keys (of link holders)
     $output = $this->parent->getOutput();
     $linkCache = LinkCache::singleton();
     $threshold = $this->parent->getOptions()->getStubThreshold();
     $titlesToBeConverted = '';
     $titlesAttrs = array();
     // Concatenate titles to a single string, thus we only need auto convert the
     // single string to all variants. This would improve parser's performance
     // significantly.
     foreach ($this->internals as $ns => $entries) {
         foreach ($entries as $index => $entry) {
             $pdbk = $entry['pdbk'];
             // we only deal with new links (in its first query)
             if (!isset($colours[$pdbk])) {
                 $title = $entry['title'];
                 $titleText = $title->getText();
                 $titlesAttrs[] = array('ns' => $ns, 'key' => "{$ns}:{$index}", 'titleText' => $titleText);
                 // separate titles with \0 because it would never appears
                 // in a valid title
                 $titlesToBeConverted .= $titleText . "";
             }
         }
     }
     // Now do the conversion and explode string to text of titles
     $titlesAllVariants = $wgContLang->autoConvertToAllVariants($titlesToBeConverted);
     $allVariantsName = array_keys($titlesAllVariants);
     foreach ($titlesAllVariants as &$titlesVariant) {
         $titlesVariant = explode("", $titlesVariant);
     }
     $l = count($titlesAttrs);
     // Then add variants of links to link batch
     for ($i = 0; $i < $l; $i++) {
         foreach ($allVariantsName as $variantName) {
             $textVariant = $titlesAllVariants[$variantName][$i];
             if ($textVariant != $titlesAttrs[$i]['titleText']) {
                 $variantTitle = Title::makeTitle($titlesAttrs[$i]['ns'], $textVariant);
                 if (is_null($variantTitle)) {
                     continue;
                 }
                 $linkBatch->addObj($variantTitle);
                 $variantMap[$variantTitle->getPrefixedDBkey()][] = $titlesAttrs[$i]['key'];
             }
         }
     }
     // process categories, check if a category exists in some variant
     $categoryMap = array();
     // maps $category_variant => $category (dbkeys)
     $varCategories = array();
     // category replacements oldDBkey => newDBkey
     foreach ($output->getCategoryLinks() as $category) {
         $variants = $wgContLang->autoConvertToAllVariants($category);
         foreach ($variants as $variant) {
             if ($variant != $category) {
                 $variantTitle = Title::newFromDBkey(Title::makeName(NS_CATEGORY, $variant));
                 if (is_null($variantTitle)) {
                     continue;
                 }
                 $linkBatch->addObj($variantTitle);
                 $categoryMap[$variant] = $category;
             }
         }
     }
     if (!$linkBatch->isEmpty()) {
         // construct query
         $dbr = wfGetDB(DB_SLAVE);
         $varRes = $dbr->select('page', array('page_id', 'page_namespace', 'page_title', 'page_is_redirect', 'page_len', 'page_latest'), $linkBatch->constructSet('page', $dbr), __METHOD__);
         $linkcolour_ids = array();
         // for each found variants, figure out link holders and replace
         foreach ($varRes as $s) {
             $variantTitle = Title::makeTitle($s->page_namespace, $s->page_title);
             $varPdbk = $variantTitle->getPrefixedDBkey();
             $vardbk = $variantTitle->getDBkey();
             $holderKeys = array();
             if (isset($variantMap[$varPdbk])) {
                 $holderKeys = $variantMap[$varPdbk];
                 $linkCache->addGoodLinkObjFromRow($variantTitle, $s);
                 $output->addLink($variantTitle, $s->page_id);
             }
             // loop over link holders
             foreach ($holderKeys as $key) {
                 list($ns, $index) = explode(':', $key, 2);
                 $entry =& $this->internals[$ns][$index];
                 $pdbk = $entry['pdbk'];
                 if (!isset($colours[$pdbk])) {
                     // found link in some of the variants, replace the link holder data
                     $entry['title'] = $variantTitle;
                     $entry['pdbk'] = $varPdbk;
                     // set pdbk and colour
                     # @todo FIXME: Convoluted data flow
                     # The redirect status and length is passed to getLinkColour via the LinkCache
                     # Use formal parameters instead
                     $colours[$varPdbk] = Linker::getLinkColour($variantTitle, $threshold);
                     $linkcolour_ids[$s->page_id] = $pdbk;
                 }
             }
             // check if the object is a variant of a category
             if (isset($categoryMap[$vardbk])) {
                 $oldkey = $categoryMap[$vardbk];
                 if ($oldkey != $vardbk) {
                     $varCategories[$oldkey] = $vardbk;
                 }
             }
         }
         wfRunHooks('GetLinkColours', array($linkcolour_ids, &$colours));
         // rebuild the categories in original order (if there are replacements)
         if (count($varCategories) > 0) {
             $newCats = array();
             $originalCats = $output->getCategories();
             foreach ($originalCats as $cat => $sortkey) {
                 // make the replacement
                 if (array_key_exists($cat, $varCategories)) {
                     $newCats[$varCategories[$cat]] = $sortkey;
                 } else {
                     $newCats[$cat] = $sortkey;
                 }
             }
             $output->setCategoryLinks($newCats);
         }
     }
 }
Exemplo n.º 21
0
 /**
  * Get information about watched status and put it in $this->watched
  * and $this->notificationtimestamps
  */
 private function getWatchedInfo()
 {
     $user = $this->getUser();
     if ($user->isAnon() || count($this->everything) == 0) {
         return;
     }
     $this->watched = array();
     $this->notificationtimestamps = array();
     $db = $this->getDB();
     $lb = new LinkBatch($this->everything);
     $this->resetQueryParams();
     $this->addTables(array('watchlist'));
     $this->addFields(array('wl_title', 'wl_namespace'));
     $this->addFieldsIf('wl_notificationtimestamp', $this->fld_notificationtimestamp);
     $this->addWhere(array($lb->constructSet('wl', $db), 'wl_user' => $user->getID()));
     $res = $this->select(__METHOD__);
     foreach ($res as $row) {
         if ($this->fld_watched) {
             $this->watched[$row->wl_namespace][$row->wl_title] = true;
         }
         if ($this->fld_notificationtimestamp) {
             $this->notificationtimestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
         }
     }
 }
Exemplo n.º 22
0
 /**
  * Make a WHERE clause from a 2-d NS/dbkey array
  *
  * @param array $arr 2-d array indexed by namespace and DB key
  * @param string $prefix Field name prefix, without the underscore
  */
 function makeWhereFrom2d(&$arr, $prefix)
 {
     $lb = new LinkBatch();
     $lb->setArray($arr);
     return $lb->constructSet($prefix, $this->mDb);
 }
Exemplo n.º 23
0
 /**
  * @param ApiPageSet $resultPageSet
  */
 private function run(ApiPageSet $resultPageSet = null)
 {
     $settings = self::$settings[$this->getModuleName()];
     $db = $this->getDB();
     $params = $this->extractRequestParams();
     $prop = array_flip($params['prop']);
     $emptyString = $db->addQuotes('');
     $pageSet = $this->getPageSet();
     $titles = $pageSet->getGoodTitles() + $pageSet->getMissingTitles();
     $map = $pageSet->getAllTitlesByNamespace();
     // Determine our fields to query on
     $p = $settings['prefix'];
     $hasNS = !isset($settings['to_namespace']);
     if ($hasNS) {
         $bl_namespace = "{$p}_namespace";
         $bl_title = "{$p}_title";
     } else {
         $bl_namespace = $settings['to_namespace'];
         $bl_title = "{$p}_to";
         $titles = array_filter($titles, function ($t) use($bl_namespace) {
             return $t->getNamespace() === $bl_namespace;
         });
         $map = array_intersect_key($map, array($bl_namespace => true));
     }
     $bl_from = "{$p}_from";
     if (!$titles) {
         return;
         // nothing to do
     }
     // Figure out what we're sorting by, and add associated WHERE clauses.
     // MySQL's query planner screws up if we include a field in ORDER BY
     // when it's constant in WHERE, so we have to test that for each field.
     $sortby = array();
     if ($hasNS && count($map) > 1) {
         $sortby[$bl_namespace] = 'ns';
     }
     $theTitle = null;
     foreach ($map as $nsTitles) {
         reset($nsTitles);
         $key = key($nsTitles);
         if ($theTitle === null) {
             $theTitle = $key;
         }
         if (count($nsTitles) > 1 || $key !== $theTitle) {
             $sortby[$bl_title] = 'title';
             break;
         }
     }
     $miser_ns = null;
     if ($params['namespace'] !== null) {
         if (empty($settings['from_namespace']) && $this->getConfig()->get('MiserMode')) {
             $miser_ns = $params['namespace'];
         } else {
             $this->addWhereFld("{$p}_from_namespace", $params['namespace']);
             if (!empty($settings['from_namespace']) && count($params['namespace']) > 1) {
                 $sortby["{$p}_from_namespace"] = 'int';
             }
         }
     }
     $sortby[$bl_from] = 'int';
     // Now use the $sortby to figure out the continuation
     if (!is_null($params['continue'])) {
         $cont = explode('|', $params['continue']);
         $this->dieContinueUsageIf(count($cont) != count($sortby));
         $where = '';
         $i = count($sortby) - 1;
         $cont_ns = 0;
         $cont_title = '';
         foreach (array_reverse($sortby, true) as $field => $type) {
             $v = $cont[$i];
             switch ($type) {
                 case 'ns':
                     $cont_ns = (int) $v;
                     /* fall through */
                 /* fall through */
                 case 'int':
                     $v = (int) $v;
                     $this->dieContinueUsageIf($v != $cont[$i]);
                     break;
                 case 'title':
                     $cont_title = $v;
                     /* fall through */
                 /* fall through */
                 default:
                     $v = $db->addQuotes($v);
                     break;
             }
             if ($where === '') {
                 $where = "{$field} >= {$v}";
             } else {
                 $where = "{$field} > {$v} OR ({$field} = {$v} AND ({$where}))";
             }
             $i--;
         }
         $this->addWhere($where);
     }
     // Populate the rest of the query
     $this->addTables(array($settings['linktable'], 'page'));
     $this->addWhere("{$bl_from} = page_id");
     if ($this->getModuleName() === 'redirects') {
         $this->addWhere("rd_interwiki = {$emptyString} OR rd_interwiki IS NULL");
     }
     $this->addFields(array_keys($sortby));
     $this->addFields(array('bl_namespace' => $bl_namespace, 'bl_title' => $bl_title));
     if (is_null($resultPageSet)) {
         $fld_pageid = isset($prop['pageid']);
         $fld_title = isset($prop['title']);
         $fld_redirect = isset($prop['redirect']);
         $this->addFieldsIf('page_id', $fld_pageid);
         $this->addFieldsIf(array('page_title', 'page_namespace'), $fld_title);
         $this->addFieldsIf('page_is_redirect', $fld_redirect);
         // prop=redirects
         $fld_fragment = isset($prop['fragment']);
         $this->addFieldsIf('rd_fragment', $fld_fragment);
     } else {
         $this->addFields($resultPageSet->getPageTableFields());
     }
     $this->addFieldsIf('page_namespace', $miser_ns !== null);
     if ($hasNS) {
         $lb = new LinkBatch($titles);
         $this->addWhere($lb->constructSet($p, $db));
     } else {
         $where = array();
         foreach ($titles as $t) {
             if ($t->getNamespace() == $bl_namespace) {
                 $where[] = "{$bl_title} = " . $db->addQuotes($t->getDBkey());
             }
         }
         $this->addWhere($db->makeList($where, LIST_OR));
     }
     if ($params['show'] !== null) {
         // prop=redirects only
         $show = array_flip($params['show']);
         if (isset($show['fragment']) && isset($show['!fragment']) || isset($show['redirect']) && isset($show['!redirect'])) {
             $this->dieUsageMsg('show');
         }
         $this->addWhereIf("rd_fragment != {$emptyString}", isset($show['fragment']));
         $this->addWhereIf("rd_fragment = {$emptyString} OR rd_fragment IS NULL", isset($show['!fragment']));
         $this->addWhereIf(array('page_is_redirect' => 1), isset($show['redirect']));
         $this->addWhereIf(array('page_is_redirect' => 0), isset($show['!redirect']));
     }
     // Override any ORDER BY from above with what we calculated earlier.
     $this->addOption('ORDER BY', array_keys($sortby));
     $this->addOption('LIMIT', $params['limit'] + 1);
     $res = $this->select(__METHOD__);
     if (is_null($resultPageSet)) {
         $count = 0;
         foreach ($res as $row) {
             if (++$count > $params['limit']) {
                 // We've reached the one extra which shows that
                 // there are additional pages to be had. Stop here...
                 $this->setContinue($row, $sortby);
                 break;
             }
             if ($miser_ns !== null && !in_array($row->page_namespace, $miser_ns)) {
                 // Miser mode namespace check
                 continue;
             }
             // Get the ID of the current page
             $id = $map[$row->bl_namespace][$row->bl_title];
             $vals = array();
             if ($fld_pageid) {
                 $vals['pageid'] = $row->page_id;
             }
             if ($fld_title) {
                 ApiQueryBase::addTitleInfo($vals, Title::makeTitle($row->page_namespace, $row->page_title));
             }
             if ($fld_fragment && $row->rd_fragment !== null && $row->rd_fragment !== '') {
                 $vals['fragment'] = $row->rd_fragment;
             }
             if ($fld_redirect && $row->page_is_redirect) {
                 $vals['redirect'] = '';
             }
             $fit = $this->addPageSubItem($id, $vals);
             if (!$fit) {
                 $this->setContinue($row, $sortby);
                 break;
             }
         }
     } else {
         $titles = array();
         $count = 0;
         foreach ($res as $row) {
             if (++$count > $params['limit']) {
                 // We've reached the one extra which shows that
                 // there are additional pages to be had. Stop here...
                 $this->setContinue($row, $sortby);
                 break;
             }
             $titles[] = Title::makeTitle($row->page_namespace, $row->page_title);
         }
         $resultPageSet->populateFromTitles($titles);
     }
 }
Exemplo n.º 24
0
 private function run($resultPageSet = null)
 {
     if ($this->getPageSet()->getGoodTitleCount() == 0) {
         return;
         // nothing to do
     }
     $params = $this->extractRequestParams();
     $this->addFields(array($this->prefix . '_from AS pl_from', $this->prefix . '_namespace AS pl_namespace', $this->prefix . '_title AS pl_title'));
     $this->addTables($this->table);
     $this->addWhereFld($this->prefix . '_from', array_keys($this->getPageSet()->getGoodTitles()));
     $this->addWhereFld($this->prefix . '_namespace', $params['namespace']);
     if (!is_null($params[$this->titlesParam])) {
         $lb = new LinkBatch();
         foreach ($params[$this->titlesParam] as $t) {
             $title = Title::newFromText($t);
             if (!$title) {
                 $this->setWarning("``{$t}'' is not a valid title");
             } else {
                 $lb->addObj($title);
             }
         }
         $cond = $lb->constructSet($this->prefix, $this->getDB());
         if ($cond) {
             $this->addWhere($cond);
         }
     }
     if (!is_null($params['continue'])) {
         $cont = explode('|', $params['continue']);
         if (count($cont) != 3) {
             $this->dieUsage('Invalid continue param. You should pass the ' . 'original value returned by the previous query', '_badcontinue');
         }
         $plfrom = intval($cont[0]);
         $plns = intval($cont[1]);
         $pltitle = $this->getDB()->strencode($this->titleToKey($cont[2]));
         $this->addWhere("{$this->prefix}_from > {$plfrom} OR " . "({$this->prefix}_from = {$plfrom} AND " . "({$this->prefix}_namespace > {$plns} OR " . "({$this->prefix}_namespace = {$plns} AND " . "{$this->prefix}_title >= '{$pltitle}')))");
     }
     // Here's some MySQL craziness going on: if you use WHERE foo='bar'
     // and later ORDER BY foo MySQL doesn't notice the ORDER BY is pointless
     // but instead goes and filesorts, because the index for foo was used
     // already. To work around this, we drop constant fields in the WHERE
     // clause from the ORDER BY clause
     $order = array();
     if (count($this->getPageSet()->getGoodTitles()) != 1) {
         $order[] = "{$this->prefix}_from";
     }
     if (count($params['namespace']) != 1) {
         $order[] = "{$this->prefix}_namespace";
     }
     $order[] = "{$this->prefix}_title";
     $this->addOption('ORDER BY', implode(', ', $order));
     $this->addOption('USE INDEX', "{$this->prefix}_from");
     $this->addOption('LIMIT', $params['limit'] + 1);
     $res = $this->select(__METHOD__);
     if (is_null($resultPageSet)) {
         $count = 0;
         foreach ($res as $row) {
             if (++$count > $params['limit']) {
                 // We've reached the one extra which shows that
                 // there are additional pages to be had. Stop here...
                 $this->setContinueEnumParameter('continue', "{$row->pl_from}|{$row->pl_namespace}|" . $this->keyToTitle($row->pl_title));
                 break;
             }
             $vals = array();
             ApiQueryBase::addTitleInfo($vals, Title::makeTitle($row->pl_namespace, $row->pl_title));
             $fit = $this->addPageSubItem($row->pl_from, $vals);
             if (!$fit) {
                 $this->setContinueEnumParameter('continue', "{$row->pl_from}|{$row->pl_namespace}|" . $this->keyToTitle($row->pl_title));
                 break;
             }
         }
     } else {
         $titles = array();
         $count = 0;
         foreach ($res as $row) {
             if (++$count > $params['limit']) {
                 // We've reached the one extra which shows that
                 // there are additional pages to be had. Stop here...
                 $this->setContinueEnumParameter('continue', "{$row->pl_from}|{$row->pl_namespace}|" . $this->keyToTitle($row->pl_title));
                 break;
             }
             $titles[] = Title::makeTitle($row->pl_namespace, $row->pl_title);
         }
         $resultPageSet->populateFromTitles($titles);
     }
 }
Exemplo n.º 25
0
 private function formatCategoryLinks($links)
 {
     $result = array();
     if (!$links) {
         return $result;
     }
     // Fetch hiddencat property
     $lb = new LinkBatch();
     $lb->setArray(array(NS_CATEGORY => $links));
     $db = $this->getDB();
     $res = $db->select(array('page', 'page_props'), array('page_title', 'pp_propname'), $lb->constructSet('page', $db), __METHOD__, array(), array('page_props' => array('LEFT JOIN', array('pp_propname' => 'hiddencat', 'pp_page = page_id'))));
     $hiddencats = array();
     foreach ($res as $row) {
         $hiddencats[$row->page_title] = isset($row->pp_propname);
     }
     foreach ($links as $link => $sortkey) {
         $entry = array();
         $entry['sortkey'] = $sortkey;
         ApiResult::setContentValue($entry, 'category', $link);
         if (!isset($hiddencats[$link])) {
             $entry['missing'] = true;
         } elseif ($hiddencats[$link]) {
             $entry['hidden'] = true;
         }
         $result[] = $entry;
     }
     return $result;
 }
Exemplo n.º 26
0
 /**
  * Generates and outputs the result of this query based upon the provided parameters.
  */
 public function execute()
 {
     /* Get the parameters of the request. */
     $params = $this->extractRequestParams();
     /* Build our basic query. Namely, something along the lines of:
      * SELECT * FROM recentchanges WHERE rc_timestamp > $start
      * 		AND rc_timestamp < $end AND rc_namespace = $namespace
      * 		AND rc_deleted = '0'
      */
     $db = $this->getDB();
     $this->addTables('recentchanges');
     $this->addOption('USE INDEX', array('recentchanges' => 'rc_timestamp'));
     $this->addWhereRange('rc_timestamp', $params['dir'], $params['start'], $params['end']);
     $this->addWhereFld('rc_namespace', $params['namespace']);
     $this->addWhereFld('rc_deleted', 0);
     if ($params['titles']) {
         $lb = new LinkBatch();
         foreach ($params['titles'] as $t) {
             $obj = Title::newFromText($t);
             $lb->addObj($obj);
             if ($obj->getNamespace() < 0) {
                 // LinkBatch refuses these, but we need them anyway
                 if (!array_key_exists($obj->getNamespace(), $lb->data)) {
                     $lb->data[$obj->getNamespace()] = array();
                 }
                 $lb->data[$obj->getNamespace()][$obj->getDBKey()] = 1;
             }
         }
         $where = $lb->constructSet('rc', $this->getDB());
         if ($where != '') {
             $this->addWhere($where);
         }
     }
     if (!is_null($params['type'])) {
         $this->addWhereFld('rc_type', $this->parseRCType($params['type']));
     }
     if (!is_null($params['show'])) {
         $show = array_flip($params['show']);
         /* Check for conflicting parameters. */
         if (isset($show['minor']) && isset($show['!minor']) || isset($show['bot']) && isset($show['!bot']) || isset($show['anon']) && isset($show['!anon']) || isset($show['redirect']) && isset($show['!redirect']) || isset($show['patrolled']) && isset($show['!patrolled'])) {
             $this->dieUsage("Incorrect parameter - mutually exclusive values may not be supplied", 'show');
         }
         // Check permissions
         global $wgUser;
         if ((isset($show['patrolled']) || isset($show['!patrolled'])) && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) {
             $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied');
         }
         /* Add additional conditions to query depending upon parameters. */
         $this->addWhereIf('rc_minor = 0', isset($show['!minor']));
         $this->addWhereIf('rc_minor != 0', isset($show['minor']));
         $this->addWhereIf('rc_bot = 0', isset($show['!bot']));
         $this->addWhereIf('rc_bot != 0', isset($show['bot']));
         $this->addWhereIf('rc_user = 0', isset($show['anon']));
         $this->addWhereIf('rc_user != 0', isset($show['!anon']));
         $this->addWhereIf('rc_patrolled = 0', isset($show['!patrolled']));
         $this->addWhereIf('rc_patrolled != 0', isset($show['patrolled']));
         $this->addWhereIf('page_is_redirect = 1', isset($show['redirect']));
         // Don't throw log entries out the window here
         $this->addWhereIf('page_is_redirect = 0 OR page_is_redirect IS NULL', isset($show['!redirect']));
     }
     /* Add the fields we're concerned with to out query. */
     $this->addFields(array('rc_timestamp', 'rc_namespace', 'rc_title', 'rc_cur_id', 'rc_type', 'rc_moved_to_ns', 'rc_moved_to_title'));
     /* Determine what properties we need to display. */
     if (!is_null($params['prop'])) {
         $prop = array_flip($params['prop']);
         /* Set up internal members based upon params. */
         $this->fld_comment = isset($prop['comment']);
         $this->fld_user = isset($prop['user']);
         $this->fld_flags = isset($prop['flags']);
         $this->fld_timestamp = isset($prop['timestamp']);
         $this->fld_title = isset($prop['title']);
         $this->fld_ids = isset($prop['ids']);
         $this->fld_sizes = isset($prop['sizes']);
         $this->fld_redirect = isset($prop['redirect']);
         $this->fld_patrolled = isset($prop['patrolled']);
         $this->fld_loginfo = isset($prop['loginfo']);
         global $wgUser;
         if ($this->fld_patrolled && !$wgUser->useRCPatrol() && !$wgUser->useNPPatrol()) {
             $this->dieUsage("You need the patrol right to request the patrolled flag", 'permissiondenied');
         }
         /* Add fields to our query if they are specified as a needed parameter. */
         $this->addFieldsIf('rc_id', $this->fld_ids);
         $this->addFieldsIf('rc_this_oldid', $this->fld_ids);
         $this->addFieldsIf('rc_last_oldid', $this->fld_ids);
         $this->addFieldsIf('rc_comment', $this->fld_comment);
         $this->addFieldsIf('rc_user', $this->fld_user);
         $this->addFieldsIf('rc_user_text', $this->fld_user);
         $this->addFieldsIf('rc_minor', $this->fld_flags);
         $this->addFieldsIf('rc_bot', $this->fld_flags);
         $this->addFieldsIf('rc_new', $this->fld_flags);
         $this->addFieldsIf('rc_old_len', $this->fld_sizes);
         $this->addFieldsIf('rc_new_len', $this->fld_sizes);
         $this->addFieldsIf('rc_patrolled', $this->fld_patrolled);
         $this->addFieldsIf('rc_logid', $this->fld_loginfo);
         $this->addFieldsIf('rc_log_type', $this->fld_loginfo);
         $this->addFieldsIf('rc_log_action', $this->fld_loginfo);
         $this->addFieldsIf('rc_params', $this->fld_loginfo);
         if ($this->fld_redirect || isset($show['redirect']) || isset($show['!redirect'])) {
             $this->addTables('page');
             $this->addJoinConds(array('page' => array('LEFT JOIN', array('rc_namespace=page_namespace', 'rc_title=page_title'))));
             $this->addFields('page_is_redirect');
         }
     }
     $this->token = $params['token'];
     $this->addOption('LIMIT', $params['limit'] + 1);
     $data = array();
     $count = 0;
     /* Perform the actual query. */
     $db = $this->getDB();
     $res = $this->select(__METHOD__);
     /* Iterate through the rows, adding data extracted from them to our query result. */
     while ($row = $db->fetchObject($res)) {
         if (++$count > $params['limit']) {
             // We've reached the one extra which shows that there are additional pages to be had. Stop here...
             $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->rc_timestamp));
             break;
         }
         /* Extract the data from a single row. */
         $vals = $this->extractRowInfo($row);
         /* Add that row's data to our final output. */
         if ($vals) {
             $data[] = $vals;
         }
     }
     $db->freeResult($res);
     /* Format the result */
     $result = $this->getResult();
     $result->setIndexedTagName($data, 'rc');
     $result->addValue('query', $this->getModuleName(), $data);
 }
 public function execute()
 {
     $user = $this->getUser();
     if ($user->isAnon()) {
         $this->dieUsage('Anonymous users cannot use watchlist change notifications', 'notloggedin');
     }
     $params = $this->extractRequestParams();
     $this->requireMaxOneParameter($params, 'timestamp', 'torevid', 'newerthanrevid');
     $pageSet = new ApiPageSet($this);
     $args = array_merge(array($params, 'entirewatchlist'), array_keys($pageSet->getAllowedParams()));
     call_user_func_array(array($this, 'requireOnlyOneParameter'), $args);
     $dbw = $this->getDB(DB_MASTER);
     $timestamp = null;
     if (isset($params['timestamp'])) {
         $timestamp = $dbw->timestamp($params['timestamp']);
     }
     if (!$params['entirewatchlist']) {
         $pageSet->execute();
     }
     if (isset($params['torevid'])) {
         if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) {
             $this->dieUsage('torevid may only be used with a single page', 'multpages');
         }
         $title = reset($pageSet->getGoodTitles());
         $timestamp = Revision::getTimestampFromId($title, $params['torevid']);
         if ($timestamp) {
             $timestamp = $dbw->timestamp($timestamp);
         } else {
             $timestamp = null;
         }
     } elseif (isset($params['newerthanrevid'])) {
         if ($params['entirewatchlist'] || $pageSet->getGoodTitleCount() > 1) {
             $this->dieUsage('newerthanrevid may only be used with a single page', 'multpages');
         }
         $title = reset($pageSet->getGoodTitles());
         $revid = $title->getNextRevisionID($params['newerthanrevid']);
         if ($revid) {
             $timestamp = $dbw->timestamp(Revision::getTimestampFromId($title, $revid));
         } else {
             $timestamp = null;
         }
     }
     $apiResult = $this->getResult();
     $result = array();
     if ($params['entirewatchlist']) {
         // Entire watchlist mode: Just update the thing and return a success indicator
         $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID()), __METHOD__);
         $result['notificationtimestamp'] = is_null($timestamp) ? '' : wfTimestamp(TS_ISO_8601, $timestamp);
     } else {
         // First, log the invalid titles
         foreach ($pageSet->getInvalidTitles() as $title) {
             $r = array();
             $r['title'] = $title;
             $r['invalid'] = '';
             $result[] = $r;
         }
         foreach ($pageSet->getMissingPageIDs() as $p) {
             $page = array();
             $page['pageid'] = $p;
             $page['missing'] = '';
             $page['notwatched'] = '';
             $result[] = $page;
         }
         foreach ($pageSet->getMissingRevisionIDs() as $r) {
             $rev = array();
             $rev['revid'] = $r;
             $rev['missing'] = '';
             $rev['notwatched'] = '';
             $result[] = $rev;
         }
         // Now process the valid titles
         $lb = new LinkBatch($pageSet->getTitles());
         $dbw->update('watchlist', array('wl_notificationtimestamp' => $timestamp), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__);
         // Query the results of our update
         $timestamps = array();
         $res = $dbw->select('watchlist', array('wl_namespace', 'wl_title', 'wl_notificationtimestamp'), array('wl_user' => $user->getID(), $lb->constructSet('wl', $dbw)), __METHOD__);
         foreach ($res as $row) {
             $timestamps[$row->wl_namespace][$row->wl_title] = $row->wl_notificationtimestamp;
         }
         // Now, put the valid titles into the result
         foreach ($pageSet->getTitles() as $title) {
             $ns = $title->getNamespace();
             $dbkey = $title->getDBkey();
             $r = array('ns' => intval($ns), 'title' => $title->getPrefixedText());
             if (!$title->exists()) {
                 $r['missing'] = '';
             }
             if (isset($timestamps[$ns]) && array_key_exists($dbkey, $timestamps[$ns])) {
                 $r['notificationtimestamp'] = '';
                 if ($timestamps[$ns][$dbkey] !== null) {
                     $r['notificationtimestamp'] = wfTimestamp(TS_ISO_8601, $timestamps[$ns][$dbkey]);
                 }
             } else {
                 $r['notwatched'] = '';
             }
             $result[] = $r;
         }
         $apiResult->setIndexedTagName($result, 'page');
     }
     $apiResult->addValue(null, $this->getModuleName(), $result);
 }
Exemplo n.º 28
0
 /**
  * Get the count of watchers and put it in $this->watchers
  */
 private function getWatcherInfo()
 {
     if (count($this->everything) == 0) {
         return;
     }
     $user = $this->getUser();
     $canUnwatchedpages = $user->isAllowed('unwatchedpages');
     $unwatchedPageThreshold = $this->getConfig()->get('UnwatchedPageThreshold');
     if (!$canUnwatchedpages && !is_int($unwatchedPageThreshold)) {
         return;
     }
     $this->watchers = array();
     $this->showZeroWatchers = $canUnwatchedpages;
     $db = $this->getDB();
     $lb = new LinkBatch($this->everything);
     $this->resetQueryParams();
     $this->addTables(array('watchlist'));
     $this->addFields(array('wl_title', 'wl_namespace', 'count' => 'COUNT(*)'));
     $this->addWhere(array($lb->constructSet('wl', $db)));
     $this->addOption('GROUP BY', array('wl_namespace', 'wl_title'));
     if (!$canUnwatchedpages) {
         $this->addOption('HAVING', "COUNT(*) >= {$unwatchedPageThreshold}");
     }
     $res = $this->select(__METHOD__);
     foreach ($res as $row) {
         $this->watchers[$row->wl_namespace][$row->wl_title] = (int) $row->count;
     }
 }
Exemplo n.º 29
0
 /**
  * Add an array of categories, with names in the keys
  *
  * @param array $categories Mapping category name => sort key
  */
 public function addCategoryLinks(array $categories)
 {
     global $wgContLang;
     if (!is_array($categories) || count($categories) == 0) {
         return;
     }
     # Add the links to a LinkBatch
     $arr = array(NS_CATEGORY => $categories);
     $lb = new LinkBatch();
     $lb->setArray($arr);
     # Fetch existence plus the hiddencat property
     $dbr = wfGetDB(DB_SLAVE);
     $fields = array('page_id', 'page_namespace', 'page_title', 'page_len', 'page_is_redirect', 'page_latest', 'pp_value');
     if ($this->getConfig()->get('ContentHandlerUseDB')) {
         $fields[] = 'page_content_model';
     }
     $res = $dbr->select(array('page', 'page_props'), $fields, $lb->constructSet('page', $dbr), __METHOD__, array(), array('page_props' => array('LEFT JOIN', array('pp_propname' => 'hiddencat', 'pp_page = page_id'))));
     # Add the results to the link cache
     $lb->addResultToCache(LinkCache::singleton(), $res);
     # Set all the values to 'normal'.
     $categories = array_fill_keys(array_keys($categories), 'normal');
     # Mark hidden categories
     foreach ($res as $row) {
         if (isset($row->pp_value)) {
             $categories[$row->page_title] = 'hidden';
         }
     }
     # Add the remaining categories to the skin
     if (Hooks::run('OutputPageMakeCategoryLinks', array(&$this, $categories, &$this->mCategoryLinks))) {
         foreach ($categories as $category => $type) {
             $origcategory = $category;
             $title = Title::makeTitleSafe(NS_CATEGORY, $category);
             if (!$title) {
                 continue;
             }
             $wgContLang->findVariantLink($category, $title, true);
             if ($category != $origcategory && array_key_exists($category, $categories)) {
                 continue;
             }
             $text = $wgContLang->convertHtml($title->getText());
             $this->mCategories[] = $title->getText();
             $this->mCategoryLinks[$type][] = Linker::link($title, $text);
         }
     }
 }
Exemplo n.º 30
0
 /**
  * Add an array of categories, with names in the keys
  *
  * @param array $categories Mapping category name => sort key
  */
 public function addCategoryLinks(array $categories)
 {
     global $wgContLang;
     if (!is_array($categories) || count($categories) == 0) {
         return;
     }
     # Add the links to a LinkBatch
     $arr = [NS_CATEGORY => $categories];
     $lb = new LinkBatch();
     $lb->setArray($arr);
     # Fetch existence plus the hiddencat property
     $dbr = wfGetDB(DB_REPLICA);
     $fields = array_merge(LinkCache::getSelectFields(), ['page_namespace', 'page_title', 'pp_value']);
     $res = $dbr->select(['page', 'page_props'], $fields, $lb->constructSet('page', $dbr), __METHOD__, [], ['page_props' => ['LEFT JOIN', ['pp_propname' => 'hiddencat', 'pp_page = page_id']]]);
     # Add the results to the link cache
     $lb->addResultToCache(LinkCache::singleton(), $res);
     # Set all the values to 'normal'.
     $categories = array_fill_keys(array_keys($categories), 'normal');
     # Mark hidden categories
     foreach ($res as $row) {
         if (isset($row->pp_value)) {
             $categories[$row->page_title] = 'hidden';
         }
     }
     # Add the remaining categories to the skin
     if (Hooks::run('OutputPageMakeCategoryLinks', [&$this, $categories, &$this->mCategoryLinks])) {
         foreach ($categories as $category => $type) {
             // array keys will cast numeric category names to ints, so cast back to string
             $category = (string) $category;
             $origcategory = $category;
             $title = Title::makeTitleSafe(NS_CATEGORY, $category);
             if (!$title) {
                 continue;
             }
             $wgContLang->findVariantLink($category, $title, true);
             if ($category != $origcategory && array_key_exists($category, $categories)) {
                 continue;
             }
             $text = $wgContLang->convertHtml($title->getText());
             $this->mCategories[] = $title->getText();
             $this->mCategoryLinks[$type][] = Linker::link($title, $text);
         }
     }
 }