/** * UI entry point for page deletion */ public function delete() { # This code desperately needs to be totally rewritten $title = $this->getTitle(); $context = $this->getContext(); $user = $context->getUser(); # Check permissions $permissionErrors = $title->getUserPermissionsErrors('delete', $user); if (count($permissionErrors)) { throw new PermissionsError('delete', $permissionErrors); } # Read-only check... if (wfReadOnly()) { throw new ReadOnlyError(); } # Better double-check that it hasn't been deleted yet! $this->mPage->loadPageData('fromdbmaster'); if (!$this->mPage->exists()) { $deleteLogPage = new LogPage('delete'); $outputPage = $context->getOutput(); $outputPage->setPageTitle($context->msg('cannotdelete-title', $title->getPrefixedText())); $outputPage->wrapWikiMsg("<div class=\"error mw-error-cannotdelete\">\n\$1\n</div>", array('cannotdelete', wfEscapeWikiText($title->getPrefixedText()))); $outputPage->addHTML(Xml::element('h2', null, $deleteLogPage->getName()->text())); LogEventsList::showLogExtract($outputPage, 'delete', $title); return; } $request = $context->getRequest(); $deleteReasonList = $request->getText('wpDeleteReasonList', 'other'); $deleteReason = $request->getText('wpReason'); if ($deleteReasonList == 'other') { $reason = $deleteReason; } elseif ($deleteReason != '') { // Entry from drop down menu + additional comment $colonseparator = wfMessage('colon-separator')->inContentLanguage()->text(); $reason = $deleteReasonList . $colonseparator . $deleteReason; } else { $reason = $deleteReasonList; } if ($request->wasPosted() && $user->matchEditToken($request->getVal('wpEditToken'), array('delete', $this->getTitle()->getPrefixedText()))) { # Flag to hide all contents of the archived revisions $suppress = $request->getVal('wpSuppress') && $user->isAllowed('suppressrevision'); $this->doDelete($reason, $suppress); WatchAction::doWatchOrUnwatch($request->getCheck('wpWatch'), $title, $user); return; } // Generate deletion reason $hasHistory = false; if (!$reason) { try { $reason = $this->generateReason($hasHistory); } catch (Exception $e) { # if a page is horribly broken, we still want to be able to # delete it. So be lenient about errors here. wfDebug("Error while building auto delete summary: {$e}"); $reason = ''; } } // If the page has a history, insert a warning if ($hasHistory) { $title = $this->getTitle(); // The following can use the real revision count as this is only being shown for users // that can delete this page. // This, as a side-effect, also makes sure that the following query isn't being run for // pages with a larger history, unless the user has the 'bigdelete' right // (and is about to delete this page). $dbr = wfGetDB(DB_SLAVE); $revisions = $edits = (int) $dbr->selectField('revision', 'COUNT(rev_page)', array('rev_page' => $title->getArticleID()), __METHOD__); // @todo FIXME: i18n issue/patchwork message $context->getOutput()->addHTML('<strong class="mw-delete-warning-revisions">' . $context->msg('historywarning')->numParams($revisions)->parse() . $context->msg('word-separator')->escaped() . Linker::linkKnown($title, $context->msg('history')->escaped(), array(), array('action' => 'history')) . '</strong>'); if ($title->isBigDeletion()) { global $wgDeleteRevisionsLimit; $context->getOutput()->wrapWikiMsg("<div class='error'>\n\$1\n</div>\n", array('delete-warning-toobig', $context->getLanguage()->formatNum($wgDeleteRevisionsLimit))); } } $this->confirmDelete($reason); }
/** * Save submitted protection form * * @return Boolean: success */ function save() { global $wgRequest, $wgUser, $wgOut; # Permission check! if ($this->disabled) { $this->show(); return false; } $token = $wgRequest->getVal('wpEditToken'); if (!$wgUser->matchEditToken($token, array('protect', $this->mTitle->getPrefixedDBkey()))) { $this->show(array('sessionfailure')); return false; } # Create reason string. Use list and/or custom string. $reasonstr = $this->mReasonSelection; if ($reasonstr != 'other' && $this->mReason != '') { // Entry from drop down menu + additional comment $reasonstr .= wfMessage('colon-separator')->text() . $this->mReason; } elseif ($reasonstr == 'other') { $reasonstr = $this->mReason; } $expiry = array(); foreach ($this->mApplicableTypes as $action) { $expiry[$action] = $this->getExpiry($action); if (empty($this->mRestrictions[$action])) { continue; // unprotected } if (!$expiry[$action]) { $this->show(array('protect_expiry_invalid')); return false; } if ($expiry[$action] < wfTimestampNow()) { $this->show(array('protect_expiry_old')); return false; } } $this->mCascade = $wgRequest->getBool('mwProtect-cascade'); $status = $this->mArticle->doUpdateRestrictions($this->mRestrictions, $expiry, $this->mCascade, $reasonstr, $wgUser); if (!$status->isOK()) { $this->show($wgOut->parseInline($status->getWikiText())); return false; } /** * Give extensions a change to handle added form items * * @since 1.19 you can (and you should) return false to abort saving; * you can also return an array of message name and its parameters */ $errorMsg = ''; if (!wfRunHooks('ProtectionForm::save', array($this->mArticle, &$errorMsg, $reasonstr))) { if ($errorMsg == '') { $errorMsg = array('hookaborted'); } } if ($errorMsg != '') { $this->show($errorMsg); return false; } WatchAction::doWatchOrUnwatch($wgRequest->getCheck('mwProtectWatch'), $this->mTitle, $wgUser); return true; }
/** * Set a watch (or unwatch) based the based on a watchlist parameter. * @param string $watch Valid values: 'watch', 'unwatch', 'preferences', 'nochange' * @param Title $titleObj The article's title to change * @param string $userOption The user option to consider when $watch=preferences */ protected function setWatch($watch, $titleObj, $userOption = null) { $value = $this->getWatchlistValue($watch, $titleObj, $userOption); if ($value === null) { return; } WatchAction::doWatchOrUnwatch($value, $titleObj, $this->getUser()); }
function doSubmit() { $user = $this->getUser(); if ($user->pingLimiter('move')) { throw new ThrottledError(); } $ot = $this->oldTitle; $nt = $this->newTitle; # don't allow moving to pages with # in if (!$nt || $nt->hasFragment()) { $this->showForm(array(array('badtitletext'))); return; } # Show a warning if the target file exists on a shared repo if ($nt->getNamespace() == NS_FILE && !($this->moveOverShared && $user->isAllowed('reupload-shared')) && !RepoGroup::singleton()->getLocalRepo()->findFile($nt) && wfFindFile($nt)) { $this->showForm(array(array('file-exists-sharedrepo'))); return; } # Delete to make way if requested if ($this->deleteAndMove) { $permErrors = $nt->getUserPermissionsErrors('delete', $user); if (count($permErrors)) { # Only show the first error $this->showForm($permErrors); return; } $reason = $this->msg('delete_and_move_reason', $ot)->inContentLanguage()->text(); // Delete an associated image if there is if ($nt->getNamespace() == NS_FILE) { $file = wfLocalFile($nt); $file->load(File::READ_LATEST); if ($file->exists()) { $file->delete($reason, false, $user); } } $error = ''; // passed by ref $page = WikiPage::factory($nt); $deleteStatus = $page->doDeleteArticleReal($reason, false, 0, true, $error, $user); if (!$deleteStatus->isGood()) { $this->showForm($deleteStatus->getErrorsArray()); return; } } $handler = ContentHandler::getForTitle($ot); if (!$handler->supportsRedirects()) { $createRedirect = false; } elseif ($user->isAllowed('suppressredirect')) { $createRedirect = $this->leaveRedirect; } else { $createRedirect = true; } # Do the actual move. $mp = new MovePage($ot, $nt); $valid = $mp->isValidMove(); if (!$valid->isOK()) { $this->showForm($valid->getErrorsArray()); return; } $permStatus = $mp->checkPermissions($user, $this->reason); if (!$permStatus->isOK()) { $this->showForm($permStatus->getErrorsArray()); return; } $status = $mp->move($user, $this->reason, $createRedirect); if (!$status->isOK()) { $this->showForm($status->getErrorsArray()); return; } if ($this->getConfig()->get('FixDoubleRedirects') && $this->fixRedirects) { DoubleRedirectJob::fixRedirects('move', $ot, $nt); } $out = $this->getOutput(); $out->setPageTitle($this->msg('pagemovedsub')); $oldLink = Linker::link($ot, null, array('id' => 'movepage-oldlink'), array('redirect' => 'no')); $newLink = Linker::linkKnown($nt, null, array('id' => 'movepage-newlink')); $oldText = $ot->getPrefixedText(); $newText = $nt->getPrefixedText(); if ($ot->exists()) { // NOTE: we assume that if the old title exists, it's because it was re-created as // a redirect to the new title. This is not safe, but what we did before was // even worse: we just determined whether a redirect should have been created, // and reported that it was created if it should have, without any checks. // Also note that isRedirect() is unreliable because of bug 37209. $msgName = 'movepage-moved-redirect'; } else { $msgName = 'movepage-moved-noredirect'; } $out->addHTML($this->msg('movepage-moved')->rawParams($oldLink, $newLink)->params($oldText, $newText)->parseAsBlock()); $out->addWikiMsg($msgName); Hooks::run('SpecialMovepageAfterMove', array(&$this, &$ot, &$nt)); # Now we move extra pages we've been asked to move: subpages and talk # pages. First, if the old page or the new page is a talk page, we # can't move any talk pages: cancel that. if ($ot->isTalkPage() || $nt->isTalkPage()) { $this->moveTalk = false; } if (count($ot->getUserPermissionsErrors('move-subpages', $user))) { $this->moveSubpages = false; } # Next make a list of id's. This might be marginally less efficient # than a more direct method, but this is not a highly performance-cri- # tical code path and readable code is more important here. # # Note: this query works nicely on MySQL 5, but the optimizer in MySQL # 4 might get confused. If so, consider rewriting as a UNION. # # If the target namespace doesn't allow subpages, moving with subpages # would mean that you couldn't move them back in one operation, which # is bad. # @todo FIXME: A specific error message should be given in this case. // @todo FIXME: Use Title::moveSubpages() here $dbr = wfGetDB(DB_MASTER); if ($this->moveSubpages && (MWNamespace::hasSubpages($nt->getNamespace()) || $this->moveTalk && MWNamespace::hasSubpages($nt->getTalkPage()->getNamespace()))) { $conds = array('page_title' . $dbr->buildLike($ot->getDBkey() . '/', $dbr->anyString()) . ' OR page_title = ' . $dbr->addQuotes($ot->getDBkey())); $conds['page_namespace'] = array(); if (MWNamespace::hasSubpages($nt->getNamespace())) { $conds['page_namespace'][] = $ot->getNamespace(); } if ($this->moveTalk && MWNamespace::hasSubpages($nt->getTalkPage()->getNamespace())) { $conds['page_namespace'][] = $ot->getTalkPage()->getNamespace(); } } elseif ($this->moveTalk) { $conds = array('page_namespace' => $ot->getTalkPage()->getNamespace(), 'page_title' => $ot->getDBkey()); } else { # Skip the query $conds = null; } $extraPages = array(); if (!is_null($conds)) { $extraPages = TitleArray::newFromResult($dbr->select('page', array('page_id', 'page_namespace', 'page_title'), $conds, __METHOD__)); } $extraOutput = array(); $count = 1; foreach ($extraPages as $oldSubpage) { if ($ot->equals($oldSubpage) || $nt->equals($oldSubpage)) { # Already did this one. continue; } $newPageName = preg_replace('#^' . preg_quote($ot->getDBkey(), '#') . '#', StringUtils::escapeRegexReplacement($nt->getDBkey()), $oldSubpage->getDBkey()); if ($oldSubpage->isSubpage() && ($ot->isTalkPage() xor $nt->isTalkPage())) { // Moving a subpage from a subject namespace to a talk namespace or vice-versa $newNs = $nt->getNamespace(); } elseif ($oldSubpage->isTalkPage()) { $newNs = $nt->getTalkPage()->getNamespace(); } else { $newNs = $nt->getSubjectPage()->getNamespace(); } # Bug 14385: we need makeTitleSafe because the new page names may # be longer than 255 characters. $newSubpage = Title::makeTitleSafe($newNs, $newPageName); if (!$newSubpage) { $oldLink = Linker::linkKnown($oldSubpage); $extraOutput[] = $this->msg('movepage-page-unmoved')->rawParams($oldLink)->params(Title::makeName($newNs, $newPageName))->escaped(); continue; } # This was copy-pasted from Renameuser, bleh. if ($newSubpage->exists() && !$oldSubpage->isValidMoveTarget($newSubpage)) { $link = Linker::linkKnown($newSubpage); $extraOutput[] = $this->msg('movepage-page-exists')->rawParams($link)->escaped(); } else { $success = $oldSubpage->moveTo($newSubpage, true, $this->reason, $createRedirect); if ($success === true) { if ($this->fixRedirects) { DoubleRedirectJob::fixRedirects('move', $oldSubpage, $newSubpage); } $oldLink = Linker::link($oldSubpage, null, array(), array('redirect' => 'no')); $newLink = Linker::linkKnown($newSubpage); $extraOutput[] = $this->msg('movepage-page-moved')->rawParams($oldLink, $newLink)->escaped(); ++$count; $maximumMovedPages = $this->getConfig()->get('MaximumMovedPages'); if ($count >= $maximumMovedPages) { $extraOutput[] = $this->msg('movepage-max-pages')->numParams($maximumMovedPages)->escaped(); break; } } else { $oldLink = Linker::linkKnown($oldSubpage); $newLink = Linker::link($newSubpage); $extraOutput[] = $this->msg('movepage-page-unmoved')->rawParams($oldLink, $newLink)->escaped(); } } } if ($extraOutput !== array()) { $out->addHTML("<ul>\n<li>" . implode("</li>\n<li>", $extraOutput) . "</li>\n</ul>"); } # Deal with watches (we don't watch subpages) WatchAction::doWatchOrUnwatch($this->watch, $ot, $user); WatchAction::doWatchOrUnwatch($this->watch, $nt, $user); }
/** * 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(); }
/** * Register the change of watch status */ protected function updateWatchlist() { global $wgUser; if ($wgUser->isLoggedIn() && $this->watchthis != $wgUser->isWatched($this->mTitle, WatchedItem::IGNORE_USER_RIGHTS)) { $fname = __METHOD__; $title = $this->mTitle; $watch = $this->watchthis; // Do this in its own transaction to reduce contention... $dbw = wfGetDB(DB_MASTER); $dbw->onTransactionIdle(function () use($dbw, $title, $watch, $wgUser, $fname) { WatchAction::doWatchOrUnwatch($watch, $title, $wgUser); }); } }
/** * Register the change of watch status */ protected function updateWatchlist() { global $wgUser; if (!$wgUser->isLoggedIn()) { return; } $user = $wgUser; $title = $this->mTitle; $watch = $this->watchthis; // Do this in its own transaction to reduce contention... DeferredUpdates::addCallableUpdate(function () use($user, $title, $watch) { if ($watch == $user->isWatched($title, WatchedItem::IGNORE_USER_RIGHTS)) { return; // nothing to change } WatchAction::doWatchOrUnwatch($watch, $title, $user); }); }