/** * If patrol is possible, output a patrol UI box. This is called from the * footer section of ordinary page views. If patrol is not possible or not * desired, does nothing. * Side effect: When the patrol link is build, this method will call * OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax. * * @return bool */ public function showPatrolFooter() { global $wgUseNPPatrol, $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI; $outputPage = $this->getContext()->getOutput(); $user = $this->getContext()->getUser(); $cache = wfGetMainCache(); $rc = false; if (!$this->getTitle()->quickUserCan('patrol', $user) || !($wgUseRCPatrol || $wgUseNPPatrol)) { // Patrolling is disabled or the user isn't allowed to return false; } // New page patrol: Get the timestamp of the oldest revison which // the revision table holds for the given page. Then we look // whether it's within the RC lifespan and if it is, we try // to get the recentchanges row belonging to that entry // (with rc_new = 1). if ($this->mRevision && !RecentChange::isInRCLifespan($this->mRevision->getTimestamp(), 21600)) { // The current revision is already older than what could be in the RC table // 6h tolerance because the RC might not be cleaned out regularly return false; } // Check for cached results $key = wfMemcKey('NotPatrollablePage', $this->getTitle()->getArticleID()); if ($cache->get($key)) { return false; } $dbr = wfGetDB(DB_SLAVE); $oldestRevisionTimestamp = $dbr->selectField('revision', 'MIN( rev_timestamp )', array('rev_page' => $this->getTitle()->getArticleID()), __METHOD__); if ($oldestRevisionTimestamp && RecentChange::isInRCLifespan($oldestRevisionTimestamp, 21600)) { // 6h tolerance because the RC might not be cleaned out regularly $rc = RecentChange::newFromConds(array('rc_new' => 1, 'rc_timestamp' => $oldestRevisionTimestamp, 'rc_namespace' => $this->getTitle()->getNamespace(), 'rc_cur_id' => $this->getTitle()->getArticleID()), __METHOD__, array('USE INDEX' => 'new_name_timestamp')); } else { // Cache the information we gathered above in case we can't patrol // Don't cache in case we can patrol as this could change $cache->set($key, '1'); } if (!$rc) { // Don't cache: This can be hit if the page gets accessed very fast after // its creation or in case we have high slave lag. In case the revision is // too old, we will already return above. return false; } if ($rc->getAttribute('rc_patrolled')) { // Patrolled RC entry around // Cache the information we gathered above in case we can't patrol // Don't cache in case we can patrol as this could change $cache->set($key, '1'); return false; } if ($rc->getPerformer()->equals($user)) { // Don't show a patrol link for own creations. If the user could // patrol them, they already would be patrolled return false; } $rcid = $rc->getAttribute('rc_id'); $token = $user->getEditToken($rcid); $outputPage->preventClickjacking(); if ($wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed('writeapi')) { $outputPage->addModules('mediawiki.page.patrol.ajax'); } $link = Linker::linkKnown($this->getTitle(), wfMessage('markaspatrolledtext')->escaped(), array(), array('action' => 'markpatrolled', 'rcid' => $rcid, 'token' => $token)); $outputPage->addHTML("<div class='patrollink'>" . wfMessage('markaspatrolledlink')->rawParams($link)->escaped() . '</div>'); return true; }
/** * Returns an array of meta data needed to build a "mark as patrolled" link and * adds the mediawiki.page.patrol.ajax to the output. * * @return array|false An array of meta data for a patrol link (rcid & token) * or false if no link is needed */ protected function getMarkPatrolledLinkInfo() { global $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI; $user = $this->getUser(); // Prepare a change patrol link, if applicable if ($wgUseRCPatrol && $this->mNewPage->quickUserCan('patrol', $user) && RecentChange::isInRCLifespan($this->mNewRev->getTimestamp(), 21600)) { // Look for an unpatrolled change corresponding to this diff $db = wfGetDB(DB_SLAVE); $change = RecentChange::newFromConds(array('rc_timestamp' => $db->timestamp($this->mNewRev->getTimestamp()), 'rc_this_oldid' => $this->mNewid, 'rc_patrolled' => 0), __METHOD__); if ($change && !$change->getPerformer()->equals($user)) { $rcid = $change->getAttribute('rc_id'); } else { // None found or the page has been created by the current user. // If the user could patrol this it already would be patrolled $rcid = 0; } // Build the link if ($rcid) { $this->getOutput()->preventClickjacking(); if ($wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed('writeapi')) { $this->getOutput()->addModules('mediawiki.page.patrol.ajax'); } $token = $user->getEditToken($rcid); return array('rcid' => $rcid, 'token' => $token); } } // No mark as patrolled link applicable return false; }
/** * @covers RecentChange::isInRCLifespan * @dataProvider provideIsInRCLifespan */ public function testIsInRCLifespan($maxAge, $timestamp, $tolerance, $expected) { $this->setMwGlobals('wgRCMaxAge', $maxAge); $this->assertEquals($expected, RecentChange::isInRCLifespan($timestamp, $tolerance)); }
/** * Get a link to mark the change as patrolled, or '' if there's either no * revision to patrol or the user is not allowed to to it. * Side effect: When the patrol link is build, this method will call * OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax. * * @return string */ protected function markPatrolledLink() { global $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI; $user = $this->getUser(); if ($this->mMarkPatrolledLink === null) { // Prepare a change patrol link, if applicable if ($wgUseRCPatrol && $this->mNewPage->quickUserCan('patrol', $user) && RecentChange::isInRCLifespan($this->mNewRev->getTimestamp(), 21600)) { // Look for an unpatrolled change corresponding to this diff $db = wfGetDB(DB_SLAVE); $change = RecentChange::newFromConds(array('rc_timestamp' => $db->timestamp($this->mNewRev->getTimestamp()), 'rc_this_oldid' => $this->mNewid, 'rc_patrolled' => 0), __METHOD__, array('USE INDEX' => 'rc_timestamp')); if ($change && $change->getPerformer()->getName() !== $user->getName()) { $rcid = $change->getAttribute('rc_id'); } else { // None found or the page has been created by the current user. // If the user could patrol this it already would be patrolled $rcid = 0; } // Build the link if ($rcid) { $this->getOutput()->preventClickjacking(); if ($wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed('writeapi')) { $this->getOutput()->addModules('mediawiki.page.patrol.ajax'); } $token = $user->getEditToken($rcid); $this->mMarkPatrolledLink = ' <span class="patrollink">[' . Linker::linkKnown($this->mNewPage, $this->msg('markaspatrolleddiff')->escaped(), array(), array('action' => 'markpatrolled', 'rcid' => $rcid, 'token' => $token)) . ']</span>'; } else { $this->mMarkPatrolledLink = ''; } } else { $this->mMarkPatrolledLink = ''; } } return $this->mMarkPatrolledLink; }
/** * If patrol is possible, output a patrol UI box. This is called from the * footer section of ordinary page views. If patrol is not possible or not * desired, does nothing. * Side effect: When the patrol link is build, this method will call * OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax. * * @return bool */ public function showPatrolFooter() { global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI; $outputPage = $this->getContext()->getOutput(); $user = $this->getContext()->getUser(); $title = $this->getTitle(); $rc = false; if (!$title->quickUserCan('patrol', $user) || !($wgUseRCPatrol || $wgUseNPPatrol || $wgUseFilePatrol && $title->inNamespace(NS_FILE))) { // Patrolling is disabled or the user isn't allowed to return false; } if ($this->mRevision && !RecentChange::isInRCLifespan($this->mRevision->getTimestamp(), 21600)) { // The current revision is already older than what could be in the RC table // 6h tolerance because the RC might not be cleaned out regularly return false; } // Check for cached results $key = wfMemcKey('unpatrollable-page', $title->getArticleID()); $cache = ObjectCache::getMainWANInstance(); if ($cache->get($key)) { return false; } $dbr = wfGetDB(DB_SLAVE); $oldestRevisionTimestamp = $dbr->selectField('revision', 'MIN( rev_timestamp )', ['rev_page' => $title->getArticleID()], __METHOD__); // New page patrol: Get the timestamp of the oldest revison which // the revision table holds for the given page. Then we look // whether it's within the RC lifespan and if it is, we try // to get the recentchanges row belonging to that entry // (with rc_new = 1). $recentPageCreation = false; if ($oldestRevisionTimestamp && RecentChange::isInRCLifespan($oldestRevisionTimestamp, 21600)) { // 6h tolerance because the RC might not be cleaned out regularly $recentPageCreation = true; $rc = RecentChange::newFromConds(['rc_new' => 1, 'rc_timestamp' => $oldestRevisionTimestamp, 'rc_namespace' => $title->getNamespace(), 'rc_cur_id' => $title->getArticleID()], __METHOD__); if ($rc) { // Use generic patrol message for new pages $markPatrolledMsg = wfMessage('markaspatrolledtext'); } } // File patrol: Get the timestamp of the latest upload for this page, // check whether it is within the RC lifespan and if it is, we try // to get the recentchanges row belonging to that entry // (with rc_type = RC_LOG, rc_log_type = upload). $recentFileUpload = false; if ((!$rc || $rc->getAttribute('rc_patrolled')) && $wgUseFilePatrol && $title->getNamespace() === NS_FILE) { // Retrieve timestamp of most recent upload $newestUploadTimestamp = $dbr->selectField('image', 'MAX( img_timestamp )', ['img_name' => $title->getDBkey()], __METHOD__); if ($newestUploadTimestamp && RecentChange::isInRCLifespan($newestUploadTimestamp, 21600)) { // 6h tolerance because the RC might not be cleaned out regularly $recentFileUpload = true; $rc = RecentChange::newFromConds(['rc_type' => RC_LOG, 'rc_log_type' => 'upload', 'rc_timestamp' => $newestUploadTimestamp, 'rc_namespace' => NS_FILE, 'rc_cur_id' => $title->getArticleID()], __METHOD__, ['USE INDEX' => 'rc_timestamp']); if ($rc) { // Use patrol message specific to files $markPatrolledMsg = wfMessage('markaspatrolledtext-file'); } } } if (!$recentPageCreation && !$recentFileUpload) { // Page creation and latest upload (for files) is too old to be in RC // We definitely can't patrol so cache the information // When a new file version is uploaded, the cache is cleared $cache->set($key, '1'); return false; } if (!$rc) { // Don't cache: This can be hit if the page gets accessed very fast after // its creation / latest upload or in case we have high slave lag. In case // the revision is too old, we will already return above. return false; } if ($rc->getAttribute('rc_patrolled')) { // Patrolled RC entry around // Cache the information we gathered above in case we can't patrol // Don't cache in case we can patrol as this could change $cache->set($key, '1'); return false; } if ($rc->getPerformer()->equals($user)) { // Don't show a patrol link for own creations/uploads. If the user could // patrol them, they already would be patrolled return false; } $rcid = $rc->getAttribute('rc_id'); $token = $user->getEditToken($rcid); $outputPage->preventClickjacking(); if ($wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed('writeapi')) { $outputPage->addModules('mediawiki.page.patrol.ajax'); } $link = Linker::linkKnown($title, $markPatrolledMsg->escaped(), [], ['action' => 'markpatrolled', 'rcid' => $rcid, 'token' => $token]); $outputPage->addHTML("<div class='patrollink' data-mw='interface'>" . wfMessage('markaspatrolledlink')->rawParams($link)->escaped() . '</div>'); return true; }
/** * Returns an array of meta data needed to build a "mark as patrolled" link and * adds the mediawiki.page.patrol.ajax to the output. * * @return array|false An array of meta data for a patrol link (rcid & token) * or false if no link is needed */ protected function getMarkPatrolledLinkInfo() { global $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI; $user = $this->getUser(); // Prepare a change patrol link, if applicable if ($wgUseRCPatrol && $this->mNewPage->quickUserCan('patrol', $user) && RecentChange::isInRCLifespan($this->mNewRev->getTimestamp(), 21600)) { // Look for an unpatrolled change corresponding to this diff $db = wfGetDB(DB_REPLICA); $change = RecentChange::newFromConds(['rc_timestamp' => $db->timestamp($this->mNewRev->getTimestamp()), 'rc_this_oldid' => $this->mNewid, 'rc_patrolled' => 0], __METHOD__); if ($change && !$change->getPerformer()->equals($user)) { $rcid = $change->getAttribute('rc_id'); } else { // None found or the page has been created by the current user. // If the user could patrol this it already would be patrolled $rcid = 0; } // Allow extensions to possibly change the rcid here // For example the rcid might be set to zero due to the user // being the same as the performer of the change but an extension // might still want to show it under certain conditions Hooks::run('DifferenceEngineMarkPatrolledRCID', [&$rcid, $this, $change, $user]); // Build the link if ($rcid) { $this->getOutput()->preventClickjacking(); if ($wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed('writeapi')) { $this->getOutput()->addModules('mediawiki.page.patrol.ajax'); } $token = $user->getEditToken($rcid); return ['rcid' => $rcid, 'token' => $token]; } } // No mark as patrolled link applicable return false; }
public function markPatrolledLink() { global $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI; $user = $this->getUser(); if ($this->mMarkPatrolledLink === null) { // Prepare a change patrol link, if applicable if ($wgUseRCPatrol && $this->mNewPage->quickUserCan('patrol', $user) && RecentChange::isInRCLifespan($this->mNewRev->getTimestamp(), 21600)) { // Look for an unpatrolled change corresponding to this diff $db = wfGetDB(DB_SLAVE); $change = RecentChange::newFromConds(array('rc_timestamp' => $db->timestamp($this->mNewRev->getTimestamp()), 'rc_this_oldid' => $this->mNewid, 'rc_patrolled' => 0), __METHOD__, array('USE INDEX' => 'rc_timestamp')); if ($change && $change->getPerformer()->getName() !== $user->getName()) { $rcid = $change->getAttribute('rc_id'); } else { // None found or the page has been created by the current user. // If the user could patrol this it already would be patrolled $rcid = 0; } // WIKIHOW - we might want to change the rcid here // for example the rcid might be set to zero due to the user being the same // as the performer of the change but on wikihow we still want to show it // under certain conditions wfRunHooks('DifferenceEngineMarkPatrolledRCID', array(&$rcid, $this, $change, $user)); // Build the link if ($rcid) { $this->getOutput()->preventClickjacking(); if ($wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed('writeapi')) { $this->getOutput()->addModules('mediawiki.page.patrol.ajax'); } $token = $user->getEditToken($rcid); $this->mMarkPatrolledLink = ' <span class="patrollink">[' . Linker::linkKnown($this->mNewPage, $this->msg('markaspatrolleddiff')->escaped(), array(), array('action' => 'markpatrolled', 'rcid' => $rcid, 'token' => $token)) . ']</span>'; // WIKIHOW - added this hook to change the markpatrolled link wfRunHooks('DifferenceEngineMarkPatrolledLink', array($this, &$this->mMarkPatrolledLink, $rcid, $token)); } else { $this->mMarkPatrolledLink = ''; } } else { $this->mMarkPatrolledLink = ''; } } return $this->mMarkPatrolledLink; }