MediaWiki  master
SpecialEditWatchlist.php
Go to the documentation of this file.
1 <?php
32 
46  public const EDIT_CLEAR = 1;
47  public const EDIT_RAW = 2;
48  public const EDIT_NORMAL = 3;
49 
50  protected $successMessage;
51 
52  protected $toc;
53 
54  private $badItems = [];
55 
59  private $titleParser;
60 
65 
68 
70  parent::__construct( 'EditWatchlist', 'editmywatchlist' );
71  $this->watchedItemStore = $watchedItemStore;
72  $this->isWatchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
73  }
74 
79  private function initServices() {
80  if ( !$this->titleParser ) {
81  $this->titleParser = MediaWikiServices::getInstance()->getTitleParser();
82  }
83  }
84 
85  public function doesWrites() {
86  return true;
87  }
88 
94  public function execute( $mode ) {
95  $this->initServices();
96  $this->setHeaders();
97 
98  # Anons don't get a watchlist
99  $this->requireLogin( 'watchlistanontext' );
100 
101  $out = $this->getOutput();
102 
103  $this->checkPermissions();
104  $this->checkReadOnly();
105 
106  $this->outputHeader();
107  $this->outputSubtitle();
108  $out->addModuleStyles( 'mediawiki.special' );
109 
110  # B/C: $mode used to be waaay down the parameter list, and the first parameter
111  # was $wgUser
112  if ( $mode instanceof User ) {
113  $args = func_get_args();
114  if ( count( $args ) >= 4 ) {
115  $mode = $args[3];
116  }
117  }
118  $mode = self::getMode( $this->getRequest(), $mode );
119 
120  switch ( $mode ) {
121  case self::EDIT_RAW:
122  $out->setPageTitle( $this->msg( 'watchlistedit-raw-title' ) );
123  $form = $this->getRawForm();
124  if ( $form->show() ) {
125  $out->addHTML( $this->successMessage );
126  $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) );
127  }
128  break;
129  case self::EDIT_CLEAR:
130  $out->setPageTitle( $this->msg( 'watchlistedit-clear-title' ) );
131  $form = $this->getClearForm();
132  if ( $form->show() ) {
133  $out->addHTML( $this->successMessage );
134  $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) );
135  }
136  break;
137 
138  case self::EDIT_NORMAL:
139  default:
140  $this->executeViewEditWatchlist();
141  break;
142  }
143  }
144 
148  protected function outputSubtitle() {
149  $out = $this->getOutput();
150  $out->addSubtitle( $this->msg( 'watchlistfor2', $this->getUser()->getName() )
151  ->rawParams(
152  self::buildTools(
153  $this->getLanguage(),
154  $this->getLinkRenderer()
155  )
156  )
157  );
158  }
159 
163  protected function executeViewEditWatchlist() {
164  $out = $this->getOutput();
165  $out->setPageTitle( $this->msg( 'watchlistedit-normal-title' ) );
166  $form = $this->getNormalForm();
167  if ( $form->show() ) {
168  $out->addHTML( $this->successMessage );
169  $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) );
170  } elseif ( $this->toc !== false ) {
171  $out->prependHTML( $this->toc );
172  $out->addModuleStyles( 'mediawiki.toc.styles' );
173  }
174  }
175 
182  public function getSubpagesForPrefixSearch() {
183  // SpecialWatchlist uses SpecialEditWatchlist::getMode, so new types should be added
184  // here and there - no 'edit' here, because that the default for this page
185  return [
186  'clear',
187  'raw',
188  ];
189  }
190 
198  private function extractTitles( $list ) {
199  $list = explode( "\n", trim( $list ) );
200  if ( !is_array( $list ) ) {
201  return [];
202  }
203 
204  $titles = [];
205 
206  foreach ( $list as $text ) {
207  $text = trim( $text );
208  if ( strlen( $text ) > 0 ) {
209  $title = Title::newFromText( $text );
210  if ( $title instanceof Title && $title->isWatchable() ) {
211  $titles[] = $title;
212  }
213  }
214  }
215 
216  MediaWikiServices::getInstance()->getGenderCache()->doTitlesArray( $titles );
217 
218  $list = [];
220  foreach ( $titles as $title ) {
221  $list[] = $title->getPrefixedText();
222  }
223 
224  return array_unique( $list );
225  }
226 
227  public function submitRaw( $data ) {
228  $wanted = $this->extractTitles( $data['Titles'] );
229  $current = $this->getWatchlist();
230 
231  if ( count( $wanted ) > 0 ) {
232  $toWatch = array_diff( $wanted, $current );
233  $toUnwatch = array_diff( $current, $wanted );
234  $this->watchTitles( $toWatch );
235  $this->unwatchTitles( $toUnwatch );
236  $this->getUser()->invalidateCache();
237 
238  if ( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 ) {
239  $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse();
240  } else {
241  return false;
242  }
243 
244  if ( count( $toWatch ) > 0 ) {
245  $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-added' )
246  ->numParams( count( $toWatch ) )->parse();
247  $this->showTitles( $toWatch, $this->successMessage );
248  }
249 
250  if ( count( $toUnwatch ) > 0 ) {
251  $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-removed' )
252  ->numParams( count( $toUnwatch ) )->parse();
253  $this->showTitles( $toUnwatch, $this->successMessage );
254  }
255  } else {
256 
257  if ( count( $current ) === 0 ) {
258  return false;
259  }
260 
261  $this->clearUserWatchedItems( 'raw' );
262  $this->showTitles( $current, $this->successMessage );
263  }
264 
265  return true;
266  }
267 
274  public function submitClear( $data ): bool {
275  $this->clearUserWatchedItems( 'clear' );
276  return true;
277  }
278 
285  private function clearUserWatchedItems( string $messageFor ): void {
286  if ( $this->watchedItemStore->mustClearWatchedItemsUsingJobQueue( $this->getUser() ) ) {
288  } else {
289  $this->clearUserWatchedItemsNow( $messageFor );
290  }
291  }
292 
298  private function clearUserWatchedItemsNow( string $messageFor ): void {
299  $current = $this->getWatchlist();
300  if ( !$this->watchedItemStore->clearUserWatchedItems( $this->getUser() ) ) {
301  throw new LogicException(
302  __METHOD__ . ' should only be called when able to clear synchronously'
303  );
304  }
305  $this->successMessage = $this->msg( 'watchlistedit-' . $messageFor . '-done' )->parse();
306  $this->successMessage .= ' ' . $this->msg( 'watchlistedit-' . $messageFor . '-removed' )
307  ->numParams( count( $current ) )->parse();
308  $this->getUser()->invalidateCache();
309  $this->showTitles( $current, $this->successMessage );
310  }
311 
315  private function clearUserWatchedItemsUsingJobQueue(): void {
316  $this->watchedItemStore->clearUserWatchedItemsUsingJobQueue( $this->getUser() );
317  $this->successMessage = $this->msg( 'watchlistedit-clear-jobqueue' )->parse();
318  }
319 
329  private function showTitles( $titles, &$output ) {
330  $talk = $this->msg( 'talkpagelinktext' )->text();
331  // Do a batch existence check
332  $batch = new LinkBatch();
333  if ( count( $titles ) >= 100 ) {
334  $output = $this->msg( 'watchlistedit-too-many' )->parse();
335  return;
336  }
337  foreach ( $titles as $title ) {
338  if ( !$title instanceof Title ) {
340  }
341 
342  if ( $title instanceof Title ) {
343  $batch->addObj( $title );
344  $batch->addObj( $title->getTalkPage() );
345  }
346  }
347 
348  $batch->execute();
349 
350  // Print out the list
351  $output .= "<ul>\n";
352 
353  $linkRenderer = $this->getLinkRenderer();
354  foreach ( $titles as $title ) {
355  if ( !$title instanceof Title ) {
357  }
358 
359  if ( $title instanceof Title ) {
360  $output .= '<li>' .
361  $linkRenderer->makeLink( $title ) . ' ' .
362  $this->msg( 'parentheses' )->rawParams(
363  $linkRenderer->makeLink( $title->getTalkPage(), $talk )
364  )->escaped() .
365  "</li>\n";
366  }
367  }
368 
369  $output .= "</ul>\n";
370  }
371 
378  private function getWatchlist() {
379  $list = [];
380 
381  $watchedItems = $this->watchedItemStore->getWatchedItemsForUser(
382  $this->getUser(),
383  [ 'forWrite' => $this->getRequest()->wasPosted() ]
384  );
385 
386  if ( $watchedItems ) {
388  $titles = [];
389  foreach ( $watchedItems as $watchedItem ) {
390  $namespace = $watchedItem->getLinkTarget()->getNamespace();
391  $dbKey = $watchedItem->getLinkTarget()->getDBkey();
392  $title = Title::makeTitleSafe( $namespace, $dbKey );
393 
394  if ( $this->checkTitle( $title, $namespace, $dbKey )
395  && !$title->isTalkPage()
396  ) {
397  $titles[] = $title;
398  }
399  }
400 
401  MediaWikiServices::getInstance()->getGenderCache()->doTitlesArray( $titles );
402 
403  foreach ( $titles as $title ) {
404  $list[] = $title->getPrefixedText();
405  }
406  }
407 
408  $this->cleanupWatchlist();
409 
410  return $list;
411  }
412 
419  protected function getWatchlistInfo() {
420  $titles = [];
421  $services = MediaWikiServices::getInstance();
422  $options = [ 'sort' => WatchedItemStore::SORT_ASC ];
423 
424  if ( $this->isWatchlistExpiryEnabled ) {
425  $options[ 'sortByExpiry'] = true;
426  }
427 
428  $watchedItems = $this->watchedItemStore->getWatchedItemsForUser(
429  $this->getUser(), $options
430  );
431 
432  $lb = new LinkBatch();
433 
434  foreach ( $watchedItems as $watchedItem ) {
435  $namespace = $watchedItem->getLinkTarget()->getNamespace();
436  $dbKey = $watchedItem->getLinkTarget()->getDBkey();
437  $lb->add( $namespace, $dbKey );
438  if ( !$services->getNamespaceInfo()->isTalk( $namespace ) ) {
439  $titles[$namespace][$dbKey] = $watchedItem->getExpiryInDays();
440  }
441  }
442 
443  $lb->execute();
444 
445  return $titles;
446  }
447 
456  private function checkTitle( $title, $namespace, $dbKey ) {
457  if ( $title
458  && ( $title->isExternal()
459  || $title->getNamespace() < 0
460  )
461  ) {
462  $title = false; // unrecoverable
463  }
464 
465  if ( !$title
466  || $title->getNamespace() != $namespace
467  || $title->getDBkey() != $dbKey
468  ) {
469  $this->badItems[] = [ $title, $namespace, $dbKey ];
470  }
471 
472  return (bool)$title;
473  }
474 
478  private function cleanupWatchlist() {
479  if ( $this->badItems === [] ) {
480  return; // nothing to do
481  }
482 
483  $user = $this->getUser();
485  DeferredUpdates::addCallableUpdate( function () use ( $user, $badItems ) {
486  foreach ( $badItems as $row ) {
487  list( $title, $namespace, $dbKey ) = $row;
488  $action = $title ? 'cleaning up' : 'deleting';
489  wfDebug( "User {$user->getName()} has broken watchlist item " .
490  "ns($namespace):$dbKey, $action." );
491 
492  // NOTE: We *know* that the title is invalid. TitleValue may refuse instantiation.
493  // XXX: We may need an InvalidTitleValue class that allows instantiation of
494  // known bad title values.
495  $this->watchedItemStore->removeWatch( $user, Title::makeTitle( (int)$namespace, $dbKey ) );
496  // Can't just do an UPDATE instead of DELETE/INSERT due to unique index
497  if ( $title ) {
498  $user->addWatch( $title );
499  }
500  }
501  } );
502  }
503 
512  private function watchTitles( array $targets ) {
513  return $this->watchedItemStore->addWatchBatchForUser(
514  $this->getUser(), $this->getExpandedTargets( $targets )
515  ) && $this->runWatchUnwatchCompleteHook( 'Watch', $targets );
516  }
517 
530  private function unwatchTitles( array $targets ) {
531  return $this->watchedItemStore->removeWatchBatchForUser(
532  $this->getUser(), $this->getExpandedTargets( $targets )
533  ) && $this->runWatchUnwatchCompleteHook( 'Unwatch', $targets );
534  }
535 
544  private function runWatchUnwatchCompleteHook( $action, $targets ) {
545  foreach ( $targets as $target ) {
546  $title = $target instanceof TitleValue ?
547  Title::newFromTitleValue( $target ) :
548  Title::newFromText( $target );
549  $page = WikiPage::factory( $title );
550  $user = $this->getUser();
551  if ( $action === 'Watch' ) {
552  $this->getHookRunner()->onWatchArticleComplete( $user, $page );
553  } else {
554  $this->getHookRunner()->onUnwatchArticleComplete( $user, $page );
555  }
556  }
557  return true;
558  }
559 
564  private function getExpandedTargets( array $targets ) {
565  $expandedTargets = [];
566  $services = MediaWikiServices::getInstance();
567  foreach ( $targets as $target ) {
568  if ( !$target instanceof LinkTarget ) {
569  try {
570  $target = $this->titleParser->parseTitle( $target, NS_MAIN );
571  }
572  catch ( MalformedTitleException $e ) {
573  continue;
574  }
575  }
576 
577  $ns = $target->getNamespace();
578  $dbKey = $target->getDBkey();
579  $expandedTargets[] =
580  new TitleValue( $services->getNamespaceInfo()->getSubject( $ns ), $dbKey );
581  $expandedTargets[] =
582  new TitleValue( $services->getNamespaceInfo()->getTalk( $ns ), $dbKey );
583  }
584  return $expandedTargets;
585  }
586 
587  public function submitNormal( $data ) {
588  $removed = [];
589 
590  foreach ( $data as $titles ) {
591  $this->unwatchTitles( $titles );
592  $removed = array_merge( $removed, $titles );
593  }
594 
595  if ( count( $removed ) > 0 ) {
596  $this->successMessage = $this->msg( 'watchlistedit-normal-done'
597  )->numParams( count( $removed ) )->parse();
598  $this->showTitles( $removed, $this->successMessage );
599 
600  return true;
601  } else {
602  return false;
603  }
604  }
605 
611  protected function getNormalForm() {
612  $fields = [];
613  $count = 0;
614 
615  // Allow subscribers to manipulate the list of watched pages (or use it
616  // to preload lots of details at once)
617  $watchlistInfo = $this->getWatchlistInfo();
618  $this->getHookRunner()->onWatchlistEditorBeforeFormRender( $watchlistInfo );
619 
620  foreach ( $watchlistInfo as $namespace => $pages ) {
621  $options = [];
622  foreach ( $pages as $dbkey => $expiryDays ) {
623  $title = Title::makeTitleSafe( $namespace, $dbkey );
624 
625  if ( $this->checkTitle( $title, $namespace, $dbkey ) ) {
626  $text = $this->buildRemoveLine( $title, $expiryDays );
627  $options[$text] = $title->getPrefixedText();
628  $count++;
629  }
630  }
631 
632  // checkTitle can filter some options out, avoid empty sections
633  if ( count( $options ) > 0 ) {
634  $fields['TitlesNs' . $namespace] = [
635  'class' => EditWatchlistCheckboxSeriesField::class,
636  'options' => $options,
637  'section' => "ns$namespace",
638  ];
639  }
640  }
641  $this->cleanupWatchlist();
642 
643  if ( count( $fields ) > 1 && $count > 30 ) {
644  $this->toc = Linker::tocIndent();
645  $tocLength = 0;
646  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
647 
648  foreach ( $fields as $data ) {
649  # strip out the 'ns' prefix from the section name:
650  $ns = substr( $data['section'], 2 );
651 
652  $nsText = ( $ns == NS_MAIN )
653  ? $this->msg( 'blanknamespace' )->escaped()
654  : htmlspecialchars( $contLang->getFormattedNsText( $ns ) );
655  $this->toc .= Linker::tocLine( "editwatchlist-{$data['section']}", $nsText,
656  $this->getLanguage()->formatNum( ++$tocLength ), 1 ) . Linker::tocLineEnd();
657  }
658 
659  $this->toc = Linker::tocList( $this->toc );
660  } else {
661  $this->toc = false;
662  }
663 
664  $context = new DerivativeContext( $this->getContext() );
665  $context->setTitle( $this->getPageTitle() ); // Remove subpage
666  $form = new EditWatchlistNormalHTMLForm( $fields, $context );
667  $form->setSubmitTextMsg( 'watchlistedit-normal-submit' );
668  $form->setSubmitDestructive();
669  # Used message keys:
670  # 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit'
671  $form->setSubmitTooltip( 'watchlistedit-normal-submit' );
672  $form->setWrapperLegendMsg( 'watchlistedit-normal-legend' );
673  $form->addHeaderText( $this->msg( 'watchlistedit-normal-explain' )->parse() );
674  $form->setSubmitCallback( [ $this, 'submitNormal' ] );
675 
676  return $form;
677  }
678 
687  private function buildRemoveLine( $title, ?int $expiryDays = null ): string {
688  $linkRenderer = $this->getLinkRenderer();
689  $link = $linkRenderer->makeLink( $title );
690 
691  $tools = [];
692  $tools['talk'] = $linkRenderer->makeLink(
693  $title->getTalkPage(),
694  $this->msg( 'talkpagelinktext' )->text()
695  );
696 
697  if ( $title->exists() ) {
698  $tools['history'] = $linkRenderer->makeKnownLink(
699  $title,
700  $this->msg( 'history_small' )->text(),
701  [],
702  [ 'action' => 'history' ]
703  );
704  }
705 
706  if ( $title->getNamespace() === NS_USER && !$title->isSubpage() ) {
707  $tools['contributions'] = $linkRenderer->makeKnownLink(
708  SpecialPage::getTitleFor( 'Contributions', $title->getText() ),
709  $this->msg( 'contribslink' )->text()
710  );
711  }
712 
713  $this->getHookRunner()->onWatchlistEditorBuildRemoveLine(
714  $tools, $title, $title->isRedirect(), $this->getSkin(), $link );
715 
716  if ( $title->isRedirect() ) {
717  // Linker already makes class mw-redirect, so this is redundant
718  $link = '<span class="watchlistredir">' . $link . '</span>';
719  }
720 
721  $watchlistExpiringMessage = '';
722  if ( $this->isWatchlistExpiryEnabled && $expiryDays !== null ) {
723  $watchlistExpiringMessage = Html::element( 'span', [ 'class' => 'watchlistexpiry-msg' ],
724  $expiryDays > 0 ?
725  $this->msg( 'watchlist-expiring-msg', $expiryDays )->text() :
726  $this->msg( 'watchlist-expiring-hours-msg' )->text()
727  );
728  }
729 
730  return $link . ' ' .
731  $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList( $tools ) )->escaped() .
732  $watchlistExpiringMessage;
733  }
734 
740  protected function getRawForm() {
741  $titles = implode( "\n", $this->getWatchlist() );
742  $fields = [
743  'Titles' => [
744  'type' => 'textarea',
745  'label-message' => 'watchlistedit-raw-titles',
746  'default' => $titles,
747  ],
748  ];
749  $context = new DerivativeContext( $this->getContext() );
750  $context->setTitle( $this->getPageTitle( 'raw' ) ); // Reset subpage
751  $form = new OOUIHTMLForm( $fields, $context );
752  $form->setSubmitTextMsg( 'watchlistedit-raw-submit' );
753  # Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit'
754  $form->setSubmitTooltip( 'watchlistedit-raw-submit' );
755  $form->setWrapperLegendMsg( 'watchlistedit-raw-legend' );
756  $form->addHeaderText( $this->msg( 'watchlistedit-raw-explain' )->parse() );
757  $form->setSubmitCallback( [ $this, 'submitRaw' ] );
758 
759  return $form;
760  }
761 
767  protected function getClearForm() {
768  $context = new DerivativeContext( $this->getContext() );
769  $context->setTitle( $this->getPageTitle( 'clear' ) ); // Reset subpage
770  $form = new OOUIHTMLForm( [], $context );
771  $form->setSubmitTextMsg( 'watchlistedit-clear-submit' );
772  # Used message keys: 'accesskey-watchlistedit-clear-submit', 'tooltip-watchlistedit-clear-submit'
773  $form->setSubmitTooltip( 'watchlistedit-clear-submit' );
774  $form->setWrapperLegendMsg( 'watchlistedit-clear-legend' );
775  $form->addHeaderText( $this->msg( 'watchlistedit-clear-explain' )->parse() );
776  $form->setSubmitCallback( [ $this, 'submitClear' ] );
777  $form->setSubmitDestructive();
778 
779  return $form;
780  }
781 
790  public static function getMode( $request, $par ) {
791  $mode = strtolower( $request->getVal( 'action', $par ) );
792 
793  switch ( $mode ) {
794  case 'clear':
795  case self::EDIT_CLEAR:
796  return self::EDIT_CLEAR;
797  case 'raw':
798  case self::EDIT_RAW:
799  return self::EDIT_RAW;
800  case 'edit':
801  case self::EDIT_NORMAL:
802  return self::EDIT_NORMAL;
803  default:
804  return false;
805  }
806  }
807 
816  public static function buildTools( $lang, LinkRenderer $linkRenderer = null ) {
817  if ( !$lang instanceof Language ) {
818  // back-compat where the first parameter was $unused
819  global $wgLang;
820  $lang = $wgLang;
821  }
822  if ( !$linkRenderer ) {
823  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
824  }
825 
826  $tools = [];
827  $modes = [
828  'view' => [ 'Watchlist', false ],
829  'edit' => [ 'EditWatchlist', false ],
830  'raw' => [ 'EditWatchlist', 'raw' ],
831  'clear' => [ 'EditWatchlist', 'clear' ],
832  ];
833 
834  foreach ( $modes as $mode => $arr ) {
835  // can use messages 'watchlisttools-view', 'watchlisttools-edit', 'watchlisttools-raw'
836  $tools[] = $linkRenderer->makeKnownLink(
837  SpecialPage::getTitleFor( $arr[0], $arr[1] ),
838  wfMessage( "watchlisttools-{$mode}" )->text()
839  );
840  }
841 
842  return Html::rawElement(
843  'span',
844  [ 'class' => 'mw-watchlist-toollinks' ],
845  wfMessage( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped()
846  );
847  }
848 }
SpecialPage\getPageTitle
getPageTitle( $subpage=false)
Get a self-referential title object.
Definition: SpecialPage.php:697
SpecialEditWatchlist\submitNormal
submitNormal( $data)
Definition: SpecialEditWatchlist.php:587
SpecialEditWatchlist\EDIT_CLEAR
const EDIT_CLEAR
Editing modes.
Definition: SpecialEditWatchlist.php:46
SpecialEditWatchlist\unwatchTitles
unwatchTitles(array $targets)
Remove a list of titles from a user's watchlist.
Definition: SpecialEditWatchlist.php:530
SpecialEditWatchlist\getWatchlist
getWatchlist()
Prepare a list of titles on a user's watchlist (excluding talk pages) and return an array of (prefixe...
Definition: SpecialEditWatchlist.php:378
SpecialEditWatchlist\$watchedItemStore
WatchedItemStoreInterface $watchedItemStore
Definition: SpecialEditWatchlist.php:64
SpecialPage\msg
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:828
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:329
SpecialEditWatchlist\EDIT_RAW
const EDIT_RAW
Definition: SpecialEditWatchlist.php:47
SpecialEditWatchlist\checkTitle
checkTitle( $title, $namespace, $dbKey)
Validates watchlist entry.
Definition: SpecialEditWatchlist.php:456
SpecialEditWatchlist\initServices
initServices()
Initialize any services we'll need (unless it has already been provided via a setter).
Definition: SpecialEditWatchlist.php:79
LinkBatch
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:35
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:744
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:154
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
UnlistedSpecialPage
Shortcut to construct a special page which is unlisted by default.
Definition: UnlistedSpecialPage.php:31
Linker\tocIndent
static tocIndent()
Add another level to the Table of Contents.
Definition: Linker.php:1665
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
SpecialEditWatchlist\submitRaw
submitRaw( $data)
Definition: SpecialEditWatchlist.php:227
SpecialPage\checkPermissions
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
Definition: SpecialPage.php:343
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1219
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Definition: SpecialPage.php:92
SpecialEditWatchlist\watchTitles
watchTitles(array $targets)
Add a list of targets to a user's watchlist.
Definition: SpecialEditWatchlist.php:512
SpecialEditWatchlist\getExpandedTargets
getExpandedTargets(array $targets)
Definition: SpecialEditWatchlist.php:564
SpecialPage\getLanguage
getLanguage()
Shortcut to get user's language.
Definition: SpecialPage.php:774
SpecialPage\getName
getName()
Get the name of this Special Page.
Definition: SpecialPage.php:164
Linker\tocLine
static tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex=false)
parameter level defines if we are on an indentation level
Definition: Linker.php:1691
SpecialEditWatchlist\$badItems
$badItems
Definition: SpecialEditWatchlist.php:54
Linker\tocList
static tocList( $toc, Language $lang=null)
Wraps the TOC in a div with ARIA navigation role and provides the hide/collapse JavaScript.
Definition: Linker.php:1727
SpecialEditWatchlist\$isWatchlistExpiryEnabled
bool $isWatchlistExpiryEnabled
Watchlist Expiry flag.
Definition: SpecialEditWatchlist.php:67
SpecialEditWatchlist\clearUserWatchedItemsNow
clearUserWatchedItemsNow(string $messageFor)
You should call clearUserWatchedItems() instead to decide if this should use the JobQueue.
Definition: SpecialEditWatchlist.php:298
SpecialEditWatchlist\$titleParser
TitleParser $titleParser
Definition: SpecialEditWatchlist.php:59
SpecialEditWatchlist\runWatchUnwatchCompleteHook
runWatchUnwatchCompleteHook( $action, $targets)
Definition: SpecialEditWatchlist.php:544
SpecialEditWatchlist\clearUserWatchedItemsUsingJobQueue
clearUserWatchedItemsUsingJobQueue()
You should call clearUserWatchedItems() instead to decide if this should use the JobQueue.
Definition: SpecialEditWatchlist.php:315
OOUIHTMLForm
Compact stacked vertical format for forms, implemented using OOUI widgets.
Definition: OOUIHTMLForm.php:29
SpecialEditWatchlist\execute
execute( $mode)
Main execution point.
Definition: SpecialEditWatchlist.php:94
NS_MAIN
const NS_MAIN
Definition: Defines.php:69
EditWatchlistNormalHTMLForm
Extend OOUIHTMLForm purely so we can have a more sane way of getting the section headers.
Definition: EditWatchlistNormalHTMLForm.php:24
DerivativeContext
An IContextSource implementation which will inherit context from another source but allow individual ...
Definition: DerivativeContext.php:31
SpecialEditWatchlist\EDIT_NORMAL
const EDIT_NORMAL
Definition: SpecialEditWatchlist.php:48
SpecialPage\getHookRunner
getHookRunner()
Definition: SpecialPage.php:1010
SpecialPage\getConfig
getConfig()
Shortcut to get main config object.
Definition: SpecialPage.php:794
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:156
SpecialEditWatchlist\__construct
__construct(WatchedItemStoreInterface $watchedItemStore)
Definition: SpecialEditWatchlist.php:69
SpecialEditWatchlist\getRawForm
getRawForm()
Get a form for editing the watchlist in "raw" mode.
Definition: SpecialEditWatchlist.php:740
SpecialEditWatchlist\executeViewEditWatchlist
executeViewEditWatchlist()
Executes an edit mode for the watchlist view, from which you can manage your watchlist.
Definition: SpecialEditWatchlist.php:163
SpecialEditWatchlist\extractTitles
extractTitles( $list)
Extract a list of titles from a blob of text, returning (prefixed) strings; unwatchable titles are ig...
Definition: SpecialEditWatchlist.php:198
$wgLang
$wgLang
Definition: Setup.php:776
Linker\tocLineEnd
static tocLineEnd()
End a Table Of Contents line.
Definition: Linker.php:1715
$args
if( $line===false) $args
Definition: mcc.php:124
SpecialEditWatchlist
Provides the UI through which users can perform editing operations on their watchlist.
Definition: SpecialEditWatchlist.php:41
$title
$title
Definition: testCompression.php:38
SpecialPage\setHeaders
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!...
Definition: SpecialPage.php:571
SpecialPage\getUser
getUser()
Shortcut to get the User executing this instance.
Definition: SpecialPage.php:754
SpecialEditWatchlist\showTitles
showTitles( $titles, &$output)
Print out a list of linked titles.
Definition: SpecialEditWatchlist.php:329
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:592
TitleParser
A title parser service for MediaWiki.
Definition: TitleParser.php:33
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:909
SpecialEditWatchlist\buildRemoveLine
buildRemoveLine( $title, ?int $expiryDays=null)
Build the label for a checkbox, with a link to the title, and various additional bits.
Definition: SpecialEditWatchlist.php:687
SpecialPage\getContext
getContext()
Gets the context this SpecialPage is executed in.
Definition: SpecialPage.php:717
SpecialPage\requireLogin
requireLogin( $reasonMsg='exception-nologin-text', $titleMsg='exception-nologin')
If the user is not logged in, throws UserNotLoggedIn error.
Definition: SpecialPage.php:373
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:618
SpecialEditWatchlist\getMode
static getMode( $request, $par)
Determine whether we are editing the watchlist, and if so, what kind of editing operation.
Definition: SpecialEditWatchlist.php:790
SpecialEditWatchlist\getSubpagesForPrefixSearch
getSubpagesForPrefixSearch()
Return an array of subpages that this special page will accept.
Definition: SpecialEditWatchlist.php:182
SpecialPage\getRequest
getRequest()
Get the WebRequest being used for this instance.
Definition: SpecialPage.php:734
SpecialEditWatchlist\cleanupWatchlist
cleanupWatchlist()
Attempts to clean up broken items.
Definition: SpecialEditWatchlist.php:478
SpecialEditWatchlist\doesWrites
doesWrites()
Indicates whether this special page may perform database writes.
Definition: SpecialEditWatchlist.php:85
WatchedItemStoreInterface\SORT_ASC
const SORT_ASC
Definition: WatchedItemStoreInterface.php:35
SpecialEditWatchlist\outputSubtitle
outputSubtitle()
Renders a subheader on the watchlist page.
Definition: SpecialEditWatchlist.php:148
SpecialEditWatchlist\$toc
$toc
Definition: SpecialEditWatchlist.php:52
SpecialPage\getLinkRenderer
getLinkRenderer()
Definition: SpecialPage.php:944
SpecialEditWatchlist\buildTools
static buildTools( $lang, LinkRenderer $linkRenderer=null)
Build a set of links for convenient navigation between watchlist viewing and editing modes.
Definition: SpecialEditWatchlist.php:816
Title
Represents a title within MediaWiki.
Definition: Title.php:42
SpecialEditWatchlist\getClearForm
getClearForm()
Get a form for clearing the watchlist.
Definition: SpecialEditWatchlist.php:767
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:26
SpecialEditWatchlist\clearUserWatchedItems
clearUserWatchedItems(string $messageFor)
Makes a decision about using the JobQueue or not for clearing a users watchlist.
Definition: SpecialEditWatchlist.php:285
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
NS_USER
const NS_USER
Definition: Defines.php:71
SpecialEditWatchlist\getNormalForm
getNormalForm()
Get the standard watchlist editing form.
Definition: SpecialEditWatchlist.php:611
SpecialPage\checkReadOnly
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
Definition: SpecialPage.php:356
SpecialPage\$linkRenderer
MediaWiki Linker LinkRenderer null $linkRenderer
Definition: SpecialPage.php:71
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
SpecialEditWatchlist\getWatchlistInfo
getWatchlistInfo()
Get a list of titles on a user's watchlist, excluding talk pages, and return as a two-dimensional arr...
Definition: SpecialEditWatchlist.php:419
WatchedItemStoreInterface
Definition: WatchedItemStoreInterface.php:30
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:56
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add a callable update.
Definition: DeferredUpdates.php:145
SpecialEditWatchlist\submitClear
submitClear( $data)
Handler for the clear form submission.
Definition: SpecialEditWatchlist.php:274
SpecialPage\outputHeader
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Definition: SpecialPage.php:662
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:41
SpecialEditWatchlist\$successMessage
$successMessage
Definition: SpecialEditWatchlist.php:50
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:39
Title\newFromTitleValue
static newFromTitleValue(TitleValue $titleValue, $forceClone='')
Returns a Title given a TitleValue.
Definition: Title.php:266