public function checkPermissions(User $user, $reason) { $status = new Status(); $errors = wfMergeErrorArrays($this->oldTitle->getUserPermissionsErrors('move', $user), $this->oldTitle->getUserPermissionsErrors('edit', $user), $this->newTitle->getUserPermissionsErrors('move-target', $user), $this->newTitle->getUserPermissionsErrors('edit', $user)); // Convert into a Status object if ($errors) { foreach ($errors as $error) { call_user_func_array(array($status, 'fatal'), $error); } } if (EditPage::matchSummarySpamRegex($reason) !== false) { // This is kind of lame, won't display nice $status->fatal('spamprotectiontext'); } # The move is allowed only if (1) the target doesn't exist, or # (2) the target is a redirect to the source, and has no history # (so we can undo bad moves right after they're done). if ($this->newTitle->getArticleID()) { # Target exists; check for validity if (!$this->isValidMoveTarget()) { $status->fatal('articleexists'); } } else { $tp = $this->newTitle->getTitleProtection(); if ($tp !== false) { if (!$user->isAllowed($tp['permission'])) { $status->fatal('cantmove-titleprotected'); } } } Hooks::run('MovePageCheckPermissions', array($this->oldTitle, $this->newTitle, $user, $reason, $status)); return $status; }
/** * Fulfil the request; shows the form or deletes the file, * pending authentication, confirmation, etc. */ public function execute() { global $wgOut, $wgRequest, $wgUser, $wgUploadMaintenance; $permissionErrors = $this->title->getUserPermissionsErrors('delete', $wgUser); if (count($permissionErrors)) { throw new PermissionsError('delete', $permissionErrors); } if (wfReadOnly()) { throw new ReadOnlyError(); } if ($wgUploadMaintenance) { throw new ErrorPageError('filedelete-maintenance-title', 'filedelete-maintenance'); } $this->setHeaders(); $this->oldimage = $wgRequest->getText('oldimage', false); $token = $wgRequest->getText('wpEditToken'); # Flag to hide all contents of the archived revisions $suppress = $wgRequest->getVal('wpSuppress') && $wgUser->isAllowed('suppressrevision'); if ($this->oldimage) { $this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName($this->title, $this->oldimage); } if (!self::haveDeletableFile($this->file, $this->oldfile, $this->oldimage)) { $wgOut->addHTML($this->prepareMessage('filedelete-nofile')); $wgOut->addReturnTo($this->title); return; } // Perform the deletion if appropriate if ($wgRequest->wasPosted() && $wgUser->matchEditToken($token, $this->oldimage)) { $deleteReasonList = $wgRequest->getText('wpDeleteReasonList'); $deleteReason = $wgRequest->getText('wpReason'); if ($deleteReasonList == 'other') { $reason = $deleteReason; } elseif ($deleteReason != '') { // Entry from drop down menu + additional comment $reason = $deleteReasonList . wfMessage('colon-separator')->inContentLanguage()->text() . $deleteReason; } else { $reason = $deleteReasonList; } $status = self::doDelete($this->title, $this->file, $this->oldimage, $reason, $suppress, $wgUser); if (!$status->isGood()) { $wgOut->addHTML('<h2>' . $this->prepareMessage('filedeleteerror-short') . "</h2>\n"); $wgOut->addWikiText('<div class="error">' . $status->getWikiText('filedeleteerror-short', 'filedeleteerror-long') . '</div>'); } if ($status->ok) { $wgOut->setPageTitle(wfMessage('actioncomplete')); $wgOut->addHTML($this->prepareMessage('filedelete-success')); // Return to the main page if we just deleted all versions of the // file, otherwise go back to the description page $wgOut->addReturnTo($this->oldimage ? $this->title : Title::newMainPage()); WatchAction::doWatchOrUnwatch($wgRequest->getCheck('wpWatch'), $this->title, $wgUser); } return; } $this->showForm(); $this->showLogEntries(); }
/** * Roll back the most recent consecutive set of edits to a page * from the same user; fails if there are no eligible edits to * roll back to, e.g. user is the sole contributor. This function * performs permissions checks on $user, then calls commitRollback() * to do the dirty work * * @todo Separate the business/permission stuff out from backend code * * @param string $fromP Name of the user whose edits to rollback. * @param string $summary Custom summary. Set to default summary if empty. * @param string $token Rollback token. * @param $bot Boolean: If true, mark all reverted edits as bot. * * @param array $resultDetails contains result-specific array of additional values * 'alreadyrolled' : 'current' (rev) * success : 'summary' (str), 'current' (rev), 'target' (rev) * * @param $user User The user performing the rollback * @return array of errors, each error formatted as * array(messagekey, param1, param2, ...). * On success, the array is empty. This array can also be passed to * OutputPage::showPermissionsErrorPage(). */ public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user ) { $resultDetails = null; // Check permissions $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $user ); $rollbackErrors = $this->mTitle->getUserPermissionsErrors( 'rollback', $user ); $errors = array_merge( $editErrors, wfArrayDiff2( $rollbackErrors, $editErrors ) ); if ( !$user->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) ) { $errors[] = array( 'sessionfailure' ); } if ( $user->pingLimiter( 'rollback' ) || $user->pingLimiter() ) { $errors[] = array( 'actionthrottledtext' ); } // If there were errors, bail out now if ( !empty( $errors ) ) { return $errors; } return $this->commitRollback( $fromP, $summary, $bot, $resultDetails, $user ); }
public function testUserBlock() { global $wgEmailConfirmToEdit, $wgEmailAuthentication; $wgEmailConfirmToEdit = true; $wgEmailAuthentication = true; $this->setUserPerm(array("createpage", "move")); $this->setTitle(NS_HELP, "test page"); # $short $this->assertEquals(array(array('confirmedittext')), $this->title->getUserPermissionsErrors('move-target', $this->user)); $wgEmailConfirmToEdit = false; $this->assertEquals(true, $this->title->userCan('move-target', $this->user)); # $wgEmailConfirmToEdit && !$user->isEmailConfirmed() && $action != 'createaccount' $this->assertEquals(array(), $this->title->getUserPermissionsErrors('move-target', $this->user)); global $wgLang; $prev = time(); $now = time() + 120; $this->user->mBlockedby = $this->user->getId(); $this->user->mBlock = new Block('127.0.8.1', 0, $this->user->getId(), 'no reason given', $prev + 3600, 1, 0); $this->user->mBlock->mTimestamp = 0; $this->assertEquals(array(array('autoblockedtext', '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1', 'Useruser', null, 'infinite', '127.0.8.1', $wgLang->timeanddate(wfTimestamp(TS_MW, $prev), true))), $this->title->getUserPermissionsErrors('move-target', $this->user)); $this->assertEquals(false, $this->title->userCan('move-target', $this->user)); // quickUserCan should ignore user blocks $this->assertEquals(true, $this->title->quickUserCan('move-target', $this->user)); global $wgLocalTZoffset; $wgLocalTZoffset = -60; $this->user->mBlockedby = $this->user->getName(); $this->user->mBlock = new Block('127.0.8.1', 0, $this->user->getId(), 'no reason given', $now, 0, 10); $this->assertEquals(array(array('blockedtext', '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1', 'Useruser', null, '23:00, 31 December 1969', '127.0.8.1', $wgLang->timeanddate(wfTimestamp(TS_MW, $now), true))), $this->title->getUserPermissionsErrors('move-target', $this->user)); # $action != 'read' && $action != 'createaccount' && $user->isBlockedFrom( $this ) # $user->blockedFor() == '' # $user->mBlock->mExpiry == 'infinity' }
/** * Do the basic checks whether moving is possible and whether * the input looks anywhere near sane. * @return bool */ protected function doBasicChecks() { global $wgOut; # Check for database lock if ( wfReadOnly() ) { $wgOut->readOnlyPage(); return false; } if ( $this->title === null ) { $wgOut->showErrorPage( 'notargettitle', 'notargettext' ); return false; } if ( !$this->title->exists() ) { $wgOut->showErrorPage( 'nopagetitle', 'nopagetext' ); return false; } # Check rights $permErrors = $this->title->getUserPermissionsErrors( 'delete', $this->user ); if ( !empty( $permErrors ) ) { $wgOut->showPermissionsErrorPage( $permErrors ); return false; } // Let the caller know it's safe to continue return true; }
public function checkPermissions(User $user, $reason) { $status = new Status(); $errors = wfMergeErrorArrays($this->oldTitle->getUserPermissionsErrors('move', $user), $this->oldTitle->getUserPermissionsErrors('edit', $user), $this->newTitle->getUserPermissionsErrors('move-target', $user), $this->newTitle->getUserPermissionsErrors('edit', $user)); // Convert into a Status object if ($errors) { foreach ($errors as $error) { call_user_func_array(array($status, 'fatal'), $error); } } if (EditPage::matchSummarySpamRegex($reason) !== false) { // This is kind of lame, won't display nice $status->fatal('spamprotectiontext'); } $tp = $this->newTitle->getTitleProtection(); if ($tp !== false && !$user->isAllowed($tp['permission'])) { $status->fatal('cantmove-titleprotected'); } Hooks::run('MovePageCheckPermissions', array($this->oldTitle, $this->newTitle, $user, $reason, $status)); return $status; }
/** * @return array */ protected function getEditPermissionErrors() { global $wgUser; $permErrors = $this->mTitle->getUserPermissionsErrors('edit', $wgUser); # Can this title be created? if (!$this->mTitle->exists()) { $permErrors = array_merge($permErrors, wfArrayDiff2($this->mTitle->getUserPermissionsErrors('create', $wgUser), $permErrors)); } # Ignore some permissions errors when a user is just previewing/viewing diffs $remove = array(); foreach ($permErrors as $error) { if (($this->preview || $this->diff) && ($error[0] == 'blockedtext' || $error[0] == 'autoblockedtext')) { $remove[] = $error; } } $permErrors = wfArrayDiff2($permErrors, $remove); return $permErrors; }
/** * Do the basic checks whether moving is possible and whether * the input looks anywhere near sane. * @throws PermissionsError|ErrorPageError|ReadOnlyError * @return bool */ protected function doBasicChecks() { # Check rights if (!$this->userCanExecute($this->getUser())) { $this->displayRestrictionError(); } if ($this->title === null) { throw new ErrorPageError('notargettitle', 'notargettext'); } if (!$this->title->exists()) { throw new ErrorPageError('nopagetitle', 'nopagetext'); } $permissionErrors = $this->title->getUserPermissionsErrors('delete', $this->getUser()); if (count($permissionErrors)) { throw new PermissionsError('delete', $permissionErrors); } # Check for database lock if (wfReadOnly()) { throw new ReadOnlyError(); } // Let the caller know it's safe to continue return true; }
public function showDiffPage($diffOnly = false) { # Allow frames except in certain special cases $out = $this->getOutput(); $out->allowClickjacking(); $out->setRobotPolicy('noindex,nofollow'); if (!$this->loadRevisionData()) { $this->showMissingRevision(); return; } $user = $this->getUser(); $permErrors = $this->mNewPage->getUserPermissionsErrors('read', $user); if ($this->mOldPage) { # mOldPage might not be set, see below. $permErrors = wfMergeErrorArrays($permErrors, $this->mOldPage->getUserPermissionsErrors('read', $user)); } if (count($permErrors)) { throw new PermissionsError('read', $permErrors); } $rollback = ''; $query = array(); # Carry over 'diffonly' param via navigation links if ($diffOnly != $user->getBoolOption('diffonly')) { $query['diffonly'] = $diffOnly; } # Cascade unhide param in links for easy deletion browsing if ($this->unhide) { $query['unhide'] = 1; } # Check if one of the revisions is deleted/suppressed $deleted = $suppressed = false; $allowed = $this->mNewRev->userCan(Revision::DELETED_TEXT, $user); $revisionTools = array(); # mOldRev is false if the difference engine is called with a "vague" query for # a diff between a version V and its previous version V' AND the version V # is the first version of that article. In that case, V' does not exist. if ($this->mOldRev === false) { $out->setPageTitle($this->msg('difference-title', $this->mNewPage->getPrefixedText())); $samePage = true; $oldHeader = ''; } else { Hooks::run('DiffViewHeader', array($this, $this->mOldRev, $this->mNewRev)); if ($this->mNewPage->equals($this->mOldPage)) { $out->setPageTitle($this->msg('difference-title', $this->mNewPage->getPrefixedText())); $samePage = true; } else { $out->setPageTitle($this->msg('difference-title-multipage', $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText())); $out->addSubtitle($this->msg('difference-multipage')); $samePage = false; } if ($samePage && $this->mNewPage->quickUserCan('edit', $user)) { if ($this->mNewRev->isCurrent() && $this->mNewPage->userCan('rollback', $user)) { $rollbackLink = Linker::generateRollback($this->mNewRev, $this->getContext()); if ($rollbackLink) { $out->preventClickjacking(); $rollback = '   ' . $rollbackLink; } } if (!$this->mOldRev->isDeleted(Revision::DELETED_TEXT) && !$this->mNewRev->isDeleted(Revision::DELETED_TEXT)) { $undoLink = Html::element('a', array('href' => $this->mNewPage->getLocalURL(array('action' => 'edit', 'undoafter' => $this->mOldid, 'undo' => $this->mNewid)), 'title' => Linker::titleAttrib('undo')), $this->msg('editundo')->text()); $revisionTools['mw-diff-undo'] = $undoLink; } } # Make "previous revision link" if ($samePage && $this->mOldRev->getPrevious()) { $prevlink = Linker::linkKnown($this->mOldPage, $this->msg('previousdiff')->escaped(), array('id' => 'differences-prevlink'), array('diff' => 'prev', 'oldid' => $this->mOldid) + $query); } else { $prevlink = ' '; } if ($this->mOldRev->isMinor()) { $oldminor = ChangesList::flag('minor'); } else { $oldminor = ''; } $ldel = $this->revisionDeleteLink($this->mOldRev); $oldRevisionHeader = $this->getRevisionHeader($this->mOldRev, 'complete'); $oldChangeTags = ChangeTags::formatSummaryRow($this->mOldTags, 'diff'); $oldHeader = '<div id="mw-diff-otitle1"><strong>' . $oldRevisionHeader . '</strong></div>' . '<div id="mw-diff-otitle2">' . Linker::revUserTools($this->mOldRev, !$this->unhide) . '</div>' . '<div id="mw-diff-otitle3">' . $oldminor . Linker::revComment($this->mOldRev, !$diffOnly, !$this->unhide) . $ldel . '</div>' . '<div id="mw-diff-otitle5">' . $oldChangeTags[0] . '</div>' . '<div id="mw-diff-otitle4">' . $prevlink . '</div>'; if ($this->mOldRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // old revisions text is hidden if ($this->mOldRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; // also suppressed } } # Check if this user can see the revisions if (!$this->mOldRev->userCan(Revision::DELETED_TEXT, $user)) { $allowed = false; } } # Make "next revision link" # Skip next link on the top revision if ($samePage && !$this->mNewRev->isCurrent()) { $nextlink = Linker::linkKnown($this->mNewPage, $this->msg('nextdiff')->escaped(), array('id' => 'differences-nextlink'), array('diff' => 'next', 'oldid' => $this->mNewid) + $query); } else { $nextlink = ' '; } if ($this->mNewRev->isMinor()) { $newminor = ChangesList::flag('minor'); } else { $newminor = ''; } # Handle RevisionDelete links... $rdel = $this->revisionDeleteLink($this->mNewRev); # Allow extensions to define their own revision tools Hooks::run('DiffRevisionTools', array($this->mNewRev, &$revisionTools, $this->mOldRev, $user)); $formattedRevisionTools = array(); // Put each one in parentheses (poor man's button) foreach ($revisionTools as $key => $tool) { $toolClass = is_string($key) ? $key : 'mw-diff-tool'; $element = Html::rawElement('span', array('class' => $toolClass), $this->msg('parentheses')->rawParams($tool)->escaped()); $formattedRevisionTools[] = $element; } $newRevisionHeader = $this->getRevisionHeader($this->mNewRev, 'complete') . ' ' . implode(' ', $formattedRevisionTools); $newChangeTags = ChangeTags::formatSummaryRow($this->mNewTags, 'diff'); $newHeader = '<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader . '</strong></div>' . '<div id="mw-diff-ntitle2">' . Linker::revUserTools($this->mNewRev, !$this->unhide) . " {$rollback}</div>" . '<div id="mw-diff-ntitle3">' . $newminor . Linker::revComment($this->mNewRev, !$diffOnly, !$this->unhide) . $rdel . '</div>' . '<div id="mw-diff-ntitle5">' . $newChangeTags[0] . '</div>' . '<div id="mw-diff-ntitle4">' . $nextlink . $this->markPatrolledLink() . '</div>'; if ($this->mNewRev->isDeleted(Revision::DELETED_TEXT)) { $deleted = true; // new revisions text is hidden if ($this->mNewRev->isDeleted(Revision::DELETED_RESTRICTED)) { $suppressed = true; // also suppressed } } # If the diff cannot be shown due to a deleted revision, then output # the diff header and links to unhide (if available)... if ($deleted && (!$this->unhide || !$allowed)) { $this->showDiffStyle(); $multi = $this->getMultiNotice(); $out->addHTML($this->addHeader('', $oldHeader, $newHeader, $multi)); if (!$allowed) { $msg = $suppressed ? 'rev-suppressed-no-diff' : 'rev-deleted-no-diff'; # Give explanation for why revision is not visible $out->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg)); } else { # Give explanation and add a link to view the diff... $query = $this->getRequest()->appendQueryValue('unhide', '1'); $link = $this->getTitle()->getFullURL($query); $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff'; $out->wrapWikiMsg("<div id='mw-{$msg}' class='mw-warning plainlinks'>\n\$1\n</div>\n", array($msg, $link)); } # Otherwise, output a regular diff... } else { # Add deletion notice if the user is viewing deleted content $notice = ''; if ($deleted) { $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view'; $notice = "<div id='mw-{$msg}' class='mw-warning plainlinks'>\n" . $this->msg($msg)->parse() . "</div>\n"; } $this->showDiff($oldHeader, $newHeader, $notice); if (!$diffOnly) { $this->renderNewRevision(); } } }
/** * Checks that the user has permissions to perform rotations. * @param User $user The user to check * @param Title $title * @return string|null Permission error message, or null if there is no error */ protected function checkPermissions($user, $title) { $permissionErrors = array_merge($title->getUserPermissionsErrors('edit', $user), $title->getUserPermissionsErrors('upload', $user)); if ($permissionErrors) { // Just return the first error $msg = $this->parseMsg($permissionErrors[0]); return $msg['info']; } return null; }
public function onSubmit(array $data) { global $wgContLang; if ($data['pagetitle'] === '') { // Initial form view of special page, pass return false; } // At this point, it has to be a POST request. This is enforced by HTMLForm, // but lets be safe verify that. if (!$this->getRequest()->wasPosted()) { throw new RuntimeException("Form submission was not POSTed"); } $this->title = Title::newFromText($data['pagetitle']); $user = $this->getUser(); // Check permissions and make sure the user has permission to edit the specific page $errors = $this->title->getUserPermissionsErrors('editcontentmodel', $user); $errors = wfMergeErrorArrays($errors, $this->title->getUserPermissionsErrors('edit', $user)); if ($errors) { $out = $this->getOutput(); $wikitext = $out->formatPermissionsErrorMessage($errors); // Hack to get our wikitext parsed return Status::newFatal(new RawMessage('$1', array($wikitext))); } $page = WikiPage::factory($this->title); if ($this->oldRevision === null) { $this->oldRevision = $page->getRevision() ?: false; } $oldModel = $this->title->getContentModel(); if ($this->oldRevision) { $oldContent = $this->oldRevision->getContent(); try { $newContent = ContentHandler::makeContent($oldContent->getNativeData(), $this->title, $data['model']); } catch (MWException $e) { return Status::newFatal($this->msg('changecontentmodel-cannot-convert')->params($this->title->getPrefixedText(), ContentHandler::getLocalizedName($data['model']))); } } else { // Page doesn't exist, create an empty content object $newContent = ContentHandler::getForModelID($data['model'])->makeEmptyContent(); } $flags = $this->oldRevision ? EDIT_UPDATE : EDIT_NEW; if ($user->isAllowed('bot')) { $flags |= EDIT_FORCE_BOT; } $log = new ManualLogEntry('contentmodel', 'change'); $log->setPerformer($user); $log->setTarget($this->title); $log->setComment($data['reason']); $log->setParameters(array('4::oldmodel' => $oldModel, '5::newmodel' => $data['model'])); $formatter = LogFormatter::newFromEntry($log); $formatter->setContext(RequestContext::newExtraneousContext($this->title)); $reason = $formatter->getPlainActionText(); if ($data['reason'] !== '') { $reason .= $this->msg('colon-separator')->inContentLanguage()->text() . $data['reason']; } # Truncate for whole multibyte characters. $reason = $wgContLang->truncate($reason, 255); $status = $page->doEditContent($newContent, $reason, $flags, $this->oldRevision ? $this->oldRevision->getId() : false, $user); if (!$status->isOK()) { return $status; } $logid = $log->insert(); $log->publish($logid); return $status; }
/** * Show the form * * @param array $err Error messages. Each item is an error message. * It may either be a string message name or array message name and * parameters, like the second argument to OutputPage::wrapWikiMsg(). */ function showForm($err) { global $wgContLang; $this->getSkin()->setRelevantTitle($this->oldTitle); $oldTitleLink = Linker::link($this->oldTitle); $out = $this->getOutput(); $out->setPageTitle($this->msg('move-page', $this->oldTitle->getPrefixedText())); $out->addModules('mediawiki.special.movePage'); $out->addModuleStyles('mediawiki.special.movePage.styles'); $this->addHelpLink('Help:Moving a page'); $newTitle = $this->newTitle; if (!$newTitle) { # Show the current title as a default # when the form is first opened. $newTitle = $this->oldTitle; } elseif (!count($err)) { # If a title was supplied, probably from the move log revert # link, check for validity. We can then show some diagnostic # information and save a click. $newerr = $this->oldTitle->isValidMoveOperation($newTitle); if (is_array($newerr)) { $err = $newerr; } } $user = $this->getUser(); if (count($err) == 1 && isset($err[0][0]) && $err[0][0] == 'articleexists' && $newTitle->quickUserCan('delete', $user)) { $out->addWikiMsg('delete_and_move_text', $newTitle->getPrefixedText()); $movepagebtn = $this->msg('delete_and_move')->text(); $submitVar = 'wpDeleteAndMove'; $confirm = true; $err = array(); } else { if ($this->oldTitle->getNamespace() == NS_USER && !$this->oldTitle->isSubpage()) { $out->wrapWikiMsg("<div class=\"error mw-moveuserpage-warning\">\n\$1\n</div>", 'moveuserpage-warning'); } elseif ($this->oldTitle->getNamespace() == NS_CATEGORY) { $out->wrapWikiMsg("<div class=\"error mw-movecategorypage-warning\">\n\$1\n</div>", 'movecategorypage-warning'); } $out->addWikiMsg($this->getConfig()->get('FixDoubleRedirects') ? 'movepagetext' : 'movepagetext-noredirectfixer'); $movepagebtn = $this->msg('movepagebtn')->text(); $submitVar = 'wpMove'; $confirm = false; } if (count($err) == 1 && isset($err[0][0]) && $err[0][0] == 'file-exists-sharedrepo' && $user->isAllowed('reupload-shared')) { $out->addWikiMsg('move-over-sharedrepo', $newTitle->getPrefixedText()); $submitVar = 'wpMoveOverSharedFile'; $err = array(); } $oldTalk = $this->oldTitle->getTalkPage(); $oldTitleSubpages = $this->oldTitle->hasSubpages(); $oldTitleTalkSubpages = $this->oldTitle->getTalkPage()->hasSubpages(); $canMoveSubpage = ($oldTitleSubpages || $oldTitleTalkSubpages) && !count($this->oldTitle->getUserPermissionsErrors('move-subpages', $user)); # We also want to be able to move assoc. subpage talk-pages even if base page # has no associated talk page, so || with $oldTitleTalkSubpages. $considerTalk = !$this->oldTitle->isTalkPage() && ($oldTalk->exists() || $oldTitleTalkSubpages && $canMoveSubpage); $dbr = wfGetDB(DB_SLAVE); if ($this->getConfig()->get('FixDoubleRedirects')) { $hasRedirects = $dbr->selectField('redirect', '1', array('rd_namespace' => $this->oldTitle->getNamespace(), 'rd_title' => $this->oldTitle->getDBkey()), __METHOD__); } else { $hasRedirects = false; } if ($considerTalk) { $out->addWikiMsg('movepagetalktext'); } if (count($err)) { $out->addHTML("<div class='error'>\n"); $action_desc = $this->msg('action-move')->plain(); $out->addWikiMsg('permissionserrorstext-withaction', count($err), $action_desc); if (count($err) == 1) { $errMsg = $err[0]; $errMsgName = array_shift($errMsg); if ($errMsgName == 'hookaborted') { $out->addHTML("<p>{$errMsg[0]}</p>\n"); } else { $out->addWikiMsgArray($errMsgName, $errMsg); } } else { $errStr = array(); foreach ($err as $errMsg) { if ($errMsg[0] == 'hookaborted') { $errStr[] = $errMsg[1]; } else { $errMsgName = array_shift($errMsg); $errStr[] = $this->msg($errMsgName, $errMsg)->parse(); } } $out->addHTML('<ul><li>' . implode("</li>\n<li>", $errStr) . "</li></ul>\n"); } $out->addHTML("</div>\n"); } if ($this->oldTitle->isProtected('move')) { # Is the title semi-protected? if ($this->oldTitle->isSemiProtected('move')) { $noticeMsg = 'semiprotectedpagemovewarning'; $classes[] = 'mw-textarea-sprotected'; } else { # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagemovewarning'; $classes[] = 'mw-textarea-protected'; } $out->addHTML("<div class='mw-warning-with-logexcerpt'>\n"); $out->addWikiMsg($noticeMsg); LogEventsList::showLogExtract($out, 'protect', $this->oldTitle, '', array('lim' => 1)); $out->addHTML("</div>\n"); } // Byte limit (not string length limit) for wpReason and wpNewTitleMain // is enforced in the mediawiki.special.movePage module $immovableNamespaces = array(); foreach (array_keys($this->getLanguage()->getNamespaces()) as $nsId) { if (!MWNamespace::isMovable($nsId)) { $immovableNamespaces[] = $nsId; } } $handler = ContentHandler::getForTitle($this->oldTitle); $out->enableOOUI(); $fields = array(); $fields[] = new OOUI\FieldLayout(new OOUI\LabelWidget(array('label' => new OOUI\HtmlSnippet("<strong>{$oldTitleLink}</strong>"))), array('label' => $this->msg('movearticle')->text(), 'align' => 'top')); $fields[] = new OOUI\FieldLayout(new MediaWiki\Widget\ComplexTitleInputWidget(array('id' => 'wpNewTitle', 'namespace' => array('id' => 'wpNewTitleNs', 'name' => 'wpNewTitleNs', 'value' => $newTitle->getNamespace(), 'exclude' => $immovableNamespaces), 'title' => array('id' => 'wpNewTitleMain', 'name' => 'wpNewTitleMain', 'value' => $wgContLang->recodeForEdit($newTitle->getText()), 'suggestions' => false), 'infusable' => true)), array('label' => $this->msg('newtitle')->text(), 'align' => 'top')); $fields[] = new OOUI\FieldLayout(new OOUI\TextInputWidget(array('name' => 'wpReason', 'id' => 'wpReason', 'maxLength' => 200, 'infusable' => true, 'value' => $this->reason)), array('label' => $this->msg('movereason')->text(), 'align' => 'top')); if ($considerTalk) { $fields[] = new OOUI\FieldLayout(new OOUI\CheckboxInputWidget(array('name' => 'wpMovetalk', 'id' => 'wpMovetalk', 'value' => '1', 'selected' => $this->moveTalk)), array('label' => $this->msg('movetalk')->text(), 'align' => 'inline')); } if ($user->isAllowed('suppressredirect')) { if ($handler->supportsRedirects()) { $isChecked = $this->leaveRedirect; $isDisabled = false; } else { $isChecked = false; $isDisabled = true; } $fields[] = new OOUI\FieldLayout(new OOUI\CheckboxInputWidget(array('name' => 'wpLeaveRedirect', 'id' => 'wpLeaveRedirect', 'value' => '1', 'selected' => $isChecked, 'disabled' => $isDisabled)), array('label' => $this->msg('move-leave-redirect')->text(), 'align' => 'inline')); } if ($hasRedirects) { $fields[] = new OOUI\FieldLayout(new OOUI\CheckboxInputWidget(array('name' => 'wpFixRedirects', 'id' => 'wpFixRedirects', 'value' => '1', 'selected' => $this->fixRedirects)), array('label' => $this->msg('fix-double-redirects')->text(), 'align' => 'inline')); } if ($canMoveSubpage) { $maximumMovedPages = $this->getConfig()->get('MaximumMovedPages'); $fields[] = new OOUI\FieldLayout(new OOUI\CheckboxInputWidget(array('name' => 'wpMovesubpages', 'id' => 'wpMovesubpages', 'value' => '1', 'selected' => $this->moveSubpages && ($this->oldTitle->hasSubpages() || $this->moveTalk))), array('label' => new OOUI\HtmlSnippet($this->msg($this->oldTitle->hasSubpages() ? 'move-subpages' : 'move-talk-subpages')->numParams($maximumMovedPages)->params($maximumMovedPages)->parse()), 'align' => 'inline')); } # Don't allow watching if user is not logged in if ($user->isLoggedIn()) { $watchChecked = $user->isLoggedIn() && ($this->watch || $user->getBoolOption('watchmoves') || $user->isWatched($this->oldTitle)); $fields[] = new OOUI\FieldLayout(new OOUI\CheckboxInputWidget(array('name' => 'wpWatch', 'id' => 'watch', 'value' => '1', 'selected' => $watchChecked)), array('label' => $this->msg('move-watch')->text(), 'align' => 'inline')); } if ($confirm) { $fields[] = new OOUI\FieldLayout(new OOUI\CheckboxInputWidget(array('name' => 'wpConfirm', 'id' => 'wpConfirm', 'value' => '1')), array('label' => $this->msg('delete_and_move_confirm')->text(), 'align' => 'inline')); } $fields[] = new OOUI\FieldLayout(new OOUI\ButtonInputWidget(array('name' => $submitVar, 'value' => $movepagebtn, 'label' => $movepagebtn, 'flags' => array('constructive', 'primary'), 'type' => 'submit')), array('align' => 'top')); $fieldset = new OOUI\FieldsetLayout(array('label' => $this->msg('move-page-legend')->text(), 'id' => 'mw-movepage-table', 'items' => $fields)); $form = new OOUI\FormLayout(array('method' => 'post', 'action' => $this->getPageTitle()->getLocalURL('action=submit'), 'id' => 'movepage')); $form->appendContent($fieldset, new OOUI\HtmlSnippet(Html::hidden('wpOldTitle', $this->oldTitle->getPrefixedText()) . Html::hidden('wpEditToken', $user->getEditToken()))); $out->addHTML(new OOUI\PanelLayout(array('classes' => array('movepage-wrapper'), 'expanded' => false, 'padded' => true, 'framed' => true, 'content' => $form))); $this->showLogFragment($this->oldTitle); $this->showSubpages($this->oldTitle); }
/** * @param Title $title * @param User $user User doing the action * @param string $token * @return array */ private static function getPermissionsError($title, $user, $token) { // Check permissions return $title->getUserPermissionsErrors('delete', $user); }
/** * Really format a diff for the newsfeed * * @param Title $title Title object * @param int $oldid Old revision's id * @param int $newid New revision's id * @param int $timestamp New revision's timestamp * @param string $comment New revision's comment * @param string $actiontext Text of the action; in case of log event * @return string */ public static function formatDiffRow($title, $oldid, $newid, $timestamp, $comment, $actiontext = '') { global $wgFeedDiffCutoff, $wgLang; // log entries $completeText = '<p>' . implode(' ', array_filter(array($actiontext, Linker::formatComment($comment)))) . "</p>\n"; // NOTE: Check permissions for anonymous users, not current user. // No "privileged" version should end up in the cache. // Most feed readers will not log in anyway. $anon = new User(); $accErrors = $title->getUserPermissionsErrors('read', $anon, true); // Can't diff special pages, unreadable pages or pages with no new revision // to compare against: just return the text. if ($title->getNamespace() < 0 || $accErrors || !$newid) { return $completeText; } if ($oldid) { #$diffText = $de->getDiff( wfMessage( 'revisionasof', # $wgLang->timeanddate( $timestamp ), # $wgLang->date( $timestamp ), # $wgLang->time( $timestamp ) )->text(), # wfMessage( 'currentrev' )->text() ); $diffText = ''; // Don't bother generating the diff if we won't be able to show it if ($wgFeedDiffCutoff > 0) { $rev = Revision::newFromId($oldid); if (!$rev) { $diffText = false; } else { $context = clone RequestContext::getMain(); $context->setTitle($title); $contentHandler = $rev->getContentHandler(); $de = $contentHandler->createDifferenceEngine($context, $oldid, $newid); $diffText = $de->getDiff(wfMessage('previousrevision')->text(), wfMessage('revisionasof', $wgLang->timeanddate($timestamp), $wgLang->date($timestamp), $wgLang->time($timestamp))->text()); } } if ($wgFeedDiffCutoff <= 0 || strlen($diffText) > $wgFeedDiffCutoff) { // Omit large diffs $diffText = self::getDiffLink($title, $newid, $oldid); } elseif ($diffText === false) { // Error in diff engine, probably a missing revision $diffText = "<p>Can't load revision {$newid}</p>"; } else { // Diff output fine, clean up any illegal UTF-8 $diffText = UtfNormal\Validator::cleanUp($diffText); $diffText = self::applyDiffStyle($diffText); } } else { $rev = Revision::newFromId($newid); if ($wgFeedDiffCutoff <= 0 || is_null($rev)) { $newContent = ContentHandler::getForTitle($title)->makeEmptyContent(); } else { $newContent = $rev->getContent(); } if ($newContent instanceof TextContent) { // only textual content has a "source view". $text = $newContent->getNativeData(); if ($wgFeedDiffCutoff <= 0 || strlen($text) > $wgFeedDiffCutoff) { $html = null; } else { $html = nl2br(htmlspecialchars($text)); } } else { //XXX: we could get an HTML representation of the content via getParserOutput, but that may // contain JS magic and generally may not be suitable for inclusion in a feed. // Perhaps Content should have a getDescriptiveHtml method and/or a getSourceText method. //Compare also ApiFeedContributions::feedItemDesc $html = null; } if ($html === null) { // Omit large new page diffs, bug 29110 // Also use diff link for non-textual content $diffText = self::getDiffLink($title, $newid); } else { $diffText = '<p><b>' . wfMessage('newpage')->text() . '</b></p>' . '<div>' . $html . '</div>'; } } $completeText .= $diffText; return $completeText; }