[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @defgroup Watchlist Users watchlist handling 4 */ 5 6 /** 7 * Implements Special:EditWatchlist 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 * http://www.gnu.org/copyleft/gpl.html 23 * 24 * @file 25 * @ingroup SpecialPage 26 * @ingroup Watchlist 27 */ 28 29 /** 30 * Provides the UI through which users can perform editing 31 * operations on their watchlist 32 * 33 * @ingroup SpecialPage 34 * @ingroup Watchlist 35 * @author Rob Church <[email protected]> 36 */ 37 class SpecialEditWatchlist extends UnlistedSpecialPage { 38 /** 39 * Editing modes. EDIT_CLEAR is no longer used; the "Clear" link scared people 40 * too much. Now it's passed on to the raw editor, from which it's very easy to clear. 41 */ 42 const EDIT_CLEAR = 1; 43 const EDIT_RAW = 2; 44 const EDIT_NORMAL = 3; 45 46 protected $successMessage; 47 48 protected $toc; 49 50 private $badItems = array(); 51 52 public function __construct() { 53 parent::__construct( 'EditWatchlist', 'editmywatchlist' ); 54 } 55 56 /** 57 * Main execution point 58 * 59 * @param int $mode 60 */ 61 public function execute( $mode ) { 62 $this->setHeaders(); 63 64 # Anons don't get a watchlist 65 $this->requireLogin( 'watchlistanontext' ); 66 67 $out = $this->getOutput(); 68 69 $this->checkPermissions(); 70 $this->checkReadOnly(); 71 72 $this->outputHeader(); 73 $this->outputSubtitle(); 74 75 # B/C: $mode used to be waaay down the parameter list, and the first parameter 76 # was $wgUser 77 if ( $mode instanceof User ) { 78 $args = func_get_args(); 79 if ( count( $args ) >= 4 ) { 80 $mode = $args[3]; 81 } 82 } 83 $mode = self::getMode( $this->getRequest(), $mode ); 84 85 switch ( $mode ) { 86 case self::EDIT_RAW: 87 $out->setPageTitle( $this->msg( 'watchlistedit-raw-title' ) ); 88 $form = $this->getRawForm(); 89 if ( $form->show() ) { 90 $out->addHTML( $this->successMessage ); 91 $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); 92 } 93 break; 94 case self::EDIT_CLEAR: 95 $out->setPageTitle( $this->msg( 'watchlistedit-clear-title' ) ); 96 $form = $this->getClearForm(); 97 if ( $form->show() ) { 98 $out->addHTML( $this->successMessage ); 99 $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); 100 } 101 break; 102 103 case self::EDIT_NORMAL: 104 default: 105 $this->executeViewEditWatchlist(); 106 break; 107 } 108 } 109 110 /** 111 * Renders a subheader on the watchlist page. 112 */ 113 protected function outputSubtitle() { 114 $out = $this->getOutput(); 115 $out->addSubtitle( $this->msg( 'watchlistfor2', $this->getUser()->getName() ) 116 ->rawParams( SpecialEditWatchlist::buildTools( null ) ) ); 117 } 118 119 /** 120 * Executes an edit mode for the watchlist view, from which you can manage your watchlist 121 * 122 */ 123 protected function executeViewEditWatchlist() { 124 $out = $this->getOutput(); 125 $out->setPageTitle( $this->msg( 'watchlistedit-normal-title' ) ); 126 $form = $this->getNormalForm(); 127 if ( $form->show() ) { 128 $out->addHTML( $this->successMessage ); 129 $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); 130 } elseif ( $this->toc !== false ) { 131 $out->prependHTML( $this->toc ); 132 $out->addModules( 'mediawiki.toc' ); 133 } 134 } 135 136 /** 137 * Return an array of subpages beginning with $search that this special page will accept. 138 * 139 * @param string $search Prefix to search for 140 * @param int $limit Maximum number of results to return 141 * @return string[] Matching subpages 142 */ 143 public function prefixSearchSubpages( $search, $limit = 10 ) { 144 return self::prefixSearchArray( 145 $search, 146 $limit, 147 // SpecialWatchlist uses SpecialEditWatchlist::getMode, so new types should be added 148 // here and there - no 'edit' here, because that the default for this page 149 array( 150 'clear', 151 'raw', 152 ) 153 ); 154 } 155 156 /** 157 * Extract a list of titles from a blob of text, returning 158 * (prefixed) strings; unwatchable titles are ignored 159 * 160 * @param string $list 161 * @return array 162 */ 163 private function extractTitles( $list ) { 164 $list = explode( "\n", trim( $list ) ); 165 if ( !is_array( $list ) ) { 166 return array(); 167 } 168 169 $titles = array(); 170 171 foreach ( $list as $text ) { 172 $text = trim( $text ); 173 if ( strlen( $text ) > 0 ) { 174 $title = Title::newFromText( $text ); 175 if ( $title instanceof Title && $title->isWatchable() ) { 176 $titles[] = $title; 177 } 178 } 179 } 180 181 GenderCache::singleton()->doTitlesArray( $titles ); 182 183 $list = array(); 184 /** @var Title $title */ 185 foreach ( $titles as $title ) { 186 $list[] = $title->getPrefixedText(); 187 } 188 189 return array_unique( $list ); 190 } 191 192 public function submitRaw( $data ) { 193 $wanted = $this->extractTitles( $data['Titles'] ); 194 $current = $this->getWatchlist(); 195 196 if ( count( $wanted ) > 0 ) { 197 $toWatch = array_diff( $wanted, $current ); 198 $toUnwatch = array_diff( $current, $wanted ); 199 $this->watchTitles( $toWatch ); 200 $this->unwatchTitles( $toUnwatch ); 201 $this->getUser()->invalidateCache(); 202 203 if ( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 ) { 204 $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse(); 205 } else { 206 return false; 207 } 208 209 if ( count( $toWatch ) > 0 ) { 210 $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-added' ) 211 ->numParams( count( $toWatch ) )->parse(); 212 $this->showTitles( $toWatch, $this->successMessage ); 213 } 214 215 if ( count( $toUnwatch ) > 0 ) { 216 $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-removed' ) 217 ->numParams( count( $toUnwatch ) )->parse(); 218 $this->showTitles( $toUnwatch, $this->successMessage ); 219 } 220 } else { 221 $this->clearWatchlist(); 222 $this->getUser()->invalidateCache(); 223 224 if ( count( $current ) > 0 ) { 225 $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse(); 226 } else { 227 return false; 228 } 229 230 $this->successMessage .= ' ' . $this->msg( 'watchlistedit-raw-removed' ) 231 ->numParams( count( $current ) )->parse(); 232 $this->showTitles( $current, $this->successMessage ); 233 } 234 235 return true; 236 } 237 238 public function submitClear( $data ) { 239 $current = $this->getWatchlist(); 240 $this->clearWatchlist(); 241 $this->getUser()->invalidateCache(); 242 $this->successMessage = $this->msg( 'watchlistedit-clear-done' )->parse(); 243 $this->successMessage .= ' ' . $this->msg( 'watchlistedit-clear-removed' ) 244 ->numParams( count( $current ) )->parse(); 245 $this->showTitles( $current, $this->successMessage ); 246 247 return true; 248 } 249 250 /** 251 * Print out a list of linked titles 252 * 253 * $titles can be an array of strings or Title objects; the former 254 * is preferred, since Titles are very memory-heavy 255 * 256 * @param array $titles Array of strings, or Title objects 257 * @param string $output 258 */ 259 private function showTitles( $titles, &$output ) { 260 $talk = $this->msg( 'talkpagelinktext' )->escaped(); 261 // Do a batch existence check 262 $batch = new LinkBatch(); 263 if ( count( $titles ) >= 100 ) { 264 $output = wfMessage( 'watchlistedit-too-many' )->parse(); 265 return; 266 } 267 foreach ( $titles as $title ) { 268 if ( !$title instanceof Title ) { 269 $title = Title::newFromText( $title ); 270 } 271 272 if ( $title instanceof Title ) { 273 $batch->addObj( $title ); 274 $batch->addObj( $title->getTalkPage() ); 275 } 276 } 277 278 $batch->execute(); 279 280 // Print out the list 281 $output .= "<ul>\n"; 282 283 foreach ( $titles as $title ) { 284 if ( !$title instanceof Title ) { 285 $title = Title::newFromText( $title ); 286 } 287 288 if ( $title instanceof Title ) { 289 $output .= "<li>" 290 . Linker::link( $title ) 291 . ' (' . Linker::link( $title->getTalkPage(), $talk ) 292 . ")</li>\n"; 293 } 294 } 295 296 $output .= "</ul>\n"; 297 } 298 299 /** 300 * Prepare a list of titles on a user's watchlist (excluding talk pages) 301 * and return an array of (prefixed) strings 302 * 303 * @return array 304 */ 305 private function getWatchlist() { 306 $list = array(); 307 $dbr = wfGetDB( DB_MASTER ); 308 309 $res = $dbr->select( 310 'watchlist', 311 array( 312 'wl_namespace', 'wl_title' 313 ), array( 314 'wl_user' => $this->getUser()->getId(), 315 ), 316 __METHOD__ 317 ); 318 319 if ( $res->numRows() > 0 ) { 320 $titles = array(); 321 foreach ( $res as $row ) { 322 $title = Title::makeTitleSafe( $row->wl_namespace, $row->wl_title ); 323 324 if ( $this->checkTitle( $title, $row->wl_namespace, $row->wl_title ) 325 && !$title->isTalkPage() 326 ) { 327 $titles[] = $title; 328 } 329 } 330 $res->free(); 331 332 GenderCache::singleton()->doTitlesArray( $titles ); 333 334 foreach ( $titles as $title ) { 335 $list[] = $title->getPrefixedText(); 336 } 337 } 338 339 $this->cleanupWatchlist(); 340 341 return $list; 342 } 343 344 /** 345 * Get a list of titles on a user's watchlist, excluding talk pages, 346 * and return as a two-dimensional array with namespace and title. 347 * 348 * @return array 349 */ 350 protected function getWatchlistInfo() { 351 $titles = array(); 352 $dbr = wfGetDB( DB_MASTER ); 353 354 $res = $dbr->select( 355 array( 'watchlist' ), 356 array( 'wl_namespace', 'wl_title' ), 357 array( 'wl_user' => $this->getUser()->getId() ), 358 __METHOD__, 359 array( 'ORDER BY' => array( 'wl_namespace', 'wl_title' ) ) 360 ); 361 362 $lb = new LinkBatch(); 363 364 foreach ( $res as $row ) { 365 $lb->add( $row->wl_namespace, $row->wl_title ); 366 if ( !MWNamespace::isTalk( $row->wl_namespace ) ) { 367 $titles[$row->wl_namespace][$row->wl_title] = 1; 368 } 369 } 370 371 $lb->execute(); 372 373 return $titles; 374 } 375 376 /** 377 * Validates watchlist entry 378 * 379 * @param Title $title 380 * @param int $namespace 381 * @param string $dbKey 382 * @return bool Whether this item is valid 383 */ 384 private function checkTitle( $title, $namespace, $dbKey ) { 385 if ( $title 386 && ( $title->isExternal() 387 || $title->getNamespace() < 0 388 ) 389 ) { 390 $title = false; // unrecoverable 391 } 392 393 if ( !$title 394 || $title->getNamespace() != $namespace 395 || $title->getDBkey() != $dbKey 396 ) { 397 $this->badItems[] = array( $title, $namespace, $dbKey ); 398 } 399 400 return (bool)$title; 401 } 402 403 /** 404 * Attempts to clean up broken items 405 */ 406 private function cleanupWatchlist() { 407 if ( !count( $this->badItems ) ) { 408 return; //nothing to do 409 } 410 411 $dbw = wfGetDB( DB_MASTER ); 412 $user = $this->getUser(); 413 414 foreach ( $this->badItems as $row ) { 415 list( $title, $namespace, $dbKey ) = $row; 416 $action = $title ? 'cleaning up' : 'deleting'; 417 wfDebug( "User {$user->getName()} has broken watchlist item ns($namespace):$dbKey, $action.\n" ); 418 419 $dbw->delete( 'watchlist', 420 array( 421 'wl_user' => $user->getId(), 422 'wl_namespace' => $namespace, 423 'wl_title' => $dbKey, 424 ), 425 __METHOD__ 426 ); 427 428 // Can't just do an UPDATE instead of DELETE/INSERT due to unique index 429 if ( $title ) { 430 $user->addWatch( $title ); 431 } 432 } 433 } 434 435 /** 436 * Remove all titles from a user's watchlist 437 */ 438 private function clearWatchlist() { 439 $dbw = wfGetDB( DB_MASTER ); 440 $dbw->delete( 441 'watchlist', 442 array( 'wl_user' => $this->getUser()->getId() ), 443 __METHOD__ 444 ); 445 } 446 447 /** 448 * Add a list of titles to a user's watchlist 449 * 450 * $titles can be an array of strings or Title objects; the former 451 * is preferred, since Titles are very memory-heavy 452 * 453 * @param array $titles Array of strings, or Title objects 454 */ 455 private function watchTitles( $titles ) { 456 $dbw = wfGetDB( DB_MASTER ); 457 $rows = array(); 458 459 foreach ( $titles as $title ) { 460 if ( !$title instanceof Title ) { 461 $title = Title::newFromText( $title ); 462 } 463 464 if ( $title instanceof Title ) { 465 $rows[] = array( 466 'wl_user' => $this->getUser()->getId(), 467 'wl_namespace' => MWNamespace::getSubject( $title->getNamespace() ), 468 'wl_title' => $title->getDBkey(), 469 'wl_notificationtimestamp' => null, 470 ); 471 $rows[] = array( 472 'wl_user' => $this->getUser()->getId(), 473 'wl_namespace' => MWNamespace::getTalk( $title->getNamespace() ), 474 'wl_title' => $title->getDBkey(), 475 'wl_notificationtimestamp' => null, 476 ); 477 } 478 } 479 480 $dbw->insert( 'watchlist', $rows, __METHOD__, 'IGNORE' ); 481 } 482 483 /** 484 * Remove a list of titles from a user's watchlist 485 * 486 * $titles can be an array of strings or Title objects; the former 487 * is preferred, since Titles are very memory-heavy 488 * 489 * @param array $titles Array of strings, or Title objects 490 */ 491 private function unwatchTitles( $titles ) { 492 $dbw = wfGetDB( DB_MASTER ); 493 494 foreach ( $titles as $title ) { 495 if ( !$title instanceof Title ) { 496 $title = Title::newFromText( $title ); 497 } 498 499 if ( $title instanceof Title ) { 500 $dbw->delete( 501 'watchlist', 502 array( 503 'wl_user' => $this->getUser()->getId(), 504 'wl_namespace' => MWNamespace::getSubject( $title->getNamespace() ), 505 'wl_title' => $title->getDBkey(), 506 ), 507 __METHOD__ 508 ); 509 510 $dbw->delete( 511 'watchlist', 512 array( 513 'wl_user' => $this->getUser()->getId(), 514 'wl_namespace' => MWNamespace::getTalk( $title->getNamespace() ), 515 'wl_title' => $title->getDBkey(), 516 ), 517 __METHOD__ 518 ); 519 520 $page = WikiPage::factory( $title ); 521 wfRunHooks( 'UnwatchArticleComplete', array( $this->getUser(), &$page ) ); 522 } 523 } 524 } 525 526 public function submitNormal( $data ) { 527 $removed = array(); 528 529 foreach ( $data as $titles ) { 530 $this->unwatchTitles( $titles ); 531 $removed = array_merge( $removed, $titles ); 532 } 533 534 if ( count( $removed ) > 0 ) { 535 $this->successMessage = $this->msg( 'watchlistedit-normal-done' 536 )->numParams( count( $removed ) )->parse(); 537 $this->showTitles( $removed, $this->successMessage ); 538 539 return true; 540 } else { 541 return false; 542 } 543 } 544 545 /** 546 * Get the standard watchlist editing form 547 * 548 * @return HTMLForm 549 */ 550 protected function getNormalForm() { 551 global $wgContLang; 552 553 $fields = array(); 554 $count = 0; 555 556 // Allow subscribers to manipulate the list of watched pages (or use it 557 // to preload lots of details at once) 558 $watchlistInfo = $this->getWatchlistInfo(); 559 wfRunHooks( 560 'WatchlistEditorBeforeFormRender', 561 array( &$watchlistInfo ) 562 ); 563 564 foreach ( $watchlistInfo as $namespace => $pages ) { 565 $options = array(); 566 567 foreach ( array_keys( $pages ) as $dbkey ) { 568 $title = Title::makeTitleSafe( $namespace, $dbkey ); 569 570 if ( $this->checkTitle( $title, $namespace, $dbkey ) ) { 571 $text = $this->buildRemoveLine( $title ); 572 $options[$text] = $title->getPrefixedText(); 573 $count++; 574 } 575 } 576 577 // checkTitle can filter some options out, avoid empty sections 578 if ( count( $options ) > 0 ) { 579 $fields['TitlesNs' . $namespace] = array( 580 'class' => 'EditWatchlistCheckboxSeriesField', 581 'options' => $options, 582 'section' => "ns$namespace", 583 ); 584 } 585 } 586 $this->cleanupWatchlist(); 587 588 if ( count( $fields ) > 1 && $count > 30 ) { 589 $this->toc = Linker::tocIndent(); 590 $tocLength = 0; 591 592 foreach ( $fields as $data ) { 593 # strip out the 'ns' prefix from the section name: 594 $ns = substr( $data['section'], 2 ); 595 596 $nsText = ( $ns == NS_MAIN ) 597 ? $this->msg( 'blanknamespace' )->escaped() 598 : htmlspecialchars( $wgContLang->getFormattedNsText( $ns ) ); 599 $this->toc .= Linker::tocLine( "editwatchlist-{$data['section']}", $nsText, 600 $this->getLanguage()->formatNum( ++$tocLength ), 1 ) . Linker::tocLineEnd(); 601 } 602 603 $this->toc = Linker::tocList( $this->toc ); 604 } else { 605 $this->toc = false; 606 } 607 608 $context = new DerivativeContext( $this->getContext() ); 609 $context->setTitle( $this->getPageTitle() ); // Remove subpage 610 $form = new EditWatchlistNormalHTMLForm( $fields, $context ); 611 $form->setSubmitTextMsg( 'watchlistedit-normal-submit' ); 612 # Used message keys: 613 # 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit' 614 $form->setSubmitTooltip( 'watchlistedit-normal-submit' ); 615 $form->setWrapperLegendMsg( 'watchlistedit-normal-legend' ); 616 $form->addHeaderText( $this->msg( 'watchlistedit-normal-explain' )->parse() ); 617 $form->setSubmitCallback( array( $this, 'submitNormal' ) ); 618 619 return $form; 620 } 621 622 /** 623 * Build the label for a checkbox, with a link to the title, and various additional bits 624 * 625 * @param Title $title 626 * @return string 627 */ 628 private function buildRemoveLine( $title ) { 629 $link = Linker::link( $title ); 630 631 $tools['talk'] = Linker::link( $title->getTalkPage(), $this->msg( 'talkpagelinktext' )->escaped() ); 632 633 if ( $title->exists() ) { 634 $tools['history'] = Linker::linkKnown( 635 $title, 636 $this->msg( 'history_short' )->escaped(), 637 array(), 638 array( 'action' => 'history' ) 639 ); 640 } 641 642 if ( $title->getNamespace() == NS_USER && !$title->isSubpage() ) { 643 $tools['contributions'] = Linker::linkKnown( 644 SpecialPage::getTitleFor( 'Contributions', $title->getText() ), 645 $this->msg( 'contributions' )->escaped() 646 ); 647 } 648 649 wfRunHooks( 650 'WatchlistEditorBuildRemoveLine', 651 array( &$tools, $title, $title->isRedirect(), $this->getSkin(), &$link ) 652 ); 653 654 if ( $title->isRedirect() ) { 655 // Linker already makes class mw-redirect, so this is redundant 656 $link = '<span class="watchlistredir">' . $link . '</span>'; 657 } 658 659 return $link . " (" . $this->getLanguage()->pipeList( $tools ) . ")"; 660 } 661 662 /** 663 * Get a form for editing the watchlist in "raw" mode 664 * 665 * @return HTMLForm 666 */ 667 protected function getRawForm() { 668 $titles = implode( $this->getWatchlist(), "\n" ); 669 $fields = array( 670 'Titles' => array( 671 'type' => 'textarea', 672 'label-message' => 'watchlistedit-raw-titles', 673 'default' => $titles, 674 ), 675 ); 676 $context = new DerivativeContext( $this->getContext() ); 677 $context->setTitle( $this->getPageTitle( 'raw' ) ); // Reset subpage 678 $form = new HTMLForm( $fields, $context ); 679 $form->setSubmitTextMsg( 'watchlistedit-raw-submit' ); 680 # Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit' 681 $form->setSubmitTooltip( 'watchlistedit-raw-submit' ); 682 $form->setWrapperLegendMsg( 'watchlistedit-raw-legend' ); 683 $form->addHeaderText( $this->msg( 'watchlistedit-raw-explain' )->parse() ); 684 $form->setSubmitCallback( array( $this, 'submitRaw' ) ); 685 686 return $form; 687 } 688 689 /** 690 * Get a form for clearing the watchlist 691 * 692 * @return HTMLForm 693 */ 694 protected function getClearForm() { 695 $context = new DerivativeContext( $this->getContext() ); 696 $context->setTitle( $this->getPageTitle( 'clear' ) ); // Reset subpage 697 $form = new HTMLForm( array(), $context ); 698 $form->setSubmitTextMsg( 'watchlistedit-clear-submit' ); 699 # Used message keys: 'accesskey-watchlistedit-clear-submit', 'tooltip-watchlistedit-clear-submit' 700 $form->setSubmitTooltip( 'watchlistedit-clear-submit' ); 701 $form->setWrapperLegendMsg( 'watchlistedit-clear-legend' ); 702 $form->addHeaderText( $this->msg( 'watchlistedit-clear-explain' )->parse() ); 703 $form->setSubmitCallback( array( $this, 'submitClear' ) ); 704 705 return $form; 706 } 707 708 /** 709 * Determine whether we are editing the watchlist, and if so, what 710 * kind of editing operation 711 * 712 * @param WebRequest $request 713 * @param string $par 714 * @return int 715 */ 716 public static function getMode( $request, $par ) { 717 $mode = strtolower( $request->getVal( 'action', $par ) ); 718 719 switch ( $mode ) { 720 case 'clear': 721 case self::EDIT_CLEAR: 722 return self::EDIT_CLEAR; 723 case 'raw': 724 case self::EDIT_RAW: 725 return self::EDIT_RAW; 726 case 'edit': 727 case self::EDIT_NORMAL: 728 return self::EDIT_NORMAL; 729 default: 730 return false; 731 } 732 } 733 734 /** 735 * Build a set of links for convenient navigation 736 * between watchlist viewing and editing modes 737 * 738 * @param null $unused 739 * @return string 740 */ 741 public static function buildTools( $unused ) { 742 global $wgLang; 743 744 $tools = array(); 745 $modes = array( 746 'view' => array( 'Watchlist', false ), 747 'edit' => array( 'EditWatchlist', false ), 748 'raw' => array( 'EditWatchlist', 'raw' ), 749 'clear' => array( 'EditWatchlist', 'clear' ), 750 ); 751 752 foreach ( $modes as $mode => $arr ) { 753 // can use messages 'watchlisttools-view', 'watchlisttools-edit', 'watchlisttools-raw' 754 $tools[] = Linker::linkKnown( 755 SpecialPage::getTitleFor( $arr[0], $arr[1] ), 756 wfMessage( "watchlisttools-{$mode}" )->escaped() 757 ); 758 } 759 760 return Html::rawElement( 761 'span', 762 array( 'class' => 'mw-watchlist-toollinks' ), 763 wfMessage( 'parentheses', $wgLang->pipeList( $tools ) )->text() 764 ); 765 } 766 } 767 768 /** 769 * Extend HTMLForm purely so we can have a more sane way of getting the section headers 770 */ 771 class EditWatchlistNormalHTMLForm extends HTMLForm { 772 public function getLegend( $namespace ) { 773 $namespace = substr( $namespace, 2 ); 774 775 return $namespace == NS_MAIN 776 ? $this->msg( 'blanknamespace' )->escaped() 777 : htmlspecialchars( $this->getContext()->getLanguage()->getFormattedNsText( $namespace ) ); 778 } 779 780 public function getBody() { 781 return $this->displaySection( $this->mFieldTree, '', 'editwatchlist-' ); 782 } 783 } 784 785 class EditWatchlistCheckboxSeriesField extends HTMLMultiSelectField { 786 /** 787 * HTMLMultiSelectField throws validation errors if we get input data 788 * that doesn't match the data set in the form setup. This causes 789 * problems if something gets removed from the watchlist while the 790 * form is open (bug 32126), but we know that invalid items will 791 * be harmless so we can override it here. 792 * 793 * @param string $value The value the field was submitted with 794 * @param array $alldata The data collected from the form 795 * @return bool|string Bool true on success, or String error to display. 796 */ 797 function validate( $value, $alldata ) { 798 // Need to call into grandparent to be a good citizen. :) 799 return HTMLFormField::validate( $value, $alldata ); 800 } 801 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |