Reedy has uploaded a new change for review.
https://gerrit.wikimedia.org/r/170588
Change subject: Watchlist grouping rebase attempt
......................................................................
Watchlist grouping rebase attempt
Doesn't include Messages. Includes merge conflict in Special(Edit)?Watchlist.php
Based on I702b9a8f3cda39a49c625654ca10fdd292e1e74c
Change-Id: If87504366263964aafc5826be9c1752513d18c8f
---
M includes/AutoLoader.php
M includes/User.php
M includes/WatchedItem.php
A includes/WatchlistGroup.php
M includes/installer/MysqlUpdater.php
M includes/installer/PostgresUpdater.php
M includes/installer/SqliteUpdater.php
M includes/specialpage/SpecialPageFactory.php
M includes/specials/SpecialEditWatchlist.php
A includes/specials/SpecialEditWatchlistGroup.php
M includes/specials/SpecialWatchlist.php
A maintenance/archives/patch-watchlist_groups.sql
A maintenance/archives/patch-watchlist_groups_newfield.sql
A maintenance/postgres/archives/patch-watchlist_groups.sql
A maintenance/postgres/archives/patch-watchlist_groups_newfield.sql
M maintenance/postgres/tables.sql
A maintenance/sqlite/archives/patch-watchlist_groups_newfield.sql
M maintenance/tables.sql
18 files changed, 1,032 insertions(+), 24 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/88/170588/1
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 36a0fcb..b94be90 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -177,6 +177,7 @@
'UserArrayFromResult' => 'includes/UserArrayFromResult.php',
'UserRightsProxy' => 'includes/UserRightsProxy.php',
'WatchedItem' => 'includes/WatchedItem.php',
+ 'WatchlistGroup' => 'includes/WatchlistGroup.php',
'WebRequest' => 'includes/WebRequest.php',
'WebRequestUpload' => 'includes/WebRequest.php',
'WebResponse' => 'includes/WebResponse.php',
@@ -1045,6 +1046,7 @@
'SpecialCreateAccount' => 'includes/specials/SpecialCreateAccount.php',
'SpecialDiff' => 'includes/specials/SpecialDiff.php',
'SpecialEditWatchlist' => 'includes/specials/SpecialEditWatchlist.php',
+ 'SpecialEditWatchlistGroup' =>
'includes/specials/SpecialEditWatchlistGroup.php',
'SpecialEmailUser' => 'includes/specials/SpecialEmailuser.php',
'SpecialExpandTemplates' =>
'includes/specials/SpecialExpandTemplates.php',
'SpecialExport' => 'includes/specials/SpecialExport.php',
diff --git a/includes/User.php b/includes/User.php
index 90d33fb..e2e2f66 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -3234,9 +3234,10 @@
* @param Title $title Title of the article to look at
* @param int $checkRights Whether to check
'viewmywatchlist'/'editmywatchlist' rights.
* Pass WatchedItem::CHECK_USER_RIGHTS or
WatchedItem::IGNORE_USER_RIGHTS.
+ * * @param int $group ID of the watchlist group
*/
- public function addWatch( $title, $checkRights =
WatchedItem::CHECK_USER_RIGHTS ) {
- $this->getWatchedItem( $title, $checkRights )->addWatch();
+ public function addWatch( $title, $checkRights =
WatchedItem::CHECK_USER_RIGHTS, $group = 0 ) {
+ $this->getWatchedItem( $title, $checkRights )->setGroup( $group
)->addWatch();
$this->invalidateCache();
}
diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php
index ab136b8..b84a661 100644
--- a/includes/WatchedItem.php
+++ b/includes/WatchedItem.php
@@ -60,21 +60,36 @@
private $timestamp;
/**
- * Create a WatchedItem object with the given user and title
+ * Internal identifier for the group,
+ * default: WatchlistGroup::DEFAULT_GROUP
+ *
+ * @var Integer
+ */
+ protected $group = WatchlistGroup::DEFAULT_GROUP;
+
+ /**
+ * Create a WatchedItem object with the given user and title.
+ *
* @since 1.22 $checkRights parameter added
+ *
* @param User $user The user to use for (un)watching
* @param Title $title The title we're going to (un)watch
* @param int $checkRights Whether to check the 'viewmywatchlist' and
'editmywatchlist' rights.
* Pass either WatchedItem::IGNORE_USER_RIGHTS or
WatchedItem::CHECK_USER_RIGHTS.
+ * @param $group Int: (default WatchlistGroup::DEFAULT_GROUP)
* @return WatchedItem
*/
- public static function fromUserTitle( $user, $title,
- $checkRights = WatchedItem::CHECK_USER_RIGHTS
+ public static function fromUserTitle(
+ $user,
+ $title,
+ $checkRights = WatchedItem::CHECK_USER_RIGHTS,
+ $group = WatchlistGroup::DEFAULT_GROUP
) {
$wl = new WatchedItem;
$wl->mUser = $user;
$wl->mTitle = $title;
$wl->mCheckRights = $checkRights;
+ $wl->setGroup( $group );
return $wl;
}
@@ -271,10 +286,22 @@
}
/**
+ * Sets the group of the watched item.
+ * @return WatchedItem
+ */
+ public function setGroup( $group ) {
+ $isGroup = is_int( $group );
+ if( $isGroup ){
+ $this->group = $group;
+ }
+ return $this;
+ }
+
+ /**
* @param WatchedItem[] $items
* @return bool
*/
- public static function batchAddWatch( array $items ) {
+ public function batchAddWatch( array $items ) {
$section = new ProfileSection( __METHOD__ );
if ( wfReadOnly() ) {
@@ -289,6 +316,7 @@
}
$rows[] = array(
'wl_user' => $item->getUserId(),
+ 'wl_group' => $this->group,
'wl_namespace' => MWNamespace::getSubject(
$item->getTitleNs() ),
'wl_title' => $item->getTitleDBkey(),
'wl_notificationtimestamp' => null,
@@ -297,6 +325,7 @@
// namespace:page and namespace_talk:page need separate
entries:
$rows[] = array(
'wl_user' => $item->getUserId(),
+ 'wl_group' => $this->group,
'wl_namespace' => MWNamespace::getTalk(
$item->getTitleNs() ),
'wl_title' => $item->getTitleDBkey(),
'wl_notificationtimestamp' => null
@@ -323,7 +352,7 @@
* @return bool
*/
public function addWatch() {
- return self::batchAddWatch( array( $this ) );
+ return $this->batchAddWatch( array( $this ) );
}
/**
@@ -432,4 +461,4 @@
return true;
}
-}
+}
\ No newline at end of file
diff --git a/includes/WatchlistGroup.php b/includes/WatchlistGroup.php
new file mode 100644
index 0000000..c9f1f2d
--- /dev/null
+++ b/includes/WatchlistGroup.php
@@ -0,0 +1,285 @@
+<?php
+/**
+ * Accessor and mutator for watchlist groups.
+ *
+ * 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
+ */
+
+/**
+ * Class representing whatchlist groups.
+ *
+ * @note mutators require a User object to check permissions, while accessors
+ * just take user IDs.
+ *
+*/
+class WatchlistGroup {
+
+ /**
+ * User to whom belong the whatched group of items
+ */
+ protected $user;
+
+ /**
+ * Id of the user
+ */
+ protected $id;
+
+ /**
+ * Array representing this user watchlists.
+ * Key is the groupID, the value contains the group name and
+ * the permission.
+ *
+ */
+ protected $groups = array();
+
+ /**
+ * Constant representing the ID of the general group which is used
+ * whenever an item is watched but not actually assigned to a specifc
+ * group.
+ */
+ const DEFAULT_GROUP = 0;
+
+ /**
+ * Create a WatchlistGroup object with the given user and title
+ * @param $user User: the user that owns the watchlist
+ * @return WatchlistGroup object
+ */
+ public static function newFromUser( $user ) {
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'watchlist_groups',
+ array( 'wg_id', 'wg_name', 'wg_perm' ),
+ array( 'wg_user' => $user->getId() ), __METHOD__
+ );
+ $groups = array();
+ foreach ( $res as $s ) {
+ $groups[$s->wg_id] = array( $s->wg_name, $s->wg_perm );
+ }
+
+ # Instantiate a represent the whatchlist
+ $wg = new WatchlistGroup;
+ $wg->user = $user;
+ $wg->id = $user->getId();
+ $wg->groups = $groups;
+
+ return $wg;
+ }
+
+ /**
+ * Returns a two dimensional array containing user's watchlist groups in
+ * the following format:
+ *
+ * @code
+ * array(
+ * id => array( <group name>, <permission> ),
+ * )
+ * @endcode
+ *
+ * @see $groups
+ *
+ * @param bool $include_nogroup (default false)
+ * @return array of groups
+ */
+ public function getGroups( $include_nogroup = false ) {
+
+ $groups = $this->groups;
+
+ // Include the "ungrouped" group
+ if( $include_nogroup ) {
+ $groups[WatchlistGroup::DEFAULT_GROUP] = array(
+ wfMsg( 'watchlistedit-nogroup' ),
+ 0 /** permission */
+ );
+ }
+ return $groups;
+ }
+
+ /**
+ * Gets the group ID for a group name in a user's watchlist
+ *
+ * @param $groupName string
+ * @return bool|string False on failure
+ */
+ public function getGroupFromName( $groupName ) {
+ if( $groupName == 'none' ){
+ return WatchlistGroup::DEFAULT_GROUP;
+ }
+ foreach ($this->groups as $gid => $ginfo) {
+ if( $groupName == $ginfo[0] ) {
+ return intval( $gid );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the group name for a group ID in a user's watchlist
+ *
+ * @param $groupId int
+ *
+ * @return String
+ */
+ public function getGroupNameFromID( $groupId ) {
+ return $this->groups[$groupId][0];
+ }
+
+ /**
+ * Checks a group name and returns the name without invalid characters
if
+ * it is otherwise okay.
+ * Returns false if is a reserved keyword.
+ *
+ * @param $groupName string: the group name
+ *
+ * @return bool|string False on failure
+ */
+ public static function checkValidGroupName( $groupName ) {
+ # FIXME this is most probably wrong
+ $groupName = preg_replace( '/[^a-zA-Z0-9]+/', '', $groupName );
+ if( $groupName == '' || $groupName == 'clear' || $groupName ==
'raw'
+ || $groupName == 'edit' || $groupName == '0' ||
$groupName == '1'
+ || $groupName == '2'
+ ) {
+ return false;
+ }
+ return $groupName;
+ }
+
+ /**
+ * Existence/permission-checking method for watchlist groups.
+ *
+ * @param $groupId int: the group ID
+ * @param $perm bool: if true, the group must also be viewable by the
given
+ * user to return true
+ *
+ * @return bool
+ */
+ public function isGroup( $groupId, $perm = false ) {
+ $dbw = wfGetDB( DB_SLAVE );
+ $res = $dbw->selectRow( 'watchlist_groups',
+ array( 'wg_name', 'wg_user', 'wg_perm' ),
+ array( 'wg_id' => $groupId, ),
+ __METHOD__
+ );
+
+ # FIXME this need to be made easier to understand
+ return $res && (
+ // we're not supposed to check permissions:
+ !$perm ||
+ // the user has permission:
+ ( $perm && $res->wg_perm ) ||
+ // users can access their own watchlists:
+ $this->id == $res->wg_user
+ );
+ }
+
+ /**
+ * Mutators
+ **/
+
+ /**
+ * Changes the group associated with titles in a watchlist
+ *
+ * @param $titles array: titles to be regrouped
+ * @param $group int: the group ID of the new desired group
+ *
+ * @return bool
+ */
+ public function regroupTitles( $titles, $group ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $lb = new LinkBatch( $titles );
+ $where_titles = $lb->constructSet( 'wl', $dbw );
+ $res = $dbw->update( 'watchlist',
+ array( 'wl_group' => $group ),
+ array( 'wl_user' => $this->id, $where_titles ),
+ __METHOD__
+ );
+ return $res;
+ }
+
+ /**
+ * Create a watchlist group.
+ *
+ * @param $groupName string: the name of the new group
+ *
+ * @return bool
+ */
+ public function createGroup( $groupName ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->insert( 'watchlist_groups',
+ array( 'wg_name' => $groupName, 'wg_user' => $this->id
),
+ __METHOD__
+ );
+ return $res;
+ }
+
+ /**
+ * Rename a watchlist group.
+ *
+ * @param $group int: the group ID
+ * @param $newName string: the new name of the group
+ *
+ * @return bool
+ */
+ public function renameGroup( $groupId, $newName ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->update( 'watchlist_groups',
+ array( 'wg_name' => $newName ),
+ array( 'wg_id' => $groupId, 'wg_user' => $this->id ),
+ __METHOD__
+ );
+ return $res;
+ }
+
+ /**
+ * Change the permissions for a watchlist group
+ *
+ * @param $groupId int: the group ID
+ * @param $perm int: 0 (private) or 1 (public)
+ *
+ * @return bool
+ */
+ public function changePerm( $groupId, $perm ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $res = $dbw->update( 'watchlist_groups',
+ array( 'wg_perm' => $perm ),
+ array( 'wg_id' => $groupId, 'wg_user' => $this->id ),
+ __METHOD__
+ );
+ return $res;
+ }
+
+ /**
+ * Delete a watchlist group
+ *
+ * @param $groupId int: the group ID
+ *
+ * @return bool
+ */
+ public function deleteGroup( $groupId ) {
+ $dbw = wfGetDB( DB_MASTER );
+ # FIXME: this should be wrapped in a transaction
+ $dbw->update( 'watchlist',
+ array( 'wl_group' => 0 ), array( 'wl_group' => $groupId
),
+ __METHOD__
+ );
+ $res = $dbw->delete( 'watchlist_groups',
+ array( 'wg_id' => $groupId, 'wg_user' => $this->id),
+ __METHOD__
+ );
+ return $res;
+ }
+}
diff --git a/includes/installer/MysqlUpdater.php
b/includes/installer/MysqlUpdater.php
index 990b5b0..6dfe8d2 100644
--- a/includes/installer/MysqlUpdater.php
+++ b/includes/installer/MysqlUpdater.php
@@ -266,6 +266,10 @@
'patch-oi_major_mime-chemical.sql' ),
array( 'modifyField', 'filearchive', 'fa_major_mime',
'patch-fa_major_mime-chemical.sql' ),
+
+ // 1.25
+ array( 'addTable', 'watchlist_groups',
'patch-watchlist_groups.sql' ),
+ array( 'addField', 'watchlist', 'wl_group',
'patch-watchlist_groups_newfield.sql' ),
);
}
diff --git a/includes/installer/PostgresUpdater.php
b/includes/installer/PostgresUpdater.php
index 9e8ee94..4af71ca 100644
--- a/includes/installer/PostgresUpdater.php
+++ b/includes/installer/PostgresUpdater.php
@@ -91,6 +91,7 @@
array( 'addTable', 'uploadstash',
'patch-uploadstash.sql' ),
array( 'addTable', 'user_former_groups',
'patch-user_former_groups.sql' ),
array( 'addTable', 'sites', 'patch-sites.sql' ),
+ array( 'addTable', 'watchlist_groups',
'patch-watchlist_groups.sql' ),
# Needed before new field
array( 'convertArchive2' ),
@@ -169,6 +170,7 @@
array( 'addPgField', 'externallinks', 'el_id',
"INTEGER NOT NULL PRIMARY KEY DEFAULT
nextval('externallinks_el_id_seq')" ),
array( 'addPgField', 'uploadstash', 'us_props', "BYTEA"
),
+ array( 'addPgField', 'watchlist', 'wl_group', "INTEGER
NOT NULL DEFAULT 0" ),
# type changes
array( 'changeField', 'archive', 'ar_deleted',
'smallint', '' ),
diff --git a/includes/installer/SqliteUpdater.php
b/includes/installer/SqliteUpdater.php
index ab5ab7d..ce6b0cc 100644
--- a/includes/installer/SqliteUpdater.php
+++ b/includes/installer/SqliteUpdater.php
@@ -137,6 +137,10 @@
array( 'addField', 'pagelinks', 'pl_from_namespace',
'patch-pl_from_namespace.sql' ),
array( 'addField', 'templatelinks',
'tl_from_namespace', 'patch-tl_from_namespace.sql' ),
array( 'addField', 'imagelinks', 'il_from_namespace',
'patch-il_from_namespace.sql' ),
+
+ // 1.25
+ array( 'addTable', 'watchlist_groups',
'patch-watchlist_groups.sql' ),
+ array( 'addField', 'watchlist', 'wl_group',
'patch-watchlist_groups_newfield.sql' ),
);
}
diff --git a/includes/specialpage/SpecialPageFactory.php
b/includes/specialpage/SpecialPageFactory.php
index b110bda..4c2787d 100644
--- a/includes/specialpage/SpecialPageFactory.php
+++ b/includes/specialpage/SpecialPageFactory.php
@@ -101,6 +101,7 @@
'Listbots' => 'SpecialListBots',
'Userrights' => 'UserrightsPage',
'EditWatchlist' => 'SpecialEditWatchlist',
+ 'EditWatchlistGroup' => 'SpecialEditWatchlistGroup',
// Recent changes and logs
'Newimages' => 'SpecialNewFiles',
diff --git a/includes/specials/SpecialEditWatchlist.php
b/includes/specials/SpecialEditWatchlist.php
index bc63e99..7bd1615 100644
--- a/includes/specials/SpecialEditWatchlist.php
+++ b/includes/specials/SpecialEditWatchlist.php
@@ -48,6 +48,8 @@
protected $toc;
private $badItems = array();
+ private $wg_obj;
+ private $groupName = '';
public function __construct() {
parent::__construct( 'EditWatchlist', 'editmywatchlist' );
@@ -56,9 +58,25 @@
/**
* Main execution point
*
+<<<<<<< HEAD
* @param int $mode
+=======
+ * @param $par string: This could be the mode OR a watchlist query by
user/group
+>>>>>>> 4e55649... Watchlist grouping
*/
- public function execute( $mode ) {
+ public function execute( $par ) {
+ $this->wg_obj = WatchlistGroup::newFromUser( $this->getUser() );
+ // Set the mode
+ if( $par == 'clear' || $par == 'raw' || $par == 'edit'
+ || $par == '0' || $par == '1' || $par == '2' ) {
+ $mode = $par;
+ } else {
+ $mode = '';
+ if( $this->wg_obj->isGroup(
$this->wg_obj->getGroupFromName( $par ) ) ) {
+ $this->groupName = $par;
+ }
+ }
+
$this->setHeaders();
# Anons don't get a watchlist
@@ -91,12 +109,30 @@
$out->addReturnTo(
SpecialPage::getTitleFor( 'Watchlist' ) );
}
break;
+<<<<<<< HEAD
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' ) );
+=======
+
+ case self::EDIT_NORMAL:
+ default:
+ if( $this->getWatchlistInfo() == array() ) {
+ $out->setPageTitle( $this->msg(
'watchlistedit-normal-title' ) );
+ $out->addWikiMsg(
'watchlistedit-noitems' );
+ } else {
+ $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 );
+ }
+>>>>>>> 4e55649... Watchlist grouping
}
break;
@@ -161,17 +197,23 @@
* @return array
*/
private function extractTitles( $list ) {
+ $titles = array();
$list = explode( "\n", trim( $list ) );
if ( !is_array( $list ) ) {
return array();
}
+<<<<<<< HEAD
$titles = array();
foreach ( $list as $text ) {
+=======
+ foreach( $list as $text ) {
+>>>>>>> 4e55649... Watchlist grouping
$text = trim( $text );
if ( strlen( $text ) > 0 ) {
$title = Title::newFromText( $text );
+<<<<<<< HEAD
if ( $title instanceof Title &&
$title->isWatchable() ) {
$titles[] = $title;
}
@@ -187,11 +229,20 @@
}
return array_unique( $list );
+=======
+ if( $title instanceof Title &&
$title->isWatchable() ) {
+ $titles[] = $title->getPrefixedText();
+ }
+ }
+ }
+ return array_unique( $titles );
+>>>>>>> 4e55649... Watchlist grouping
}
public function submitRaw( $data ) {
- $wanted = $this->extractTitles( $data['Titles'] );
+ $wanted = array();
$current = $this->getWatchlist();
+<<<<<<< HEAD
if ( count( $wanted ) > 0 ) {
$toWatch = array_diff( $wanted, $current );
@@ -201,11 +252,41 @@
$this->getUser()->invalidateCache();
if ( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 )
{
+=======
+ $allWatched = array();
+ $allUnwatched = array();
+ foreach( $data as $key => $d ) {
+ if( substr( $key, 0, 12 ) == 'Titles_group' ) {
+ $group = intval( substr( $key, 12, 13 ) );
+ $wanted[$group] = $this->extractTitles(
$data['Titles_group' . $group] );
+ }
+ }
+ $groups = array_unique( array_keys( $wanted ) + array_keys(
$current ) );
+ foreach( $wanted as $gid => $titles ) {
+ if( count( $wanted ) > 0 ) {
+ $toUnwatch = array_diff( $current[$gid],
$wanted[$gid] );
+ $this->unwatchTitles( $toUnwatch );
+ $this->getUser()->invalidateCache();
+ $allUnwatched += $toUnwatch;
+ }
+ }
+ foreach( $wanted as $gid => $titles ) {
+ if( count( $wanted ) > 0 ) {
+ $toWatch = array_diff( $wanted[$gid],
$current[$gid] );
+ $this->watchTitles( $toWatch, $gid );
+ $this->getUser()->invalidateCache();
+ $allWatched += $toWatch;
+ }
+ }
+ if( count( $wanted, 1 ) > count( $wanted ) ) {
+ if( count( $allWatched ) > 0 || count( $allUnwatched )
> 0 ) {
+>>>>>>> 4e55649... Watchlist grouping
$this->successMessage = $this->msg(
'watchlistedit-raw-done' )->parse();
} else {
return false;
}
+<<<<<<< HEAD
if ( count( $toWatch ) > 0 ) {
$this->successMessage .= ' ' . $this->msg(
'watchlistedit-raw-added' )
->numParams( count( $toWatch )
)->parse();
@@ -216,12 +297,28 @@
$this->successMessage .= ' ' . $this->msg(
'watchlistedit-raw-removed' )
->numParams( count( $toUnwatch )
)->parse();
$this->showTitles( $toUnwatch,
$this->successMessage );
+=======
+ if( count( $allWatched ) > 0 ) {
+ $this->successMessage .= ' ' . $this->msg(
'watchlistedit-raw-added'
+ )->numParams( count( $allWatched )
)->parse();
+ $this->showTitles( $allWatched,
$this->successMessage );
+ }
+
+ if( count( $allUnwatched ) > 0 ) {
+ $this->successMessage .= ' ' . $this->msg(
'watchlistedit-raw-removed'
+ )->numParams( count( $allUnwatched )
)->parse();
+ $this->showTitles( $allUnwatched,
$this->successMessage );
+>>>>>>> 4e55649... Watchlist grouping
}
} else {
$this->clearWatchlist();
$this->getUser()->invalidateCache();
+<<<<<<< HEAD
if ( count( $current ) > 0 ) {
+=======
+ if( count( $current, 1 ) > count( $current ) ) {
+>>>>>>> 4e55649... Watchlist grouping
$this->successMessage = $this->msg(
'watchlistedit-raw-done' )->parse();
} else {
return false;
@@ -309,31 +406,38 @@
$res = $dbr->select(
'watchlist',
array(
- 'wl_namespace', 'wl_title'
+ 'wl_group', 'wl_namespace', 'wl_title'
), array(
'wl_user' => $this->getUser()->getId(),
),
__METHOD__
);
+<<<<<<< HEAD
if ( $res->numRows() > 0 ) {
$titles = array();
+=======
+ if( $res->numRows() > 0 ) {
+>>>>>>> 4e55649... Watchlist grouping
foreach ( $res as $row ) {
$title = Title::makeTitleSafe(
$row->wl_namespace, $row->wl_title );
if ( $this->checkTitle( $title,
$row->wl_namespace, $row->wl_title )
&& !$title->isTalkPage()
) {
- $titles[] = $title;
+ $list[$row->wl_group][] =
$title->getPrefixedText();
}
}
$res->free();
+<<<<<<< HEAD
GenderCache::singleton()->doTitlesArray( $titles );
foreach ( $titles as $title ) {
$list[] = $title->getPrefixedText();
}
+=======
+>>>>>>> 4e55649... Watchlist grouping
}
$this->cleanupWatchlist();
@@ -349,12 +453,15 @@
*/
protected function getWatchlistInfo() {
$titles = array();
+ $where = array( 'wl_user' => $this->getUser()->getId() );
+ if ( $this->groupName != '' ) {
+ $where['wl_group'] = $this->wg_obj->getGroupFromName(
$this->groupName );
+ }
$dbr = wfGetDB( DB_MASTER );
-
$res = $dbr->select(
array( 'watchlist' ),
- array( 'wl_namespace', 'wl_title' ),
- array( 'wl_user' => $this->getUser()->getId() ),
+ array( 'wl_namespace', 'wl_group', 'wl_title' ),
+ $where,
__METHOD__,
array( 'ORDER BY' => array( 'wl_namespace', 'wl_title'
) )
);
@@ -364,7 +471,7 @@
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;
+ $titles[$row->wl_namespace][$row->wl_title] =
intval( $row->wl_group );
}
}
@@ -452,7 +559,7 @@
*
* @param array $titles Array of strings, or Title objects
*/
- private function watchTitles( $titles ) {
+ private function watchTitles( $titles, $group = 0 ) {
$dbw = wfGetDB( DB_MASTER );
$rows = array();
@@ -464,12 +571,14 @@
if ( $title instanceof Title ) {
$rows[] = array(
'wl_user' => $this->getUser()->getId(),
+ 'wl_group' => $group,
'wl_namespace' =>
MWNamespace::getSubject( $title->getNamespace() ),
'wl_title' => $title->getDBkey(),
'wl_notificationtimestamp' => null,
);
$rows[] = array(
'wl_user' => $this->getUser()->getId(),
+ 'wl_group' => $group,
'wl_namespace' => MWNamespace::getTalk(
$title->getNamespace() ),
'wl_title' => $title->getDBkey(),
'wl_notificationtimestamp' => null,
@@ -526,7 +635,31 @@
public function submitNormal( $data ) {
$removed = array();
+<<<<<<< HEAD
foreach ( $data as $titles ) {
+=======
+ // Regrouping submission
+ $group = intval( $data['group'] );
+ unset($data['group']);
+ if( $group > -1 ) {
+ foreach( $data as $ns => $title_strings ) {
+ $nsid = intval( str_replace( 'TitlesNs', '',
$ns ) );
+ $titles = array();
+ foreach( $title_strings as $t ) {
+ $title = Title::newFromText( $t );
+ $titles[] = $title;
+ $titles[] = $title->getTalkPage();
+ }
+ if( count( $titles ) > 0 ) {
+ $this->wg_obj->regroupTitles( $titles,
$group );
+ }
+ }
+ $this->successMessage = $this->msg(
'watchlistedit-normal-donegrouping' )->escaped();
+ return true;
+ }
+
+ foreach( $data as $titles ) {
+>>>>>>> 4e55649... Watchlist grouping
$this->unwatchTitles( $titles );
$removed = array_merge( $removed, $titles );
}
@@ -551,6 +684,7 @@
global $wgContLang;
$fields = array();
+
$count = 0;
// Allow subscribers to manipulate the list of watched pages
(or use it
@@ -564,12 +698,20 @@
foreach ( $watchlistInfo as $namespace => $pages ) {
$options = array();
+<<<<<<< HEAD
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();
+=======
+ foreach( $pages as $dbkey => $group ) {
+ $title = Title::makeTitleSafe( $namespace,
$dbkey );
+ if ( $this->checkTitle( $title, $namespace,
$dbkey ) ) {
+ $text = $this->buildRemoveLine( $title,
$group );
+
$fields['TitlesNs'.$namespace]['options'][$text] = htmlspecialchars(
$title->getPrefixedText() );
+>>>>>>> 4e55649... Watchlist grouping
$count++;
}
}
@@ -599,15 +741,38 @@
$this->toc .= Linker::tocLine(
"editwatchlist-{$data['section']}", $nsText,
$this->getLanguage()->formatNum(
++$tocLength ), 1 ) . Linker::tocLineEnd();
}
+<<<<<<< HEAD
$this->toc = Linker::tocList( $this->toc );
+=======
+ //$this->toc = Linker::tocList( $this->toc );
+>>>>>>> 4e55649... Watchlist grouping
} else {
$this->toc = false;
}
+<<<<<<< HEAD
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $this->getPageTitle() ); // Remove subpage
$form = new EditWatchlistNormalHTMLForm( $fields, $context );
+=======
+ $groups = $this->wg_obj->getGroups();
+ foreach( $groups as &$g ) {
+ $g = $this->msg( 'watchlistedit-normal-change'
)->rawParams( $g )->parse();
+ }
+
+ $gsOptions = array_merge( array( $this->msg(
'watchlistedit-normal-remove' )->escaped() => -1,
+ $this->msg(
'watchlistedit-normal-ungroup' )->escaped() => 0 ), array_flip( $groups ) );
+
+ $fields['group'] = array(
+ 'type' => 'select',
+ 'options' => $gsOptions,
+ 'label' => $this->msg( 'watchlistedit-normal-action'
)->escaped()
+ );
+
+ $form = new EditWatchlistNormalHTMLForm( $fields,
$this->getContext()->getUser()->getId() );
+ $form->setTitle( $this->getTitle() );
+>>>>>>> 4e55649... Watchlist grouping
$form->setSubmitTextMsg( 'watchlistedit-normal-submit' );
$form->setSubmitDestructive();
# Used message keys:
@@ -615,6 +780,9 @@
$form->setSubmitTooltip( 'watchlistedit-normal-submit' );
$form->setWrapperLegendMsg( 'watchlistedit-normal-legend' );
$form->addHeaderText( $this->msg(
'watchlistedit-normal-explain' )->parse() );
+ if( $this->groupName != '' ) {
+ $form->addHeaderText( '<p>' . $this->msg(
'watchlistedit-normal-onlygroup' )->rawParams( $this->groupName )->parse() .
'</p>' );
+ }
$form->setSubmitCallback( array( $this, 'submitNormal' ) );
return $form;
@@ -623,10 +791,15 @@
/**
* Build the label for a checkbox, with a link to the title, and
various additional bits
*
+<<<<<<< HEAD
* @param Title $title
+=======
+ * @param $title Title
+ * @param $group int
+>>>>>>> 4e55649... Watchlist grouping
* @return string
*/
- private function buildRemoveLine( $title ) {
+ private function buildRemoveLine( $title, $gid ) {
$link = Linker::link( $title );
$tools['talk'] = Linker::link(
@@ -650,6 +823,7 @@
);
}
+<<<<<<< HEAD
wfRunHooks(
'WatchlistEditorBuildRemoveLine',
array( &$tools, $title, $title->isRedirect(),
$this->getSkin(), &$link )
@@ -659,8 +833,20 @@
// Linker already makes class mw-redirect, so this is
redundant
$link = '<span class="watchlistredir">' . $link .
'</span>';
}
+=======
+ $groups = $this->wg_obj->getGroups( true );
+ $wgroup = $groups[$gid];
+ if( $gid == 0 ){
+ $url = 'none';
+ } else {
+ $url = $wgroup[0];
+ }
+ $wgrouplink = Linker::link( SpecialPage::getTitleFor(
'EditWatchlist', $url ), $wgroup[0] );
- return $link . " (" . $this->getLanguage()->pipeList( $tools )
. ")";
+ wfRunHooks( 'WatchlistEditorBuildRemoveLine', array( &$tools,
$title, $title->isRedirect(), $this->getSkin() ) );
+>>>>>>> 4e55649... Watchlist grouping
+
+ return $link . " (" . $this->getLanguage()->pipeList( $tools )
. ") (" . $wgrouplink . ")";
}
/**
@@ -669,6 +855,7 @@
* @return HTMLForm
*/
protected function getRawForm() {
+<<<<<<< HEAD
$titles = implode( $this->getWatchlist(), "\n" );
$fields = array(
'Titles' => array(
@@ -680,6 +867,27 @@
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $this->getPageTitle( 'raw' ) ); // Reset
subpage
$form = new HTMLForm( $fields, $context );
+=======
+ $titles = $this->getWatchlist();
+ $fields = array();
+ $groups = array( 0 => array( $this->msg(
'watchlistedit-nogroup' )->parse(), 0 ) );
+ $groups = $groups + $this->wg_obj->getGroups();
+ foreach( $groups as $gid => $g ) {
+ if( !empty( $titles[$gid] ) ) {
+ $fields['Titles_group' . $gid] = array(
+ //'type' => 'textarea',
+ 'class' => 'HTMLTextAreaField',
+ 'section' => $g[0],
+ 'cssclass' => 'mw-collapsible
mw-collapsed',
+ 'rows' => 16,
+ 'label-message' =>
'watchlistedit-raw-titles',
+ 'default' => implode( $titles[$gid],
"\n" ),
+ );
+ }
+ }
+ $form = new EditWatchlistRawHTMLForm( $fields,
$this->getContext() );
+ $form->setTitle( $this->getTitle( 'raw' ) );
+>>>>>>> 4e55649... Watchlist grouping
$form->setSubmitTextMsg( 'watchlistedit-raw-submit' );
# Used message keys: 'accesskey-watchlistedit-raw-submit',
'tooltip-watchlistedit-raw-submit'
$form->setSubmitTooltip( 'watchlistedit-raw-submit' );
@@ -750,7 +958,11 @@
'view' => array( 'Watchlist', false ),
'edit' => array( 'EditWatchlist', false ),
'raw' => array( 'EditWatchlist', 'raw' ),
+<<<<<<< HEAD
'clear' => array( 'EditWatchlist', 'clear' ),
+=======
+ 'group' => array( 'EditWatchlistGroup', false )
+>>>>>>> 4e55649... Watchlist grouping
);
foreach ( $modes as $mode => $arr ) {
@@ -786,6 +998,12 @@
}
}
+class EditWatchlistRawHTMLForm extends HTMLForm {
+ public function getLegend( $group ) {
+ return $group;
+ }
+}
+
class EditWatchlistCheckboxSeriesField extends HTMLMultiSelectField {
/**
* HTMLMultiSelectField throws validation errors if we get input data
diff --git a/includes/specials/SpecialEditWatchlistGroup.php
b/includes/specials/SpecialEditWatchlistGroup.php
new file mode 100644
index 0000000..a1d0ac4
--- /dev/null
+++ b/includes/specials/SpecialEditWatchlistGroup.php
@@ -0,0 +1,197 @@
+<?php
+/**
+ * Implements Special:EditWatchlistGroup.
+ *
+ * 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
+ */
+
+/**
+ * Provides the UI through which users can modify their watchlist groups.
+ *
+ * @ingroup SpecialPage
+ * @ingroup Watchlist
+ * @author Aaron Pramana <[email protected]>
+ */
+class SpecialEditWatchlistGroup extends UnlistedSpecialPage {
+
+ /**
+ * Holds a WatchlistGroup object
+ */
+ protected $wg_obj;
+
+ public function __construct() {
+ parent::__construct( 'EditWatchlistGroup' );
+ }
+
+ /**
+ * Main execution point.
+ */
+ public function execute($subPage) {
+ $out = $this->getOutput();
+
+ # Anons do not have a watchlist
+ if( $this->getUser()->isAnon() ) {
+ $out->setPageTitle( $this->msg( 'watchnologin' ) );
+ $llink = Linker::linkKnown(
+ SpecialPage::getTitleFor( 'Userlogin' ),
+ $this->msg( 'loginreqlink' )->escaped(),
+ array(),
+ array( 'returnto' =>
$this->getTitle()->getPrefixedText() )
+ );
+ $out->addHTML( $this->msg( 'watchlistanontext'
)->rawParams( $llink )->parse() );
+ return;
+ }
+
+ $this->wg_obj = WatchlistGroup::newFromUser( $this->getUser() );
+
+ $this->setHeaders();
+ $this->outputHeader();
+
+ $out->addSubtitle( $this->msg( 'watchlistfor2',
$this->getUser()->getName()
+ )->rawParams( SpecialEditWatchlist::buildTools( null )
) );
+
+ $form = $this->getForm();
+ if( $form->show() ) {
+ $out->addHTML( $this->successMessage );
+ $out->returnToMain();
+ }
+ }
+
+ /**
+ * Get a form for editing watchlist groups
+ *
+ * @return HTMLForm
+ */
+ protected function getForm() {
+ $fields = array();
+ $groups = $this->wg_obj->getGroups();
+
+ // fields for each of the existing groups
+ foreach( $groups as $id => $info ) {
+
+ $fields['groupaction_' . $id] = array(
+ 'type' => 'select',
+ 'options' => array( $this->msg(
'wlgroup-noaction' )->escaped() => 0,
+ $this->msg( 'wlgroup-rename'
)->escaped() => 1,
+ $this->msg( 'wlgroup-changeperm'
)->escaped() => 2,
+ $this->msg( 'wlgroup-delete'
)->escaped() => -1 ),
+ 'label' => $this->msg( 'actions' )->parse(),
+ 'section' => $info[0]
+ );
+
+ $fields['grouprename_' . $id] = array(
+ 'type' => 'text',
+ 'label' => $this->msg( 'wlgroup-renameto'
)->rawParams( $info[0] )->parse(),
+ 'size' => '15',
+ 'section' => $info[0]
+ );
+
+ $fields['groupperm_' . $id] = array(
+ 'type' => 'select',
+ 'options' => array( $this->msg(
'wlgroup-permprivate' )->parse() => 0,
+ $this->msg( 'wlgroup-permpublic'
)->parse() => 1 ),
+ 'default' => $info[1],
+ 'label' => $this->msg( 'wlgroup-perm'
)->parse(),
+ 'section' => $info[0]
+ );
+ }
+
+ // field used to add a new group
+ $fields['groupnew'] = array(
+ 'type' => 'text',
+ 'label' => $this->msg( 'wlgroup-createnew' ),
+ 'size' => '15'
+ );
+
+ $form = new EditWatchlistGroupHTMLForm( $fields,
$this->getContext() );
+ $form->setTitle( $this->getTitle() )
+ ->setSubmitTextMsg( 'wlgroup-submit' )
+ ->setSubmitTooltip('wlgroup-submit')
+ ->setWrapperLegendMsg( 'wlgroup-legend' )
+ ->addHeaderText( $this->msg( 'wlgroup-explain'
)->parse() )
+ ->setSubmitCallback( array( $this, 'submit' ) )
+ ;
+
+ return $form;
+ }
+
+ /**
+ * The callback function for the watchlist group editing form
+ *
+ * @param $data array
+ */
+ public function submit( $data ) {
+ $wg =
WatchlistGroup::newFromUser($this->getContext()->getUser());
+ $status = true;
+ // create a new group if requested
+ if( $data['groupnew'] != '' ) {
+ // for now, group names are limited to a-z, 0-9 -
discuss tech. restrictions
+ $name = WatchlistGroup::checkValidGroupName(
$data['groupnew'] );
+ if( $name ) {
+ $status = $wg->createGroup( $name );
+ } else {
+ $status = false;
+ }
+ }
+
+ foreach( $data as $key => $val ) {
+ // rename, change permissions, or delete groups if
requested
+ if( substr( $key, 0, 12 ) == 'groupaction_' && intval(
$val ) != 0 ) {
+ $group = intval( substr( $key, 12 ) );
+ if( intval( $val ) === 1 ) {
+ // rename
+ $name =
WatchlistGroup::checkValidGroupName( $data['grouprename_' . $group] );
+ if($name) {
+ $status = $wg->renameGroup(
$group, $name );
+ } else {
+ $status = false;
+ }
+ }
+ if( intval( $val ) === 2 ) {
+ // change perms
+ $permval = intval( $data['groupperm_' .
$group] );
+ if( $permval === 0 || $permval === 1 ) {
+ $status = $wg->changePerm(
$group, $permval );
+ } else {
+ $status = false;
+ }
+ } elseif( intval( $val ) === -1 ) {
+ // delete
+ $status = $wg->deleteGroup( $group );
+ }
+ }
+ }
+ if( $status ) {
+ $this->successMessage = $this->msg( 'wlgroup-success'
)->escaped();
+ return true;
+ }
+ return false;
+ }
+}
+
+class EditWatchlistGroupHTMLForm extends HTMLForm {
+
+ protected $mSubSectionBeforeFields = false;
+
+ public function getLegend( $group ) {
+ return $group;
+ }
+
+}
diff --git a/includes/specials/SpecialWatchlist.php
b/includes/specials/SpecialWatchlist.php
index 421840f..af49f14 100644
--- a/includes/specials/SpecialWatchlist.php
+++ b/includes/specials/SpecialWatchlist.php
@@ -44,6 +44,7 @@
$output = $this->getOutput();
$request = $this->getRequest();
+<<<<<<< HEAD
$mode = SpecialEditWatchlist::getMode( $request, $subpage );
if ( $mode !== false ) {
if ( $mode === SpecialEditWatchlist::EDIT_RAW ) {
@@ -53,6 +54,21 @@
} else {
$title = SpecialPage::getTitleFor(
'EditWatchlist' );
}
+=======
+ # Anons don't get a watchlist - but let them through for public
watchlists
+ if( $user->isAnon() ) {
+ $output->setPageTitle( $this->msg( 'watchnologin' ) );
+ $output->setRobotPolicy( 'noindex,nofollow' );
+ $llink = Linker::linkKnown(
+ SpecialPage::getTitleFor( 'Userlogin' ),
+ $this->msg( 'loginreqlink' )->escaped(),
+ array(),
+ array( 'returnto' =>
$this->getTitle()->getPrefixedText() )
+ );
+ $output->addHTML( $this->msg( 'watchlistanontext'
)->rawParams( $llink )->parse() );
+ return;
+ }
+>>>>>>> 4e55649... Watchlist grouping
$output->redirect( $title->getLocalURL() );
@@ -78,6 +94,7 @@
parent::execute( $subpage );
}
+<<<<<<< HEAD
/**
* Return an array of subpages beginning with $search that this special
page will accept.
*
@@ -95,9 +112,29 @@
'edit',
'raw',
)
+=======
+ // @TODO: use FormOptions!
+ $defaults = array(
+ /* float */ 'days' => floatval( $user->getOption(
'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */
+ /* bool */ 'hideMinor' => (int)$user->getBoolOption(
'watchlisthideminor' ),
+ /* bool */ 'hideBots' => (int)$user->getBoolOption(
'watchlisthidebots' ),
+ /* bool */ 'hideAnons' => (int)$user->getBoolOption(
'watchlisthideanons' ),
+ /* bool */ 'hideLiu' => (int)$user->getBoolOption(
'watchlisthideliu' ),
+ /* bool */ 'hidePatrolled' => (int)$user->getBoolOption(
'watchlisthidepatrolled' ),
+ /* bool */ 'hideOwn' => (int)$user->getBoolOption(
'watchlisthideown' ),
+ /* bool */ 'extended' => (int)$user->getBoolOption(
'extendwatchlist' ),
+ /* ? */ 'namespace' => '', //means all
+ /* ? */ 'invert' => false,
+ /* bool */ 'associated' => false,
+ /* int */ 'user_id' => $user->getId(),
+ /* string */ 'user' => $user->getName(),
+ /* int */ 'group' => null,
+ /* string */ 'group_name' => null,
+>>>>>>> 4e55649... Watchlist grouping
);
}
+<<<<<<< HEAD
/**
* Get a FormOptions object containing the default options
*
@@ -106,6 +143,48 @@
public function getDefaultOptions() {
$opts = parent::getDefaultOptions();
$user = $this->getUser();
+=======
+ # Extract variables from the request, falling back to user
preferences or
+ # other default values if these don't exist
+ $values = array();
+ $values['user'] = $request->getText( 'user', $defaults['user']
);
+ if( empty( $values['user'] ) ) {
+ $values['user'] = $user->getName();
+ }
+
+ $values['group_name'] = $request->getText( 'group',
$defaults['group'] );
+
+ // Look for subpage parameters passed in the URL
+ $subpages = explode( '/', $par );
+
+ if( !empty( $subpages[0] ) ) {
+ $values['user'] = $subpages[0];
+ }
+ if( isset( $subpages[1] ) && !empty( $subpages[1] ) ) {
+ $values['group_name'] = $subpages[1];
+ }
+
+ $user_obj = User::newFromName( $values['user'] );
+ $wg_obj = WatchlistGroup::newFromUser( $user_obj );
+ $values['user_id'] = $user_obj->getId();
+ $values['group'] = $wg_obj->getGroupFromName(
$values['group_name'] );
+ // for non existing group set the name to default
+ if ( !$values['group'] ) {
+ $values['group_name'] = $defaults['group'];
+ }
+ $values['days'] = $request->getVal( 'days',
$prefs['days'] );
+ $values['hideMinor'] = (int)$request->getBool( 'hideMinor',
$prefs['hideminor'] );
+ $values['hideBots'] = (int)$request->getBool( 'hideBots' ,
$prefs['hidebots'] );
+ $values['hideAnons'] = (int)$request->getBool( 'hideAnons',
$prefs['hideanons'] );
+ $values['hideLiu'] = (int)$request->getBool( 'hideLiu' ,
$prefs['hideliu'] );
+ $values['hideOwn'] = (int)$request->getBool( 'hideOwn' ,
$prefs['hideown'] );
+ $values['hidePatrolled'] = (int)$request->getBool(
'hidePatrolled', $prefs['hidepatrolled'] );
+ $values['extended'] = (int)$request->getBool( 'extended',
$defaults['extended'] );
+
+ foreach( $this->customFilters as $key => $params ) {
+ $values[$key] = (int)$request->getBool( $key );
+ }
+>>>>>>> 4e55649... Watchlist grouping
$opts->add( 'days', $user->getOption( 'watchlistdays' ),
FormOptions::FLOAT );
@@ -121,6 +200,7 @@
return $opts;
}
+<<<<<<< HEAD
/**
* Get custom show/hide filters
*
@@ -130,6 +210,16 @@
if ( $this->customFilters === null ) {
$this->customFilters = parent::getCustomFilters();
wfRunHooks( 'SpecialWatchlistFilters', array( $this,
&$this->customFilters ), '1.23' );
+=======
+ // Backup conditions to restrict access
+ $conds[] = 'wl_group = 0 OR wg_perm = 1 OR (wg_perm = 0 AND
wg_user = ' . $user->getId() . ')';
+ if( intval( $values['group'] ) > 0 || $values['group'] === 0 ) {
+ $conds[] = 'wl_group = '. intval( $values['group'] );
+ }
+
+ if( $values['days'] > 0 ) {
+ $conds[] = 'rc_timestamp > ' . $dbr->addQuotes(
$dbr->timestamp( time() - intval( $values['days'] * 86400 ) ) );
+>>>>>>> 4e55649... Watchlist grouping
}
return $this->customFilters;
@@ -221,21 +311,79 @@
$usePage = true;
}
+<<<<<<< HEAD
$tables = array( 'recentchanges', 'watchlist' );
$fields = RecentChange::selectFields();
$query_options = array( 'ORDER BY' => 'rc_timestamp DESC' );
+=======
+ # Show a message about slave lag, if applicable
+ $lag = wfGetLB()->safeGetLag( $dbr );
+ if( $lag > 0 ) {
+ $output->showLagWarning( $lag );
+ }
+
+ # ADD USER WATCHLIST/GROUP SELECTION
+ $fields['user'] = array(
+ 'type' => 'text',
+ 'label' => $this->msg( 'watchlist-user' )->escaped(),
+ 'value' => $values['user_id']
+ );
+ $fields['group'] = array(
+ 'type' => 'text',
+ 'label' => $this->msg( 'watchlist-group' )->escaped(),
+ 'value' => $values['group']
+ );
+
+ # Create output form
+ $form = Xml::fieldset( $this->msg( 'watchlist-options'
)->text(), false, array( 'id' => 'mw-watchlist-options' ) );
+
+ # Show watchlist header
+ $form .= $this->msg( 'watchlist-details' )->numParams( $nitems
)->parse() . "\n";
+
+ if( $user->getOption( 'enotifwatchlistpages' ) &&
$wgEnotifWatchlist) {
+ $form .= $this->msg( 'wlheader-enotif'
)->parseAsBlock() . "\n";
+ }
+ if( $wgShowUpdatedMarker ) {
+ $form .= Xml::openElement( 'form', array( 'method' =>
'post',
+ 'action' =>
$this->getTitle()->getLocalUrl(),
+ 'id' =>
'mw-watchlist-resetbutton' ) ) . "\n" .
+ $this->msg( 'wlheader-showupdated'
)->parse() .
+ Xml::submitButton( $this->msg(
'enotif_reset' )->text(), array( 'name' => 'dummy' ) ) . "\n" .
+ Html::hidden( 'reset', 'all' ) . "\n";
+ foreach ( $nondefaults as $key =>
$value ) {
+ $form .= Html::hidden( $key,
$value ) . "\n";
+ }
+ $form .= Xml::closeElement( 'form' ) .
"\n";
+ }
+ $form .= "<hr />\n";
+
+ $tables = array( 'recentchanges', 'watchlist',
'watchlist_groups' );
+ $fields = array_merge( RecentChange::selectFields(), array(
$dbr->tableName( 'watchlist_groups' ) . '.*' ) );
+
+>>>>>>> 4e55649... Watchlist grouping
$join_conds = array(
'watchlist' => array(
'INNER JOIN',
array(
- 'wl_user' => $user->getId(),
+ 'wl_user' => $values['user_id'],
'wl_namespace=rc_namespace',
'wl_title=rc_title'
),
),
+ 'watchlist_groups' => array(
+ 'LEFT JOIN',
+ array(
+ 'wg_id=wl_group'
+ ),
+ )
);
+<<<<<<< HEAD
if ( $this->getConfig()->get( 'ShowUpdatedMarker' ) ) {
+=======
+ $options = array( 'ORDER BY' => 'rc_timestamp DESC' );
+ if( $wgShowUpdatedMarker ) {
+>>>>>>> 4e55649... Watchlist grouping
$fields[] = 'wl_notificationtimestamp';
}
if ( $limitWatchlist ) {
@@ -481,13 +629,68 @@
$opts['associated'],
array( 'title' => $this->msg(
'tooltip-namespace_association' )->text() )
) . ' ';
+
+ $form .= '</p><p>';
+ $form .= Xml::openElement( 'label', array( 'for' =>
'user_search' ) ) . $this->msg( 'watchlist-user' )->escaped() .
Xml::closeElement( 'label' );
+ $form .= Html::input( 'user', $values['user'], 'text', array(
'id' => 'user_search' ) ) . ' ';
+ $form .= Xml::openElement( 'label', array( 'for' =>
'group_search' ) ) . $this->msg( 'watchlist-group' )->escaped() .
Xml::closeElement( 'label' );
+ $form .= Html::input( 'group', $values['group_name'], 'text',
array( 'id' => 'group_search' ) ) . ' ';
+
$form .= Xml::submitButton( $this->msg( 'allpagessubmit'
)->text() ) . "</p>\n";
- foreach ( $hiddenFields as $key => $value ) {
- $form .= Html::hidden( $key, $value ) . "\n";
+ $form .= Html::hidden( 'days', $values['days'] ) . "\n";
+ foreach ( $filters as $key => $msg ) {
+ if ( $values[$key] ) {
+ $form .= Html::hidden( $key, 1 ) . "\n";
+ }
}
$form .= Xml::closeElement( 'fieldset' ) . "\n";
+<<<<<<< HEAD
$form .= Xml::closeElement( 'form' ) . "\n";
$this->getOutput()->addHTML( $form );
+=======
+
+ $output->addHTML( $form );
+
+ if( $values['user_id'] == 0 ) {
+ $output->addWikiMsg( 'wlfilter-nouser' );
+ return;
+ } else {
+ // Check permissions
+ $hasPerm = ( $values['user_id'] == $user->getId() ) //
if the user is self
+ || $wg_obj->isGroup( $values['group'], true );
+ // If the user doesn't have permission or there's
nothing to show, stop here
+ if( !$hasPerm ) {
+ $output->addWikiMsg( 'wlfilter-permdenied' );
+ return;
+ }
+ // No changes for the given criteria
+ if( $numRows == 0 ) {
+ $output->addWikiMsg( 'watchnochange' );
+ return;
+ }
+ }
+
+ // The filter message might be repetitive since this info is
already available in the filter form fields.
+ /*$filter_status = '<p>';
+ if( isset( $values['user'] ) ) {
+ $filter_status .= $this->msg( 'wlfilter' )->rawParams(
$values['user'] )->escaped();
+ }
+ if( isset( $values['group_name'] ) ) {
+ $filter_status .= ' ' . $this->msg( 'wlfilter-group'
)->rawParams( $values['group_name'] )->escaped();
+ }
+ $filter_status .= '</p>';
+ $output->addHTML( $filter_status );*/
+ /* End bottom header */
+
+ /* Do link batch query */
+ $linkBatch = new LinkBatch;
+ foreach ( $res as $row ) {
+ $userNameUnderscored = str_replace( ' ', '_',
$row->rc_user_text );
+ if ( $row->rc_user != 0 ) {
+ $linkBatch->add( NS_USER, $userNameUnderscored
);
+ }
+ $linkBatch->add( NS_USER_TALK, $userNameUnderscored );
+>>>>>>> 4e55649... Watchlist grouping
$this->setBottomText( $opts );
}
diff --git a/maintenance/archives/patch-watchlist_groups.sql
b/maintenance/archives/patch-watchlist_groups.sql
new file mode 100644
index 0000000..2a63a04
--- /dev/null
+++ b/maintenance/archives/patch-watchlist_groups.sql
@@ -0,0 +1,13 @@
+CREATE TABLE /*_*/watchlist_groups (
+ wg_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ wg_name varchar(255) binary NOT NULL,
+
+ -- Key to user.user_id for owner of the watchlist
+ wg_user int unsigned NOT NULL,
+
+ -- Permissions bool
+ wg_perm tinyint NOT NULL default 0
+
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/wg_user ON /*_*/watchlist_groups (wg_id, wg_name,
wg_user);
\ No newline at end of file
diff --git a/maintenance/archives/patch-watchlist_groups_newfield.sql
b/maintenance/archives/patch-watchlist_groups_newfield.sql
new file mode 100644
index 0000000..1e663df
--- /dev/null
+++ b/maintenance/archives/patch-watchlist_groups_newfield.sql
@@ -0,0 +1,4 @@
+ALTER TABLE /*$wgDBprefix*/watchlist ADD wl_group int unsigned NOT NULL
default 0;
+
+DROP INDEX /*i*/wl_user ON /*_*/watchlist;
+CREATE UNIQUE INDEX /*i*/wl_user ON /*_*/watchlist (wl_user, wl_group,
wl_namespace, wl_title);
\ No newline at end of file
diff --git a/maintenance/postgres/archives/patch-watchlist_groups.sql
b/maintenance/postgres/archives/patch-watchlist_groups.sql
new file mode 100644
index 0000000..bf68648
--- /dev/null
+++ b/maintenance/postgres/archives/patch-watchlist_groups.sql
@@ -0,0 +1,9 @@
+CREATE SEQUENCE 'watchlist_groups_wg_id_seq';
+CREATE TABLE watchlist_groups (
+ wg_id INTEGER NOT NULL PRIMARY KEY DEFAULT
nextval('watchlist_groups_wg_id_seq'),
+ wg_name TEXT NOT NULL,
+ wg_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE
DEFERRABLE INITIALLY DEFERRED,
+ wg_perm SMALLINT NOT NULL DEFAULT 0,
+);
+
+CREATE UNIQUE INDEX wg_id_name_user_perm ON watchlist (wg_id, wg_name,
wg_user, wg_perm);
\ No newline at end of file
diff --git a/maintenance/postgres/archives/patch-watchlist_groups_newfield.sql
b/maintenance/postgres/archives/patch-watchlist_groups_newfield.sql
new file mode 100644
index 0000000..67637c0
--- /dev/null
+++ b/maintenance/postgres/archives/patch-watchlist_groups_newfield.sql
@@ -0,0 +1,4 @@
+ALTER TABLE watchlist ADD COLUMN wl_group INTEGER NOT NULL DEFAULT 0;
+
+DROP INDEX wl_group_user_namespace_title;
+CREATE UNIQUE INDEX wl_group_user_namespace_title ON watchlist (wl_user,
wl_group, wl_namespace, wl_title);
\ No newline at end of file
diff --git a/maintenance/postgres/tables.sql b/maintenance/postgres/tables.sql
index 400050e..dc39de7 100644
--- a/maintenance/postgres/tables.sql
+++ b/maintenance/postgres/tables.sql
@@ -446,15 +446,26 @@
CREATE TABLE watchlist (
wl_user INTEGER NOT NULL REFERENCES mwuser(user_id)
ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
+ wl_group INTEGER NOT NULL DEFAULT 0,
wl_namespace SMALLINT NOT NULL DEFAULT 0,
wl_title TEXT NOT NULL,
wl_notificationtimestamp TIMESTAMPTZ
);
-CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace,
wl_title, wl_user);
+CREATE UNIQUE INDEX wl_group_user_namespace_title ON watchlist (wl_user,
wl_group, wl_namespace, wl_title);
CREATE INDEX wl_user ON watchlist (wl_user);
CREATE INDEX wl_user_notificationtimestamp ON watchlist (wl_user,
wl_notificationtimestamp);
+CREATE SEQUENCE 'watchlist_groups_wg_id_seq';
+CREATE TABLE watchlist_groups (
+ wg_id INTEGER NOT NULL PRIMARY KEY DEFAULT
nextval('watchlist_groups_wg_id_seq'),
+ wg_name TEXT NOT NULL,
+ wg_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE
DEFERRABLE INITIALLY DEFERRED,
+ wg_perm SMALLINT NOT NULL DEFAULT 0,
+);
+CREATE UNIQUE INDEX wg_id_name_user_perm ON watchlist (wg_id, wg_name,
wg_user, wg_perm);
+
+
CREATE TABLE interwiki (
iw_prefix TEXT NOT NULL UNIQUE,
iw_url TEXT NOT NULL,
diff --git a/maintenance/sqlite/archives/patch-watchlist_groups_newfield.sql
b/maintenance/sqlite/archives/patch-watchlist_groups_newfield.sql
new file mode 100644
index 0000000..1a9aea0
--- /dev/null
+++ b/maintenance/sqlite/archives/patch-watchlist_groups_newfield.sql
@@ -0,0 +1,4 @@
+ALTER TABLE /*$wgDBprefix*/watchlist ADD wl_group int unsigned NOT NULL
default 0;
+
+DROP INDEX wl_user;
+CREATE UNIQUE INDEX wl_user ON /*_*/watchlist (wl_user, wl_group,
wl_namespace, wl_title);
\ No newline at end of file
diff --git a/maintenance/tables.sql b/maintenance/tables.sql
index 0228684..7b28594 100644
--- a/maintenance/tables.sql
+++ b/maintenance/tables.sql
@@ -1127,6 +1127,9 @@
-- Key to user.user_id
wl_user int unsigned NOT NULL,
+ -- Key to watchlist_groups.wg_id
+ wl_group int unsigned NOT NULL default 0,
+
-- Key to page_namespace/page_title
-- Note that users may watch pages which do not exist yet,
-- or existed in the past but have been deleted.
@@ -1140,11 +1143,25 @@
) /*$wgDBTableOptions*/;
-CREATE UNIQUE INDEX /*i*/wl_user ON /*_*/watchlist (wl_user, wl_namespace,
wl_title);
+CREATE UNIQUE INDEX /*i*/wl_user ON /*_*/watchlist (wl_user, wl_group,
wl_namespace, wl_title);
CREATE INDEX /*i*/namespace_title ON /*_*/watchlist (wl_namespace, wl_title);
CREATE INDEX /*i*/wl_user_notificationtimestamp ON /*_*/watchlist (wl_user,
wl_notificationtimestamp);
+CREATE TABLE /*_*/watchlist_groups (
+ wg_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ wg_name varchar(255) binary NOT NULL,
+
+ -- Key to user.user_id for owner of the watchlist
+ wg_user int unsigned NOT NULL,
+
+ -- Permissions bool
+ wg_perm tinyint NOT NULL default 0
+
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/wg_user ON /*_*/watchlist_groups (wg_id, wg_name,
wg_user, wg_perm);
+
--
-- When using the default MySQL search backend, page titles
-- and text are munged to strip markup, do Unicode case folding,
--
To view, visit https://gerrit.wikimedia.org/r/170588
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: If87504366263964aafc5826be9c1752513d18c8f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Reedy <[email protected]>
Gerrit-Reviewer: Aaron Pramana <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits