Example #1
0
 /**
  * Attempt submission (no UI)
  *
  * @param $result
  * @param $bot bool
  *
  * @return Status object, possibly with a message, but always with one of the AS_* constants in $status->value,
  *
  * FIXME: This interface is TERRIBLE, but hard to get rid of due to various error display idiosyncrasies. There are
  * also lots of cases where error metadata is set in the object and retrieved later instead of being returned, e.g.
  * AS_CONTENT_TOO_BIG and AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some time.
  */
 function internalAttemptSave(&$result, $bot = false)
 {
     global $wgFilterCallback, $wgUser, $wgRequest, $wgParser;
     global $wgMaxArticleSize;
     $status = Status::newGood();
     wfProfileIn(__METHOD__);
     wfProfileIn(__METHOD__ . '-checks');
     if (!wfRunHooks('EditPage::attemptSave', array($this))) {
         wfDebug("Hook 'EditPage::attemptSave' aborted article saving\n");
         $status->fatal('hookaborted');
         $status->value = self::AS_HOOK_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     # Check image redirect
     if ($this->mTitle->getNamespace() == NS_FILE && Title::newFromRedirect($this->textbox1) instanceof Title && !$wgUser->isAllowed('upload')) {
         $code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
         $status->setResult(false, $code);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     # Check for spam
     $match = self::matchSummarySpamRegex($this->summary);
     if ($match === false) {
         $match = self::matchSpamRegex($this->textbox1);
     }
     if ($match !== false) {
         $result['spam'] = $match;
         $ip = $wgRequest->getIP();
         $pdbk = $this->mTitle->getPrefixedDBkey();
         $match = str_replace("\n", '', $match);
         wfDebugLog('SpamRegex', "{$ip} spam regex hit [[{$pdbk}]]: \"{$match}\"");
         $status->fatal('spamprotectionmatch', $match);
         $status->value = self::AS_SPAM_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if ($wgFilterCallback && is_callable($wgFilterCallback) && $wgFilterCallback($this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary)) {
         # Error messages or other handling should be performed by the filter function
         $status->setResult(false, self::AS_FILTERING);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if (!wfRunHooks('EditFilter', array($this, $this->textbox1, $this->section, &$this->hookError, $this->summary))) {
         # Error messages etc. could be handled within the hook...
         $status->fatal('hookaborted');
         $status->value = self::AS_HOOK_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     } elseif ($this->hookError != '') {
         # ...or the hook could be expecting us to produce an error
         $status->fatal('hookaborted');
         $status->value = self::AS_HOOK_ERROR_EXPECTED;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if ($wgUser->isBlockedFrom($this->mTitle, false)) {
         // Auto-block user's IP if the account was "hard" blocked
         $wgUser->spreadAnyEditBlock();
         # Check block state against master, thus 'false'.
         $status->setResult(false, self::AS_BLOCKED_PAGE_FOR_USER);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     $this->kblength = (int) (strlen($this->textbox1) / 1024);
     if ($this->kblength > $wgMaxArticleSize) {
         // Error will be displayed by showEditForm()
         $this->tooBig = true;
         $status->setResult(false, self::AS_CONTENT_TOO_BIG);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if (!$wgUser->isAllowed('edit')) {
         if ($wgUser->isAnon()) {
             $status->setResult(false, self::AS_READ_ONLY_PAGE_ANON);
             wfProfileOut(__METHOD__ . '-checks');
             wfProfileOut(__METHOD__);
             return $status;
         } else {
             $status->fatal('readonlytext');
             $status->value = self::AS_READ_ONLY_PAGE_LOGGED;
             wfProfileOut(__METHOD__ . '-checks');
             wfProfileOut(__METHOD__);
             return $status;
         }
     }
     if (wfReadOnly()) {
         $status->fatal('readonlytext');
         $status->value = self::AS_READ_ONLY_PAGE;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if ($wgUser->pingLimiter()) {
         $status->fatal('actionthrottledtext');
         $status->value = self::AS_RATE_LIMITED;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     # If the article has been deleted while editing, don't save it without
     # confirmation
     if ($this->wasDeletedSinceLastEdit() && !$this->recreate) {
         $status->setResult(false, self::AS_ARTICLE_WAS_DELETED);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     wfProfileOut(__METHOD__ . '-checks');
     # If article is new, insert it.
     $aid = $this->mTitle->getArticleID(Title::GAID_FOR_UPDATE);
     $new = $aid == 0;
     if ($new) {
         // Late check for create permission, just in case *PARANOIA*
         if (!$this->mTitle->userCan('create')) {
             $status->fatal('nocreatetext');
             $status->value = self::AS_NO_CREATE_PERMISSION;
             wfDebug(__METHOD__ . ": no create permission\n");
             wfProfileOut(__METHOD__);
             return $status;
         }
         # Don't save a new article if it's blank.
         if ($this->textbox1 == '') {
             $status->setResult(false, self::AS_BLANK_ARTICLE);
             wfProfileOut(__METHOD__);
             return $status;
         }
         // Run post-section-merge edit filter
         if (!wfRunHooks('EditFilterMerged', array($this, $this->textbox1, &$this->hookError, $this->summary))) {
             # Error messages etc. could be handled within the hook...
             $status->fatal('hookaborted');
             $status->value = self::AS_HOOK_ERROR;
             wfProfileOut(__METHOD__);
             return $status;
         } elseif ($this->hookError != '') {
             # ...or the hook could be expecting us to produce an error
             $status->fatal('hookaborted');
             $status->value = self::AS_HOOK_ERROR_EXPECTED;
             wfProfileOut(__METHOD__);
             return $status;
         }
         # Handle the user preference to force summaries here. Check if it's not a redirect.
         if (!$this->allowBlankSummary && !Title::newFromRedirect($this->textbox1)) {
             if (md5($this->summary) == $this->autoSumm) {
                 $this->missingSummary = true;
                 $status->fatal('missingsummary');
                 // or 'missingcommentheader' if $section == 'new'. Blegh
                 $status->value = self::AS_SUMMARY_NEEDED;
                 wfProfileOut(__METHOD__);
                 return $status;
             }
         }
         $text = $this->textbox1;
         $result['sectionanchor'] = '';
         if ($this->section == 'new') {
             if ($this->sectiontitle !== '') {
                 // Insert the section title above the content.
                 $text = wfMsgForContent('newsectionheaderdefaultlevel', $this->sectiontitle) . "\n\n" . $text;
                 // Jump to the new section
                 $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText($this->sectiontitle);
                 // If no edit summary was specified, create one automatically from the section
                 // title and have it link to the new section. Otherwise, respect the summary as
                 // passed.
                 if ($this->summary === '') {
                     $cleanSectionTitle = $wgParser->stripSectionName($this->sectiontitle);
                     $this->summary = wfMsgForContent('newsectionsummary', $cleanSectionTitle);
                 }
             } elseif ($this->summary !== '') {
                 // Insert the section title above the content.
                 $text = wfMsgForContent('newsectionheaderdefaultlevel', $this->summary) . "\n\n" . $text;
                 // Jump to the new section
                 $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText($this->summary);
                 // Create a link to the new section from the edit summary.
                 $cleanSummary = $wgParser->stripSectionName($this->summary);
                 $this->summary = wfMsgForContent('newsectionsummary', $cleanSummary);
             }
         }
         $status->value = self::AS_SUCCESS_NEW_ARTICLE;
     } else {
         # Article exists. Check for edit conflict.
         $this->mArticle->clear();
         # Force reload of dates, etc.
         $timestamp = $this->mArticle->getTimestamp();
         wfDebug("timestamp: {$timestamp}, edittime: {$this->edittime}\n");
         if ($timestamp != $this->edittime) {
             $this->isConflict = true;
             if ($this->section == 'new') {
                 if ($this->mArticle->getUserText() == $wgUser->getName() && $this->mArticle->getComment() == $this->summary) {
                     // Probably a duplicate submission of a new comment.
                     // This can happen when squid resends a request after
                     // a timeout but the first one actually went through.
                     wfDebug(__METHOD__ . ": duplicate new section submission; trigger edit conflict!\n");
                 } else {
                     // New comment; suppress conflict.
                     $this->isConflict = false;
                     wfDebug(__METHOD__ . ": conflict suppressed; new section\n");
                 }
             } elseif ($this->section == '' && $this->userWasLastToEdit($wgUser->getId(), $this->edittime)) {
                 # Suppress edit conflict with self, except for section edits where merging is required.
                 wfDebug(__METHOD__ . ": Suppressing edit conflict, same user.\n");
                 $this->isConflict = false;
             }
         }
         // If sectiontitle is set, use it, otherwise use the summary as the section title (for
         // backwards compatibility with old forms/bots).
         if ($this->sectiontitle !== '') {
             $sectionTitle = $this->sectiontitle;
         } else {
             $sectionTitle = $this->summary;
         }
         if ($this->isConflict) {
             wfDebug(__METHOD__ . ": conflict! getting section '{$this->section}' for time '{$this->edittime}' (article time '{$timestamp}')\n");
             $text = $this->mArticle->replaceSection($this->section, $this->textbox1, $sectionTitle, $this->edittime);
         } else {
             wfDebug(__METHOD__ . ": getting section '{$this->section}'\n");
             $text = $this->mArticle->replaceSection($this->section, $this->textbox1, $sectionTitle);
         }
         if (is_null($text)) {
             wfDebug(__METHOD__ . ": activating conflict; section replace failed.\n");
             $this->isConflict = true;
             $text = $this->textbox1;
             // do not try to merge here!
         } elseif ($this->isConflict) {
             # Attempt merge
             if ($this->mergeChangesInto($text)) {
                 // Successful merge! Maybe we should tell the user the good news?
                 $this->isConflict = false;
                 wfDebug(__METHOD__ . ": Suppressing edit conflict, successful merge.\n");
             } else {
                 $this->section = '';
                 $this->textbox1 = $text;
                 wfDebug(__METHOD__ . ": Keeping edit conflict, failed merge.\n");
             }
         }
         if ($this->isConflict) {
             $status->setResult(false, self::AS_CONFLICT_DETECTED);
             wfProfileOut(__METHOD__);
             return $status;
         }
         // Run post-section-merge edit filter
         if (!wfRunHooks('EditFilterMerged', array($this, $text, &$this->hookError, $this->summary))) {
             # Error messages etc. could be handled within the hook...
             $status->fatal('hookaborted');
             $status->value = self::AS_HOOK_ERROR;
             wfProfileOut(__METHOD__);
             return $status;
         } elseif ($this->hookError != '') {
             # ...or the hook could be expecting us to produce an error
             $status->fatal('hookaborted');
             $status->value = self::AS_HOOK_ERROR_EXPECTED;
             wfProfileOut(__METHOD__);
             return $status;
         }
         # Handle the user preference to force summaries here, but not for null edits
         if ($this->section != 'new' && !$this->allowBlankSummary && $this->getOriginalContent() != $text && !Title::newFromRedirect($text)) {
             if (md5($this->summary) == $this->autoSumm) {
                 $this->missingSummary = true;
                 $status->fatal('missingsummary');
                 $status->value = self::AS_SUMMARY_NEEDED;
                 wfProfileOut(__METHOD__);
                 return $status;
             }
         }
         # And a similar thing for new sections
         if ($this->section == 'new' && !$this->allowBlankSummary) {
             if (trim($this->summary) == '') {
                 $this->missingSummary = true;
                 $status->fatal('missingsummary');
                 // or 'missingcommentheader' if $section == 'new'. Blegh
                 $status->value = self::AS_SUMMARY_NEEDED;
                 wfProfileOut(__METHOD__);
                 return $status;
             }
         }
         # All's well
         wfProfileIn(__METHOD__ . '-sectionanchor');
         $sectionanchor = '';
         if ($this->section == 'new') {
             if ($this->textbox1 == '') {
                 $this->missingComment = true;
                 $status->fatal('missingcommenttext');
                 $status->value = self::AS_TEXTBOX_EMPTY;
                 wfProfileOut(__METHOD__ . '-sectionanchor');
                 wfProfileOut(__METHOD__);
                 return $status;
             }
             if ($this->sectiontitle !== '') {
                 $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText($this->sectiontitle);
                 // If no edit summary was specified, create one automatically from the section
                 // title and have it link to the new section. Otherwise, respect the summary as
                 // passed.
                 if ($this->summary === '') {
                     $cleanSectionTitle = $wgParser->stripSectionName($this->sectiontitle);
                     $this->summary = wfMsgForContent('newsectionsummary', $cleanSectionTitle);
                 }
             } elseif ($this->summary !== '') {
                 $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText($this->summary);
                 # This is a new section, so create a link to the new section
                 # in the revision summary.
                 $cleanSummary = $wgParser->stripSectionName($this->summary);
                 $this->summary = wfMsgForContent('newsectionsummary', $cleanSummary);
             }
         } elseif ($this->section != '') {
             # Try to get a section anchor from the section source, redirect to edited section if header found
             # XXX: might be better to integrate this into Article::replaceSection
             # for duplicate heading checking and maybe parsing
             $hasmatch = preg_match("/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches);
             # we can't deal with anchors, includes, html etc in the header for now,
             # headline would need to be parsed to improve this
             if ($hasmatch && strlen($matches[2]) > 0) {
                 $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText($matches[2]);
             }
         }
         $result['sectionanchor'] = $sectionanchor;
         wfProfileOut(__METHOD__ . '-sectionanchor');
         // Save errors may fall down to the edit form, but we've now
         // merged the section into full text. Clear the section field
         // so that later submission of conflict forms won't try to
         // replace that into a duplicated mess.
         $this->textbox1 = $text;
         $this->section = '';
         $status->value = self::AS_SUCCESS_UPDATE;
     }
     // Check for length errors again now that the section is merged in
     $this->kblength = (int) (strlen($text) / 1024);
     if ($this->kblength > $wgMaxArticleSize) {
         $this->tooBig = true;
         $status->setResult(false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED);
         wfProfileOut(__METHOD__);
         return $status;
     }
     $flags = EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY | ($new ? EDIT_NEW : EDIT_UPDATE) | ($this->minoredit && !$this->isNew ? EDIT_MINOR : 0) | ($bot ? EDIT_FORCE_BOT : 0);
     $doEditStatus = $this->mArticle->doEdit($text, $this->summary, $flags);
     if ($doEditStatus->isOK()) {
         $result['redirect'] = Title::newFromRedirect($text) !== null;
         $this->commitWatch();
         wfProfileOut(__METHOD__);
         return $status;
     } else {
         $this->isConflict = true;
         $doEditStatus->value = self::AS_END;
         // Destroys data doEdit() put in $status->value but who cares
         wfProfileOut(__METHOD__);
         return $doEditStatus;
     }
 }
 /**
  * Collects metadata and additional resources for this page
  * @param Title $oTitle
  * @param DOMDocument $oPageDOM
  * @param array $aParams
  * @return array array( 'meta' => ..., 'resources' => ...);
  */
 private static function collectData($oTitle, $oPageDOM, $aParams)
 {
     $aMeta = array();
     $aResources = array('ATTACHMENT' => array(), 'STYLESHEET' => array(), 'IMAGE' => array());
     // TODO RBV (01.02.12 13:51): Handle oldid
     $aCategories = array();
     if ($oTitle->exists()) {
         // TODO RBV (27.06.12 11:47): Throws an exception. Maybe better use try ... catch instead of $oTitle->exists()
         $aAPIParams = new FauxRequest(array('action' => 'parse', 'page' => $oTitle->getPrefixedText(), 'prop' => 'images|categories|links'));
         $oAPI = new ApiMain($aAPIParams);
         $oAPI->execute();
         if (defined('ApiResult::META_CONTENT')) {
             $aResult = $oAPI->getResult()->getResultData(null, array('BC' => array(), 'Types' => array(), 'Strip' => 'all'));
         } else {
             $aResult = $oAPI->getResultData();
         }
         foreach ($aResult['parse']['categories'] as $aCat) {
             $aCategories[] = $aCat['*'];
         }
     }
     /*
     		//For future use...
     		foreach($aResult['parse']['images'] as $sFileName ) {
     			$oImage = RepoGroup::singleton()->getLocalRepo()->newFile( Title::newFromText( $sFileName, NS_FILE ) );
     			if( $oImage->exists() ) {
     				$sAbsoluteFileSystemPath = $oImage->getFullPath();
     			}
     		}
     */
     //Dublin Core:
     $aMeta['DC.title'] = $oTitle->getPrefixedText();
     $aMeta['DC.date'] = wfTimestamp(TS_ISO_8601);
     // TODO RBV (14.12.10 14:01): Check for conformity. Maybe there is a better way to acquire than wfTimestamp()?
     //Custom
     global $wgLang;
     $sCurrentTS = $wgLang->userAdjust(wfTimestampNow());
     $aMeta['title'] = $oTitle->getPrefixedText();
     $aMeta['exportdate'] = $wgLang->sprintfDate('d.m.Y', $sCurrentTS);
     $aMeta['exporttime'] = $wgLang->sprintfDate('H:i', $sCurrentTS);
     $aMeta['exporttimeexact'] = $wgLang->sprintfDate('H:i:s', $sCurrentTS);
     //Custom - Categories->Keywords
     $aMeta['keywords'] = implode(', ', $aCategories);
     $oDOMXPath = new DOMXPath($oPageDOM);
     $oMetadataElements = $oDOMXPath->query("//div[@class='bs-universalexport-meta']");
     foreach ($oMetadataElements as $oMetadataElement) {
         if ($oMetadataElement->hasAttributes()) {
             foreach ($oMetadataElement->attributes as $oAttribute) {
                 if ($oAttribute->name !== 'class') {
                     $aMeta[$oAttribute->name] = $oAttribute->value;
                 }
             }
         }
         $oMetadataElement->parentNode->removeChild($oMetadataElement);
     }
     //If it's a normal article
     if (!in_array($oTitle->getNamespace(), array(NS_SPECIAL, NS_IMAGE, NS_CATEGORY))) {
         $oArticle = new Article($oTitle);
         $aMeta['author'] = $oArticle->getUserText();
         // TODO RBV (14.12.10 12:19): Realname/Username -> DisplayName
         $aMeta['date'] = $wgLang->sprintfDate('d.m.Y', $oArticle->getTouched());
     }
     wfRunHooks('BSUEModulePDFcollectMetaData', array($oTitle, $oPageDOM, &$aParams, $oDOMXPath, &$aMeta));
     $aMetaDataOverrides = json_decode(BsConfig::get('MW::UniversalExport::MetadataOverrides'), true);
     $aMeta = array_merge($aMeta, $aMetaDataOverrides);
     return array('meta' => $aMeta, 'resources' => $aResources);
 }
Example #3
0
 static function gatherImageCredits($parsed, &$credits)
 {
     foreach ($parsed as $k => $v) {
         if (!is_array($v) && ($k == "url" || $k == "html")) {
             if (strpos(strtolower($v), 'jpg') === FALSE && strpos(strtolower($v), 'png') === FALSE) {
                 continue;
             }
             if ($k == "html") {
                 $gotImage = true;
                 $pqDoc = PHPQuery::newDocument($v);
                 $v = pq("img")->attr("src");
             } elseif ($k == "url") {
                 $gotImage = true;
             }
         }
         if ($gotImage) {
             $imageName = explode("/", $v);
             $imageName = $imageName[count($imageName) - 2];
             $titleName = "Image:" . $imageName;
             $title = Title::newFromText($imageName, NS_IMAGE);
             if ($title && $title->isKnown()) {
                 $article = new Article($title);
                 if ($article) {
                     $user = $article->getUserText();
                     if ($user) {
                         $user .= ' (wikiHow)';
                     }
                     $wikitext = $article->getContent();
                     if (preg_match('@{{(cc-by[^}]+)}}@', $wikitext, $m)) {
                         $license = $m[1];
                         // From http://creativecommons.org/licenses/
                         $licenseTexts = array('cc-by-sa-nc-2.5-self' => 'Creative Commons Share-Alike Non-Commercial Attribution');
                         if (isset($licenseTexts[$license])) {
                             $license = $licenseTexts[$license];
                         }
                     } elseif (preg_match('@{{flickr[^}]+\\|([^|}]+)}}@', $wikitext, $m)) {
                         $user = $m[1] . ' (Flickr)';
                     }
                 }
             }
             if ($user) {
                 $credits['uploaders'][] = $user;
             }
             if ($license) {
                 $credits['licenses'][] = $license;
             }
         } elseif (is_array($v)) {
             self::gatherImageCredits($v, $credits);
         }
     }
 }
Example #4
0
 /**
  * Attempt submission (no UI)
  *
  * @param array $result Array to add statuses to, currently with the
  *   possible keys:
  *   - spam (string): Spam string from content if any spam is detected by
  *     matchSpamRegex.
  *   - sectionanchor (string): Section anchor for a section save.
  *   - nullEdit (boolean): Set if doEditContent is OK.  True if null edit,
  *     false otherwise.
  *   - redirect (bool): Set if doEditContent is OK. True if resulting
  *     revision is a redirect.
  * @param bool $bot True if edit is being made under the bot right.
  *
  * @return Status Status object, possibly with a message, but always with
  *   one of the AS_* constants in $status->value,
  *
  * @todo FIXME: This interface is TERRIBLE, but hard to get rid of due to
  *   various error display idiosyncrasies. There are also lots of cases
  *   where error metadata is set in the object and retrieved later instead
  *   of being returned, e.g. AS_CONTENT_TOO_BIG and
  *   AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some
  * time.
  */
 function internalAttemptSave(&$result, $bot = false)
 {
     global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
     $status = Status::newGood();
     wfProfileIn(__METHOD__);
     wfProfileIn(__METHOD__ . '-checks');
     if (!wfRunHooks('EditPage::attemptSave', array($this))) {
         wfDebug("Hook 'EditPage::attemptSave' aborted article saving\n");
         $status->fatal('hookaborted');
         $status->value = self::AS_HOOK_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     $spam = $wgRequest->getText('wpAntispam');
     if ($spam !== '') {
         wfDebugLog('SimpleAntiSpam', $wgUser->getName() . ' editing "' . $this->mTitle->getPrefixedText() . '" submitted bogus field "' . $spam . '"');
         $status->fatal('spamprotectionmatch', false);
         $status->value = self::AS_SPAM_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     try {
         # Construct Content object
         $textbox_content = $this->toEditContent($this->textbox1);
     } catch (MWContentSerializationException $ex) {
         $status->fatal('content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage());
         $status->value = self::AS_PARSE_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     # Check image redirect
     if ($this->mTitle->getNamespace() == NS_FILE && $textbox_content->isRedirect() && !$wgUser->isAllowed('upload')) {
         $code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
         $status->setResult(false, $code);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     # Check for spam
     $match = self::matchSummarySpamRegex($this->summary);
     if ($match === false && $this->section == 'new') {
         # $wgSpamRegex is enforced on this new heading/summary because, unlike
         # regular summaries, it is added to the actual wikitext.
         if ($this->sectiontitle !== '') {
             # This branch is taken when the API is used with the 'sectiontitle' parameter.
             $match = self::matchSpamRegex($this->sectiontitle);
         } else {
             # This branch is taken when the "Add Topic" user interface is used, or the API
             # is used with the 'summary' parameter.
             $match = self::matchSpamRegex($this->summary);
         }
     }
     if ($match === false) {
         $match = self::matchSpamRegex($this->textbox1);
     }
     if ($match !== false) {
         $result['spam'] = $match;
         $ip = $wgRequest->getIP();
         $pdbk = $this->mTitle->getPrefixedDBkey();
         $match = str_replace("\n", '', $match);
         wfDebugLog('SpamRegex', "{$ip} spam regex hit [[{$pdbk}]]: \"{$match}\"");
         $status->fatal('spamprotectionmatch', $match);
         $status->value = self::AS_SPAM_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if (!wfRunHooks('EditFilter', array($this, $this->textbox1, $this->section, &$this->hookError, $this->summary))) {
         # Error messages etc. could be handled within the hook...
         $status->fatal('hookaborted');
         $status->value = self::AS_HOOK_ERROR;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     } elseif ($this->hookError != '') {
         # ...or the hook could be expecting us to produce an error
         $status->fatal('hookaborted');
         $status->value = self::AS_HOOK_ERROR_EXPECTED;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if ($wgUser->isBlockedFrom($this->mTitle, false)) {
         // Auto-block user's IP if the account was "hard" blocked
         $wgUser->spreadAnyEditBlock();
         # Check block state against master, thus 'false'.
         $status->setResult(false, self::AS_BLOCKED_PAGE_FOR_USER);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     $this->kblength = (int) (strlen($this->textbox1) / 1024);
     if ($this->kblength > $wgMaxArticleSize) {
         // Error will be displayed by showEditForm()
         $this->tooBig = true;
         $status->setResult(false, self::AS_CONTENT_TOO_BIG);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if (!$wgUser->isAllowed('edit')) {
         if ($wgUser->isAnon()) {
             $status->setResult(false, self::AS_READ_ONLY_PAGE_ANON);
             wfProfileOut(__METHOD__ . '-checks');
             wfProfileOut(__METHOD__);
             return $status;
         } else {
             $status->fatal('readonlytext');
             $status->value = self::AS_READ_ONLY_PAGE_LOGGED;
             wfProfileOut(__METHOD__ . '-checks');
             wfProfileOut(__METHOD__);
             return $status;
         }
     }
     if ($this->contentModel !== $this->mTitle->getContentModel() && !$wgUser->isAllowed('editcontentmodel')) {
         $status->setResult(false, self::AS_NO_CHANGE_CONTENT_MODEL);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if (wfReadOnly()) {
         $status->fatal('readonlytext');
         $status->value = self::AS_READ_ONLY_PAGE;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     if ($wgUser->pingLimiter() || $wgUser->pingLimiter('linkpurge', 0)) {
         $status->fatal('actionthrottledtext');
         $status->value = self::AS_RATE_LIMITED;
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     # If the article has been deleted while editing, don't save it without
     # confirmation
     if ($this->wasDeletedSinceLastEdit() && !$this->recreate) {
         $status->setResult(false, self::AS_ARTICLE_WAS_DELETED);
         wfProfileOut(__METHOD__ . '-checks');
         wfProfileOut(__METHOD__);
         return $status;
     }
     wfProfileOut(__METHOD__ . '-checks');
     # Load the page data from the master. If anything changes in the meantime,
     # we detect it by using page_latest like a token in a 1 try compare-and-swap.
     $this->mArticle->loadPageData('fromdbmaster');
     $new = !$this->mArticle->exists();
     if ($new) {
         // Late check for create permission, just in case *PARANOIA*
         if (!$this->mTitle->userCan('create', $wgUser)) {
             $status->fatal('nocreatetext');
             $status->value = self::AS_NO_CREATE_PERMISSION;
             wfDebug(__METHOD__ . ": no create permission\n");
             wfProfileOut(__METHOD__);
             return $status;
         }
         // Don't save a new page if it's blank or if it's a MediaWiki:
         // message with content equivalent to default (allow empty pages
         // in this case to disable messages, see bug 50124)
         $defaultMessageText = $this->mTitle->getDefaultMessageText();
         if ($this->mTitle->getNamespace() === NS_MEDIAWIKI && $defaultMessageText !== false) {
             $defaultText = $defaultMessageText;
         } else {
             $defaultText = '';
         }
         if (!$this->allowBlankArticle && $this->textbox1 === $defaultText) {
             $this->blankArticle = true;
             $status->fatal('blankarticle');
             $status->setResult(false, self::AS_BLANK_ARTICLE);
             wfProfileOut(__METHOD__);
             return $status;
         }
         if (!$this->runPostMergeFilters($textbox_content, $status, $wgUser)) {
             wfProfileOut(__METHOD__);
             return $status;
         }
         $content = $textbox_content;
         $result['sectionanchor'] = '';
         if ($this->section == 'new') {
             if ($this->sectiontitle !== '') {
                 // Insert the section title above the content.
                 $content = $content->addSectionHeader($this->sectiontitle);
             } elseif ($this->summary !== '') {
                 // Insert the section title above the content.
                 $content = $content->addSectionHeader($this->summary);
             }
             $this->summary = $this->newSectionSummary($result['sectionanchor']);
         }
         $status->value = self::AS_SUCCESS_NEW_ARTICLE;
     } else {
         # not $new
         # Article exists. Check for edit conflict.
         $this->mArticle->clear();
         # Force reload of dates, etc.
         $timestamp = $this->mArticle->getTimestamp();
         wfDebug("timestamp: {$timestamp}, edittime: {$this->edittime}\n");
         if ($timestamp != $this->edittime) {
             $this->isConflict = true;
             if ($this->section == 'new') {
                 if ($this->mArticle->getUserText() == $wgUser->getName() && $this->mArticle->getComment() == $this->newSectionSummary()) {
                     // Probably a duplicate submission of a new comment.
                     // This can happen when squid resends a request after
                     // a timeout but the first one actually went through.
                     wfDebug(__METHOD__ . ": duplicate new section submission; trigger edit conflict!\n");
                 } else {
                     // New comment; suppress conflict.
                     $this->isConflict = false;
                     wfDebug(__METHOD__ . ": conflict suppressed; new section\n");
                 }
             } elseif ($this->section == '' && Revision::userWasLastToEdit(DB_MASTER, $this->mTitle->getArticleID(), $wgUser->getId(), $this->edittime)) {
                 # Suppress edit conflict with self, except for section edits where merging is required.
                 wfDebug(__METHOD__ . ": Suppressing edit conflict, same user.\n");
                 $this->isConflict = false;
             }
         }
         // If sectiontitle is set, use it, otherwise use the summary as the section title.
         if ($this->sectiontitle !== '') {
             $sectionTitle = $this->sectiontitle;
         } else {
             $sectionTitle = $this->summary;
         }
         $content = null;
         if ($this->isConflict) {
             wfDebug(__METHOD__ . ": conflict! getting section '{$this->section}' for time '{$this->edittime}'" . " (article time '{$timestamp}')\n");
             $content = $this->mArticle->replaceSectionContent($this->section, $textbox_content, $sectionTitle, $this->edittime);
         } else {
             wfDebug(__METHOD__ . ": getting section '{$this->section}'\n");
             $content = $this->mArticle->replaceSectionContent($this->section, $textbox_content, $sectionTitle);
         }
         if (is_null($content)) {
             wfDebug(__METHOD__ . ": activating conflict; section replace failed.\n");
             $this->isConflict = true;
             $content = $textbox_content;
             // do not try to merge here!
         } elseif ($this->isConflict) {
             # Attempt merge
             if ($this->mergeChangesIntoContent($content)) {
                 // Successful merge! Maybe we should tell the user the good news?
                 $this->isConflict = false;
                 wfDebug(__METHOD__ . ": Suppressing edit conflict, successful merge.\n");
             } else {
                 $this->section = '';
                 $this->textbox1 = ContentHandler::getContentText($content);
                 wfDebug(__METHOD__ . ": Keeping edit conflict, failed merge.\n");
             }
         }
         if ($this->isConflict) {
             $status->setResult(false, self::AS_CONFLICT_DETECTED);
             wfProfileOut(__METHOD__);
             return $status;
         }
         if (!$this->runPostMergeFilters($content, $status, $wgUser)) {
             wfProfileOut(__METHOD__);
             return $status;
         }
         if ($this->section == 'new') {
             // Handle the user preference to force summaries here
             if (!$this->allowBlankSummary && trim($this->summary) == '') {
                 $this->missingSummary = true;
                 $status->fatal('missingsummary');
                 // or 'missingcommentheader' if $section == 'new'. Blegh
                 $status->value = self::AS_SUMMARY_NEEDED;
                 wfProfileOut(__METHOD__);
                 return $status;
             }
             // Do not allow the user to post an empty comment
             if ($this->textbox1 == '') {
                 $this->missingComment = true;
                 $status->fatal('missingcommenttext');
                 $status->value = self::AS_TEXTBOX_EMPTY;
                 wfProfileOut(__METHOD__);
                 return $status;
             }
         } elseif (!$this->allowBlankSummary && !$content->equals($this->getOriginalContent($wgUser)) && !$content->isRedirect() && md5($this->summary) == $this->autoSumm) {
             $this->missingSummary = true;
             $status->fatal('missingsummary');
             $status->value = self::AS_SUMMARY_NEEDED;
             wfProfileOut(__METHOD__);
             return $status;
         }
         # All's well
         wfProfileIn(__METHOD__ . '-sectionanchor');
         $sectionanchor = '';
         if ($this->section == 'new') {
             $this->summary = $this->newSectionSummary($sectionanchor);
         } elseif ($this->section != '') {
             # Try to get a section anchor from the section source, redirect
             # to edited section if header found.
             # XXX: Might be better to integrate this into Article::replaceSection
             # for duplicate heading checking and maybe parsing.
             $hasmatch = preg_match("/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches);
             # We can't deal with anchors, includes, html etc in the header for now,
             # headline would need to be parsed to improve this.
             if ($hasmatch && strlen($matches[2]) > 0) {
                 $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText($matches[2]);
             }
         }
         $result['sectionanchor'] = $sectionanchor;
         wfProfileOut(__METHOD__ . '-sectionanchor');
         // Save errors may fall down to the edit form, but we've now
         // merged the section into full text. Clear the section field
         // so that later submission of conflict forms won't try to
         // replace that into a duplicated mess.
         $this->textbox1 = $this->toEditText($content);
         $this->section = '';
         $status->value = self::AS_SUCCESS_UPDATE;
     }
     // Check for length errors again now that the section is merged in
     $this->kblength = (int) (strlen($this->toEditText($content)) / 1024);
     if ($this->kblength > $wgMaxArticleSize) {
         $this->tooBig = true;
         $status->setResult(false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED);
         wfProfileOut(__METHOD__);
         return $status;
     }
     $flags = EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY | ($new ? EDIT_NEW : EDIT_UPDATE) | ($this->minoredit && !$this->isNew ? EDIT_MINOR : 0) | ($bot ? EDIT_FORCE_BOT : 0);
     $doEditStatus = $this->mArticle->doEditContent($content, $this->summary, $flags, false, null, $content->getDefaultFormat());
     if (!$doEditStatus->isOK()) {
         // Failure from doEdit()
         // Show the edit conflict page for certain recognized errors from doEdit(),
         // but don't show it for errors from extension hooks
         $errors = $doEditStatus->getErrorsArray();
         if (in_array($errors[0][0], array('edit-gone-missing', 'edit-conflict', 'edit-already-exists'))) {
             $this->isConflict = true;
             // Destroys data doEdit() put in $status->value but who cares
             $doEditStatus->value = self::AS_END;
         }
         wfProfileOut(__METHOD__);
         return $doEditStatus;
     }
     $result['nullEdit'] = $doEditStatus->hasMessage('edit-no-change');
     if ($result['nullEdit']) {
         // We don't know if it was a null edit until now, so increment here
         $wgUser->pingLimiter('linkpurge');
     }
     $result['redirect'] = $content->isRedirect();
     $this->updateWatchlist();
     wfProfileOut(__METHOD__);
     return $status;
 }
Example #5
0
 /**
  * Constructor for a single item in the feed. Requires the URI of the item.
  */
 public function __construct(Title $t, $c, $d)
 {
     $this->title = $t;
     $this->uri = $t->getFullURL();
     $this->label = $t->getText();
     $article = null;
     if (count($c) == 0) {
         $article = new Article($t);
         $this->creator = array();
         $this->creator[] = $article->getUserText();
     } else {
         $this->creator = $c;
     }
     $this->date = array();
     if (count($d) == 0) {
         if (is_null($article)) {
             $article = new Article($t);
         }
         $this->date[] = date("c", strtotime($article->getTimestamp()));
     } else {
         foreach ($d as $date) {
             $this->date[] = $date;
         }
     }
     // get content
     if ($t->getNamespace() == NS_MAIN) {
         $this->articlename = ':' . $t->getDBkey();
     } else {
         $this->articlename = $t->getPrefixedDBKey();
     }
 }