/**
  * @since 2.2
  */
 public function getEditInfo(Title $title)
 {
     $this->page = new \WikiPage($title);
     if (class_exists('WikitextContent')) {
         $content = $this->page->getRevision()->getContent();
         $format = $content->getContentHandler()->getDefaultFormat();
         return $this->page->prepareContentForEdit($content, null, null, $format);
     }
     return $this->page->prepareTextForEdit($this->page->getRevision()->getRawText(), null, null);
 }
 /**
  * @param WikiPage $page
  * @param Content $content
  * @param User $user
  * @return integer ApiStashEdit::ERROR_* constant
  * @since 1.25
  */
 public static function parseAndStash(WikiPage $page, Content $content, User $user)
 {
     global $wgMemc;
     $format = $content->getDefaultFormat();
     $editInfo = $page->prepareContentForEdit($content, null, $user, $format, false);
     if ($editInfo && $editInfo->output) {
         $key = self::getStashKey($page->getTitle(), $content, $user);
         list($stashInfo, $ttl) = self::buildStashValue($editInfo->pstContent, $editInfo->output, $editInfo->timestamp);
         if ($stashInfo) {
             $ok = $wgMemc->set($key, $stashInfo, $ttl);
             if ($ok) {
                 wfDebugLog('StashEdit', "Cached parser output for key '{$key}'.");
                 return self::ERROR_NONE;
             } else {
                 wfDebugLog('StashEdit', "Failed to cache parser output for key '{$key}'.");
                 return self::ERROR_CACHE;
             }
         } else {
             wfDebugLog('StashEdit', "Uncacheable parser output for key '{$key}'.");
             return self::ERROR_UNCACHEABLE;
         }
     }
     return self::ERROR_PARSE;
 }
Example #3
0
 /**
  * @param WikiPage $page
  * @param Content $content
  * @param User $user
  * @return integer ApiStashEdit::ERROR_* constant
  * @since 1.25
  */
 public static function parseAndStash(WikiPage $page, Content $content, User $user)
 {
     $cache = ObjectCache::getLocalClusterInstance();
     $logger = LoggerFactory::getInstance('StashEdit');
     $format = $content->getDefaultFormat();
     $editInfo = $page->prepareContentForEdit($content, null, $user, $format, false);
     if ($editInfo && $editInfo->output) {
         $key = self::getStashKey($page->getTitle(), $content, $user);
         list($stashInfo, $ttl) = self::buildStashValue($editInfo->pstContent, $editInfo->output, $editInfo->timestamp);
         if ($stashInfo) {
             $ok = $cache->set($key, $stashInfo, $ttl);
             if ($ok) {
                 $logger->debug("Cached parser output for key '{$key}'.");
                 return self::ERROR_NONE;
             } else {
                 $logger->error("Failed to cache parser output for key '{$key}'.");
                 return self::ERROR_CACHE;
             }
         } else {
             $logger->info("Uncacheable parser output for key '{$key}'.");
             return self::ERROR_UNCACHEABLE;
         }
     }
     return self::ERROR_PARSE;
 }
 /**
  * Automatically review an revision and add a log entry in the review log.
  *
  * This is called during edit operations after the new revision is added
  * and the page tables updated, but before LinksUpdate is called.
  *
  * $auto is here for revisions checked off to be reviewed. Auto-review
  * triggers on edit, but we don't want those to count as just automatic.
  * This also makes it so the user's name shows up in the page history.
  *
  * If $flags is given, then they will be the review tags. If not, the one
  * from the stable version will be used or minimal tags if that's not possible.
  * If no appropriate tags can be found, then the review will abort.
  */
 public static function autoReviewEdit(WikiPage $article, $user, Revision $rev, array $flags = null, $auto = true)
 {
     wfProfileIn(__METHOD__);
     $title = $article->getTitle();
     // convenience
     # Get current stable version ID (for logging)
     $oldSv = FlaggedRevision::newFromStable($title, FR_MASTER);
     $oldSvId = $oldSv ? $oldSv->getRevId() : 0;
     # Set the auto-review tags from the prior stable version.
     # Normally, this should already be done and given here...
     if (!is_array($flags)) {
         if ($oldSv) {
             # Use the last stable version if $flags not given
             if ($user->isAllowed('bot')) {
                 $flags = $oldSv->getTags();
                 // no change for bot edits
             } else {
                 # Account for perms/tags...
                 $flags = self::getAutoReviewTags($user, $oldSv->getTags());
             }
         } else {
             // new page?
             $flags = self::quickTags(FR_CHECKED);
             // use minimal level
         }
         if (!is_array($flags)) {
             wfProfileOut(__METHOD__);
             return false;
             // can't auto-review this revision
         }
     }
     # Get review property flags
     $propFlags = $auto ? array('auto') : array();
     # Note: this needs to match the prepareContentForEdit() call WikiPage::doEditContent.
     # This is for consistency and also to avoid triggering a second parse otherwise.
     $editInfo = $article->prepareContentForEdit($rev->getContent(), null, $user, $rev->getContentFormat());
     $poutput = $editInfo->output;
     // revision HTML output
     # Get the "review time" versions of templates and files.
     # This tries to make sure each template/file version either came from the stable
     # version of that template/file or was a "review time" version used in the stable
     # version of this page. If a pending version of a template/file is currently vandalism,
     # we try to avoid storing its ID as the "review time" version so it won't show up when
     # someone views the page. If not possible, this stores the current template/file.
     if (FlaggedRevs::inclusionSetting() === FR_INCLUDES_CURRENT) {
         $tVersions = $poutput->getTemplateIds();
         $fVersions = $poutput->getFileSearchOptions();
     } else {
         $tVersions = $oldSv ? $oldSv->getTemplateVersions() : array();
         $fVersions = $oldSv ? $oldSv->getFileVersions() : array();
         foreach ($poutput->getTemplateIds() as $ns => $pages) {
             foreach ($pages as $dbKey => $revId) {
                 if (!isset($tVersions[$ns][$dbKey])) {
                     $srev = FlaggedRevision::newFromStable(Title::makeTitle($ns, $dbKey));
                     if ($srev) {
                         // use stable
                         $tVersions[$ns][$dbKey] = $srev->getRevId();
                     } else {
                         // use current
                         $tVersions[$ns][$dbKey] = $revId;
                     }
                 }
             }
         }
         foreach ($poutput->getFileSearchOptions() as $dbKey => $info) {
             if (!isset($fVersions[$dbKey])) {
                 $srev = FlaggedRevision::newFromStable(Title::makeTitle(NS_FILE, $dbKey));
                 if ($srev && $srev->getFileTimestamp()) {
                     // use stable
                     $fVersions[$dbKey]['time'] = $srev->getFileTimestamp();
                     $fVersions[$dbKey]['sha1'] = $srev->getFileSha1();
                 } else {
                     // use current
                     $fVersions[$dbKey]['time'] = $info['time'];
                     $fVersions[$dbKey]['sha1'] = $info['sha1'];
                 }
             }
         }
     }
     # If this is an image page, get the corresponding file version info...
     $fileData = array('name' => null, 'timestamp' => null, 'sha1' => null);
     if ($title->getNamespace() == NS_FILE) {
         # We must use WikiFilePage process cache on upload or get bitten by slave lag
         $file = $article instanceof WikiFilePage || $article instanceof ImagePage ? $article->getFile() : wfFindFile($title, array('bypassCache' => true));
         // skip cache; bug 31056
         if (is_object($file) && $file->exists()) {
             $fileData['name'] = $title->getDBkey();
             $fileData['timestamp'] = $file->getTimestamp();
             $fileData['sha1'] = $file->getSha1();
         }
     }
     # Our review entry
     $flaggedRevision = new FlaggedRevision(array('rev' => $rev, 'user_id' => $user->getId(), 'timestamp' => $rev->getTimestamp(), 'quality' => FlaggedRevs::getQualityTier($flags, 0), 'tags' => FlaggedRevision::flattenRevisionTags($flags), 'img_name' => $fileData['name'], 'img_timestamp' => $fileData['timestamp'], 'img_sha1' => $fileData['sha1'], 'templateVersions' => $tVersions, 'fileVersions' => $fVersions, 'flags' => implode(',', $propFlags)));
     $flaggedRevision->insert();
     # Update the article review log
     FlaggedRevsLog::updateReviewLog($title, $flags, array(), '', $rev->getId(), $oldSvId, true, $auto);
     # Update page and tracking tables and clear cache
     FlaggedRevs::stableVersionUpdates($article);
     wfProfileOut(__METHOD__);
     return true;
 }
Example #5
0
 /**
  * @param WikiPage $page
  * @param $content Content|string
  * @param $section string
  * @param $isContent bool If true, $content is a Content object
  * @param $oldtext string The content of the revision prior to $content.  When 
  *  null this will be loaded from the database.
  * @return bool true if the captcha should run
  */
 function shouldCheck(WikiPage $page, $content, $section, $isContent = false, $oldtext = null)
 {
     $title = $page->getTitle();
     $this->trigger = '';
     if ($oldtext === null) {
         global $wgRequest;
         $loadOldtextFlags = $wgRequest->wasPosted() ? Revision::READ_LATEST : Revision::READ_NORMAL;
     }
     if ($isContent) {
         if ($content->getModel() == CONTENT_MODEL_WIKITEXT) {
             $newtext = $content->getNativeData();
         } else {
             $newtext = null;
         }
         $isEmpty = $content->isEmpty();
     } else {
         $newtext = $content;
         $isEmpty = $content === '';
     }
     global $wgUser;
     if ($wgUser->isAllowed('skipcaptcha')) {
         wfDebug("ConfirmEdit: user group allows skipping captcha\n");
         return false;
     }
     if ($this->isIPWhitelisted()) {
         return false;
     }
     global $wgEmailAuthentication, $ceAllowConfirmedEmail;
     if ($wgEmailAuthentication && $ceAllowConfirmedEmail && $wgUser->isEmailConfirmed()) {
         wfDebug("ConfirmEdit: user has confirmed mail, skipping captcha\n");
         return false;
     }
     if ($this->captchaTriggers($title, 'edit')) {
         // Check on all edits
         global $wgUser;
         $this->trigger = sprintf("edit trigger by '%s' at [[%s]]", $wgUser->getName(), $title->getPrefixedText());
         $this->action = 'edit';
         wfDebug("ConfirmEdit: checking all edits...\n");
         return true;
     }
     if ($this->captchaTriggers($title, 'create') && !$title->exists()) {
         // Check if creating a page
         global $wgUser;
         $this->trigger = sprintf("Create trigger by '%s' at [[%s]]", $wgUser->getName(), $title->getPrefixedText());
         $this->action = 'create';
         wfDebug("ConfirmEdit: checking on page creation...\n");
         return true;
     }
     if (!$isEmpty && $this->captchaTriggers($title, 'addurl')) {
         // Only check edits that add URLs
         if ($isContent) {
             // Get links from the database
             $oldLinks = $this->getLinksFromTracker($title);
             // Share a parse operation with Article::doEdit()
             $editInfo = $page->prepareContentForEdit($content);
             if ($editInfo->output) {
                 $newLinks = array_keys($editInfo->output->getExternalLinks());
             } else {
                 $newLinks = array();
             }
         } else {
             // Get link changes in the slowest way known to man
             $oldtext = isset($oldtext) ? $oldtext : $this->loadText($title, $section, $loadOldtextFlags);
             $oldLinks = $this->findLinks($title, $oldtext);
             $newLinks = $this->findLinks($title, $newtext);
         }
         $unknownLinks = array_filter($newLinks, array(&$this, 'filterLink'));
         $addedLinks = array_diff($unknownLinks, $oldLinks);
         $numLinks = count($addedLinks);
         if ($numLinks > 0) {
             global $wgUser;
             $this->trigger = sprintf("%dx url trigger by '%s' at [[%s]]: %s", $numLinks, $wgUser->getName(), $title->getPrefixedText(), implode(", ", $addedLinks));
             $this->action = 'addurl';
             return true;
         }
     }
     global $wgCaptchaRegexes;
     if ($newtext !== null && $wgCaptchaRegexes) {
         // Custom regex checks. Reuse $oldtext if set above.
         $oldtext = isset($oldtext) ? $oldtext : $this->loadText($title, $section, $loadOldtextFlags);
         foreach ($wgCaptchaRegexes as $regex) {
             $newMatches = array();
             if (preg_match_all($regex, $newtext, $newMatches)) {
                 $oldMatches = array();
                 preg_match_all($regex, $oldtext, $oldMatches);
                 $addedMatches = array_diff($newMatches[0], $oldMatches[0]);
                 $numHits = count($addedMatches);
                 if ($numHits > 0) {
                     global $wgUser;
                     $this->trigger = sprintf("%dx %s at [[%s]]: %s", $numHits, $regex, $wgUser->getName(), $title->getPrefixedText(), implode(", ", $addedMatches));
                     $this->action = 'edit';
                     return true;
                 }
             }
         }
     }
     return false;
 }
Example #6
0
 /**
  * @param WikiPage $page
  * @param $content Content|string
  * @param $section string
  * @param IContextSource $context
  * @param $oldtext string The content of the revision prior to $content.  When
  *  null this will be loaded from the database.
  * @return bool true if the captcha should run
  */
 function shouldCheck(WikiPage $page, $content, $section, $context, $oldtext = null)
 {
     global $ceAllowConfirmedEmail;
     if (!$context instanceof IContextSource) {
         $context = RequestContext::getMain();
     }
     $request = $context->getRequest();
     $user = $context->getUser();
     // captcha check exceptions, which will return always false
     if ($user->isAllowed('skipcaptcha')) {
         wfDebug("ConfirmEdit: user group allows skipping captcha\n");
         return false;
     } elseif ($this->isIPWhitelisted()) {
         wfDebug("ConfirmEdit: user IP is whitelisted");
         return false;
     } elseif ($ceAllowConfirmedEmail && $user->isEmailConfirmed()) {
         wfDebug("ConfirmEdit: user has confirmed mail, skipping captcha\n");
         return false;
     }
     $title = $page->getTitle();
     $this->trigger = '';
     if ($content instanceof Content) {
         if ($content->getModel() == CONTENT_MODEL_WIKITEXT) {
             $newtext = $content->getNativeData();
         } else {
             $newtext = null;
         }
         $isEmpty = $content->isEmpty();
     } else {
         $newtext = $content;
         $isEmpty = $content === '';
     }
     if ($this->captchaTriggers($title, 'edit')) {
         // Check on all edits
         $this->trigger = sprintf("edit trigger by '%s' at [[%s]]", $user->getName(), $title->getPrefixedText());
         $this->action = 'edit';
         wfDebug("ConfirmEdit: checking all edits...\n");
         return true;
     }
     if ($this->captchaTriggers($title, 'create') && !$title->exists()) {
         // Check if creating a page
         $this->trigger = sprintf("Create trigger by '%s' at [[%s]]", $user->getName(), $title->getPrefixedText());
         $this->action = 'create';
         wfDebug("ConfirmEdit: checking on page creation...\n");
         return true;
     }
     // The following checks are expensive and should be done only, if we can assume, that the edit will be saved
     if (!$request->wasPosted()) {
         wfDebug("ConfirmEdit: request not posted, assuming that no content will be saved -> no CAPTCHA check");
         return false;
     }
     if (!$isEmpty && $this->captchaTriggers($title, 'addurl')) {
         // Only check edits that add URLs
         if ($content instanceof Content) {
             // Get links from the database
             $oldLinks = $this->getLinksFromTracker($title);
             // Share a parse operation with Article::doEdit()
             $editInfo = $page->prepareContentForEdit($content);
             if ($editInfo->output) {
                 $newLinks = array_keys($editInfo->output->getExternalLinks());
             } else {
                 $newLinks = array();
             }
         } else {
             // Get link changes in the slowest way known to man
             $oldtext = isset($oldtext) ? $oldtext : $this->loadText($title, $section);
             $oldLinks = $this->findLinks($title, $oldtext);
             $newLinks = $this->findLinks($title, $newtext);
         }
         $unknownLinks = array_filter($newLinks, array(&$this, 'filterLink'));
         $addedLinks = array_diff($unknownLinks, $oldLinks);
         $numLinks = count($addedLinks);
         if ($numLinks > 0) {
             $this->trigger = sprintf("%dx url trigger by '%s' at [[%s]]: %s", $numLinks, $user->getName(), $title->getPrefixedText(), implode(", ", $addedLinks));
             $this->action = 'addurl';
             return true;
         }
     }
     global $wgCaptchaRegexes;
     if ($newtext !== null && $wgCaptchaRegexes) {
         if (!is_array($wgCaptchaRegexes)) {
             throw new UnexpectedValueException('$wgCaptchaRegexes is required to be an array, ' . gettype($wgCaptchaRegexes) . ' given.');
         }
         // Custom regex checks. Reuse $oldtext if set above.
         $oldtext = isset($oldtext) ? $oldtext : $this->loadText($title, $section);
         foreach ($wgCaptchaRegexes as $regex) {
             $newMatches = array();
             if (preg_match_all($regex, $newtext, $newMatches)) {
                 $oldMatches = array();
                 preg_match_all($regex, $oldtext, $oldMatches);
                 $addedMatches = array_diff($newMatches[0], $oldMatches[0]);
                 $numHits = count($addedMatches);
                 if ($numHits > 0) {
                     $this->trigger = sprintf("%dx %s at [[%s]]: %s", $numHits, $regex, $user->getName(), $title->getPrefixedText(), implode(", ", $addedMatches));
                     $this->action = 'edit';
                     return true;
                 }
             }
         }
     }
     return false;
 }
Example #7
0
 /**
  * @param WikiPage $page
  * @param Content $content Edit content
  * @param User $user
  * @param string $summary Edit summary
  * @return integer ApiStashEdit::ERROR_* constant
  * @since 1.25
  */
 public static function parseAndStash(WikiPage $page, Content $content, User $user, $summary)
 {
     $cache = ObjectCache::getLocalClusterInstance();
     $logger = LoggerFactory::getInstance('StashEdit');
     $title = $page->getTitle();
     $key = self::getStashKey($title, self::getContentHash($content), $user);
     // Use the master DB for fast blocking locks
     $dbw = wfGetDB(DB_MASTER);
     if (!$dbw->lock($key, __METHOD__, 1)) {
         // De-duplicate requests on the same key
         return self::ERROR_BUSY;
     }
     /** @noinspection PhpUnusedLocalVariableInspection */
     $unlocker = new ScopedCallback(function () use($dbw, $key) {
         $dbw->unlock($key, __METHOD__);
     });
     $cutoffTime = time() - self::PRESUME_FRESH_TTL_SEC;
     // Reuse any freshly build matching edit stash cache
     $editInfo = $cache->get($key);
     if ($editInfo && wfTimestamp(TS_UNIX, $editInfo->timestamp) >= $cutoffTime) {
         $alreadyCached = true;
     } else {
         $format = $content->getDefaultFormat();
         $editInfo = $page->prepareContentForEdit($content, null, $user, $format, false);
         $alreadyCached = false;
     }
     if ($editInfo && $editInfo->output) {
         // Let extensions add ParserOutput metadata or warm other caches
         Hooks::run('ParserOutputStashForEdit', [$page, $content, $editInfo->output, $summary, $user]);
         if ($alreadyCached) {
             $logger->debug("Already cached parser output for key '{$key}' ('{$title}').");
             return self::ERROR_NONE;
         }
         list($stashInfo, $ttl, $code) = self::buildStashValue($editInfo->pstContent, $editInfo->output, $editInfo->timestamp, $user);
         if ($stashInfo) {
             $ok = $cache->set($key, $stashInfo, $ttl);
             if ($ok) {
                 $logger->debug("Cached parser output for key '{$key}' ('{$title}').");
                 return self::ERROR_NONE;
             } else {
                 $logger->error("Failed to cache parser output for key '{$key}' ('{$title}').");
                 return self::ERROR_CACHE;
             }
         } else {
             $logger->info("Uncacheable parser output for key '{$key}' ('{$title}') [{$code}].");
             return self::ERROR_UNCACHEABLE;
         }
     }
     return self::ERROR_PARSE;
 }