MediaWiki  master
SpecialEditWatchlist.php
Go to the documentation of this file.
1 <?php
34 
48  public const EDIT_CLEAR = 1;
49  public const EDIT_RAW = 2;
50  public const EDIT_NORMAL = 3;
51 
52  protected $successMessage;
53 
54  protected $toc;
55 
56  private $badItems = [];
57 
59  private $titleParser;
60 
63 
65  private $genderCache;
66 
69 
71  private $nsInfo;
72 
75 
78 
87  public function __construct(
92  NamespaceInfo $nsInfo = null,
94  ) {
95  parent::__construct( 'EditWatchlist', 'editmywatchlist' );
96  // This class is extended and therefor fallback to global state - T266065
97  $services = MediaWikiServices::getInstance();
98  $this->watchedItemStore = $watchedItemStore ?? $services->getWatchedItemStore();
99  $this->titleParser = $titleParser ?? $services->getTitleParser();
100  $this->genderCache = $genderCache ?? $services->getGenderCache();
101  $this->linkBatchFactory = $linkBatchFactory ?? $services->getLinkBatchFactory();
102  $this->nsInfo = $nsInfo ?? $services->getNamespaceInfo();
103  $this->wikiPageFactory = $wikiPageFactory ?? $services->getWikiPageFactory();
104  $this->isWatchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
105  }
106 
107  public function doesWrites() {
108  return true;
109  }
110 
116  public function execute( $mode ) {
117  $this->setHeaders();
118 
119  # Anons don't get a watchlist
120  $this->requireLogin( 'watchlistanontext' );
121 
122  $out = $this->getOutput();
123 
124  $this->checkPermissions();
125  $this->checkReadOnly();
126 
127  $this->outputHeader();
128  $this->outputSubtitle();
129  $out->addModuleStyles( 'mediawiki.special' );
130 
131  $mode = self::getMode( $this->getRequest(), $mode );
132 
133  switch ( $mode ) {
134  case self::EDIT_RAW:
135  $out->setPageTitle( $this->msg( 'watchlistedit-raw-title' ) );
136  $form = $this->getRawForm();
137  if ( $form->show() ) {
138  $out->addHTML( $this->successMessage );
139  $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) );
140  }
141  break;
142  case self::EDIT_CLEAR:
143  $out->setPageTitle( $this->msg( 'watchlistedit-clear-title' ) );
144  $form = $this->getClearForm();
145  if ( $form->show() ) {
146  $out->addHTML( $this->successMessage );
147  $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) );
148  }
149  break;
150 
151  case self::EDIT_NORMAL:
152  default:
153  $this->executeViewEditWatchlist();
154  break;
155  }
156  }
157 
161  protected function outputSubtitle() {
162  $out = $this->getOutput();
163  $out->addSubtitle( $this->msg( 'watchlistfor2', $this->getUser()->getName() )
164  ->rawParams(
165  self::buildTools(
166  $this->getLanguage(),
167  $this->getLinkRenderer()
168  )
169  )
170  );
171  }
172 
176  protected function executeViewEditWatchlist() {
177  $out = $this->getOutput();
178  $out->setPageTitle( $this->msg( 'watchlistedit-normal-title' ) );
179  $form = $this->getNormalForm();
180  if ( $form->show() ) {
181  $out->addHTML( $this->successMessage );
182  $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) );
183  } elseif ( $this->toc !== false ) {
184  $out->prependHTML( $this->toc );
185  }
186  }
187 
194  public function getSubpagesForPrefixSearch() {
195  // SpecialWatchlist uses SpecialEditWatchlist::getMode, so new types should be added
196  // here and there - no 'edit' here, because that the default for this page
197  return [
198  'clear',
199  'raw',
200  ];
201  }
202 
210  private function extractTitles( $list ) {
211  $list = explode( "\n", trim( $list ) );
212  if ( !is_array( $list ) ) {
213  return [];
214  }
215 
216  $titles = [];
217 
218  foreach ( $list as $text ) {
219  $text = trim( $text );
220  if ( strlen( $text ) > 0 ) {
221  $title = Title::newFromText( $text );
222  if ( $title instanceof Title && $title->isWatchable() ) {
223  $titles[] = $title;
224  }
225  }
226  }
227 
228  $this->genderCache->doTitlesArray( $titles );
229 
230  $list = [];
232  foreach ( $titles as $title ) {
233  $list[] = $title->getPrefixedText();
234  }
235 
236  return array_unique( $list );
237  }
238 
239  public function submitRaw( $data ) {
240  $wanted = $this->extractTitles( $data['Titles'] );
241  $current = $this->getWatchlist();
242 
243  if ( count( $wanted ) > 0 ) {
244  $toWatch = array_diff( $wanted, $current );
245  $toUnwatch = array_diff( $current, $wanted );
246  $this->watchTitles( $toWatch );
247  $this->unwatchTitles( $toUnwatch );
248  $this->getUser()->invalidateCache();
249 
250  if ( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 ) {
251  $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse();
252  } else {
253  return false;
254  }
255 
256  if ( count( $toWatch ) > 0 ) {
257  $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-added' )
258  ->numParams( count( $toWatch ) )->parse();
259  $this->showTitles( $toWatch, $this->successMessage );
260  }
261 
262  if ( count( $toUnwatch ) > 0 ) {
263  $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-removed' )
264  ->numParams( count( $toUnwatch ) )->parse();
265  $this->showTitles( $toUnwatch, $this->successMessage );
266  }
267  } else {
268 
269  if ( count( $current ) === 0 ) {
270  return false;
271  }
272 
273  $this->clearUserWatchedItems( 'raw' );
274  $this->showTitles( $current, $this->successMessage );
275  }
276 
277  return true;
278  }
279 
286  public function submitClear( $data ): bool {
287  $this->clearUserWatchedItems( 'clear' );
288  return true;
289  }
290 
297  private function clearUserWatchedItems( string $messageFor ): void {
298  if ( $this->watchedItemStore->mustClearWatchedItemsUsingJobQueue( $this->getUser() ) ) {
300  } else {
301  $this->clearUserWatchedItemsNow( $messageFor );
302  }
303  }
304 
310  private function clearUserWatchedItemsNow( string $messageFor ): void {
311  $current = $this->getWatchlist();
312  if ( !$this->watchedItemStore->clearUserWatchedItems( $this->getUser() ) ) {
313  throw new LogicException(
314  __METHOD__ . ' should only be called when able to clear synchronously'
315  );
316  }
317  $this->successMessage = $this->msg( 'watchlistedit-' . $messageFor . '-done' )->parse();
318  $this->successMessage .= ' ' . $this->msg( 'watchlistedit-' . $messageFor . '-removed' )
319  ->numParams( count( $current ) )->parse();
320  $this->getUser()->invalidateCache();
321  $this->showTitles( $current, $this->successMessage );
322  }
323 
327  private function clearUserWatchedItemsUsingJobQueue(): void {
328  $this->watchedItemStore->clearUserWatchedItemsUsingJobQueue( $this->getUser() );
329  $this->successMessage = $this->msg( 'watchlistedit-clear-jobqueue' )->parse();
330  }
331 
341  private function showTitles( $titles, &$output ) {
342  $talk = $this->msg( 'talkpagelinktext' )->text();
343  // Do a batch existence check
344  $batch = $this->linkBatchFactory->newLinkBatch();
345  if ( count( $titles ) >= 100 ) {
346  $output = $this->msg( 'watchlistedit-too-many' )->parse();
347  return;
348  }
349  foreach ( $titles as $title ) {
350  if ( !$title instanceof Title ) {
352  }
353 
354  if ( $title instanceof Title ) {
355  $batch->addObj( $title );
356  $batch->addObj( $title->getTalkPage() );
357  }
358  }
359 
360  $batch->execute();
361 
362  // Print out the list
363  $output .= "<ul>\n";
364 
365  $linkRenderer = $this->getLinkRenderer();
366  foreach ( $titles as $title ) {
367  if ( !$title instanceof Title ) {
369  }
370 
371  if ( $title instanceof Title ) {
372  $output .= '<li>' .
373  $linkRenderer->makeLink( $title ) . ' ' .
374  $this->msg( 'parentheses' )->rawParams(
375  $linkRenderer->makeLink( $title->getTalkPage(), $talk )
376  )->escaped() .
377  "</li>\n";
378  }
379  }
380 
381  $output .= "</ul>\n";
382  }
383 
390  private function getWatchlist() {
391  $list = [];
392 
393  $watchedItems = $this->watchedItemStore->getWatchedItemsForUser(
394  $this->getUser(),
395  [ 'forWrite' => $this->getRequest()->wasPosted() ]
396  );
397 
398  if ( $watchedItems ) {
400  $titles = [];
401  foreach ( $watchedItems as $watchedItem ) {
402  $namespace = $watchedItem->getLinkTarget()->getNamespace();
403  $dbKey = $watchedItem->getLinkTarget()->getDBkey();
404  $title = Title::makeTitleSafe( $namespace, $dbKey );
405 
406  if ( $this->checkTitle( $title, $namespace, $dbKey )
407  && !$title->isTalkPage()
408  ) {
409  $titles[] = $title;
410  }
411  }
412 
413  $this->genderCache->doTitlesArray( $titles );
414 
415  foreach ( $titles as $title ) {
416  $list[] = $title->getPrefixedText();
417  }
418  }
419 
420  $this->cleanupWatchlist();
421 
422  return $list;
423  }
424 
431  protected function getWatchlistInfo() {
432  $titles = [];
433  $options = [ 'sort' => WatchedItemStore::SORT_ASC ];
434 
435  if ( $this->isWatchlistExpiryEnabled ) {
436  $options[ 'sortByExpiry'] = true;
437  }
438 
439  $watchedItems = $this->watchedItemStore->getWatchedItemsForUser(
440  $this->getUser(), $options
441  );
442 
443  $lb = $this->linkBatchFactory->newLinkBatch();
444  $context = $this->getContext();
445 
446  foreach ( $watchedItems as $watchedItem ) {
447  $namespace = $watchedItem->getLinkTarget()->getNamespace();
448  $dbKey = $watchedItem->getLinkTarget()->getDBkey();
449  $lb->add( $namespace, $dbKey );
450  if ( !$this->nsInfo->isTalk( $namespace ) ) {
451  $titles[$namespace][$dbKey] = $watchedItem->getExpiryInDaysText( $context );
452  }
453  }
454 
455  $lb->execute();
456 
457  return $titles;
458  }
459 
468  private function checkTitle( $title, $namespace, $dbKey ) {
469  if ( $title
470  && ( $title->isExternal()
471  || $title->getNamespace() < 0
472  )
473  ) {
474  $title = false; // unrecoverable
475  }
476 
477  if ( !$title
478  || $title->getNamespace() != $namespace
479  || $title->getDBkey() != $dbKey
480  ) {
481  $this->badItems[] = [ $title, $namespace, $dbKey ];
482  }
483 
484  return (bool)$title;
485  }
486 
490  private function cleanupWatchlist() {
491  if ( $this->badItems === [] ) {
492  return; // nothing to do
493  }
494 
495  $user = $this->getUser();
497  DeferredUpdates::addCallableUpdate( function () use ( $user, $badItems ) {
498  foreach ( $badItems as [ $title, $namespace, $dbKey ] ) {
499  $action = $title ? 'cleaning up' : 'deleting';
500  wfDebug( "User {$user->getName()} has broken watchlist item " .
501  "ns($namespace):$dbKey, $action." );
502 
503  // NOTE: We *know* that the title is invalid. TitleValue may refuse instantiation.
504  // XXX: We may need an InvalidTitleValue class that allows instantiation of
505  // known bad title values.
506  $this->watchedItemStore->removeWatch( $user, Title::makeTitle( (int)$namespace, $dbKey ) );
507  // Can't just do an UPDATE instead of DELETE/INSERT due to unique index
508  if ( $title ) {
509  $user->addWatch( $title );
510  }
511  }
512  } );
513  }
514 
523  private function watchTitles( array $targets ) {
524  return $this->watchedItemStore->addWatchBatchForUser(
525  $this->getUser(), $this->getExpandedTargets( $targets )
526  ) && $this->runWatchUnwatchCompleteHook( 'Watch', $targets );
527  }
528 
541  private function unwatchTitles( array $targets ) {
542  return $this->watchedItemStore->removeWatchBatchForUser(
543  $this->getUser(), $this->getExpandedTargets( $targets )
544  ) && $this->runWatchUnwatchCompleteHook( 'Unwatch', $targets );
545  }
546 
555  private function runWatchUnwatchCompleteHook( $action, $targets ) {
556  foreach ( $targets as $target ) {
557  $title = $target instanceof LinkTarget ?
558  Title::newFromLinkTarget( $target ) :
559  Title::newFromText( $target );
560  $page = $this->wikiPageFactory->newFromTitle( $title );
561  $user = $this->getUser();
562  if ( $action === 'Watch' ) {
563  $this->getHookRunner()->onWatchArticleComplete( $user, $page );
564  } else {
565  $this->getHookRunner()->onUnwatchArticleComplete( $user, $page );
566  }
567  }
568  return true;
569  }
570 
575  private function getExpandedTargets( array $targets ) {
576  $expandedTargets = [];
577  foreach ( $targets as $target ) {
578  if ( !$target instanceof LinkTarget ) {
579  try {
580  $target = $this->titleParser->parseTitle( $target, NS_MAIN );
581  }
582  catch ( MalformedTitleException $e ) {
583  continue;
584  }
585  }
586 
587  $ns = $target->getNamespace();
588  $dbKey = $target->getDBkey();
589  $expandedTargets[] =
590  new TitleValue( $this->nsInfo->getSubject( $ns ), $dbKey );
591  $expandedTargets[] =
592  new TitleValue( $this->nsInfo->getTalk( $ns ), $dbKey );
593  }
594  return $expandedTargets;
595  }
596 
597  public function submitNormal( $data ) {
598  $removed = [];
599 
600  foreach ( $data as $titles ) {
601  $this->unwatchTitles( $titles );
602  $removed = array_merge( $removed, $titles );
603  }
604 
605  if ( count( $removed ) > 0 ) {
606  $this->successMessage = $this->msg( 'watchlistedit-normal-done'
607  )->numParams( count( $removed ) )->parse();
608  $this->showTitles( $removed, $this->successMessage );
609 
610  return true;
611  } else {
612  return false;
613  }
614  }
615 
621  protected function getNormalForm() {
622  $fields = [];
623  $count = 0;
624 
625  // Allow subscribers to manipulate the list of watched pages (or use it
626  // to preload lots of details at once)
627  $watchlistInfo = $this->getWatchlistInfo();
628  $this->getHookRunner()->onWatchlistEditorBeforeFormRender( $watchlistInfo );
629 
630  foreach ( $watchlistInfo as $namespace => $pages ) {
631  $options = [];
632  foreach ( $pages as $dbkey => $expiryDaysText ) {
633  $title = Title::makeTitleSafe( $namespace, $dbkey );
634 
635  if ( $this->checkTitle( $title, $namespace, $dbkey ) ) {
636  $text = $this->buildRemoveLine( $title, $expiryDaysText );
637  $options[$text] = $title->getPrefixedText();
638  $count++;
639  }
640  }
641 
642  // checkTitle can filter some options out, avoid empty sections
643  if ( count( $options ) > 0 ) {
644  $fields['TitlesNs' . $namespace] = [
645  'class' => EditWatchlistCheckboxSeriesField::class,
646  'options' => $options,
647  'section' => "ns$namespace",
648  ];
649  }
650  }
651  $this->cleanupWatchlist();
652 
653  if ( count( $fields ) > 1 && $count > 30 ) {
654  $this->toc = Linker::tocIndent();
655  $tocLength = 0;
656  $contLang = $this->getContentLanguage();
657 
658  foreach ( $fields as $data ) {
659  # strip out the 'ns' prefix from the section name:
660  $ns = substr( $data['section'], 2 );
661 
662  $nsText = ( $ns == NS_MAIN )
663  ? $this->msg( 'blanknamespace' )->escaped()
664  : htmlspecialchars( $contLang->getFormattedNsText( $ns ) );
665  $this->toc .= Linker::tocLine( "editwatchlist-{$data['section']}", $nsText,
666  $this->getLanguage()->formatNum( ++$tocLength ), 1 ) . Linker::tocLineEnd();
667  }
668 
669  $this->toc = Linker::tocList( $this->toc );
670  } else {
671  $this->toc = false;
672  }
673 
674  $context = new DerivativeContext( $this->getContext() );
675  $context->setTitle( $this->getPageTitle() ); // Remove subpage
676  $form = new EditWatchlistNormalHTMLForm( $fields, $context );
677  $form->setSubmitTextMsg( 'watchlistedit-normal-submit' );
678  $form->setSubmitDestructive();
679  # Used message keys:
680  # 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit'
681  $form->setSubmitTooltip( 'watchlistedit-normal-submit' );
682  $form->setWrapperLegendMsg( 'watchlistedit-normal-legend' );
683  $form->addHeaderText( $this->msg( 'watchlistedit-normal-explain' )->parse() );
684  $form->setSubmitCallback( [ $this, 'submitNormal' ] );
685 
686  return $form;
687  }
688 
697  private function buildRemoveLine( $title, string $expiryDaysText = '' ): string {
698  $linkRenderer = $this->getLinkRenderer();
699  $link = $linkRenderer->makeLink( $title );
700 
701  $tools = [];
702  $tools['talk'] = $linkRenderer->makeLink(
703  $title->getTalkPage(),
704  $this->msg( 'talkpagelinktext' )->text()
705  );
706 
707  if ( $title->exists() ) {
708  $tools['history'] = $linkRenderer->makeKnownLink(
709  $title,
710  $this->msg( 'history_small' )->text(),
711  [],
712  [ 'action' => 'history' ]
713  );
714  }
715 
716  if ( $title->getNamespace() === NS_USER && !$title->isSubpage() ) {
717  $tools['contributions'] = $linkRenderer->makeKnownLink(
718  SpecialPage::getTitleFor( 'Contributions', $title->getText() ),
719  $this->msg( 'contribslink' )->text()
720  );
721  }
722 
723  $this->getHookRunner()->onWatchlistEditorBuildRemoveLine(
724  $tools, $title, $title->isRedirect(), $this->getSkin(), $link );
725 
726  if ( $title->isRedirect() ) {
727  // Linker already makes class mw-redirect, so this is redundant
728  $link = '<span class="watchlistredir">' . $link . '</span>';
729  }
730 
731  $watchlistExpiringMessage = '';
732  if ( $this->isWatchlistExpiryEnabled && $expiryDaysText ) {
733  $watchlistExpiringMessage = Html::element(
734  'span',
735  [ 'class' => 'mw-watchlistexpiry-msg' ],
736  $expiryDaysText
737  );
738  }
739 
740  return $link . ' ' .
741  $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList( $tools ) )->escaped() .
742  $watchlistExpiringMessage;
743  }
744 
750  protected function getRawForm() {
751  $titles = implode( "\n", $this->getWatchlist() );
752  $fields = [
753  'Titles' => [
754  'type' => 'textarea',
755  'label-message' => 'watchlistedit-raw-titles',
756  'default' => $titles,
757  ],
758  ];
759  $context = new DerivativeContext( $this->getContext() );
760  $context->setTitle( $this->getPageTitle( 'raw' ) ); // Reset subpage
761  $form = new OOUIHTMLForm( $fields, $context );
762  $form->setSubmitTextMsg( 'watchlistedit-raw-submit' );
763  # Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit'
764  $form->setSubmitTooltip( 'watchlistedit-raw-submit' );
765  $form->setWrapperLegendMsg( 'watchlistedit-raw-legend' );
766  $form->addHeaderText( $this->msg( 'watchlistedit-raw-explain' )->parse() );
767  $form->setSubmitCallback( [ $this, 'submitRaw' ] );
768 
769  return $form;
770  }
771 
777  protected function getClearForm() {
778  $context = new DerivativeContext( $this->getContext() );
779  $context->setTitle( $this->getPageTitle( 'clear' ) ); // Reset subpage
780  $form = new OOUIHTMLForm( [], $context );
781  $form->setSubmitTextMsg( 'watchlistedit-clear-submit' );
782  # Used message keys: 'accesskey-watchlistedit-clear-submit', 'tooltip-watchlistedit-clear-submit'
783  $form->setSubmitTooltip( 'watchlistedit-clear-submit' );
784  $form->setWrapperLegendMsg( 'watchlistedit-clear-legend' );
785  $form->addHeaderText( $this->msg( 'watchlistedit-clear-explain' )->parse() );
786  $form->setSubmitCallback( [ $this, 'submitClear' ] );
787  $form->setSubmitDestructive();
788 
789  return $form;
790  }
791 
800  public static function getMode( $request, $par ) {
801  $mode = strtolower( $request->getRawVal( 'action', $par ) );
802 
803  switch ( $mode ) {
804  case 'clear':
805  case self::EDIT_CLEAR:
806  return self::EDIT_CLEAR;
807  case 'raw':
808  case self::EDIT_RAW:
809  return self::EDIT_RAW;
810  case 'edit':
811  case self::EDIT_NORMAL:
812  return self::EDIT_NORMAL;
813  default:
814  return false;
815  }
816  }
817 
826  public static function buildTools( $lang, LinkRenderer $linkRenderer = null ) {
827  if ( !$lang instanceof Language ) {
828  // back-compat where the first parameter was $unused
829  global $wgLang;
830  $lang = $wgLang;
831  }
832  if ( !$linkRenderer ) {
833  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
834  }
835 
836  $tools = [];
837  $modes = [
838  'view' => [ 'Watchlist', false ],
839  'edit' => [ 'EditWatchlist', false ],
840  'raw' => [ 'EditWatchlist', 'raw' ],
841  'clear' => [ 'EditWatchlist', 'clear' ],
842  ];
843 
844  foreach ( $modes as $mode => $arr ) {
845  // can use messages 'watchlisttools-view', 'watchlisttools-edit', 'watchlisttools-raw'
846  $tools[] = $linkRenderer->makeKnownLink(
847  SpecialPage::getTitleFor( $arr[0], $arr[1] ),
848  wfMessage( "watchlisttools-{$mode}" )->text()
849  );
850  }
851 
852  return Html::rawElement(
853  'span',
854  [ 'class' => 'mw-watchlist-toollinks' ],
855  wfMessage( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped()
856  );
857  }
858 }
SpecialPage\$linkRenderer
LinkRenderer null $linkRenderer
Definition: SpecialPage.php:79
SpecialPage\getPageTitle
getPageTitle( $subpage=false)
Get a self-referential title object.
Definition: SpecialPage.php:742
SpecialEditWatchlist\submitNormal
submitNormal( $data)
Definition: SpecialEditWatchlist.php:597
SpecialEditWatchlist\EDIT_CLEAR
const EDIT_CLEAR
Editing modes.
Definition: SpecialEditWatchlist.php:48
SpecialEditWatchlist\unwatchTitles
unwatchTitles(array $targets)
Remove a list of titles from a user's watchlist.
Definition: SpecialEditWatchlist.php:541
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:390
SpecialEditWatchlist\$watchedItemStore
WatchedItemStoreInterface $watchedItemStore
Definition: SpecialEditWatchlist.php:62
SpecialPage\msg
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:900
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:328
SpecialEditWatchlist\$nsInfo
NamespaceInfo $nsInfo
Definition: SpecialEditWatchlist.php:71
SpecialEditWatchlist\EDIT_RAW
const EDIT_RAW
Definition: SpecialEditWatchlist.php:49
SpecialEditWatchlist\checkTitle
checkTitle( $title, $namespace, $dbKey)
Validates watchlist entry.
Definition: SpecialEditWatchlist.php:468
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:788
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:165
$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:239
SpecialPage\checkPermissions
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
Definition: SpecialPage.php:356
GenderCache
Caches user genders when needed to use correct namespace aliases.
Definition: GenderCache.php:36
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1220
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:106
SpecialEditWatchlist\watchTitles
watchTitles(array $targets)
Add a list of targets to a user's watchlist.
Definition: SpecialEditWatchlist.php:523
SpecialEditWatchlist\$linkBatchFactory
LinkBatchFactory $linkBatchFactory
Definition: SpecialEditWatchlist.php:68
SpecialEditWatchlist\getExpandedTargets
getExpandedTargets(array $targets)
Definition: SpecialEditWatchlist.php:575
SpecialPage\getLanguage
getLanguage()
Shortcut to get user's language.
Definition: SpecialPage.php:818
SpecialPage\getName
getName()
Get the name of this Special Page.
Definition: SpecialPage.php:178
Linker\tocLine
static tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex=false)
parameter level defines if we are on an indentation level
Definition: Linker.php:1691
$wgLang
$wgLang
Definition: Setup.php:778
SpecialEditWatchlist\$badItems
$badItems
Definition: SpecialEditWatchlist.php:56
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:77
SpecialEditWatchlist\clearUserWatchedItemsNow
clearUserWatchedItemsNow(string $messageFor)
You should call clearUserWatchedItems() instead to decide if this should use the JobQueue.
Definition: SpecialEditWatchlist.php:310
SpecialEditWatchlist\$titleParser
TitleParser $titleParser
Definition: SpecialEditWatchlist.php:59
SpecialEditWatchlist\runWatchUnwatchCompleteHook
runWatchUnwatchCompleteHook( $action, $targets)
Definition: SpecialEditWatchlist.php:555
SpecialEditWatchlist\clearUserWatchedItemsUsingJobQueue
clearUserWatchedItemsUsingJobQueue()
You should call clearUserWatchedItems() instead to decide if this should use the JobQueue.
Definition: SpecialEditWatchlist.php:327
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:116
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:50
SpecialPage\getHookRunner
getHookRunner()
Definition: SpecialPage.php:1083
SpecialPage\getConfig
getConfig()
Shortcut to get main config object.
Definition: SpecialPage.php:866
SpecialEditWatchlist\$wikiPageFactory
WikiPageFactory $wikiPageFactory
Definition: SpecialEditWatchlist.php:74
SpecialEditWatchlist\getRawForm
getRawForm()
Get a form for editing the watchlist in "raw" mode.
Definition: SpecialEditWatchlist.php:750
MediaWiki\Cache\LinkBatchFactory
Definition: LinkBatchFactory.php:38
SpecialEditWatchlist\executeViewEditWatchlist
executeViewEditWatchlist()
Executes an edit mode for the watchlist view, from which you can manage your watchlist.
Definition: SpecialEditWatchlist.php:176
SpecialEditWatchlist\extractTitles
extractTitles( $list)
Extract a list of titles from a blob of text, returning (prefixed) strings; unwatchable titles are ig...
Definition: SpecialEditWatchlist.php:210
Linker\tocLineEnd
static tocLineEnd()
End a Table Of Contents line.
Definition: Linker.php:1715
SpecialEditWatchlist
Provides the UI through which users can perform editing operations on their watchlist.
Definition: SpecialEditWatchlist.php:43
Page\WikiPageFactory
Definition: WikiPageFactory.php:19
SpecialEditWatchlist\$genderCache
GenderCache $genderCache
Definition: SpecialEditWatchlist.php:65
$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:616
SpecialPage\getUser
getUser()
Shortcut to get the User executing this instance.
Definition: SpecialPage.php:798
SpecialEditWatchlist\showTitles
showTitles( $titles, &$output)
Print out a list of linked titles.
Definition: SpecialEditWatchlist.php:341
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:591
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:910
SpecialPage\getContext
getContext()
Gets the context this SpecialPage is executed in.
Definition: SpecialPage.php:762
SpecialPage\requireLogin
requireLogin( $reasonMsg='exception-nologin-text', $titleMsg='exception-nologin')
If the user is not logged in, throws UserNotLoggedIn error.
Definition: SpecialPage.php:386
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:617
SpecialEditWatchlist\getMode
static getMode( $request, $par)
Determine whether we are editing the watchlist, and if so, what kind of editing operation.
Definition: SpecialEditWatchlist.php:800
SpecialEditWatchlist\getSubpagesForPrefixSearch
getSubpagesForPrefixSearch()
Return an array of subpages that this special page will accept.
Definition: SpecialEditWatchlist.php:194
SpecialPage\getRequest
getRequest()
Get the WebRequest being used for this instance.
Definition: SpecialPage.php:778
SpecialEditWatchlist\cleanupWatchlist
cleanupWatchlist()
Attempts to clean up broken items.
Definition: SpecialEditWatchlist.php:490
SpecialEditWatchlist\doesWrites
doesWrites()
Indicates whether this special page may perform database writes.
Definition: SpecialEditWatchlist.php:107
WatchedItemStoreInterface\SORT_ASC
const SORT_ASC
Definition: WatchedItemStoreInterface.php:35
SpecialEditWatchlist\__construct
__construct(WatchedItemStoreInterface $watchedItemStore=null, TitleParser $titleParser=null, GenderCache $genderCache=null, LinkBatchFactory $linkBatchFactory=null, NamespaceInfo $nsInfo=null, WikiPageFactory $wikiPageFactory=null)
Definition: SpecialEditWatchlist.php:87
SpecialEditWatchlist\buildRemoveLine
buildRemoveLine( $title, string $expiryDaysText='')
Build the label for a checkbox, with a link to the title, and various additional bits.
Definition: SpecialEditWatchlist.php:697
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:280
SpecialEditWatchlist\outputSubtitle
outputSubtitle()
Renders a subheader on the watchlist page.
Definition: SpecialEditWatchlist.php:161
SpecialEditWatchlist\$toc
$toc
Definition: SpecialEditWatchlist.php:54
SpecialPage\getLinkRenderer
getLinkRenderer()
Definition: SpecialPage.php:1016
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:826
Title
Represents a title within MediaWiki.
Definition: Title.php:41
SpecialEditWatchlist\getClearForm
getClearForm()
Get a form for clearing the watchlist.
Definition: SpecialEditWatchlist.php:777
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:26
MediaWiki\Linker\LinkRenderer\makeKnownLink
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:225
SpecialEditWatchlist\clearUserWatchedItems
clearUserWatchedItems(string $messageFor)
Makes a decision about using the JobQueue or not for clearing a users watchlist.
Definition: SpecialEditWatchlist.php:297
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:212
NS_USER
const NS_USER
Definition: Defines.php:71
SpecialEditWatchlist\getNormalForm
getNormalForm()
Get the standard watchlist editing form.
Definition: SpecialEditWatchlist.php:621
NamespaceInfo
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Definition: NamespaceInfo.php:35
SpecialPage\getContentLanguage
getContentLanguage()
Shortcut to get content language.
Definition: SpecialPage.php:828
SpecialPage\checkReadOnly
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
Definition: SpecialPage.php:369
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:234
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:431
WatchedItemStoreInterface
Definition: WatchedItemStoreInterface.php:30
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:286
MediaWiki\Linker\LinkRenderer\makeLink
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:160
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:707
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:42
SpecialEditWatchlist\$successMessage
$successMessage
Definition: SpecialEditWatchlist.php:52
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:39