Index: trunk/phase3/maintenance/language/messages.inc |
— | — | @@ -1080,10 +1080,6 @@ |
1081 | 1081 | 'nowatchlist', |
1082 | 1082 | 'watchlistanontext', |
1083 | 1083 | 'watchlistcount', |
1084 | | - 'clearwatchlist', |
1085 | | - 'watchlistcleartext', |
1086 | | - 'watchlistclearbutton', |
1087 | | - 'watchlistcleardone', |
1088 | 1084 | 'watchnologin', |
1089 | 1085 | 'watchnologintext', |
1090 | 1086 | 'addedwatch', |
— | — | @@ -1101,11 +1097,7 @@ |
1102 | 1098 | 'wlheader-showupdated', |
1103 | 1099 | 'watchmethod-recent', |
1104 | 1100 | 'watchmethod-list', |
1105 | | - 'removechecked', |
1106 | 1101 | 'watchlistcontains', |
1107 | | - 'watcheditlist', |
1108 | | - 'removingchecked', |
1109 | | - 'couldntremove', |
1110 | 1102 | 'iteminvalidname', |
1111 | 1103 | 'wlnote', |
1112 | 1104 | 'wlshowlast', |
— | — | @@ -1116,7 +1108,6 @@ |
1117 | 1109 | 'watchlist-hide-own', |
1118 | 1110 | 'watchlist-show-minor', |
1119 | 1111 | 'watchlist-hide-minor', |
1120 | | - 'wldone', |
1121 | 1112 | ), |
1122 | 1113 | 'watching' => array( |
1123 | 1114 | 'watching', |
— | — | @@ -2131,6 +2122,26 @@ |
2132 | 2123 | 'lag-warn-normal', |
2133 | 2124 | 'lag-warn-high', |
2134 | 2125 | ), |
| 2126 | + 'watchlisteditor' => array( |
| 2127 | + 'watchlistedit-numitems', |
| 2128 | + 'watchlistedit-noitems', |
| 2129 | + 'watchlistedit-clear-title', |
| 2130 | + 'watchlistedit-clear-legend', |
| 2131 | + 'watchlistedit-clear-confirm', |
| 2132 | + 'watchlistedit-clear-submit', |
| 2133 | + 'watchlistedit-clear-done', |
| 2134 | + 'watchlistedit-normal-title', |
| 2135 | + 'watchlistedit-normal-legend', |
| 2136 | + 'watchlistedit-normal-explain', |
| 2137 | + 'watchlistedit-normal-submit', |
| 2138 | + 'watchlistedit-normal-done', |
| 2139 | + 'watchlistedit-raw-title', |
| 2140 | + 'watchlistedit-raw-legend', |
| 2141 | + 'watchlistedit-raw-explain', |
| 2142 | + 'watchlistedit-raw-titles', |
| 2143 | + 'watchlistedit-raw-submit', |
| 2144 | + 'watchlistedit-raw-done', |
| 2145 | + ), |
2135 | 2146 | ); |
2136 | 2147 | /** Comments for each block */ |
2137 | 2148 | $wgBlockComments = array( |
— | — | @@ -2297,6 +2308,7 @@ |
2298 | 2309 | 'sizeunits' => 'Size units', |
2299 | 2310 | 'livepreview' => 'Live preview', |
2300 | 2311 | 'lagwarning' => 'Friendlier slave lag warnings', |
| 2312 | + 'watchlisteditor' => 'Watchlist editor', |
2301 | 2313 | ); |
2302 | 2314 | |
2303 | 2315 | /** Short comments for standalone messages */ |
Index: trunk/phase3/includes/WatchlistEditor.php |
— | — | @@ -0,0 +1,393 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Provides the UI through which users can perform editing |
| 6 | + * operations on their watchlist |
| 7 | + * |
| 8 | + * @addtogroup Watchlist |
| 9 | + * @author Rob Church <robchur@gmail.com> |
| 10 | + */ |
| 11 | +class WatchlistEditor { |
| 12 | + |
| 13 | + /** |
| 14 | + * Editing modes |
| 15 | + */ |
| 16 | + const EDIT_CLEAR = 1; |
| 17 | + const EDIT_RAW = 2; |
| 18 | + const EDIT_NORMAL = 3; |
| 19 | + |
| 20 | + /** |
| 21 | + * Main execution point |
| 22 | + * |
| 23 | + * @param User $user |
| 24 | + * @param OutputPage $output |
| 25 | + * @param WebRequest $request |
| 26 | + * @param int $mode |
| 27 | + */ |
| 28 | + public function execute( $user, $output, $request, $mode ) { |
| 29 | + if( wfReadOnly() ) { |
| 30 | + $output->readOnlyPage(); |
| 31 | + return; |
| 32 | + } |
| 33 | + switch( $mode ) { |
| 34 | + case self::EDIT_CLEAR: |
| 35 | + $output->setPageTitle( wfMsg( 'watchlistedit-clear-title' ) ); |
| 36 | + if( $request->wasPosted() && $this->checkToken( $request, $user ) ) { |
| 37 | + $this->clearWatchlist( $user ); |
| 38 | + $user->invalidateCache(); |
| 39 | + $output->addHtml( wfMsgExt( 'watchlistedit-clear-done', 'parse' ) ); |
| 40 | + } else { |
| 41 | + $this->showClearForm( $output, $user ); |
| 42 | + } |
| 43 | + break; |
| 44 | + case self::EDIT_RAW: |
| 45 | + $output->setPageTitle( wfMsg( 'watchlistedit-raw-title' ) ); |
| 46 | + if( $request->wasPosted() && $this->checkToken( $request, $user ) ) { |
| 47 | + $titles = $this->extractTitles( $request->getText( 'titles' ) ); |
| 48 | + $this->clearWatchlist( $user ); |
| 49 | + $this->watchTitles( $titles, $user ); |
| 50 | + $user->invalidateCache(); |
| 51 | + $output->addHtml( wfMsgExt( 'watchlistedit-raw-done', 'parse' ) ); |
| 52 | + } |
| 53 | + $this->showRawForm( $output, $user ); |
| 54 | + break; |
| 55 | + case self::EDIT_NORMAL: |
| 56 | + $output->setPageTitle( wfMsg( 'watchlistedit-normal-title' ) ); |
| 57 | + if( $request->wasPosted() && $this->checkToken( $request, $user ) ) { |
| 58 | + $titles = $this->extractTitles( $request->getArray( 'titles' ) ); |
| 59 | + $this->unwatchTitles( $titles, $user ); |
| 60 | + $user->invalidateCache(); |
| 61 | + $output->addHtml( wfMsgExt( 'watchlistedit-normal-done', 'parse', |
| 62 | + $GLOBALS['wgLang']->formatNum( count( $titles ) ) ) ); |
| 63 | + $this->showTitles( $titles, $output, $user->getSkin() ); |
| 64 | + } |
| 65 | + $this->showNormalForm( $output, $user ); |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + /** |
| 70 | + * Check the edit token from a form submission |
| 71 | + * |
| 72 | + * @param WebRequest $request |
| 73 | + * @param User $user |
| 74 | + * @return bool |
| 75 | + */ |
| 76 | + private function checkToken( $request, $user ) { |
| 77 | + return $user->matchEditToken( $request->getVal( 'token' ), 'watchlistedit' ); |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * Extract a list of titles from a text list; if we're given |
| 82 | + * an array, convert each item into a Title |
| 83 | + * |
| 84 | + * @param mixed $list |
| 85 | + * @return array |
| 86 | + */ |
| 87 | + private function extractTitles( $list ) { |
| 88 | + if( !is_array( $list ) ) { |
| 89 | + $list = explode( "\n", $list ); |
| 90 | + if( !is_array( $list ) ) |
| 91 | + return array(); |
| 92 | + } |
| 93 | + for( $i = 0; $i < count( $list ); $i++ ) { |
| 94 | + $list[$i] = Title::newFromText( $list[$i] ); |
| 95 | + if( !$list[$i] instanceof Title ) |
| 96 | + unset( $list[$i] ); |
| 97 | + } |
| 98 | + return $list; |
| 99 | + } |
| 100 | + |
| 101 | + /** |
| 102 | + * Print out a list of linked titles |
| 103 | + * |
| 104 | + * @param array $titles |
| 105 | + * @param OutputPage $output |
| 106 | + * @param Skin $skin |
| 107 | + */ |
| 108 | + private function showTitles( $titles, $output, $skin ) { |
| 109 | + $talk = htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( NS_TALK ) ); |
| 110 | + // Do a batch existence check |
| 111 | + $batch = new LinkBatch(); |
| 112 | + foreach( $titles as $title ) { |
| 113 | + $batch->addObj( $title ); |
| 114 | + $batch->addObj( $title->getTalkPage() ); |
| 115 | + } |
| 116 | + $batch->execute(); |
| 117 | + // Print out the list |
| 118 | + $output->addHtml( "<ul>\n" ); |
| 119 | + foreach( $titles as $title ) |
| 120 | + $output->addHtml( "<li>" . $skin->makeLinkObj( $title ) |
| 121 | + . ' (' . $skin->makeLinkObj( $title->getTalkPage(), $talk ) . ")</li>\n" ); |
| 122 | + $output->addHtml( "</ul>\n" ); |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Count the number of titles on a user's watchlist, excluding talk pages |
| 127 | + * |
| 128 | + * @param User $user |
| 129 | + * @return int |
| 130 | + */ |
| 131 | + private function countWatchlist( $user ) { |
| 132 | + $dbr = wfGetDB( DB_SLAVE ); |
| 133 | + $res = $dbr->select( 'watchlist', 'COUNT(*) AS count', array( 'wl_user' => $user->getId() ), __METHOD__ ); |
| 134 | + $row = $dbr->fetchObject( $res ); |
| 135 | + return ceil( $row->count / 2 ); // Paranoia |
| 136 | + } |
| 137 | + |
| 138 | + /** |
| 139 | + * Get a list of titles on a user's watchlist, excluding talk pages, |
| 140 | + * and return as a two-dimensional array with namespace, title and |
| 141 | + * redirect status |
| 142 | + * |
| 143 | + * @param User $user |
| 144 | + * @return array |
| 145 | + */ |
| 146 | + private function getWatchlist( $user ) { |
| 147 | + $titles = array(); |
| 148 | + $dbr = wfGetDB( DB_SLAVE ); |
| 149 | + $uid = intval( $user->getId() ); |
| 150 | + list( $watchlist, $page ) = $dbr->tableNamesN( 'watchlist', 'page' ); |
| 151 | + $sql = "SELECT wl_namespace, wl_title, page_id, page_is_redirect |
| 152 | + FROM {$watchlist} LEFT JOIN {$page} ON ( wl_namespace = page_namespace |
| 153 | + AND wl_title = page_title ) WHERE wl_user = {$uid}"; |
| 154 | + $res = $dbr->query( $sql, __METHOD__ ); |
| 155 | + if( $res && $dbr->numRows( $res ) > 0 ) { |
| 156 | + $cache = LinkCache::singleton(); |
| 157 | + while( $row = $dbr->fetchObject( $res ) ) { |
| 158 | + $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title ); |
| 159 | + if( $title instanceof Title ) { |
| 160 | + // Update the link cache while we're at it |
| 161 | + if( $row->page_id ) { |
| 162 | + $cache->addGoodLinkObj( $row->page_id, $title ); |
| 163 | + } else { |
| 164 | + $cache->addBadLinkObj( $title ); |
| 165 | + } |
| 166 | + // Ignore non-talk |
| 167 | + if( !$title->isTalkPage() ) |
| 168 | + $titles[$row->wl_namespace][$row->wl_title] = $row->page_is_redirect; |
| 169 | + } |
| 170 | + } |
| 171 | + } |
| 172 | + return $titles; |
| 173 | + } |
| 174 | + |
| 175 | + /** |
| 176 | + * Show a message indicating the number of items on the user's watchlist, |
| 177 | + * and return this count for additional checking |
| 178 | + * |
| 179 | + * @param OutputPage $output |
| 180 | + * @param User $user |
| 181 | + * @return int |
| 182 | + */ |
| 183 | + private function showItemCount( $output, $user ) { |
| 184 | + if( ( $count = $this->countWatchlist( $user ) ) > 0 ) { |
| 185 | + $output->addHtml( wfMsgExt( 'watchlistedit-numitems', 'parse', |
| 186 | + $GLOBALS['wgLang']->formatNum( $count ) ) ); |
| 187 | + } else { |
| 188 | + $output->addHtml( wfMsgExt( 'watchlistedit-noitems', 'parse' ) ); |
| 189 | + } |
| 190 | + return $count; |
| 191 | + } |
| 192 | + |
| 193 | + /** |
| 194 | + * Remove all titles from a user's watchlist |
| 195 | + * |
| 196 | + * @param User $user |
| 197 | + */ |
| 198 | + private function clearWatchlist( $user ) { |
| 199 | + $dbw = wfGetDB( DB_MASTER ); |
| 200 | + $dbw->delete( 'watchlist', array( 'wl_user' => $user->getId() ), __METHOD__ ); |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * Add a list of titles to a user's watchlist |
| 205 | + * |
| 206 | + * @param array $titles |
| 207 | + * @param User $user |
| 208 | + */ |
| 209 | + private function watchTitles( $titles, $user ) { |
| 210 | + $dbw = wfGetDB( DB_MASTER ); |
| 211 | + $rows = array(); |
| 212 | + foreach( $titles as $title ) { |
| 213 | + $rows[] = array( |
| 214 | + 'wl_user' => $user->getId(), |
| 215 | + 'wl_namespace' => ( $title->getNamespace() & ~1 ), |
| 216 | + 'wl_title' => $title->getDBkey(), |
| 217 | + 'wl_notificationtimestamp' => null, |
| 218 | + ); |
| 219 | + $rows[] = array( |
| 220 | + 'wl_user' => $user->getId(), |
| 221 | + 'wl_namespace' => ( $title->getNamespace() | 1 ), |
| 222 | + 'wl_title' => $title->getDBkey(), |
| 223 | + 'wl_notificationtimestamp' => null, |
| 224 | + ); |
| 225 | + } |
| 226 | + $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' ); |
| 227 | + } |
| 228 | + |
| 229 | + /** |
| 230 | + * Remove a list of titles from a user's watchlist |
| 231 | + * |
| 232 | + * @param array $titles |
| 233 | + * @param User $user |
| 234 | + */ |
| 235 | + private function unwatchTitles( $titles, $user ) { |
| 236 | + $dbw = wfGetDB( DB_MASTER ); |
| 237 | + foreach( $titles as $title ) { |
| 238 | + $dbw->delete( |
| 239 | + 'watchlist', |
| 240 | + array( |
| 241 | + 'wl_user' => $user->getId(), |
| 242 | + 'wl_namespace' => ( $title->getNamespace() & ~1 ), |
| 243 | + 'wl_title' => $title->getDBkey(), |
| 244 | + ), |
| 245 | + __METHOD__ |
| 246 | + ); |
| 247 | + $dbw->delete( |
| 248 | + 'watchlist', |
| 249 | + array( |
| 250 | + 'wl_user' => $user->getId(), |
| 251 | + 'wl_namespace' => ( $title->getNamespace() | 1 ), |
| 252 | + 'wl_title' => $title->getDBkey(), |
| 253 | + ), |
| 254 | + __METHOD__ |
| 255 | + ); |
| 256 | + } |
| 257 | + } |
| 258 | + |
| 259 | + /** |
| 260 | + * Show a confirmation form for users wishing to clear their watchlist |
| 261 | + * |
| 262 | + * @param OutputPage $output |
| 263 | + * @param User $user |
| 264 | + */ |
| 265 | + private function showClearForm( $output, $user ) { |
| 266 | + if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) { |
| 267 | + $self = SpecialPage::getTitleFor( 'Watchlist' ); |
| 268 | + $form = Xml::openElement( 'form', array( 'method' => 'post', |
| 269 | + 'action' => $self->getLocalUrl( 'action=clear' ) ) ); |
| 270 | + $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) ); |
| 271 | + $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-clear-legend' ) . '</legend>'; |
| 272 | + $form .= wfMsgExt( 'watchlistedit-clear-confirm', 'parse' ); |
| 273 | + $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-clear-submit' ) ) . '</p>'; |
| 274 | + $form .= '</fieldset></form>'; |
| 275 | + $output->addHtml( $form ); |
| 276 | + } |
| 277 | + } |
| 278 | + |
| 279 | + /** |
| 280 | + * Show the standard watchlist editing form |
| 281 | + * |
| 282 | + * @param OutputPage $output |
| 283 | + * @param User $user |
| 284 | + */ |
| 285 | + private function showNormalForm( $output, $user ) { |
| 286 | + if( ( $count = $this->showItemCount( $output, $user ) ) > 0 ) { |
| 287 | + $self = SpecialPage::getTitleFor( 'Watchlist' ); |
| 288 | + $form = Xml::openElement( 'form', array( 'method' => 'post', |
| 289 | + 'action' => $self->getLocalUrl( 'action=edit' ) ) ); |
| 290 | + $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) ); |
| 291 | + $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-normal-legend' ) . '</legend>'; |
| 292 | + $form .= wfMsgExt( 'watchlistedit-normal-explain', 'parse' ); |
| 293 | + foreach( $this->getWatchlist( $user ) as $namespace => $pages ) { |
| 294 | + $form .= '<h2>' . $this->getNamespaceHeading( $namespace ) . '</h2>'; |
| 295 | + $form .= '<ul>'; |
| 296 | + foreach( $pages as $dbkey => $redirect ) { |
| 297 | + $title = Title::makeTitleSafe( $namespace, $dbkey ); |
| 298 | + $form .= $this->buildRemoveLine( $title, $redirect, $user->getSkin() ); |
| 299 | + } |
| 300 | + $form .= '</ul>'; |
| 301 | + } |
| 302 | + $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-normal-submit' ) ) . '</p>'; |
| 303 | + $form .= '</fieldset></form>'; |
| 304 | + $output->addHtml( $form ); |
| 305 | + } |
| 306 | + } |
| 307 | + |
| 308 | + /** |
| 309 | + * Get the correct "heading" for a namespace |
| 310 | + * |
| 311 | + * @param int $namespace |
| 312 | + * @return string |
| 313 | + */ |
| 314 | + private function getNamespaceHeading( $namespace ) { |
| 315 | + return $namespace == NS_MAIN |
| 316 | + ? wfMsgHtml( 'blanknamespace' ) |
| 317 | + : htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( $namespace ) ); |
| 318 | + } |
| 319 | + |
| 320 | + /** |
| 321 | + * Build a single list item containing a check box selecting a title |
| 322 | + * and a link to that title, with various additional bits |
| 323 | + * |
| 324 | + * @param Title $title |
| 325 | + * @param bool $redirect |
| 326 | + * @param Skin $skin |
| 327 | + * @return string |
| 328 | + */ |
| 329 | + private function buildRemoveLine( $title, $redirect, $skin ) { |
| 330 | + $link = $skin->makeLinkObj( $title ); |
| 331 | + if( $redirect ) |
| 332 | + $link = '<span class="watchlistredir">' . $link . '</span>'; |
| 333 | + $tools[] = $skin->makeLinkObj( $title->getTalkPage(), |
| 334 | + htmlspecialchars( $GLOBALS['wgContLang']->getFormattedNsText( NS_TALK ) ) ); |
| 335 | + if( $title->exists() ) |
| 336 | + $tools[] = $skin->makeKnownLinkObj( $title, wfMsgHtml( 'history_short' ), 'action=history' ); |
| 337 | + return '<li>' |
| 338 | + . Xml::check( 'titles[]', false, array( 'value' => $title->getPrefixedText() ) ) |
| 339 | + . $link . ' (' . implode( ' | ', $tools ) . ')' . '</li>'; |
| 340 | + } |
| 341 | + |
| 342 | + /** |
| 343 | + * Show a form for editing the watchlist in "raw" mode |
| 344 | + * |
| 345 | + * @param OutputPage $output |
| 346 | + * @param User $user |
| 347 | + */ |
| 348 | + public function showRawForm( $output, $user ) { |
| 349 | + $this->showItemCount( $output, $user ); |
| 350 | + $self = SpecialPage::getTitleFor( 'Watchlist' ); |
| 351 | + $form = Xml::openElement( 'form', array( 'method' => 'post', |
| 352 | + 'action' => $self->getLocalUrl( 'action=raw' ) ) ); |
| 353 | + $form .= Xml::hidden( 'token', $user->editToken( 'watchlistedit' ) ); |
| 354 | + $form .= '<fieldset><legend>' . wfMsgHtml( 'watchlistedit-raw-legend' ) . '</legend>'; |
| 355 | + $form .= wfMsgExt( 'watchlistedit-raw-explain', 'parse' ); |
| 356 | + $form .= Xml::label( wfMsg( 'watchlistedit-raw-titles' ), 'titles' ); |
| 357 | + $form .= Xml::openElement( 'textarea', array( 'id' => 'titles', 'name' => 'titles', |
| 358 | + 'rows' => 6, 'cols' => 80 ) ); |
| 359 | + foreach( $this->getWatchlist( $user ) as $namespace => $pages ) { |
| 360 | + foreach( $pages as $dbkey => $redirect ) { |
| 361 | + $title = Title::makeTitleSafe( $namespace, $dbkey ); |
| 362 | + if( $title instanceof Title ) |
| 363 | + $form .= htmlspecialchars( $title->getPrefixedText() ) . "\n"; |
| 364 | + } |
| 365 | + } |
| 366 | + $form .= '</textarea>'; |
| 367 | + $form .= '<p>' . Xml::submitButton( wfMsg( 'watchlistedit-raw-submit' ) ) . '</p>'; |
| 368 | + $form .= '</fieldset></form>'; |
| 369 | + $output->addHtml( $form ); |
| 370 | + } |
| 371 | + |
| 372 | + /** |
| 373 | + * Determine whether we are editing the watchlist, and if so, what |
| 374 | + * kind of editing operation |
| 375 | + * |
| 376 | + * @param WebRequest $request |
| 377 | + * @param mixed $par |
| 378 | + * @return int |
| 379 | + */ |
| 380 | + public static function getMode( $request, $par ) { |
| 381 | + $mode = strtolower( $request->getVal( 'action', $par ) ); |
| 382 | + switch( $mode ) { |
| 383 | + case 'clear': |
| 384 | + return self::EDIT_CLEAR; |
| 385 | + case 'raw': |
| 386 | + return self::EDIT_RAW; |
| 387 | + case 'edit': |
| 388 | + return self::EDIT_NORMAL; |
| 389 | + default: |
| 390 | + return false; |
| 391 | + } |
| 392 | + } |
| 393 | + |
| 394 | +} |
\ No newline at end of file |
Property changes on: trunk/phase3/includes/WatchlistEditor.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 395 | + native |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -252,6 +252,7 @@ |
253 | 253 | 'ZhClient' => 'includes/ZhClient.php', |
254 | 254 | 'memcached' => 'includes/memcached-client.php', |
255 | 255 | 'EmaillingJob' => 'includes/JobQueue.php', |
| 256 | + 'WatchlistEditor' => 'includes/WatchlistEditor.php', |
256 | 257 | |
257 | 258 | # filerepo |
258 | 259 | 'ArchivedFile' => 'includes/filerepo/ArchivedFile.php', |
Index: trunk/phase3/includes/SpecialWatchlist.php |
— | — | @@ -35,10 +35,17 @@ |
36 | 36 | $wgOut->setSubtitle( wfMsgWikiHtml( 'watchlistfor', htmlspecialchars( $wgUser->getName() ) ) ); |
37 | 37 | } |
38 | 38 | |
39 | | - if( wlHandleClear( $wgOut, $wgRequest, $par ) ) { |
| 39 | + if( ( $mode = WatchlistEditor::getMode( $wgRequest, $par ) ) !== false ) { |
| 40 | + $editor = new WatchlistEditor(); |
| 41 | + $editor->execute( $wgUser, $wgOut, $wgRequest, $mode ); |
40 | 42 | return; |
41 | 43 | } |
42 | 44 | |
| 45 | + $uid = $wgUser->getId(); |
| 46 | + if( $wgEnotifWatchlist && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) { |
| 47 | + $wgUser->clearAllNotifications( $uid ); |
| 48 | + } |
| 49 | + |
43 | 50 | $defaults = array( |
44 | 51 | /* float */ 'days' => floatval( $wgUser->getOption( 'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */ |
45 | 52 | /* bool */ 'hideOwn' => (int)$wgUser->getBoolOption( 'watchlisthideown' ), |
— | — | @@ -72,37 +79,6 @@ |
73 | 80 | $nameSpaceClause = ''; |
74 | 81 | } |
75 | 82 | |
76 | | - # Watchlist editing |
77 | | - $action = $wgRequest->getVal( 'action' ); |
78 | | - $remove = $wgRequest->getVal( 'remove' ); |
79 | | - $id = $wgRequest->getArray( 'id' ); |
80 | | - |
81 | | - $uid = $wgUser->getID(); |
82 | | - if( $wgEnotifWatchlist && $wgRequest->getVal( 'reset' ) && $wgRequest->wasPosted() ) { |
83 | | - $wgUser->clearAllNotifications( $uid ); |
84 | | - } |
85 | | - |
86 | | - # Deleting items from watchlist |
87 | | - if(($action == 'submit') && isset($remove) && is_array($id)) { |
88 | | - $wgOut->addWikiText( wfMsg( 'removingchecked' ) ); |
89 | | - $wgOut->addHTML( "<ul id=\"mw-unwatch-list\">\n" ); |
90 | | - foreach($id as $one) { |
91 | | - $t = Title::newFromURL( $one ); |
92 | | - if( !is_null( $t ) ) { |
93 | | - $wl = WatchedItem::fromUserTitle( $wgUser, $t ); |
94 | | - if( $wl->removeWatch() === false ) { |
95 | | - $wgOut->addHTML( '<li class="mw-unwatch-failure">' . wfMsg( 'couldntremove', htmlspecialchars($one) ) . "</li>\n" ); |
96 | | - } else { |
97 | | - wfRunHooks('UnwatchArticle', array(&$wgUser, new Article($t))); |
98 | | - $wgOut->addHTML( '<li class="mw-unwatch-success">[[' . htmlspecialchars($one) . "]]</li>\n" ); |
99 | | - } |
100 | | - } else { |
101 | | - $wgOut->addHTML( '<li class="mw-unwatch-invalid">' . wfMsg( 'iteminvalidname', htmlspecialchars($one) ) . "</li>\n" ); |
102 | | - } |
103 | | - } |
104 | | - $wgOut->addHTML( "</ul>\n<p>" . wfMsg( 'wldone' ) . "</p>\n" ); |
105 | | - } |
106 | | - |
107 | 83 | $dbr = wfGetDB( DB_SLAVE, 'watchlist' ); |
108 | 84 | list( $page, $watchlist, $recentchanges ) = $dbr->tableNamesN( 'page', 'watchlist', 'recentchanges' ); |
109 | 85 | |
— | — | @@ -155,81 +131,6 @@ |
156 | 132 | $npages = 40000 * $days; |
157 | 133 | } |
158 | 134 | |
159 | | - /* Edit watchlist form */ |
160 | | - if($wgRequest->getBool('edit') || $par == 'edit' ) { |
161 | | - $wgOut->addWikiText( wfMsgExt( 'watchlistcontains', array( 'parseinline' ), $wgLang->formatNum( $nitems ) ) . |
162 | | - "\n\n" . wfMsg( 'watcheditlist' ) ); |
163 | | - |
164 | | - $wgOut->addHTML( '<form action=\'' . |
165 | | - $specialTitle->escapeLocalUrl( 'action=submit' ) . |
166 | | - "' method='post'>\n" ); |
167 | | - |
168 | | -# Patch A2 |
169 | | -# The following was proposed by KTurner 07.11.2004 to T.Gries |
170 | | -# $sql = "SELECT distinct (wl_namespace & ~1),wl_title FROM $watchlist WHERE wl_user=$uid"; |
171 | | - $sql = "SELECT wl_namespace, wl_title, page_is_redirect FROM $watchlist LEFT JOIN $page ON wl_namespace = page_namespace AND wl_title = page_title WHERE wl_user=$uid"; |
172 | | - |
173 | | - $res = $dbr->query( $sql, $fname ); |
174 | | - |
175 | | - # Batch existence check |
176 | | - $linkBatch = new LinkBatch(); |
177 | | - while( $row = $dbr->fetchObject( $res ) ) |
178 | | - $linkBatch->addObj( Title::makeTitleSafe( $row->wl_namespace, $row->wl_title ) ); |
179 | | - $linkBatch->execute(); |
180 | | - |
181 | | - if( $dbr->numRows( $res ) > 0 ) |
182 | | - $dbr->dataSeek( $res, 0 ); # Let's do the time warp again! |
183 | | - |
184 | | - $sk = $wgUser->getSkin(); |
185 | | - |
186 | | - $list = array(); |
187 | | - while( $s = $dbr->fetchObject( $res ) ) { |
188 | | - $list[$s->wl_namespace][$s->wl_title] = $s->page_is_redirect; |
189 | | - } |
190 | | - |
191 | | - // TODO: Display a TOC |
192 | | - foreach($list as $ns => $titles) { |
193 | | - if (Namespace::isTalk($ns)) |
194 | | - continue; |
195 | | - if ($ns != NS_MAIN) |
196 | | - $wgOut->addHTML( '<h2>' . $wgContLang->getFormattedNsText( $ns ) . '</h2>' ); |
197 | | - $wgOut->addHTML( '<ul>' ); |
198 | | - foreach( $titles as $title => $redir ) { |
199 | | - $titleObj = Title::makeTitle( $ns, $title ); |
200 | | - if( is_null( $titleObj ) ) { |
201 | | - $wgOut->addHTML( |
202 | | - '<!-- bad title "' . |
203 | | - htmlspecialchars( $s->wl_title ) . '" in namespace ' . $s->wl_namespace . " -->\n" |
204 | | - ); |
205 | | - } else { |
206 | | - global $wgContLang; |
207 | | - $toolLinks = array(); |
208 | | - $pageLink = $sk->makeLinkObj( $titleObj ); |
209 | | - $toolLinks[] = $sk->makeLinkObj( $titleObj->getTalkPage(), $wgLang->getNsText( NS_TALK ) ); |
210 | | - if( $titleObj->exists() ) |
211 | | - $toolLinks[] = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml( 'history_short' ), 'action=history' ); |
212 | | - $toolLinks = '(' . implode( ' | ', $toolLinks ) . ')'; |
213 | | - $checkbox = '<input type="checkbox" name="id[]" value="' . htmlspecialchars( $titleObj->getPrefixedText() ) . '" /> ' . ( $wgContLang->isRTL() ? '‏' : '‎' ); |
214 | | - if( $redir ) { |
215 | | - $spanopen = '<span class="watchlistredir">'; |
216 | | - $spanclosed = '</span>'; |
217 | | - } else { |
218 | | - $spanopen = $spanclosed = ''; |
219 | | - } |
220 | | - |
221 | | - $wgOut->addHTML( "<li>{$checkbox}{$spanopen}{$pageLink}{$spanclosed} {$toolLinks}</li>\n" ); |
222 | | - } |
223 | | - } |
224 | | - $wgOut->addHTML( '</ul>' ); |
225 | | - } |
226 | | - $wgOut->addHTML( |
227 | | - wfSubmitButton( wfMsg('removechecked'), array('name' => 'remove') ) . |
228 | | - "\n</form>\n" |
229 | | - ); |
230 | | - |
231 | | - return; |
232 | | - } |
233 | | - |
234 | 135 | # If the watchlist is relatively short, it's simplest to zip |
235 | 136 | # down its entirety and then sort the results. |
236 | 137 | |
— | — | @@ -463,54 +364,4 @@ |
464 | 365 | $count = floor( $count / 2 ); |
465 | 366 | |
466 | 367 | return( $count ); |
467 | | -} |
468 | | - |
469 | | -/** |
470 | | - * Allow the user to clear their watchlist |
471 | | - * |
472 | | - * @param $out Output object |
473 | | - * @param $request Request object |
474 | | - * @param $par Parameters passed to the watchlist page |
475 | | - * @return bool True if it's been taken care of; false indicates the watchlist |
476 | | - * code needs to do something further |
477 | | - */ |
478 | | -function wlHandleClear( &$out, &$request, $par ) { |
479 | | - global $wgLang; |
480 | | - |
481 | | - # Check this function has something to do |
482 | | - if( $request->getText( 'action' ) == 'clear' || $par == 'clear' ) { |
483 | | - global $wgUser; |
484 | | - $out->setPageTitle( wfMsgHtml( 'clearwatchlist' ) ); |
485 | | - $count = wlCountItems( $wgUser ); |
486 | | - if( $count > 0 ) { |
487 | | - # See if we're clearing or confirming |
488 | | - if( $request->wasPosted() && $wgUser->matchEditToken( $request->getText( 'token' ), 'clearwatchlist' ) ) { |
489 | | - # Clearing, so do it and report the result |
490 | | - $dbw = wfGetDB( DB_MASTER ); |
491 | | - $dbw->delete( 'watchlist', array( 'wl_user' => $wgUser->mId ), 'wlHandleClear' ); |
492 | | - $out->addWikiText( wfMsgExt( 'watchlistcleardone', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) ); |
493 | | - $out->returnToMain(); |
494 | | - } else { |
495 | | - # Confirming, so show a form |
496 | | - $wlTitle = SpecialPage::getTitleFor( 'Watchlist' ); |
497 | | - $out->addHTML( wfElement( 'form', array( 'method' => 'post', 'action' => $wlTitle->getLocalUrl( 'action=clear' ) ), NULL ) ); |
498 | | - $out->addWikiText( wfMsgExt( 'watchlistcount', array( 'parsemag', 'escape'), $wgLang->formatNum( $count ) ) ); |
499 | | - $out->addWikiText( wfMsg( 'watchlistcleartext' ) ); |
500 | | - $out->addHTML( |
501 | | - wfHidden( 'token', $wgUser->editToken( 'clearwatchlist' ) ) . |
502 | | - wfElement( 'input', array( 'type' => 'submit', 'name' => 'submit', 'value' => wfMsgHtml( 'watchlistclearbutton' ) ), '' ) . |
503 | | - wfCloseElement( 'form' ) |
504 | | - ); |
505 | | - } |
506 | | - return( true ); |
507 | | - } else { |
508 | | - # Nothing on the watchlist; nothing to do here |
509 | | - $out->addWikiText( wfMsg( 'nowatchlist' ) ); |
510 | | - $out->returnToMain(); |
511 | | - return( true ); |
512 | | - } |
513 | | - } else { |
514 | | - return( false ); |
515 | | - } |
516 | | -} |
517 | | - |
| 368 | +} |
\ No newline at end of file |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -117,9 +117,11 @@ |
118 | 118 | * New javascript for upload page that will show a warning if a file with the |
119 | 119 | "destination filename" already exists. |
120 | 120 | * Add 'editsection-brackets' message to allow localization (or removal) of the |
121 | | - brackets in the "[edit]" link for sections. |
122 | | -* (bug 10437) Move texvc styling to shared.css. |
123 | | -* (bug 10438) HTML TeX formulas should not wrap. |
| 121 | + brackets in the "[edit]" link for sections |
| 122 | +* (bug 10437) Move texvc styling to shared.css |
| 123 | +* (bug 10438) HTML TeX formulas should not wrap |
| 124 | +* Introduce "raw editing" mode for the watchlist, to allow bulk additions, |
| 125 | + removals, and convenient exporting of watchlist contents |
124 | 126 | |
125 | 127 | == Bugfixes since 1.10 == |
126 | 128 | |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -1692,10 +1692,6 @@ |
1693 | 1693 | 'nowatchlist' => 'You have no items on your watchlist.', |
1694 | 1694 | 'watchlistanontext' => 'Please $1 to view or edit items on your watchlist.', |
1695 | 1695 | 'watchlistcount' => "'''You have {{PLURAL:$1|$1 item|$1 items}} on your watchlist, including talk pages.'''", |
1696 | | -'clearwatchlist' => 'Clear watchlist', |
1697 | | -'watchlistcleartext' => 'Are you sure you wish to remove them?', |
1698 | | -'watchlistclearbutton' => 'Clear watchlist', |
1699 | | -'watchlistcleardone' => 'Your watchlist has been cleared. {{PLURAL:$1|$1 item was|$1 items were}} removed.', |
1700 | 1696 | 'watchnologin' => 'Not logged in', |
1701 | 1697 | 'watchnologintext' => 'You must be [[Special:Userlogin|logged in]] to modify your watchlist.', |
1702 | 1698 | 'addedwatch' => 'Added to watchlist', |
— | — | @@ -1720,13 +1716,7 @@ |
1721 | 1717 | 'wlheader-showupdated' => "* Pages which have been changed since you last visited them are shown in '''bold'''", |
1722 | 1718 | 'watchmethod-recent' => 'checking recent edits for watched pages', |
1723 | 1719 | 'watchmethod-list' => 'checking watched pages for recent edits', |
1724 | | -'removechecked' => 'Remove checked items from watchlist', |
1725 | 1720 | 'watchlistcontains' => 'Your watchlist contains $1 {{PLURAL:$1|page|pages}}.', |
1726 | | -'watcheditlist' => "Here's an alphabetical list of your |
1727 | | -watched content pages. Check the boxes of pages you want to remove from your watchlist and click the 'remove checked' button |
1728 | | -at the bottom of the screen (deleting a content page also deletes the accompanying talk page and vice versa).", |
1729 | | -'removingchecked' => 'Removing requested items from watchlist...', |
1730 | | -'couldntremove' => "Couldn't remove item '$1'...", |
1731 | 1721 | 'iteminvalidname' => "Problem with item '$1', invalid name...", |
1732 | 1722 | 'wlnote' => "Below {{PLURAL:$1|is the last change|are the last '''$1''' changes}} in the last {{PLURAL:$2|hour|'''$2''' hours}}.", |
1733 | 1723 | 'wlshowlast' => 'Show last $1 hours $2 days $3', |
— | — | @@ -1737,7 +1727,6 @@ |
1738 | 1728 | 'watchlist-hide-own' => 'Hide my edits', |
1739 | 1729 | 'watchlist-show-minor' => 'Show minor edits', |
1740 | 1730 | 'watchlist-hide-minor' => 'Hide minor edits', |
1741 | | -'wldone' => 'Done.', |
1742 | 1731 | |
1743 | 1732 | # Displayed when you click the "watch" button and it's in the process of watching |
1744 | 1733 | 'watching' => 'Watching...', |
— | — | @@ -2896,4 +2885,29 @@ |
2897 | 2886 | 'lag-warn-normal' => 'Changes newer than $1 seconds may not be shown in this list.', |
2898 | 2887 | 'lag-warn-high' => 'Due to high database server lag, changes newer than $1 seconds may not be shown in this list.', |
2899 | 2888 | |
2900 | | -); |
| 2889 | +# Watchlist editor |
| 2890 | +'watchlistedit-numitems' => 'Your watchlist contains $1 title(s), excluding talk pages.', |
| 2891 | +'watchlistedit-noitems' => 'Your watchlist contains no titles.', |
| 2892 | +'watchlistedit-clear-title' => 'Clear watchlist', |
| 2893 | +'watchlistedit-clear-legend' => 'Clear watchlist', |
| 2894 | +'watchlistedit-clear-confirm' => 'This will remove all titles from your watchlist. Are you sure you |
| 2895 | + want to do this? You can also [[Special:Watchlist/edit|remove individual titles]].', |
| 2896 | +'watchlistedit-clear-submit' => 'Clear', |
| 2897 | +'watchlistedit-clear-done' => 'Your watchlist has been cleared. All titles were removed.', |
| 2898 | +'watchlistedit-normal-title' => 'Edit watchlist', |
| 2899 | +'watchlistedit-normal-legend' => 'Remove titles from watchlist', |
| 2900 | +'watchlistedit-normal-explain' => 'Titles on your watchlist are shown below. To remove a title, check |
| 2901 | + the box next to it, and click Remove Titles. You can also [[Special:Watchlist/raw|edit the raw list]], |
| 2902 | + or [[Special:Watchlist/clear|remove all titles]].', |
| 2903 | +'watchlistedit-normal-submit' => 'Remove Titles', |
| 2904 | +'watchlistedit-normal-done' => '$1 title(s) were removed from your watchlist:', |
| 2905 | +'watchlistedit-raw-title' => 'Edit raw watchlist', |
| 2906 | +'watchlistedit-raw-legend' => 'Edit raw watchlist', |
| 2907 | +'watchlistedit-raw-explain' => 'Titles on your watchlist are shown below, and can be edited by |
| 2908 | + adding to and removing from the list; one title per line. When finished, click Update Watchlist. |
| 2909 | + You can also [[Special:Watchlist/edit|use the standard editor]].', |
| 2910 | +'watchlistedit-raw-titles' => 'Titles:', |
| 2911 | +'watchlistedit-raw-submit' => 'Update Watchlist', |
| 2912 | +'watchlistedit-raw-done' => 'Your watchlist has been updated.', |
| 2913 | + |
| 2914 | +); |
\ No newline at end of file |