protected $toc;
- private $badItems = array();
+ 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 ) {
+ $lang = $this->getContext()->getLanguage();
+ $this->titleParser = new MediaWikiTitleCodec( $lang, GenderCache::singleton() );
+ }
+ }
+
+ 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->outputHeader();
$this->outputSubtitle();
+ $out->addModuleStyles( 'mediawiki.special' );
# B/C: $mode used to be waaay down the parameter list, and the first parameter
# was $wgUser
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 array(
+ return [
'clear',
'raw',
- );
+ ];
}
/**
private function extractTitles( $list ) {
$list = explode( "\n", trim( $list ) );
if ( !is_array( $list ) ) {
- return array();
+ return [];
}
- $titles = array();
+ $titles = [];
foreach ( $list as $text ) {
$text = trim( $text );
GenderCache::singleton()->doTitlesArray( $titles );
- $list = array();
+ $list = [];
/** @var Title $title */
foreach ( $titles as $title ) {
$list[] = $title->getPrefixedText();
}
if ( $title instanceof Title ) {
- $output .= "<li>"
- . Linker::link( $title )
- . ' (' . Linker::link( $title->getTalkPage(), $talk )
- . ")</li>\n";
+ $output .= '<li>' .
+ Linker::link( $title ) . ' ' .
+ $this->msg( 'parentheses' )->rawParams(
+ Linker::link( $title->getTalkPage(), $talk )
+ )->escaped() .
+ "</li>\n";
}
}
* @return array
*/
private function getWatchlist() {
- $list = array();
+ $list = [];
- $index = $this->getRequest()->wasPosted() ? DB_MASTER : DB_SLAVE;
- $dbr = wfGetDB( $index );
-
- $res = $dbr->select(
- 'watchlist',
- array(
- 'wl_namespace', 'wl_title'
- ), array(
- 'wl_user' => $this->getUser()->getId(),
- ),
- __METHOD__
+ $watchedItems = WatchedItemStore::getDefaultInstance()->getWatchedItemsForUser(
+ $this->getUser(),
+ [ 'forWrite' => $this->getRequest()->wasPosted() ]
);
- if ( $res->numRows() > 0 ) {
+ if ( $watchedItems ) {
/** @var Title[] $titles */
- $titles = array();
- foreach ( $res as $row ) {
- $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title );
+ $titles = [];
+ foreach ( $watchedItems as $watchedItem ) {
+ $namespace = $watchedItem->getLinkTarget()->getNamespace();
+ $dbKey = $watchedItem->getLinkTarget()->getDBkey();
+ $title = Title::makeTitleSafe( $namespace, $dbKey );
- if ( $this->checkTitle( $title, $row->wl_namespace, $row->wl_title )
+ if ( $this->checkTitle( $title, $namespace, $dbKey )
&& !$title->isTalkPage()
) {
$titles[] = $title;
}
}
- $res->free();
GenderCache::singleton()->doTitlesArray( $titles );
* @return array
*/
protected function getWatchlistInfo() {
- $titles = array();
- $dbr = wfGetDB( DB_SLAVE );
-
- $res = $dbr->select(
- array( 'watchlist' ),
- array( 'wl_namespace', 'wl_title' ),
- array( 'wl_user' => $this->getUser()->getId() ),
- __METHOD__,
- array( 'ORDER BY' => array( 'wl_namespace', 'wl_title' ) )
- );
+ $titles = [];
+
+ $watchedItems = WatchedItemStore::getDefaultInstance()
+ ->getWatchedItemsForUser( $this->getUser(), [ 'sort' => WatchedItemStore::SORT_ASC ] );
$lb = new LinkBatch();
- foreach ( $res as $row ) {
- $lb->add( $row->wl_namespace, $row->wl_title );
- if ( !MWNamespace::isTalk( $row->wl_namespace ) ) {
- $titles[$row->wl_namespace][$row->wl_title] = 1;
+ foreach ( $watchedItems as $watchedItem ) {
+ $namespace = $watchedItem->getLinkTarget()->getNamespace();
+ $dbKey = $watchedItem->getLinkTarget()->getDBkey();
+ $lb->add( $namespace, $dbKey );
+ if ( !MWNamespace::isTalk( $namespace ) ) {
+ $titles[$namespace][$dbKey] = 1;
}
}
|| $title->getNamespace() != $namespace
|| $title->getDBkey() != $dbKey
) {
- $this->badItems[] = array( $title, $namespace, $dbKey );
+ $this->badItems[] = [ $title, $namespace, $dbKey ];
}
return (bool)$title;
return; // nothing to do
}
- $dbw = wfGetDB( DB_MASTER );
$user = $this->getUser();
+ $store = WatchedItemStore::getDefaultInstance();
foreach ( $this->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" );
- $dbw->delete( 'watchlist',
- array(
- 'wl_user' => $user->getId(),
- 'wl_namespace' => $namespace,
- 'wl_title' => $dbKey,
- ),
- __METHOD__
- );
+ $store->removeWatch( $user, new TitleValue( (int)$namespace, $dbKey ) );
// Can't just do an UPDATE instead of DELETE/INSERT due to unique index
if ( $title ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->delete(
'watchlist',
- array( 'wl_user' => $this->getUser()->getId() ),
+ [ 'wl_user' => $this->getUser()->getId() ],
__METHOD__
);
}
/**
- * Add a list of titles to a user's watchlist
+ * Add a list of targets to 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
+ * @param string[]|LinkTarget[] $targets
*/
- private function watchTitles( $titles ) {
- $dbw = wfGetDB( DB_MASTER );
- $rows = array();
-
- foreach ( $titles as $title ) {
- if ( !$title instanceof Title ) {
- $title = Title::newFromText( $title );
+ 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;
+ }
}
- if ( $title instanceof Title ) {
- $rows[] = array(
- 'wl_user' => $this->getUser()->getId(),
- 'wl_namespace' => MWNamespace::getSubject( $title->getNamespace() ),
- 'wl_title' => $title->getDBkey(),
- 'wl_notificationtimestamp' => null,
- );
- $rows[] = array(
- 'wl_user' => $this->getUser()->getId(),
- 'wl_namespace' => MWNamespace::getTalk( $title->getNamespace() ),
- 'wl_title' => $title->getDBkey(),
- 'wl_notificationtimestamp' => null,
- );
- }
+ $ns = $target->getNamespace();
+ $dbKey = $target->getDBkey();
+ $expandedTargets[] = new TitleValue( MWNamespace::getSubject( $ns ), $dbKey );
+ $expandedTargets[] = new TitleValue( MWNamespace::getTalk( $ns ), $dbKey );
}
- $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' );
+ WatchedItemStore::getDefaultInstance()->addWatchBatchForUser(
+ $this->getUser(),
+ $expandedTargets
+ );
}
/**
* @param array $titles Array of strings, or Title objects
*/
private function unwatchTitles( $titles ) {
- $dbw = wfGetDB( DB_MASTER );
+ $store = WatchedItemStore::getDefaultInstance();
foreach ( $titles as $title ) {
if ( !$title instanceof Title ) {
}
if ( $title instanceof Title ) {
- $dbw->delete(
- 'watchlist',
- array(
- 'wl_user' => $this->getUser()->getId(),
- 'wl_namespace' => MWNamespace::getSubject( $title->getNamespace() ),
- 'wl_title' => $title->getDBkey(),
- ),
- __METHOD__
- );
-
- $dbw->delete(
- 'watchlist',
- array(
- 'wl_user' => $this->getUser()->getId(),
- 'wl_namespace' => MWNamespace::getTalk( $title->getNamespace() ),
- 'wl_title' => $title->getDBkey(),
- ),
- __METHOD__
- );
+ $store->removeWatch( $this->getUser(), $title->getSubjectPage() );
+ $store->removeWatch( $this->getUser(), $title->getTalkPage() );
$page = WikiPage::factory( $title );
- Hooks::run( 'UnwatchArticleComplete', array( $this->getUser(), &$page ) );
+ Hooks::run( 'UnwatchArticleComplete', [ $this->getUser(), &$page ] );
}
}
}
public function submitNormal( $data ) {
- $removed = array();
+ $removed = [];
foreach ( $data as $titles ) {
$this->unwatchTitles( $titles );
protected function getNormalForm() {
global $wgContLang;
- $fields = array();
+ $fields = [];
$count = 0;
// Allow subscribers to manipulate the list of watched pages (or use it
$watchlistInfo = $this->getWatchlistInfo();
Hooks::run(
'WatchlistEditorBeforeFormRender',
- array( &$watchlistInfo )
+ [ &$watchlistInfo ]
);
foreach ( $watchlistInfo as $namespace => $pages ) {
- $options = array();
+ $options = [];
foreach ( array_keys( $pages ) as $dbkey ) {
$title = Title::makeTitleSafe( $namespace, $dbkey );
// checkTitle can filter some options out, avoid empty sections
if ( count( $options ) > 0 ) {
- $fields['TitlesNs' . $namespace] = array(
+ $fields['TitlesNs' . $namespace] = [
'class' => 'EditWatchlistCheckboxSeriesField',
'options' => $options,
'section' => "ns$namespace",
- );
+ ];
}
}
$this->cleanupWatchlist();
$form->setSubmitTooltip( 'watchlistedit-normal-submit' );
$form->setWrapperLegendMsg( 'watchlistedit-normal-legend' );
$form->addHeaderText( $this->msg( 'watchlistedit-normal-explain' )->parse() );
- $form->setSubmitCallback( array( $this, 'submitNormal' ) );
+ $form->setSubmitCallback( [ $this, 'submitNormal' ] );
return $form;
}
$tools['history'] = Linker::linkKnown(
$title,
$this->msg( 'history_short' )->escaped(),
- array(),
- array( 'action' => 'history' )
+ [],
+ [ 'action' => 'history' ]
);
}
Hooks::run(
'WatchlistEditorBuildRemoveLine',
- array( &$tools, $title, $title->isRedirect(), $this->getSkin(), &$link )
+ [ &$tools, $title, $title->isRedirect(), $this->getSkin(), &$link ]
);
if ( $title->isRedirect() ) {
$link = '<span class="watchlistredir">' . $link . '</span>';
}
- return $link . " (" . $this->getLanguage()->pipeList( $tools ) . ")";
+ return $link . ' ' .
+ $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList( $tools ) )->escaped();
}
/**
*/
protected function getRawForm() {
$titles = implode( $this->getWatchlist(), "\n" );
- $fields = array(
- 'Titles' => array(
+ $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->setSubmitTooltip( 'watchlistedit-raw-submit' );
$form->setWrapperLegendMsg( 'watchlistedit-raw-legend' );
$form->addHeaderText( $this->msg( 'watchlistedit-raw-explain' )->parse() );
- $form->setSubmitCallback( array( $this, 'submitRaw' ) );
+ $form->setSubmitCallback( [ $this, 'submitRaw' ] );
return $form;
}
protected function getClearForm() {
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $this->getPageTitle( 'clear' ) ); // Reset subpage
- $form = new HTMLForm( array(), $context );
+ $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( array( $this, 'submitClear' ) );
+ $form->setSubmitCallback( [ $this, 'submitClear' ] );
$form->setSubmitDestructive();
return $form;
public static function buildTools( $unused ) {
global $wgLang;
- $tools = array();
- $modes = array(
- 'view' => array( 'Watchlist', false ),
- 'edit' => array( 'EditWatchlist', false ),
- 'raw' => array( 'EditWatchlist', 'raw' ),
- 'clear' => array( 'EditWatchlist', 'clear' ),
- );
+ $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'
return Html::rawElement(
'span',
- array( 'class' => 'mw-watchlist-toollinks' ),
+ [ 'class' => 'mw-watchlist-toollinks' ],
wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $tools ) )->escaped()
);
}