/** * Update the article's restriction field, and leave a log entry. * This works for protection both existing and non-existing pages. * * @param array $limit Set of restriction keys * @param array $expiry Per restriction type expiration * @param int &$cascade Set to false if cascading protection isn't allowed. * @param string $reason * @param User $user The user updating the restrictions * @param string|string[] $tags Change tags to add to the pages and protection log entries * ($user should be able to add the specified tags before this is called) * @return Status Status object; if action is taken, $status->value is the log_id of the * protection log entry. */ public function doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user, $tags = null) { global $wgCascadingRestrictionLevels, $wgContLang; if (wfReadOnly()) { return Status::newFatal('readonlytext', wfReadOnlyReason()); } $this->loadPageData('fromdbmaster'); $restrictionTypes = $this->mTitle->getRestrictionTypes(); $id = $this->getId(); if (!$cascade) { $cascade = false; } // Take this opportunity to purge out expired restrictions Title::purgeExpiredRestrictions(); // @todo FIXME: Same limitations as described in ProtectionForm.php (line 37); // we expect a single selection, but the schema allows otherwise. $isProtected = false; $protect = false; $changed = false; $dbw = wfGetDB(DB_MASTER); foreach ($restrictionTypes as $action) { if (!isset($expiry[$action]) || $expiry[$action] === $dbw->getInfinity()) { $expiry[$action] = 'infinity'; } if (!isset($limit[$action])) { $limit[$action] = ''; } elseif ($limit[$action] != '') { $protect = true; } // Get current restrictions on $action $current = implode('', $this->mTitle->getRestrictions($action)); if ($current != '') { $isProtected = true; } if ($limit[$action] != $current) { $changed = true; } elseif ($limit[$action] != '') { // Only check expiry change if the action is actually being // protected, since expiry does nothing on an not-protected // action. if ($this->mTitle->getRestrictionExpiry($action) != $expiry[$action]) { $changed = true; } } } if (!$changed && $protect && $this->mTitle->areRestrictionsCascading() != $cascade) { $changed = true; } // If nothing has changed, do nothing if (!$changed) { return Status::newGood(); } if (!$protect) { // No protection at all means unprotection $revCommentMsg = 'unprotectedarticle'; $logAction = 'unprotect'; } elseif ($isProtected) { $revCommentMsg = 'modifiedarticleprotection'; $logAction = 'modify'; } else { $revCommentMsg = 'protectedarticle'; $logAction = 'protect'; } // Truncate for whole multibyte characters $reason = $wgContLang->truncate($reason, 255); $logRelationsValues = []; $logRelationsField = null; $logParamsDetails = []; // Null revision (used for change tag insertion) $nullRevision = null; if ($id) { // Protection of existing page if (!Hooks::run('ArticleProtect', [&$this, &$user, $limit, $reason])) { return Status::newGood(); } // Only certain restrictions can cascade... $editrestriction = isset($limit['edit']) ? [$limit['edit']] : $this->mTitle->getRestrictions('edit'); foreach (array_keys($editrestriction, 'sysop') as $key) { $editrestriction[$key] = 'editprotected'; // backwards compatibility } foreach (array_keys($editrestriction, 'autoconfirmed') as $key) { $editrestriction[$key] = 'editsemiprotected'; // backwards compatibility } $cascadingRestrictionLevels = $wgCascadingRestrictionLevels; foreach (array_keys($cascadingRestrictionLevels, 'sysop') as $key) { $cascadingRestrictionLevels[$key] = 'editprotected'; // backwards compatibility } foreach (array_keys($cascadingRestrictionLevels, 'autoconfirmed') as $key) { $cascadingRestrictionLevels[$key] = 'editsemiprotected'; // backwards compatibility } // The schema allows multiple restrictions if (!array_intersect($editrestriction, $cascadingRestrictionLevels)) { $cascade = false; } // insert null revision to identify the page protection change as edit summary $latest = $this->getLatest(); $nullRevision = $this->insertProtectNullRevision($revCommentMsg, $limit, $expiry, $cascade, $reason, $user); if ($nullRevision === null) { return Status::newFatal('no-null-revision', $this->mTitle->getPrefixedText()); } $logRelationsField = 'pr_id'; // Update restrictions table foreach ($limit as $action => $restrictions) { $dbw->delete('page_restrictions', ['pr_page' => $id, 'pr_type' => $action], __METHOD__); if ($restrictions != '') { $cascadeValue = $cascade && $action == 'edit' ? 1 : 0; $dbw->insert('page_restrictions', ['pr_id' => $dbw->nextSequenceValue('page_restrictions_pr_id_seq'), 'pr_page' => $id, 'pr_type' => $action, 'pr_level' => $restrictions, 'pr_cascade' => $cascadeValue, 'pr_expiry' => $dbw->encodeExpiry($expiry[$action])], __METHOD__); $logRelationsValues[] = $dbw->insertId(); $logParamsDetails[] = ['type' => $action, 'level' => $restrictions, 'expiry' => $expiry[$action], 'cascade' => (bool) $cascadeValue]; } } // Clear out legacy restriction fields $dbw->update('page', ['page_restrictions' => ''], ['page_id' => $id], __METHOD__); Hooks::run('NewRevisionFromEditComplete', [$this, $nullRevision, $latest, $user]); Hooks::run('ArticleProtectComplete', [&$this, &$user, $limit, $reason]); } else { // Protection of non-existing page (also known as "title protection") // Cascade protection is meaningless in this case $cascade = false; if ($limit['create'] != '') { $dbw->replace('protected_titles', [['pt_namespace', 'pt_title']], ['pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey(), 'pt_create_perm' => $limit['create'], 'pt_timestamp' => $dbw->timestamp(), 'pt_expiry' => $dbw->encodeExpiry($expiry['create']), 'pt_user' => $user->getId(), 'pt_reason' => $reason], __METHOD__); $logParamsDetails[] = ['type' => 'create', 'level' => $limit['create'], 'expiry' => $expiry['create']]; } else { $dbw->delete('protected_titles', ['pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey()], __METHOD__); } } $this->mTitle->flushRestrictions(); InfoAction::invalidateCache($this->mTitle); if ($logAction == 'unprotect') { $params = []; } else { $protectDescriptionLog = $this->protectDescriptionLog($limit, $expiry); $params = ['4::description' => $protectDescriptionLog, '5:bool:cascade' => $cascade, 'details' => $logParamsDetails]; } // Update the protection log $logEntry = new ManualLogEntry('protect', $logAction); $logEntry->setTarget($this->mTitle); $logEntry->setComment($reason); $logEntry->setPerformer($user); $logEntry->setParameters($params); if (!is_null($nullRevision)) { $logEntry->setAssociatedRevId($nullRevision->getId()); } $logEntry->setTags($tags); if ($logRelationsField !== null && count($logRelationsValues)) { $logEntry->setRelations([$logRelationsField => $logRelationsValues]); } $logId = $logEntry->insert(); $logEntry->publish($logId); return Status::newGood($logId); }
/** * Update the article's restriction field, and leave a log entry. * This works for protection both existing and non-existing pages. * * @param array $limit set of restriction keys * @param array $expiry per restriction type expiration * @param int &$cascade Set to false if cascading protection isn't allowed. * @param string $reason * @param User $user The user updating the restrictions * @return Status */ public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) { global $wgCascadingRestrictionLevels; if ( wfReadOnly() ) { return Status::newFatal( 'readonlytext', wfReadOnlyReason() ); } $restrictionTypes = $this->mTitle->getRestrictionTypes(); $id = $this->getId(); if ( !$cascade ) { $cascade = false; } // Take this opportunity to purge out expired restrictions Title::purgeExpiredRestrictions(); // @todo FIXME: Same limitations as described in ProtectionForm.php (line 37); // we expect a single selection, but the schema allows otherwise. $isProtected = false; $protect = false; $changed = false; $dbw = wfGetDB( DB_MASTER ); foreach ( $restrictionTypes as $action ) { if ( !isset( $expiry[$action] ) ) { $expiry[$action] = $dbw->getInfinity(); } if ( !isset( $limit[$action] ) ) { $limit[$action] = ''; } elseif ( $limit[$action] != '' ) { $protect = true; } // Get current restrictions on $action $current = implode( '', $this->mTitle->getRestrictions( $action ) ); if ( $current != '' ) { $isProtected = true; } if ( $limit[$action] != $current ) { $changed = true; } elseif ( $limit[$action] != '' ) { // Only check expiry change if the action is actually being // protected, since expiry does nothing on an not-protected // action. if ( $this->mTitle->getRestrictionExpiry( $action ) != $expiry[$action] ) { $changed = true; } } } if ( !$changed && $protect && $this->mTitle->areRestrictionsCascading() != $cascade ) { $changed = true; } // If nothing has changed, do nothing if ( !$changed ) { return Status::newGood(); } if ( !$protect ) { // No protection at all means unprotection $revCommentMsg = 'unprotectedarticle'; $logAction = 'unprotect'; } elseif ( $isProtected ) { $revCommentMsg = 'modifiedarticleprotection'; $logAction = 'modify'; } else { $revCommentMsg = 'protectedarticle'; $logAction = 'protect'; } if ( $id ) { // Protection of existing page if ( !wfRunHooks( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) { return Status::newGood(); } // Only certain restrictions can cascade... $editrestriction = isset( $limit['edit'] ) ? array( $limit['edit'] ) : $this->mTitle->getRestrictions( 'edit' ); foreach ( array_keys( $editrestriction, 'sysop' ) as $key ) { $editrestriction[$key] = 'editprotected'; // backwards compatibility } foreach ( array_keys( $editrestriction, 'autoconfirmed' ) as $key ) { $editrestriction[$key] = 'editsemiprotected'; // backwards compatibility } $cascadingRestrictionLevels = $wgCascadingRestrictionLevels; foreach ( array_keys( $cascadingRestrictionLevels, 'sysop' ) as $key ) { $cascadingRestrictionLevels[$key] = 'editprotected'; // backwards compatibility } foreach ( array_keys( $cascadingRestrictionLevels, 'autoconfirmed' ) as $key ) { $cascadingRestrictionLevels[$key] = 'editsemiprotected'; // backwards compatibility } // The schema allows multiple restrictions if ( !array_intersect( $editrestriction, $cascadingRestrictionLevels ) ) { $cascade = false; } // insert null revision to identify the page protection change as edit summary $latest = $this->getLatest(); $nullRevision = $this->insertProtectNullRevision( $revCommentMsg, $limit, $expiry, $cascade, $reason ); if ( $nullRevision === null ) { return Status::newFatal( 'no-null-revision', $this->mTitle->getPrefixedText() ); } // Update restrictions table foreach ( $limit as $action => $restrictions ) { if ( $restrictions != '' ) { $dbw->replace( 'page_restrictions', array( array( 'pr_page', 'pr_type' ) ), array( 'pr_page' => $id, 'pr_type' => $action, 'pr_level' => $restrictions, 'pr_cascade' => ( $cascade && $action == 'edit' ) ? 1 : 0, 'pr_expiry' => $dbw->encodeExpiry( $expiry[$action] ) ), __METHOD__ ); } else { $dbw->delete( 'page_restrictions', array( 'pr_page' => $id, 'pr_type' => $action ), __METHOD__ ); } } // Clear out legacy restriction fields $dbw->update( 'page', array( 'page_restrictions' => '' ), array( 'page_id' => $id ), __METHOD__ ); wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $nullRevision, $latest, $user ) ); wfRunHooks( 'ArticleProtectComplete', array( &$this, &$user, $limit, $reason ) ); } else { // Protection of non-existing page (also known as "title protection") // Cascade protection is meaningless in this case $cascade = false; if ( $limit['create'] != '' ) { $dbw->replace( 'protected_titles', array( array( 'pt_namespace', 'pt_title' ) ), array( 'pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey(), 'pt_create_perm' => $limit['create'], 'pt_timestamp' => $dbw->encodeExpiry( wfTimestampNow() ), 'pt_expiry' => $dbw->encodeExpiry( $expiry['create'] ), 'pt_user' => $user->getId(), 'pt_reason' => $reason, ), __METHOD__ ); } else { $dbw->delete( 'protected_titles', array( 'pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey() ), __METHOD__ ); } } $this->mTitle->flushRestrictions(); InfoAction::invalidateCache( $this->mTitle ); if ( $logAction == 'unprotect' ) { $params = array(); } else { $protectDescriptionLog = $this->protectDescriptionLog( $limit, $expiry ); $params = array( $protectDescriptionLog, $cascade ? 'cascade' : '' ); } // Update the protection log $log = new LogPage( 'protect' ); $log->addEntry( $logAction, $this->mTitle, trim( $reason ), $params, $user ); return Status::newGood(); }
protected function showHeader() { global $wgOut, $wgUser, $wgMaxArticleSize, $wgLang; if ($this->mTitle->isTalkPage()) { $wgOut->addWikiMsg('talkpagetext'); } # Optional notices on a per-namespace and per-page basis $editnotice_ns = 'editnotice-' . $this->mTitle->getNamespace(); $editnotice_ns_message = wfMessage($editnotice_ns)->inContentLanguage(); if ($editnotice_ns_message->exists()) { $wgOut->addWikiText($editnotice_ns_message->plain()); } if (MWNamespace::hasSubpages($this->mTitle->getNamespace())) { $parts = explode('/', $this->mTitle->getDBkey()); $editnotice_base = $editnotice_ns; while (count($parts) > 0) { $editnotice_base .= '-' . array_shift($parts); $editnotice_base_msg = wfMessage($editnotice_base)->inContentLanguage(); if ($editnotice_base_msg->exists()) { $wgOut->addWikiText($editnotice_base_msg->plain()); } } } else { # Even if there are no subpages in namespace, we still don't want / in MW ns. $editnoticeText = $editnotice_ns . '-' . str_replace('/', '-', $this->mTitle->getDBkey()); $editnoticeMsg = wfMessage($editnoticeText)->inContentLanguage(); if ($editnoticeMsg->exists()) { $wgOut->addWikiText($editnoticeMsg->plain()); } } if ($this->isConflict) { $wgOut->wrapWikiMsg("<div class='mw-explainconflict'>\n\$1\n</div>", 'explainconflict'); $this->edittime = $this->mArticle->getTimestamp(); } else { if ($this->section != '' && !$this->isSectionEditSupported()) { // We use $this->section to much before this and getVal('wgSection') directly in other places // at this point we can't reset $this->section to '' to fallback to non-section editing. // Someone is welcome to try refactoring though $wgOut->showErrorPage('sectioneditnotsupported-title', 'sectioneditnotsupported-text'); return false; } if ($this->section != '' && $this->section != 'new') { if (!$this->summary && !$this->preview && !$this->diff) { $sectionTitle = self::extractSectionTitle($this->textbox1); if ($sectionTitle !== false) { $this->summary = "/* {$sectionTitle} */ "; } } } if ($this->missingComment) { $wgOut->wrapWikiMsg("<div id='mw-missingcommenttext'>\n\$1\n</div>", 'missingcommenttext'); } if ($this->missingSummary && $this->section != 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingsummary'>\n\$1\n</div>", 'missingsummary'); } if ($this->missingSummary && $this->section == 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingcommentheader'>\n\$1\n</div>", 'missingcommentheader'); } if ($this->hookError !== '') { $wgOut->addWikiText($this->hookError); } if (!$this->checkUnicodeCompliantBrowser()) { $wgOut->addWikiMsg('nonunicodebrowser'); } if ($this->section != 'new') { $revision = $this->mArticle->getRevisionFetched(); if ($revision) { // Let sysop know that this will make private content public if saved if (!$revision->userCan(Revision::DELETED_TEXT)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-permission'); } elseif ($revision->isDeleted(Revision::DELETED_TEXT)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-view'); } if (!$revision->isCurrent()) { $this->mArticle->setOldSubtitle($revision->getId()); $wgOut->addWikiMsg('editingold'); } } elseif ($this->mTitle->exists()) { // Something went wrong $wgOut->wrapWikiMsg("<div class='errorbox'>\n\$1\n</div>\n", array('missing-article', $this->mTitle->getPrefixedText(), wfMsgNoTrans('missingarticle-rev', $this->oldid))); } } } if (wfReadOnly()) { $wgOut->wrapWikiMsg("<div id=\"mw-read-only-warning\">\n\$1\n</div>", array('readonlywarning', wfReadOnlyReason())); } elseif ($wgUser->isAnon()) { if ($this->formtype != 'preview') { $wgOut->wrapWikiMsg("<div id=\"mw-anon-edit-warning\">\n\$1</div>", 'anoneditwarning'); } else { $wgOut->wrapWikiMsg("<div id=\"mw-anon-preview-warning\">\n\$1</div>", 'anonpreviewwarning'); } } else { if ($this->isCssJsSubpage) { # Check the skin exists if ($this->isWrongCaseCssJsPage) { $wgOut->wrapWikiMsg("<div class='error' id='mw-userinvalidcssjstitle'>\n\$1\n</div>", array('userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage())); } if ($this->getTitle()->isSubpageOf($wgUser->getUserPage())) { if ($this->formtype !== 'preview') { if ($this->isCssSubpage) { $wgOut->wrapWikiMsg("<div id='mw-usercssyoucanpreview'>\n\$1\n</div>", array('usercssyoucanpreview')); } if ($this->isJsSubpage) { $wgOut->wrapWikiMsg("<div id='mw-userjsyoucanpreview'>\n\$1\n</div>", array('userjsyoucanpreview')); } } } } } if ($this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected('edit')) { # Is the title semi-protected? if ($this->mTitle->isSemiProtected()) { $noticeMsg = 'semiprotectedpagewarning'; } else { # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagewarning'; } LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle, '', array('lim' => 1, 'msgKey' => array($noticeMsg))); } if ($this->mTitle->isCascadeProtected()) { # Is this page under cascading protection from some source pages? list($cascadeSources, ) = $this->mTitle->getCascadeProtectionSources(); $notice = "<div class='mw-cascadeprotectedwarning'>\n\$1\n"; $cascadeSourcesCount = count($cascadeSources); if ($cascadeSourcesCount > 0) { # Explain, and list the titles responsible foreach ($cascadeSources as $page) { $notice .= '* [[:' . $page->getPrefixedText() . "]]\n"; } } $notice .= '</div>'; $wgOut->wrapWikiMsg($notice, array('cascadeprotectedwarning', $cascadeSourcesCount)); } if (!$this->mTitle->exists() && $this->mTitle->getRestrictions('create')) { LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle, '', array('lim' => 1, 'showIfEmpty' => false, 'msgKey' => array('titleprotectedwarning'), 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n\$1</div>")); } if ($this->kblength === false) { $this->kblength = (int) (strlen($this->textbox1) / 1024); } if ($this->tooBig || $this->kblength > $wgMaxArticleSize) { $wgOut->wrapWikiMsg("<div class='error' id='mw-edit-longpageerror'>\n\$1\n</div>", array('longpageerror', $wgLang->formatNum($this->kblength), $wgLang->formatNum($wgMaxArticleSize))); } else { if (!wfMessage('longpage-hint')->isDisabled()) { $wgOut->wrapWikiMsg("<div id='mw-edit-longpage-hint'>\n\$1\n</div>", array('longpage-hint', $wgLang->formatSize(strlen($this->textbox1)), strlen($this->textbox1))); } } }
/** * Update the article's restriction field, and leave a log entry. * This works for protection both existing and non-existing pages. * * @param $limit Array: set of restriction keys * @param $reason String * @param &$cascade Integer. Set to false if cascading protection isn't allowed. * @param $expiry Array: per restriction type expiration * @param $user User The user updating the restrictions * @return Status */ public function doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user) { global $wgContLang; if (wfReadOnly()) { return Status::newFatal('readonlytext', wfReadOnlyReason()); } $restrictionTypes = $this->mTitle->getRestrictionTypes(); $id = $this->mTitle->getArticleID(); if (!$cascade) { $cascade = false; } // Take this opportunity to purge out expired restrictions Title::purgeExpiredRestrictions(); # @todo FIXME: Same limitations as described in ProtectionForm.php (line 37); # we expect a single selection, but the schema allows otherwise. $isProtected = false; $protect = false; $changed = false; $dbw = wfGetDB(DB_MASTER); foreach ($restrictionTypes as $action) { if (!isset($expiry[$action])) { $expiry[$action] = $dbw->getInfinity(); } if (!isset($limit[$action])) { $limit[$action] = ''; } elseif ($limit[$action] != '') { $protect = true; } # Get current restrictions on $action $current = implode('', $this->mTitle->getRestrictions($action)); if ($current != '') { $isProtected = true; } if ($limit[$action] != $current) { $changed = true; } elseif ($limit[$action] != '') { # Only check expiry change if the action is actually being # protected, since expiry does nothing on an not-protected # action. if ($this->mTitle->getRestrictionExpiry($action) != $expiry[$action]) { $changed = true; } } } if (!$changed && $protect && $this->mTitle->areRestrictionsCascading() != $cascade) { $changed = true; } # If nothing's changed, do nothing if (!$changed) { return Status::newGood(); } if (!$protect) { # No protection at all means unprotection $revCommentMsg = 'unprotectedarticle'; $logAction = 'unprotect'; } elseif ($isProtected) { $revCommentMsg = 'modifiedarticleprotection'; $logAction = 'modify'; } else { $revCommentMsg = 'protectedarticle'; $logAction = 'protect'; } $encodedExpiry = array(); $protectDescription = ''; foreach ($limit as $action => $restrictions) { $encodedExpiry[$action] = $dbw->encodeExpiry($expiry[$action]); if ($restrictions != '') { $protectDescription .= $wgContLang->getDirMark() . "[{$action}={$restrictions}] ("; if ($encodedExpiry[$action] != 'infinity') { $protectDescription .= wfMessage('protect-expiring', $wgContLang->timeanddate($expiry[$action], false, false), $wgContLang->date($expiry[$action], false, false), $wgContLang->time($expiry[$action], false, false))->inContentLanguage()->text(); } else { $protectDescription .= wfMessage('protect-expiry-indefinite')->inContentLanguage()->text(); } $protectDescription .= ') '; } } $protectDescription = trim($protectDescription); if ($id) { # Protection of existing page if (!wfRunHooks('ArticleProtect', array(&$this, &$user, $limit, $reason))) { return Status::newGood(); } # Only restrictions with the 'protect' right can cascade... # Otherwise, people who cannot normally protect can "protect" pages via transclusion $editrestriction = isset($limit['edit']) ? array($limit['edit']) : $this->mTitle->getRestrictions('edit'); # The schema allows multiple restrictions if (!in_array('protect', $editrestriction) && !in_array('sysop', $editrestriction)) { $cascade = false; } # Update restrictions table foreach ($limit as $action => $restrictions) { if ($restrictions != '') { $dbw->replace('page_restrictions', array(array('pr_page', 'pr_type')), array('pr_page' => $id, 'pr_type' => $action, 'pr_level' => $restrictions, 'pr_cascade' => $cascade && $action == 'edit' ? 1 : 0, 'pr_expiry' => $encodedExpiry[$action]), __METHOD__); } else { $dbw->delete('page_restrictions', array('pr_page' => $id, 'pr_type' => $action), __METHOD__); } } # Prepare a null revision to be added to the history $editComment = $wgContLang->ucfirst(wfMessage($revCommentMsg, $this->mTitle->getPrefixedText())->inContentLanguage()->text()); if ($reason) { $editComment .= ": {$reason}"; } if ($protectDescription) { $editComment .= " ({$protectDescription})"; } if ($cascade) { // FIXME: Should use 'brackets' message. $editComment .= ' [' . wfMessage('protect-summary-cascade')->inContentLanguage()->text() . ']'; } # Insert a null revision $nullRevision = Revision::newNullRevision($dbw, $id, $editComment, true); $nullRevId = $nullRevision->insertOn($dbw); $latest = $this->getLatest(); # Update page record $dbw->update('page', array('page_touched' => $dbw->timestamp(), 'page_restrictions' => '', 'page_latest' => $nullRevId), array('page_id' => $id), __METHOD__); wfRunHooks('NewRevisionFromEditComplete', array($this, $nullRevision, $latest, $user)); wfRunHooks('ArticleProtectComplete', array(&$this, &$user, $limit, $reason)); } else { # Protection of non-existing page (also known as "title protection") # Cascade protection is meaningless in this case $cascade = false; if ($limit['create'] != '') { $dbw->replace('protected_titles', array(array('pt_namespace', 'pt_title')), array('pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey(), 'pt_create_perm' => $limit['create'], 'pt_timestamp' => $dbw->encodeExpiry(wfTimestampNow()), 'pt_expiry' => $encodedExpiry['create'], 'pt_user' => $user->getId(), 'pt_reason' => $reason), __METHOD__); } else { $dbw->delete('protected_titles', array('pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey()), __METHOD__); } } $this->mTitle->flushRestrictions(); if ($logAction == 'unprotect') { $logParams = array(); } else { $logParams = array($protectDescription, $cascade ? 'cascade' : ''); } # Update the protection log $log = new LogPage('protect'); $log->addEntry($logAction, $this->mTitle, trim($reason), $logParams, $user); return Status::newGood(); }
/** * Update the article's restriction field, and leave a log entry. * * @param $limit Array: set of restriction keys * @param $reason String * @param &$cascade Integer. Set to false if cascading protection isn't allowed. * @param $expiry Array: per restriction type expiration * @param $user User The user updating the restrictions * @return bool true on success */ public function updateRestrictions($limit = array(), $reason = '', &$cascade = 0, $expiry = array(), User $user = null) { global $wgUser, $wgContLang; $user = is_null($user) ? $wgUser : $user; $restrictionTypes = $this->mTitle->getRestrictionTypes(); $id = $this->mTitle->getArticleID(); if ($id <= 0) { wfDebug("updateRestrictions failed: article id {$id} <= 0\n"); return false; } if (wfReadOnly()) { wfDebug("updateRestrictions failed: read-only\n"); return false; } if (count($this->mTitle->getUserPermissionsErrors('protect', $user))) { wfDebug("updateRestrictions failed: insufficient permissions\n"); return false; } if (!$cascade) { $cascade = false; } // Take this opportunity to purge out expired restrictions Title::purgeExpiredRestrictions(); # @todo FIXME: Same limitations as described in ProtectionForm.php (line 37); # we expect a single selection, but the schema allows otherwise. $current = array(); $updated = self::flattenRestrictions($limit); $changed = false; foreach ($restrictionTypes as $action) { if (isset($expiry[$action])) { # Get current restrictions on $action $aLimits = $this->mTitle->getRestrictions($action); $current[$action] = implode('', $aLimits); # Are any actual restrictions being dealt with here? $aRChanged = count($aLimits) || !empty($limit[$action]); # If something changed, we need to log it. Checking $aRChanged # assures that "unprotecting" a page that is not protected does # not log just because the expiry was "changed". if ($aRChanged && $this->mTitle->getRestrictionExpiry($action) != $expiry[$action]) { $changed = true; } } } $current = self::flattenRestrictions($current); $changed = $changed || $current != $updated; $changed = $changed || $updated && $this->mTitle->areRestrictionsCascading() != $cascade; $protect = $updated != ''; # If nothing's changed, do nothing if ($changed) { if (wfRunHooks('ArticleProtect', array(&$this, &$user, $limit, $reason))) { $dbw = wfGetDB(DB_MASTER); # Prepare a null revision to be added to the history $modified = $current != '' && $protect; if ($protect) { $comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle'; } else { $comment_type = 'unprotectedarticle'; } $comment = $wgContLang->ucfirst(wfMsgForContent($comment_type, $this->mTitle->getPrefixedText())); # Only restrictions with the 'protect' right can cascade... # Otherwise, people who cannot normally protect can "protect" pages via transclusion $editrestriction = isset($limit['edit']) ? array($limit['edit']) : $this->mTitle->getRestrictions('edit'); # The schema allows multiple restrictions if (!in_array('protect', $editrestriction) && !in_array('sysop', $editrestriction)) { $cascade = false; } $cascade_description = ''; if ($cascade) { $cascade_description = ' [' . wfMsgForContent('protect-summary-cascade') . ']'; } if ($reason) { $comment .= ": {$reason}"; } $editComment = $comment; $encodedExpiry = array(); $protect_description = ''; foreach ($limit as $action => $restrictions) { if (!isset($expiry[$action])) { $expiry[$action] = $dbw->getInfinity(); } $encodedExpiry[$action] = $dbw->encodeExpiry($expiry[$action]); if ($restrictions != '') { $protect_description .= "[{$action}={$restrictions}] ("; if ($encodedExpiry[$action] != 'infinity') { $protect_description .= wfMsgForContent('protect-expiring', $wgContLang->timeanddate($expiry[$action], false, false), $wgContLang->date($expiry[$action], false, false), $wgContLang->time($expiry[$action], false, false)); } else { $protect_description .= wfMsgForContent('protect-expiry-indefinite'); } $protect_description .= ') '; } } $protect_description = trim($protect_description); if ($protect_description && $protect) { $editComment .= " ({$protect_description})"; } if ($cascade) { $editComment .= "{$cascade_description}"; } # Update restrictions table foreach ($limit as $action => $restrictions) { if ($restrictions != '') { $dbw->replace('page_restrictions', array(array('pr_page', 'pr_type')), array('pr_page' => $id, 'pr_type' => $action, 'pr_level' => $restrictions, 'pr_cascade' => $cascade && $action == 'edit' ? 1 : 0, 'pr_expiry' => $encodedExpiry[$action]), __METHOD__); } else { $dbw->delete('page_restrictions', array('pr_page' => $id, 'pr_type' => $action), __METHOD__); } } # Insert a null revision $nullRevision = Revision::newNullRevision($dbw, $id, $editComment, true); $nullRevId = $nullRevision->insertOn($dbw); $latest = $this->getLatest(); # Update page record $dbw->update('page', array('page_touched' => $dbw->timestamp(), 'page_restrictions' => '', 'page_latest' => $nullRevId), array('page_id' => $id), __METHOD__); wfRunHooks('NewRevisionFromEditComplete', array($this, $nullRevision, $latest, $user)); wfRunHooks('ArticleProtectComplete', array(&$this, &$user, $limit, $reason)); # Update the protection log $log = new LogPage('protect'); if ($protect) { $params = array($protect_description, $cascade ? 'cascade' : ''); $log->addEntry($modified ? 'modify' : 'protect', $this->mTitle, trim($reason), $params); } else { $log->addEntry('unprotect', $this->mTitle, $reason); } } # End hook } # End "changed" check return true; }
/** * @return bool */ protected function showHeader() { global $wgOut, $wgUser, $wgMaxArticleSize, $wgLang; global $wgAllowUserCss, $wgAllowUserJs; if ($this->mTitle->isTalkPage()) { $wgOut->addWikiMsg('talkpagetext'); } // Add edit notices $wgOut->addHTML(implode("\n", $this->mTitle->getEditNotices($this->oldid))); if ($this->isConflict) { $wgOut->wrapWikiMsg("<div class='mw-explainconflict'>\n\$1\n</div>", 'explainconflict'); $this->edittime = $this->mArticle->getTimestamp(); } else { if ($this->section != '' && !$this->isSectionEditSupported()) { // We use $this->section to much before this and getVal('wgSection') directly in other places // at this point we can't reset $this->section to '' to fallback to non-section editing. // Someone is welcome to try refactoring though $wgOut->showErrorPage('sectioneditnotsupported-title', 'sectioneditnotsupported-text'); return false; } if ($this->section != '' && $this->section != 'new') { if (!$this->summary && !$this->preview && !$this->diff) { $sectionTitle = self::extractSectionTitle($this->textbox1); //FIXME: use Content object if ($sectionTitle !== false) { $this->summary = "/* {$sectionTitle} */ "; } } } if ($this->missingComment) { $wgOut->wrapWikiMsg("<div id='mw-missingcommenttext'>\n\$1\n</div>", 'missingcommenttext'); } if ($this->missingSummary && $this->section != 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingsummary'>\n\$1\n</div>", 'missingsummary'); } if ($this->missingSummary && $this->section == 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingcommentheader'>\n\$1\n</div>", 'missingcommentheader'); } if ($this->blankArticle) { $wgOut->wrapWikiMsg("<div id='mw-blankarticle'>\n\$1\n</div>", 'blankarticle'); } if ($this->hookError !== '') { $wgOut->addWikiText($this->hookError); } if (!$this->checkUnicodeCompliantBrowser()) { $wgOut->addWikiMsg('nonunicodebrowser'); } if ($this->section != 'new') { $revision = $this->mArticle->getRevisionFetched(); if ($revision) { // Let sysop know that this will make private content public if saved if (!$revision->userCan(Revision::DELETED_TEXT, $wgUser)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-permission'); } elseif ($revision->isDeleted(Revision::DELETED_TEXT)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-view'); } if (!$revision->isCurrent()) { $this->mArticle->setOldSubtitle($revision->getId()); $wgOut->addWikiMsg('editingold'); } } elseif ($this->mTitle->exists()) { // Something went wrong $wgOut->wrapWikiMsg("<div class='errorbox'>\n\$1\n</div>\n", array('missing-revision', $this->oldid)); } } } if (wfReadOnly()) { $wgOut->wrapWikiMsg("<div id=\"mw-read-only-warning\">\n\$1\n</div>", array('readonlywarning', wfReadOnlyReason())); } elseif ($wgUser->isAnon()) { if ($this->formtype != 'preview') { $wgOut->wrapWikiMsg("<div id='mw-anon-edit-warning'>\n\$1\n</div>", array('anoneditwarning', '{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}', '{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}')); } else { $wgOut->wrapWikiMsg("<div id=\"mw-anon-preview-warning\">\n\$1</div>", 'anonpreviewwarning'); } } else { if ($this->isCssJsSubpage) { # Check the skin exists if ($this->isWrongCaseCssJsPage) { $wgOut->wrapWikiMsg("<div class='error' id='mw-userinvalidcssjstitle'>\n\$1\n</div>", array('userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage())); } if ($this->formtype !== 'preview') { if ($this->isCssSubpage && $wgAllowUserCss) { $wgOut->wrapWikiMsg("<div id='mw-usercssyoucanpreview'>\n\$1\n</div>", array('usercssyoucanpreview')); } if ($this->isJsSubpage && $wgAllowUserJs) { $wgOut->wrapWikiMsg("<div id='mw-userjsyoucanpreview'>\n\$1\n</div>", array('userjsyoucanpreview')); } } } } if ($this->mTitle->isProtected('edit') && MWNamespace::getRestrictionLevels($this->mTitle->getNamespace()) !== array('')) { # Is the title semi-protected? if ($this->mTitle->isSemiProtected()) { $noticeMsg = 'semiprotectedpagewarning'; } else { # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagewarning'; } LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle, '', array('lim' => 1, 'msgKey' => array($noticeMsg))); } if ($this->mTitle->isCascadeProtected()) { # Is this page under cascading protection from some source pages? list($cascadeSources, ) = $this->mTitle->getCascadeProtectionSources(); $notice = "<div class='mw-cascadeprotectedwarning'>\n\$1\n"; $cascadeSourcesCount = count($cascadeSources); if ($cascadeSourcesCount > 0) { # Explain, and list the titles responsible foreach ($cascadeSources as $page) { $notice .= '* [[:' . $page->getPrefixedText() . "]]\n"; } } $notice .= '</div>'; $wgOut->wrapWikiMsg($notice, array('cascadeprotectedwarning', $cascadeSourcesCount)); } if (!$this->mTitle->exists() && $this->mTitle->getRestrictions('create')) { LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle, '', array('lim' => 1, 'showIfEmpty' => false, 'msgKey' => array('titleprotectedwarning'), 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n\$1</div>")); } if ($this->kblength === false) { $this->kblength = (int) (strlen($this->textbox1) / 1024); } if ($this->tooBig || $this->kblength > $wgMaxArticleSize) { $wgOut->wrapWikiMsg("<div class='error' id='mw-edit-longpageerror'>\n\$1\n</div>", array('longpageerror', $wgLang->formatNum($this->kblength), $wgLang->formatNum($wgMaxArticleSize))); } else { if (!wfMessage('longpage-hint')->isDisabled()) { $wgOut->wrapWikiMsg("<div id='mw-edit-longpage-hint'>\n\$1\n</div>", array('longpage-hint', $wgLang->formatSize(strlen($this->textbox1)), strlen($this->textbox1))); } } # Add header copyright warning $this->showHeaderCopyrightWarning(); return true; }
/** * Adds protection information to the Api result * @param Title $title */ private function addProtection(Title $title) { $result = $this->getResult(); $protection = array(); foreach ($title->getRestrictionTypes() as $type) { $levels = $title->getRestrictions($type); if ($levels) { $protection[$type] = $levels; $result->setIndexedTagName($protection[$type], 'level'); } } $result->addValue(null, $this->getModuleName(), array('protection' => $protection)); }
protected function showHeader() { global $wgOut, $wgUser, $wgMaxArticleSize, $wgLang; if ($this->isConflict) { $wgOut->wrapWikiMsg("<div class='mw-explainconflict'>\n\$1\n</div>", 'explainconflict'); $this->edittime = $this->mArticle->getTimestamp(); } else { if ($this->section != '' && !$this->isSectionEditSupported()) { // We use $this->section to much before this and getVal('wgSection') directly in other places // at this point we can't reset $this->section to '' to fallback to non-section editing. // Someone is welcome to try refactoring though $wgOut->showErrorPage('sectioneditnotsupported-title', 'sectioneditnotsupported-text'); return false; } if ($this->section != '' && $this->section != 'new') { $matches = array(); if (!$this->summary && !$this->preview && !$this->diff) { preg_match("/^(=+)(.+)\\1/mi", $this->textbox1, $matches); if (!empty($matches[2])) { global $wgParser; $this->summary = "/* " . $wgParser->stripSectionName(trim($matches[2])) . " */ "; } } } if ($this->missingComment) { $wgOut->wrapWikiMsg("<div id='mw-missingcommenttext'>\n\$1\n</div>", 'missingcommenttext'); } if ($this->missingSummary && $this->section != 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingsummary'>\n\$1\n</div>", 'missingsummary'); } if ($this->missingSummary && $this->section == 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingcommentheader'>\n\$1\n</div>", 'missingcommentheader'); } if ($this->hookError !== '') { $wgOut->addWikiText($this->hookError); } if (!$this->checkUnicodeCompliantBrowser()) { $wgOut->addWikiMsg('nonunicodebrowser'); } if (isset($this->mArticle) && isset($this->mArticle->mRevision)) { // Let sysop know that this will make private content public if saved if (!$this->mArticle->mRevision->userCan(Revision::DELETED_TEXT)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-permission'); } elseif ($this->mArticle->mRevision->isDeleted(Revision::DELETED_TEXT)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-view'); } if (!$this->mArticle->mRevision->isCurrent()) { $this->mArticle->setOldSubtitle($this->mArticle->mRevision->getId()); $wgOut->addWikiMsg('editingold'); } } } if (wfReadOnly()) { $wgOut->wrapWikiMsg("<div id=\"mw-read-only-warning\">\n\$1\n</div>", array('readonlywarning', wfReadOnlyReason())); } elseif ($wgUser->isAnon()) { if ($this->formtype != 'preview') { $wgOut->wrapWikiMsg("<div id=\"mw-anon-edit-warning\">\n\$1</div>", 'anoneditwarning'); } else { $wgOut->wrapWikiMsg("<div id=\"mw-anon-preview-warning\">\n\$1</div>", 'anonpreviewwarning'); } } else { if ($this->isCssJsSubpage) { # Check the skin exists if ($this->isWrongCaseCssJsPage) { $wgOut->wrapWikiMsg("<div class='error' id='mw-userinvalidcssjstitle'>\n\$1\n</div>", array('userinvalidcssjstitle', $this->getContextTitle()->getSkinFromCssJsSubpage())); } if ($this->formtype !== 'preview') { if ($this->isCssSubpage) { $wgOut->wrapWikiMsg("<div id='mw-usercssyoucanpreview'>\n\$1\n</div>", array('usercssyoucanpreview')); } if ($this->isJsSubpage) { $wgOut->wrapWikiMsg("<div id='mw-userjsyoucanpreview'>\n\$1\n</div>", array('userjsyoucanpreview')); } } } } if ($this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected('edit')) { # Is the title semi-protected? if ($this->mTitle->isSemiProtected()) { $noticeMsg = 'semiprotectedpagewarning'; } else { # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagewarning'; } LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle->getPrefixedText(), '', array('lim' => 1, 'msgKey' => array($noticeMsg))); } if ($this->mTitle->isCascadeProtected()) { # Is this page under cascading protection from some source pages? list($cascadeSources, ) = $this->mTitle->getCascadeProtectionSources(); $notice = "<div class='mw-cascadeprotectedwarning'>\n\$1\n"; $cascadeSourcesCount = count($cascadeSources); if ($cascadeSourcesCount > 0) { # Explain, and list the titles responsible foreach ($cascadeSources as $page) { $notice .= '* [[:' . $page->getPrefixedText() . "]]\n"; } } $notice .= '</div>'; $wgOut->wrapWikiMsg($notice, array('cascadeprotectedwarning', $cascadeSourcesCount)); } if (!$this->mTitle->exists() && $this->mTitle->getRestrictions('create')) { LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle->getPrefixedText(), '', array('lim' => 1, 'showIfEmpty' => false, 'msgKey' => array('titleprotectedwarning'), 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n\$1</div>")); } if ($this->kblength === false) { $this->kblength = (int) (strlen($this->textbox1) / 1024); } if ($this->tooBig || $this->kblength > $wgMaxArticleSize) { $wgOut->wrapWikiMsg("<div class='error' id='mw-edit-longpageerror'>\n\$1\n</div>", array('longpageerror', $wgLang->formatNum($this->kblength), $wgLang->formatNum($wgMaxArticleSize))); } else { if (!wfMessage('longpage-hint')->isDisabled()) { $wgOut->wrapWikiMsg("<div id='mw-edit-longpage-hint'>\n\$1\n</div>", array('longpage-hint', $wgLang->formatSize(strlen($this->textbox1)), strlen($this->textbox1))); } } }
/** * Update the article's restriction field, and leave a log entry. * This works for protection both existing and non-existing pages. * * @param array $limit set of restriction keys * @param $reason String * @param &$cascade Integer. Set to false if cascading protection isn't allowed. * @param array $expiry per restriction type expiration * @param $user User The user updating the restrictions * @return Status */ public function doUpdateRestrictions(array $limit, array $expiry, &$cascade, $reason, User $user) { global $wgContLang, $wgCascadingRestrictionLevels; if (wfReadOnly()) { return Status::newFatal('readonlytext', wfReadOnlyReason()); } $restrictionTypes = $this->mTitle->getRestrictionTypes(); $id = $this->getId(); if (!$cascade) { $cascade = false; } // Take this opportunity to purge out expired restrictions Title::purgeExpiredRestrictions(); // @todo FIXME: Same limitations as described in ProtectionForm.php (line 37); // we expect a single selection, but the schema allows otherwise. $isProtected = false; $protect = false; $changed = false; $dbw = wfGetDB(DB_MASTER); foreach ($restrictionTypes as $action) { if (!isset($expiry[$action])) { $expiry[$action] = $dbw->getInfinity(); } if (!isset($limit[$action])) { $limit[$action] = ''; } elseif ($limit[$action] != '') { $protect = true; } // Get current restrictions on $action $current = implode('', $this->mTitle->getRestrictions($action)); if ($current != '') { $isProtected = true; } if ($limit[$action] != $current) { $changed = true; } elseif ($limit[$action] != '') { // Only check expiry change if the action is actually being // protected, since expiry does nothing on an not-protected // action. if ($this->mTitle->getRestrictionExpiry($action) != $expiry[$action]) { $changed = true; } } } if (!$changed && $protect && $this->mTitle->areRestrictionsCascading() != $cascade) { $changed = true; } // If nothing has changed, do nothing if (!$changed) { return Status::newGood(); } if (!$protect) { // No protection at all means unprotection $revCommentMsg = 'unprotectedarticle'; $logAction = 'unprotect'; } elseif ($isProtected) { $revCommentMsg = 'modifiedarticleprotection'; $logAction = 'modify'; } else { $revCommentMsg = 'protectedarticle'; $logAction = 'protect'; } $encodedExpiry = array(); $protectDescription = ''; # Some bots may parse IRC lines, which are generated from log entries which contain plain # protect description text. Keep them in old format to avoid breaking compatibility. # TODO: Fix protection log to store structured description and format it on-the-fly. $protectDescriptionLog = ''; foreach ($limit as $action => $restrictions) { $encodedExpiry[$action] = $dbw->encodeExpiry($expiry[$action]); if ($restrictions != '') { $protectDescriptionLog .= $wgContLang->getDirMark() . "[{$action}={$restrictions}] ("; # $action is one of $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' ). # All possible message keys are listed here for easier grepping: # * restriction-create # * restriction-edit # * restriction-move # * restriction-upload $actionText = wfMessage('restriction-' . $action)->inContentLanguage()->text(); # $restrictions is one of $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ), # with '' filtered out. All possible message keys are listed below: # * protect-level-autoconfirmed # * protect-level-sysop $restrictionsText = wfMessage('protect-level-' . $restrictions)->inContentLanguage()->text(); if ($encodedExpiry[$action] != 'infinity') { $expiryText = wfMessage('protect-expiring', $wgContLang->timeanddate($expiry[$action], false, false), $wgContLang->date($expiry[$action], false, false), $wgContLang->time($expiry[$action], false, false))->inContentLanguage()->text(); } else { $expiryText = wfMessage('protect-expiry-indefinite')->inContentLanguage()->text(); } if ($protectDescription !== '') { $protectDescription .= wfMessage('word-separator')->inContentLanguage()->text(); } $protectDescription .= wfMessage('protect-summary-desc')->params($actionText, $restrictionsText, $expiryText)->inContentLanguage()->text(); $protectDescriptionLog .= $expiryText . ') '; } } $protectDescriptionLog = trim($protectDescriptionLog); if ($id) { // Protection of existing page if (!wfRunHooks('ArticleProtect', array(&$this, &$user, $limit, $reason))) { return Status::newGood(); } // Only certain restrictions can cascade... Otherwise, users who cannot normally protect pages // could "protect" them by transcluding them on protected pages they are allowed to edit. $editrestriction = isset($limit['edit']) ? array($limit['edit']) : $this->mTitle->getRestrictions('edit'); $cascadingRestrictionLevels = $wgCascadingRestrictionLevels; if (in_array('sysop', $cascadingRestrictionLevels)) { $cascadingRestrictionLevels[] = 'protect'; // backwards compatibility } // The schema allows multiple restrictions if (!array_intersect($editrestriction, $cascadingRestrictionLevels)) { $cascade = false; } // Update restrictions table foreach ($limit as $action => $restrictions) { if ($restrictions != '') { $dbw->replace('page_restrictions', array(array('pr_page', 'pr_type')), array('pr_page' => $id, 'pr_type' => $action, 'pr_level' => $restrictions, 'pr_cascade' => $cascade && $action == 'edit' ? 1 : 0, 'pr_expiry' => $encodedExpiry[$action]), __METHOD__); } else { $dbw->delete('page_restrictions', array('pr_page' => $id, 'pr_type' => $action), __METHOD__); } } // Prepare a null revision to be added to the history $editComment = $wgContLang->ucfirst(wfMessage($revCommentMsg, $this->mTitle->getPrefixedText())->inContentLanguage()->text()); if ($reason) { $editComment .= wfMessage('colon-separator')->inContentLanguage()->text() . $reason; } if ($protectDescription) { $editComment .= wfMessage('word-separator')->inContentLanguage()->text(); $editComment .= wfMessage('parentheses')->params($protectDescription)->inContentLanguage()->text(); } if ($cascade) { $editComment .= wfMessage('word-separator')->inContentLanguage()->text(); $editComment .= wfMessage('brackets')->params(wfMessage('protect-summary-cascade')->inContentLanguage()->text())->inContentLanguage()->text(); } // Insert a null revision $nullRevision = Revision::newNullRevision($dbw, $id, $editComment, true); $nullRevId = $nullRevision->insertOn($dbw); $latest = $this->getLatest(); // Update page record $dbw->update('page', array('page_touched' => $dbw->timestamp(), 'page_restrictions' => '', 'page_latest' => $nullRevId), array('page_id' => $id), __METHOD__); wfRunHooks('NewRevisionFromEditComplete', array($this, $nullRevision, $latest, $user)); wfRunHooks('ArticleProtectComplete', array(&$this, &$user, $limit, $reason)); } else { // Protection of non-existing page (also known as "title protection") // Cascade protection is meaningless in this case $cascade = false; if ($limit['create'] != '') { $dbw->replace('protected_titles', array(array('pt_namespace', 'pt_title')), array('pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey(), 'pt_create_perm' => $limit['create'], 'pt_timestamp' => $dbw->encodeExpiry(wfTimestampNow()), 'pt_expiry' => $encodedExpiry['create'], 'pt_user' => $user->getId(), 'pt_reason' => $reason), __METHOD__); } else { $dbw->delete('protected_titles', array('pt_namespace' => $this->mTitle->getNamespace(), 'pt_title' => $this->mTitle->getDBkey()), __METHOD__); } } $this->mTitle->flushRestrictions(); InfoAction::invalidateCache($this->mTitle); if ($logAction == 'unprotect') { $logParams = array(); } else { $logParams = array($protectDescriptionLog, $cascade ? 'cascade' : ''); } // Update the protection log $log = new LogPage('protect'); $log->addEntry($logAction, $this->mTitle, trim($reason), $logParams, $user); return Status::newGood(); }