[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Form to edit user preferences. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 */ 22 23 /** 24 * We're now using the HTMLForm object with some customisation to generate the 25 * Preferences form. This object handles generic submission, CSRF protection, 26 * layout and other logic in a reusable manner. We subclass it as a PreferencesForm 27 * to make some minor customisations. 28 * 29 * In order to generate the form, the HTMLForm object needs an array structure 30 * detailing the form fields available, and that's what this class is for. Each 31 * element of the array is a basic property-list, including the type of field, 32 * the label it is to be given in the form, callbacks for validation and 33 * 'filtering', and other pertinent information. Note that the 'default' field 34 * is named for generic forms, and does not represent the preference's default 35 * (which is stored in $wgDefaultUserOptions), but the default for the form 36 * field, which should be whatever the user has set for that preference. There 37 * is no need to override it unless you have some special storage logic (for 38 * instance, those not presently stored as options, but which are best set from 39 * the user preferences view). 40 * 41 * Field types are implemented as subclasses of the generic HTMLFormField 42 * object, and typically implement at least getInputHTML, which generates the 43 * HTML for the input field to be placed in the table. 44 * 45 * Once fields have been retrieved and validated, submission logic is handed 46 * over to the tryUISubmit static method of this class. 47 */ 48 class Preferences { 49 /** @var array */ 50 protected static $defaultPreferences = null; 51 52 /** @var array */ 53 protected static $saveFilters = array( 54 'timecorrection' => array( 'Preferences', 'filterTimezoneInput' ), 55 'cols' => array( 'Preferences', 'filterIntval' ), 56 'rows' => array( 'Preferences', 'filterIntval' ), 57 'rclimit' => array( 'Preferences', 'filterIntval' ), 58 'wllimit' => array( 'Preferences', 'filterIntval' ), 59 'searchlimit' => array( 'Preferences', 'filterIntval' ), 60 ); 61 62 // Stuff that shouldn't be saved as a preference. 63 private static $saveBlacklist = array( 64 'realname', 65 'emailaddress', 66 ); 67 68 /** 69 * @return array 70 */ 71 static function getSaveBlacklist() { 72 return self::$saveBlacklist; 73 } 74 75 /** 76 * @throws MWException 77 * @param User $user 78 * @param IContextSource $context 79 * @return array|null 80 */ 81 static function getPreferences( $user, IContextSource $context ) { 82 if ( self::$defaultPreferences ) { 83 return self::$defaultPreferences; 84 } 85 86 $defaultPreferences = array(); 87 88 self::profilePreferences( $user, $context, $defaultPreferences ); 89 self::skinPreferences( $user, $context, $defaultPreferences ); 90 self::datetimePreferences( $user, $context, $defaultPreferences ); 91 self::filesPreferences( $user, $context, $defaultPreferences ); 92 self::renderingPreferences( $user, $context, $defaultPreferences ); 93 self::editingPreferences( $user, $context, $defaultPreferences ); 94 self::rcPreferences( $user, $context, $defaultPreferences ); 95 self::watchlistPreferences( $user, $context, $defaultPreferences ); 96 self::searchPreferences( $user, $context, $defaultPreferences ); 97 self::miscPreferences( $user, $context, $defaultPreferences ); 98 99 wfRunHooks( 'GetPreferences', array( $user, &$defaultPreferences ) ); 100 101 self::loadPreferenceValues( $user, $context, $defaultPreferences ); 102 self::$defaultPreferences = $defaultPreferences; 103 return $defaultPreferences; 104 } 105 106 /** 107 * Loads existing values for a given array of preferences 108 * @throws MWException 109 * @param User $user 110 * @param IContextSource $context 111 * @param array $defaultPreferences Array to load values for 112 * @return array|null 113 */ 114 static function loadPreferenceValues( $user, $context, &$defaultPreferences ) { 115 ## Remove preferences that wikis don't want to use 116 foreach ( $context->getConfig()->get( 'HiddenPrefs' ) as $pref ) { 117 if ( isset( $defaultPreferences[$pref] ) ) { 118 unset( $defaultPreferences[$pref] ); 119 } 120 } 121 122 ## Make sure that form fields have their parent set. See bug 41337. 123 $dummyForm = new HTMLForm( array(), $context ); 124 125 $disable = !$user->isAllowed( 'editmyoptions' ); 126 127 ## Prod in defaults from the user 128 foreach ( $defaultPreferences as $name => &$info ) { 129 $prefFromUser = self::getOptionFromUser( $name, $info, $user ); 130 if ( $disable && !in_array( $name, self::$saveBlacklist ) ) { 131 $info['disabled'] = 'disabled'; 132 } 133 $field = HTMLForm::loadInputFromParameters( $name, $info ); // For validation 134 $field->mParent = $dummyForm; 135 $defaultOptions = User::getDefaultOptions(); 136 $globalDefault = isset( $defaultOptions[$name] ) 137 ? $defaultOptions[$name] 138 : null; 139 140 // If it validates, set it as the default 141 if ( isset( $info['default'] ) ) { 142 // Already set, no problem 143 continue; 144 } elseif ( !is_null( $prefFromUser ) && // Make sure we're not just pulling nothing 145 $field->validate( $prefFromUser, $user->getOptions() ) === true ) { 146 $info['default'] = $prefFromUser; 147 } elseif ( $field->validate( $globalDefault, $user->getOptions() ) === true ) { 148 $info['default'] = $globalDefault; 149 } else { 150 throw new MWException( "Global default '$globalDefault' is invalid for field $name" ); 151 } 152 } 153 154 return $defaultPreferences; 155 } 156 157 /** 158 * Pull option from a user account. Handles stuff like array-type preferences. 159 * 160 * @param string $name 161 * @param array $info 162 * @param User $user 163 * @return array|string 164 */ 165 static function getOptionFromUser( $name, $info, $user ) { 166 $val = $user->getOption( $name ); 167 168 // Handling for multiselect preferences 169 if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) || 170 ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) { 171 $options = HTMLFormField::flattenOptions( $info['options'] ); 172 $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name; 173 $val = array(); 174 175 foreach ( $options as $value ) { 176 if ( $user->getOption( "$prefix$value" ) ) { 177 $val[] = $value; 178 } 179 } 180 } 181 182 // Handling for checkmatrix preferences 183 if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) || 184 ( isset( $info['class'] ) && $info['class'] == 'HTMLCheckMatrix' ) ) { 185 $columns = HTMLFormField::flattenOptions( $info['columns'] ); 186 $rows = HTMLFormField::flattenOptions( $info['rows'] ); 187 $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name; 188 $val = array(); 189 190 foreach ( $columns as $column ) { 191 foreach ( $rows as $row ) { 192 if ( $user->getOption( "$prefix$column-$row" ) ) { 193 $val[] = "$column-$row"; 194 } 195 } 196 } 197 } 198 199 return $val; 200 } 201 202 /** 203 * @param User $user 204 * @param IContextSource $context 205 * @param array $defaultPreferences 206 * @return void 207 */ 208 static function profilePreferences( $user, IContextSource $context, &$defaultPreferences ) { 209 global $wgAuth, $wgContLang, $wgParser; 210 211 $config = $context->getConfig(); 212 // retrieving user name for GENDER and misc. 213 $userName = $user->getName(); 214 215 ## User info ##################################### 216 // Information panel 217 $defaultPreferences['username'] = array( 218 'type' => 'info', 219 'label-message' => array( 'username', $userName ), 220 'default' => $userName, 221 'section' => 'personal/info', 222 ); 223 224 # Get groups to which the user belongs 225 $userEffectiveGroups = $user->getEffectiveGroups(); 226 $userGroups = $userMembers = array(); 227 foreach ( $userEffectiveGroups as $ueg ) { 228 if ( $ueg == '*' ) { 229 // Skip the default * group, seems useless here 230 continue; 231 } 232 $groupName = User::getGroupName( $ueg ); 233 $userGroups[] = User::makeGroupLinkHTML( $ueg, $groupName ); 234 235 $memberName = User::getGroupMember( $ueg, $userName ); 236 $userMembers[] = User::makeGroupLinkHTML( $ueg, $memberName ); 237 } 238 asort( $userGroups ); 239 asort( $userMembers ); 240 241 $lang = $context->getLanguage(); 242 243 $defaultPreferences['usergroups'] = array( 244 'type' => 'info', 245 'label' => $context->msg( 'prefs-memberingroups' )->numParams( 246 count( $userGroups ) )->params( $userName )->parse(), 247 'default' => $context->msg( 'prefs-memberingroups-type', 248 $lang->commaList( $userGroups ), 249 $lang->commaList( $userMembers ) 250 )->plain(), 251 'raw' => true, 252 'section' => 'personal/info', 253 ); 254 255 $editCount = Linker::link( SpecialPage::getTitleFor( "Contributions", $userName ), 256 $lang->formatNum( $user->getEditCount() ) ); 257 258 $defaultPreferences['editcount'] = array( 259 'type' => 'info', 260 'raw' => true, 261 'label-message' => 'prefs-edits', 262 'default' => $editCount, 263 'section' => 'personal/info', 264 ); 265 266 if ( $user->getRegistration() ) { 267 $displayUser = $context->getUser(); 268 $userRegistration = $user->getRegistration(); 269 $defaultPreferences['registrationdate'] = array( 270 'type' => 'info', 271 'label-message' => 'prefs-registration', 272 'default' => $context->msg( 273 'prefs-registration-date-time', 274 $lang->userTimeAndDate( $userRegistration, $displayUser ), 275 $lang->userDate( $userRegistration, $displayUser ), 276 $lang->userTime( $userRegistration, $displayUser ) 277 )->parse(), 278 'section' => 'personal/info', 279 ); 280 } 281 282 $canViewPrivateInfo = $user->isAllowed( 'viewmyprivateinfo' ); 283 $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' ); 284 285 // Actually changeable stuff 286 $defaultPreferences['realname'] = array( 287 // (not really "private", but still shouldn't be edited without permission) 288 'type' => $canEditPrivateInfo && $wgAuth->allowPropChange( 'realname' ) ? 'text' : 'info', 289 'default' => $user->getRealName(), 290 'section' => 'personal/info', 291 'label-message' => 'yourrealname', 292 'help-message' => 'prefs-help-realname', 293 ); 294 295 if ( $canEditPrivateInfo && $wgAuth->allowPasswordChange() ) { 296 $link = Linker::link( SpecialPage::getTitleFor( 'ChangePassword' ), 297 $context->msg( 'prefs-resetpass' )->escaped(), array(), 298 array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) ); 299 300 $defaultPreferences['password'] = array( 301 'type' => 'info', 302 'raw' => true, 303 'default' => $link, 304 'label-message' => 'yourpassword', 305 'section' => 'personal/info', 306 ); 307 } 308 // Only show prefershttps if secure login is turned on 309 if ( $config->get( 'SecureLogin' ) && wfCanIPUseHTTPS( $context->getRequest()->getIP() ) ) { 310 $defaultPreferences['prefershttps'] = array( 311 'type' => 'toggle', 312 'label-message' => 'tog-prefershttps', 313 'help-message' => 'prefs-help-prefershttps', 314 'section' => 'personal/info' 315 ); 316 } 317 318 // Language 319 $languages = Language::fetchLanguageNames( null, 'mw' ); 320 $languageCode = $config->get( 'LanguageCode' ); 321 if ( !array_key_exists( $languageCode, $languages ) ) { 322 $languages[$languageCode] = $languageCode; 323 } 324 ksort( $languages ); 325 326 $options = array(); 327 foreach ( $languages as $code => $name ) { 328 $display = wfBCP47( $code ) . ' - ' . $name; 329 $options[$display] = $code; 330 } 331 $defaultPreferences['language'] = array( 332 'type' => 'select', 333 'section' => 'personal/i18n', 334 'options' => $options, 335 'label-message' => 'yourlanguage', 336 ); 337 338 $defaultPreferences['gender'] = array( 339 'type' => 'radio', 340 'section' => 'personal/i18n', 341 'options' => array( 342 $context->msg( 'parentheses', 343 $context->msg( 'gender-unknown' )->text() 344 )->text() => 'unknown', 345 $context->msg( 'gender-female' )->text() => 'female', 346 $context->msg( 'gender-male' )->text() => 'male', 347 ), 348 'label-message' => 'yourgender', 349 'help-message' => 'prefs-help-gender', 350 ); 351 352 // see if there are multiple language variants to choose from 353 if ( !$config->get( 'DisableLangConversion' ) ) { 354 foreach ( LanguageConverter::$languagesWithVariants as $langCode ) { 355 if ( $langCode == $wgContLang->getCode() ) { 356 $variants = $wgContLang->getVariants(); 357 358 if ( count( $variants ) <= 1 ) { 359 continue; 360 } 361 362 $variantArray = array(); 363 foreach ( $variants as $v ) { 364 $v = str_replace( '_', '-', strtolower( $v ) ); 365 $variantArray[$v] = $lang->getVariantname( $v, false ); 366 } 367 368 $options = array(); 369 foreach ( $variantArray as $code => $name ) { 370 $display = wfBCP47( $code ) . ' - ' . $name; 371 $options[$display] = $code; 372 } 373 374 $defaultPreferences['variant'] = array( 375 'label-message' => 'yourvariant', 376 'type' => 'select', 377 'options' => $options, 378 'section' => 'personal/i18n', 379 'help-message' => 'prefs-help-variant', 380 ); 381 } else { 382 $defaultPreferences["variant-$langCode"] = array( 383 'type' => 'api', 384 ); 385 } 386 } 387 } 388 389 // Stuff from Language::getExtraUserToggles() 390 // FIXME is this dead code? $extraUserToggles doesn't seem to be defined for any language 391 $toggles = $wgContLang->getExtraUserToggles(); 392 393 foreach ( $toggles as $toggle ) { 394 $defaultPreferences[$toggle] = array( 395 'type' => 'toggle', 396 'section' => 'personal/i18n', 397 'label-message' => "tog-$toggle", 398 ); 399 } 400 401 // show a preview of the old signature first 402 $oldsigWikiText = $wgParser->preSaveTransform( 403 '~~~', 404 $context->getTitle(), 405 $user, 406 ParserOptions::newFromContext( $context ) 407 ); 408 $oldsigHTML = $context->getOutput()->parseInline( $oldsigWikiText, true, true ); 409 $defaultPreferences['oldsig'] = array( 410 'type' => 'info', 411 'raw' => true, 412 'label-message' => 'tog-oldsig', 413 'default' => $oldsigHTML, 414 'section' => 'personal/signature', 415 ); 416 $defaultPreferences['nickname'] = array( 417 'type' => $wgAuth->allowPropChange( 'nickname' ) ? 'text' : 'info', 418 'maxlength' => $config->get( 'MaxSigChars' ), 419 'label-message' => 'yournick', 420 'validation-callback' => array( 'Preferences', 'validateSignature' ), 421 'section' => 'personal/signature', 422 'filter-callback' => array( 'Preferences', 'cleanSignature' ), 423 ); 424 $defaultPreferences['fancysig'] = array( 425 'type' => 'toggle', 426 'label-message' => 'tog-fancysig', 427 // show general help about signature at the bottom of the section 428 'help-message' => 'prefs-help-signature', 429 'section' => 'personal/signature' 430 ); 431 432 ## Email stuff 433 434 if ( $config->get( 'EnableEmail' ) ) { 435 if ( $canViewPrivateInfo ) { 436 $helpMessages[] = $config->get( 'EmailConfirmToEdit' ) 437 ? 'prefs-help-email-required' 438 : 'prefs-help-email'; 439 440 if ( $config->get( 'EnableUserEmail' ) ) { 441 // additional messages when users can send email to each other 442 $helpMessages[] = 'prefs-help-email-others'; 443 } 444 445 $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : ''; 446 if ( $canEditPrivateInfo && $wgAuth->allowPropChange( 'emailaddress' ) ) { 447 $link = Linker::link( 448 SpecialPage::getTitleFor( 'ChangeEmail' ), 449 $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->escaped(), 450 array(), 451 array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) ); 452 453 $emailAddress .= $emailAddress == '' ? $link : ( 454 $context->msg( 'word-separator' )->plain() 455 . $context->msg( 'parentheses' )->rawParams( $link )->plain() 456 ); 457 } 458 459 $defaultPreferences['emailaddress'] = array( 460 'type' => 'info', 461 'raw' => true, 462 'default' => $emailAddress, 463 'label-message' => 'youremail', 464 'section' => 'personal/email', 465 'help-messages' => $helpMessages, 466 # 'cssclass' chosen below 467 ); 468 } 469 470 $disableEmailPrefs = false; 471 472 if ( $config->get( 'EmailAuthentication' ) ) { 473 $emailauthenticationclass = 'mw-email-not-authenticated'; 474 if ( $user->getEmail() ) { 475 if ( $user->getEmailAuthenticationTimestamp() ) { 476 // date and time are separate parameters to facilitate localisation. 477 // $time is kept for backward compat reasons. 478 // 'emailauthenticated' is also used in SpecialConfirmemail.php 479 $displayUser = $context->getUser(); 480 $emailTimestamp = $user->getEmailAuthenticationTimestamp(); 481 $time = $lang->userTimeAndDate( $emailTimestamp, $displayUser ); 482 $d = $lang->userDate( $emailTimestamp, $displayUser ); 483 $t = $lang->userTime( $emailTimestamp, $displayUser ); 484 $emailauthenticated = $context->msg( 'emailauthenticated', 485 $time, $d, $t )->parse() . '<br />'; 486 $disableEmailPrefs = false; 487 $emailauthenticationclass = 'mw-email-authenticated'; 488 } else { 489 $disableEmailPrefs = true; 490 $emailauthenticated = $context->msg( 'emailnotauthenticated' )->parse() . '<br />' . 491 Linker::linkKnown( 492 SpecialPage::getTitleFor( 'Confirmemail' ), 493 $context->msg( 'emailconfirmlink' )->escaped() 494 ) . '<br />'; 495 $emailauthenticationclass = "mw-email-not-authenticated"; 496 } 497 } else { 498 $disableEmailPrefs = true; 499 $emailauthenticated = $context->msg( 'noemailprefs' )->escaped(); 500 $emailauthenticationclass = 'mw-email-none'; 501 } 502 503 if ( $canViewPrivateInfo ) { 504 $defaultPreferences['emailauthentication'] = array( 505 'type' => 'info', 506 'raw' => true, 507 'section' => 'personal/email', 508 'label-message' => 'prefs-emailconfirm-label', 509 'default' => $emailauthenticated, 510 # Apply the same CSS class used on the input to the message: 511 'cssclass' => $emailauthenticationclass, 512 ); 513 $defaultPreferences['emailaddress']['cssclass'] = $emailauthenticationclass; 514 } 515 } 516 517 if ( $config->get( 'EnableUserEmail' ) && $user->isAllowed( 'sendemail' ) ) { 518 $defaultPreferences['disablemail'] = array( 519 'type' => 'toggle', 520 'invert' => true, 521 'section' => 'personal/email', 522 'label-message' => 'allowemail', 523 'disabled' => $disableEmailPrefs, 524 ); 525 $defaultPreferences['ccmeonemails'] = array( 526 'type' => 'toggle', 527 'section' => 'personal/email', 528 'label-message' => 'tog-ccmeonemails', 529 'disabled' => $disableEmailPrefs, 530 ); 531 } 532 533 if ( $config->get( 'EnotifWatchlist' ) ) { 534 $defaultPreferences['enotifwatchlistpages'] = array( 535 'type' => 'toggle', 536 'section' => 'personal/email', 537 'label-message' => 'tog-enotifwatchlistpages', 538 'disabled' => $disableEmailPrefs, 539 ); 540 } 541 if ( $config->get( 'EnotifUserTalk' ) ) { 542 $defaultPreferences['enotifusertalkpages'] = array( 543 'type' => 'toggle', 544 'section' => 'personal/email', 545 'label-message' => 'tog-enotifusertalkpages', 546 'disabled' => $disableEmailPrefs, 547 ); 548 } 549 if ( $config->get( 'EnotifUserTalk' ) || $config->get( 'EnotifWatchlist' ) ) { 550 $defaultPreferences['enotifminoredits'] = array( 551 'type' => 'toggle', 552 'section' => 'personal/email', 553 'label-message' => 'tog-enotifminoredits', 554 'disabled' => $disableEmailPrefs, 555 ); 556 557 if ( $config->get( 'EnotifRevealEditorAddress' ) ) { 558 $defaultPreferences['enotifrevealaddr'] = array( 559 'type' => 'toggle', 560 'section' => 'personal/email', 561 'label-message' => 'tog-enotifrevealaddr', 562 'disabled' => $disableEmailPrefs, 563 ); 564 } 565 } 566 } 567 } 568 569 /** 570 * @param User $user 571 * @param IContextSource $context 572 * @param array $defaultPreferences 573 * @return void 574 */ 575 static function skinPreferences( $user, IContextSource $context, &$defaultPreferences ) { 576 ## Skin ##################################### 577 578 // Skin selector, if there is at least one valid skin 579 $skinOptions = self::generateSkinOptions( $user, $context ); 580 if ( $skinOptions ) { 581 $defaultPreferences['skin'] = array( 582 'type' => 'radio', 583 'options' => $skinOptions, 584 'label' => ' ', 585 'section' => 'rendering/skin', 586 ); 587 } 588 589 $config = $context->getConfig(); 590 $allowUserCss = $config->get( 'AllowUserCss' ); 591 $allowUserJs = $config->get( 'AllowUserJs' ); 592 # Create links to user CSS/JS pages for all skins 593 # This code is basically copied from generateSkinOptions(). It'd 594 # be nice to somehow merge this back in there to avoid redundancy. 595 if ( $allowUserCss || $allowUserJs ) { 596 $linkTools = array(); 597 $userName = $user->getName(); 598 599 if ( $allowUserCss ) { 600 $cssPage = Title::makeTitleSafe( NS_USER, $userName . '/common.css' ); 601 $linkTools[] = Linker::link( $cssPage, $context->msg( 'prefs-custom-css' )->escaped() ); 602 } 603 604 if ( $allowUserJs ) { 605 $jsPage = Title::makeTitleSafe( NS_USER, $userName . '/common.js' ); 606 $linkTools[] = Linker::link( $jsPage, $context->msg( 'prefs-custom-js' )->escaped() ); 607 } 608 609 $defaultPreferences['commoncssjs'] = array( 610 'type' => 'info', 611 'raw' => true, 612 'default' => $context->getLanguage()->pipeList( $linkTools ), 613 'label-message' => 'prefs-common-css-js', 614 'section' => 'rendering/skin', 615 ); 616 } 617 } 618 619 /** 620 * @param User $user 621 * @param IContextSource $context 622 * @param array $defaultPreferences 623 */ 624 static function filesPreferences( $user, IContextSource $context, &$defaultPreferences ) { 625 ## Files ##################################### 626 $defaultPreferences['imagesize'] = array( 627 'type' => 'select', 628 'options' => self::getImageSizes( $context ), 629 'label-message' => 'imagemaxsize', 630 'section' => 'rendering/files', 631 ); 632 $defaultPreferences['thumbsize'] = array( 633 'type' => 'select', 634 'options' => self::getThumbSizes( $context ), 635 'label-message' => 'thumbsize', 636 'section' => 'rendering/files', 637 ); 638 } 639 640 /** 641 * @param User $user 642 * @param IContextSource $context 643 * @param array $defaultPreferences 644 * @return void 645 */ 646 static function datetimePreferences( $user, IContextSource $context, &$defaultPreferences ) { 647 ## Date and time ##################################### 648 $dateOptions = self::getDateOptions( $context ); 649 if ( $dateOptions ) { 650 $defaultPreferences['date'] = array( 651 'type' => 'radio', 652 'options' => $dateOptions, 653 'label' => ' ', 654 'section' => 'rendering/dateformat', 655 ); 656 } 657 658 // Info 659 $now = wfTimestampNow(); 660 $lang = $context->getLanguage(); 661 $nowlocal = Xml::element( 'span', array( 'id' => 'wpLocalTime' ), 662 $lang->time( $now, true ) ); 663 $nowserver = $lang->time( $now, false ) . 664 Html::hidden( 'wpServerTime', (int)substr( $now, 8, 2 ) * 60 + (int)substr( $now, 10, 2 ) ); 665 666 $defaultPreferences['nowserver'] = array( 667 'type' => 'info', 668 'raw' => 1, 669 'label-message' => 'servertime', 670 'default' => $nowserver, 671 'section' => 'rendering/timeoffset', 672 ); 673 674 $defaultPreferences['nowlocal'] = array( 675 'type' => 'info', 676 'raw' => 1, 677 'label-message' => 'localtime', 678 'default' => $nowlocal, 679 'section' => 'rendering/timeoffset', 680 ); 681 682 // Grab existing pref. 683 $tzOffset = $user->getOption( 'timecorrection' ); 684 $tz = explode( '|', $tzOffset, 3 ); 685 686 $tzOptions = self::getTimezoneOptions( $context ); 687 688 $tzSetting = $tzOffset; 689 if ( count( $tz ) > 1 && $tz[0] == 'Offset' ) { 690 $minDiff = $tz[1]; 691 $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff / 60 ), abs( $minDiff ) % 60 ); 692 } elseif ( count( $tz ) > 1 && $tz[0] == 'ZoneInfo' && 693 !in_array( $tzOffset, HTMLFormField::flattenOptions( $tzOptions ) ) 694 ) { 695 # Timezone offset can vary with DST 696 $userTZ = timezone_open( $tz[2] ); 697 if ( $userTZ !== false ) { 698 $minDiff = floor( timezone_offset_get( $userTZ, date_create( 'now' ) ) / 60 ); 699 $tzSetting = "ZoneInfo|$minDiff|{$tz[2]}"; 700 } 701 } 702 703 $defaultPreferences['timecorrection'] = array( 704 'class' => 'HTMLSelectOrOtherField', 705 'label-message' => 'timezonelegend', 706 'options' => $tzOptions, 707 'default' => $tzSetting, 708 'size' => 20, 709 'section' => 'rendering/timeoffset', 710 ); 711 } 712 713 /** 714 * @param User $user 715 * @param IContextSource $context 716 * @param array $defaultPreferences 717 */ 718 static function renderingPreferences( $user, IContextSource $context, &$defaultPreferences ) { 719 ## Diffs #################################### 720 $defaultPreferences['diffonly'] = array( 721 'type' => 'toggle', 722 'section' => 'rendering/diffs', 723 'label-message' => 'tog-diffonly', 724 ); 725 $defaultPreferences['norollbackdiff'] = array( 726 'type' => 'toggle', 727 'section' => 'rendering/diffs', 728 'label-message' => 'tog-norollbackdiff', 729 ); 730 731 ## Page Rendering ############################## 732 if ( $context->getConfig()->get( 'AllowUserCssPrefs' ) ) { 733 $defaultPreferences['underline'] = array( 734 'type' => 'select', 735 'options' => array( 736 $context->msg( 'underline-never' )->text() => 0, 737 $context->msg( 'underline-always' )->text() => 1, 738 $context->msg( 'underline-default' )->text() => 2, 739 ), 740 'label-message' => 'tog-underline', 741 'section' => 'rendering/advancedrendering', 742 ); 743 } 744 745 $stubThresholdValues = array( 50, 100, 500, 1000, 2000, 5000, 10000 ); 746 $stubThresholdOptions = array( $context->msg( 'stub-threshold-disabled' )->text() => 0 ); 747 foreach ( $stubThresholdValues as $value ) { 748 $stubThresholdOptions[$context->msg( 'size-bytes', $value )->text()] = $value; 749 } 750 751 $defaultPreferences['stubthreshold'] = array( 752 'type' => 'select', 753 'section' => 'rendering/advancedrendering', 754 'options' => $stubThresholdOptions, 755 'label-raw' => $context->msg( 'stub-threshold' )->text(), // Raw HTML message. Yay? 756 ); 757 758 $defaultPreferences['showhiddencats'] = array( 759 'type' => 'toggle', 760 'section' => 'rendering/advancedrendering', 761 'label-message' => 'tog-showhiddencats' 762 ); 763 764 $defaultPreferences['numberheadings'] = array( 765 'type' => 'toggle', 766 'section' => 'rendering/advancedrendering', 767 'label-message' => 'tog-numberheadings', 768 ); 769 } 770 771 /** 772 * @param User $user 773 * @param IContextSource $context 774 * @param array $defaultPreferences 775 */ 776 static function editingPreferences( $user, IContextSource $context, &$defaultPreferences ) { 777 ## Editing ##################################### 778 $defaultPreferences['editsectiononrightclick'] = array( 779 'type' => 'toggle', 780 'section' => 'editing/advancedediting', 781 'label-message' => 'tog-editsectiononrightclick', 782 ); 783 $defaultPreferences['editondblclick'] = array( 784 'type' => 'toggle', 785 'section' => 'editing/advancedediting', 786 'label-message' => 'tog-editondblclick', 787 ); 788 789 if ( $context->getConfig()->get( 'AllowUserCssPrefs' ) ) { 790 $defaultPreferences['editfont'] = array( 791 'type' => 'select', 792 'section' => 'editing/editor', 793 'label-message' => 'editfont-style', 794 'options' => array( 795 $context->msg( 'editfont-default' )->text() => 'default', 796 $context->msg( 'editfont-monospace' )->text() => 'monospace', 797 $context->msg( 'editfont-sansserif' )->text() => 'sans-serif', 798 $context->msg( 'editfont-serif' )->text() => 'serif', 799 ) 800 ); 801 } 802 $defaultPreferences['cols'] = array( 803 'type' => 'int', 804 'label-message' => 'columns', 805 'section' => 'editing/editor', 806 'min' => 4, 807 'max' => 1000, 808 ); 809 $defaultPreferences['rows'] = array( 810 'type' => 'int', 811 'label-message' => 'rows', 812 'section' => 'editing/editor', 813 'min' => 4, 814 'max' => 1000, 815 ); 816 if ( $user->isAllowed( 'minoredit' ) ) { 817 $defaultPreferences['minordefault'] = array( 818 'type' => 'toggle', 819 'section' => 'editing/editor', 820 'label-message' => 'tog-minordefault', 821 ); 822 } 823 $defaultPreferences['forceeditsummary'] = array( 824 'type' => 'toggle', 825 'section' => 'editing/editor', 826 'label-message' => 'tog-forceeditsummary', 827 ); 828 $defaultPreferences['useeditwarning'] = array( 829 'type' => 'toggle', 830 'section' => 'editing/editor', 831 'label-message' => 'tog-useeditwarning', 832 ); 833 $defaultPreferences['showtoolbar'] = array( 834 'type' => 'toggle', 835 'section' => 'editing/editor', 836 'label-message' => 'tog-showtoolbar', 837 ); 838 839 $defaultPreferences['previewonfirst'] = array( 840 'type' => 'toggle', 841 'section' => 'editing/preview', 842 'label-message' => 'tog-previewonfirst', 843 ); 844 $defaultPreferences['previewontop'] = array( 845 'type' => 'toggle', 846 'section' => 'editing/preview', 847 'label-message' => 'tog-previewontop', 848 ); 849 $defaultPreferences['uselivepreview'] = array( 850 'type' => 'toggle', 851 'section' => 'editing/preview', 852 'label-message' => 'tog-uselivepreview', 853 ); 854 855 } 856 857 /** 858 * @param User $user 859 * @param IContextSource $context 860 * @param array $defaultPreferences 861 */ 862 static function rcPreferences( $user, IContextSource $context, &$defaultPreferences ) { 863 $config = $context->getConfig(); 864 $rcMaxAge = $config->get( 'RCMaxAge' ); 865 ## RecentChanges ##################################### 866 $defaultPreferences['rcdays'] = array( 867 'type' => 'float', 868 'label-message' => 'recentchangesdays', 869 'section' => 'rc/displayrc', 870 'min' => 1, 871 'max' => ceil( $rcMaxAge / ( 3600 * 24 ) ), 872 'help' => $context->msg( 'recentchangesdays-max' )->numParams( 873 ceil( $rcMaxAge / ( 3600 * 24 ) ) )->text() 874 ); 875 $defaultPreferences['rclimit'] = array( 876 'type' => 'int', 877 'label-message' => 'recentchangescount', 878 'help-message' => 'prefs-help-recentchangescount', 879 'section' => 'rc/displayrc', 880 ); 881 $defaultPreferences['usenewrc'] = array( 882 'type' => 'toggle', 883 'label-message' => 'tog-usenewrc', 884 'section' => 'rc/advancedrc', 885 ); 886 $defaultPreferences['hideminor'] = array( 887 'type' => 'toggle', 888 'label-message' => 'tog-hideminor', 889 'section' => 'rc/advancedrc', 890 ); 891 892 if ( $user->useRCPatrol() ) { 893 $defaultPreferences['hidepatrolled'] = array( 894 'type' => 'toggle', 895 'section' => 'rc/advancedrc', 896 'label-message' => 'tog-hidepatrolled', 897 ); 898 $defaultPreferences['newpageshidepatrolled'] = array( 899 'type' => 'toggle', 900 'section' => 'rc/advancedrc', 901 'label-message' => 'tog-newpageshidepatrolled', 902 ); 903 } 904 905 if ( $config->get( 'RCShowWatchingUsers' ) ) { 906 $defaultPreferences['shownumberswatching'] = array( 907 'type' => 'toggle', 908 'section' => 'rc/advancedrc', 909 'label-message' => 'tog-shownumberswatching', 910 ); 911 } 912 } 913 914 /** 915 * @param User $user 916 * @param IContextSource $context 917 * @param array $defaultPreferences 918 */ 919 static function watchlistPreferences( $user, IContextSource $context, &$defaultPreferences ) { 920 $config = $context->getConfig(); 921 $watchlistdaysMax = ceil( $config->get( 'RCMaxAge' ) / ( 3600 * 24 ) ); 922 923 ## Watchlist ##################################### 924 $defaultPreferences['watchlistdays'] = array( 925 'type' => 'float', 926 'min' => 0, 927 'max' => $watchlistdaysMax, 928 'section' => 'watchlist/displaywatchlist', 929 'help' => $context->msg( 'prefs-watchlist-days-max' )->numParams( 930 $watchlistdaysMax )->text(), 931 'label-message' => 'prefs-watchlist-days', 932 ); 933 $defaultPreferences['wllimit'] = array( 934 'type' => 'int', 935 'min' => 0, 936 'max' => 1000, 937 'label-message' => 'prefs-watchlist-edits', 938 'help' => $context->msg( 'prefs-watchlist-edits-max' )->escaped(), 939 'section' => 'watchlist/displaywatchlist', 940 ); 941 $defaultPreferences['extendwatchlist'] = array( 942 'type' => 'toggle', 943 'section' => 'watchlist/advancedwatchlist', 944 'label-message' => 'tog-extendwatchlist', 945 ); 946 $defaultPreferences['watchlisthideminor'] = array( 947 'type' => 'toggle', 948 'section' => 'watchlist/advancedwatchlist', 949 'label-message' => 'tog-watchlisthideminor', 950 ); 951 $defaultPreferences['watchlisthidebots'] = array( 952 'type' => 'toggle', 953 'section' => 'watchlist/advancedwatchlist', 954 'label-message' => 'tog-watchlisthidebots', 955 ); 956 $defaultPreferences['watchlisthideown'] = array( 957 'type' => 'toggle', 958 'section' => 'watchlist/advancedwatchlist', 959 'label-message' => 'tog-watchlisthideown', 960 ); 961 $defaultPreferences['watchlisthideanons'] = array( 962 'type' => 'toggle', 963 'section' => 'watchlist/advancedwatchlist', 964 'label-message' => 'tog-watchlisthideanons', 965 ); 966 $defaultPreferences['watchlisthideliu'] = array( 967 'type' => 'toggle', 968 'section' => 'watchlist/advancedwatchlist', 969 'label-message' => 'tog-watchlisthideliu', 970 ); 971 972 if ( $context->getConfig()->get( 'UseRCPatrol' ) ) { 973 $defaultPreferences['watchlisthidepatrolled'] = array( 974 'type' => 'toggle', 975 'section' => 'watchlist/advancedwatchlist', 976 'label-message' => 'tog-watchlisthidepatrolled', 977 ); 978 } 979 980 $watchTypes = array( 981 'edit' => 'watchdefault', 982 'move' => 'watchmoves', 983 'delete' => 'watchdeletion' 984 ); 985 986 // Kinda hacky 987 if ( $user->isAllowed( 'createpage' ) || $user->isAllowed( 'createtalk' ) ) { 988 $watchTypes['read'] = 'watchcreations'; 989 } 990 991 if ( $user->isAllowed( 'rollback' ) ) { 992 $watchTypes['rollback'] = 'watchrollback'; 993 } 994 995 foreach ( $watchTypes as $action => $pref ) { 996 if ( $user->isAllowed( $action ) ) { 997 // Messages: 998 // tog-watchdefault, tog-watchmoves, tog-watchdeletion, tog-watchcreations 999 // tog-watchrollback 1000 $defaultPreferences[$pref] = array( 1001 'type' => 'toggle', 1002 'section' => 'watchlist/advancedwatchlist', 1003 'label-message' => "tog-$pref", 1004 ); 1005 } 1006 } 1007 1008 if ( $config->get( 'EnableAPI' ) ) { 1009 $defaultPreferences['watchlisttoken'] = array( 1010 'type' => 'api', 1011 ); 1012 $defaultPreferences['watchlisttoken-info'] = array( 1013 'type' => 'info', 1014 'section' => 'watchlist/tokenwatchlist', 1015 'label-message' => 'prefs-watchlist-token', 1016 'default' => $user->getTokenFromOption( 'watchlisttoken' ), 1017 'help-message' => 'prefs-help-watchlist-token2', 1018 ); 1019 } 1020 } 1021 1022 /** 1023 * @param User $user 1024 * @param IContextSource $context 1025 * @param array $defaultPreferences 1026 */ 1027 static function searchPreferences( $user, IContextSource $context, &$defaultPreferences ) { 1028 foreach ( MWNamespace::getValidNamespaces() as $n ) { 1029 $defaultPreferences['searchNs' . $n] = array( 1030 'type' => 'api', 1031 ); 1032 } 1033 } 1034 1035 /** 1036 * Dummy, kept for backwards-compatibility. 1037 */ 1038 static function miscPreferences( $user, IContextSource $context, &$defaultPreferences ) { 1039 } 1040 1041 /** 1042 * @param User $user The User object 1043 * @param IContextSource $context 1044 * @return array Text/links to display as key; $skinkey as value 1045 */ 1046 static function generateSkinOptions( $user, IContextSource $context ) { 1047 $ret = array(); 1048 1049 $mptitle = Title::newMainPage(); 1050 $previewtext = $context->msg( 'skin-preview' )->text(); 1051 1052 # Only show skins that aren't disabled in $wgSkipSkins 1053 $validSkinNames = Skin::getAllowedSkins(); 1054 1055 # Sort by UI skin name. First though need to update validSkinNames as sometimes 1056 # the skinkey & UI skinname differ (e.g. "standard" skinkey is "Classic" in the UI). 1057 foreach ( $validSkinNames as $skinkey => &$skinname ) { 1058 $msg = $context->msg( "skinname-{$skinkey}" ); 1059 if ( $msg->exists() ) { 1060 $skinname = htmlspecialchars( $msg->text() ); 1061 } 1062 } 1063 asort( $validSkinNames ); 1064 1065 $config = $context->getConfig(); 1066 $defaultSkin = $config->get( 'DefaultSkin' ); 1067 $allowUserCss = $config->get( 'AllowUserCss' ); 1068 $allowUserJs = $config->get( 'AllowUserJs' ); 1069 1070 $foundDefault = false; 1071 foreach ( $validSkinNames as $skinkey => $sn ) { 1072 $linkTools = array(); 1073 1074 # Mark the default skin 1075 if ( $skinkey == $defaultSkin ) { 1076 $linkTools[] = $context->msg( 'default' )->escaped(); 1077 $foundDefault = true; 1078 } 1079 1080 # Create preview link 1081 $mplink = htmlspecialchars( $mptitle->getLocalURL( array( 'useskin' => $skinkey ) ) ); 1082 $linkTools[] = "<a target='_blank' href=\"$mplink\">$previewtext</a>"; 1083 1084 # Create links to user CSS/JS pages 1085 if ( $allowUserCss ) { 1086 $cssPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.css' ); 1087 $linkTools[] = Linker::link( $cssPage, $context->msg( 'prefs-custom-css' )->escaped() ); 1088 } 1089 1090 if ( $allowUserJs ) { 1091 $jsPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.js' ); 1092 $linkTools[] = Linker::link( $jsPage, $context->msg( 'prefs-custom-js' )->escaped() ); 1093 } 1094 1095 $display = $sn . ' ' . $context->msg( 1096 'parentheses', 1097 $context->getLanguage()->pipeList( $linkTools ) 1098 )->text(); 1099 $ret[$display] = $skinkey; 1100 } 1101 1102 if ( !$foundDefault ) { 1103 // If the default skin is not available, things are going to break horribly because the 1104 // default value for skin selector will not be a valid value. Let's just not show it then. 1105 return array(); 1106 } 1107 1108 return $ret; 1109 } 1110 1111 /** 1112 * @param IContextSource $context 1113 * @return array 1114 */ 1115 static function getDateOptions( IContextSource $context ) { 1116 $lang = $context->getLanguage(); 1117 $dateopts = $lang->getDatePreferences(); 1118 1119 $ret = array(); 1120 1121 if ( $dateopts ) { 1122 if ( !in_array( 'default', $dateopts ) ) { 1123 $dateopts[] = 'default'; // Make sure default is always valid 1124 // Bug 19237 1125 } 1126 1127 // FIXME KLUGE: site default might not be valid for user language 1128 global $wgDefaultUserOptions; 1129 if ( !in_array( $wgDefaultUserOptions['date'], $dateopts ) ) { 1130 $wgDefaultUserOptions['date'] = 'default'; 1131 } 1132 1133 $epoch = wfTimestampNow(); 1134 foreach ( $dateopts as $key ) { 1135 if ( $key == 'default' ) { 1136 $formatted = $context->msg( 'datedefault' )->escaped(); 1137 } else { 1138 $formatted = htmlspecialchars( $lang->timeanddate( $epoch, false, $key ) ); 1139 } 1140 $ret[$formatted] = $key; 1141 } 1142 } 1143 return $ret; 1144 } 1145 1146 /** 1147 * @param IContextSource $context 1148 * @return array 1149 */ 1150 static function getImageSizes( IContextSource $context ) { 1151 $ret = array(); 1152 $pixels = $context->msg( 'unit-pixel' )->text(); 1153 1154 foreach ( $context->getConfig()->get( 'ImageLimits' ) as $index => $limits ) { 1155 $display = "{$limits[0]}×{$limits[1]}" . $pixels; 1156 $ret[$display] = $index; 1157 } 1158 1159 return $ret; 1160 } 1161 1162 /** 1163 * @param IContextSource $context 1164 * @return array 1165 */ 1166 static function getThumbSizes( IContextSource $context ) { 1167 $ret = array(); 1168 $pixels = $context->msg( 'unit-pixel' )->text(); 1169 1170 foreach ( $context->getConfig()->get( 'ThumbLimits' ) as $index => $size ) { 1171 $display = $size . $pixels; 1172 $ret[$display] = $index; 1173 } 1174 1175 return $ret; 1176 } 1177 1178 /** 1179 * @param string $signature 1180 * @param array $alldata 1181 * @param HTMLForm $form 1182 * @return bool|string 1183 */ 1184 static function validateSignature( $signature, $alldata, $form ) { 1185 global $wgParser; 1186 $maxSigChars = $form->getConfig()->get( 'MaxSigChars' ); 1187 if ( mb_strlen( $signature ) > $maxSigChars ) { 1188 return Xml::element( 'span', array( 'class' => 'error' ), 1189 $form->msg( 'badsiglength' )->numParams( $maxSigChars )->text() ); 1190 } elseif ( isset( $alldata['fancysig'] ) && 1191 $alldata['fancysig'] && 1192 $wgParser->validateSig( $signature ) === false 1193 ) { 1194 return Xml::element( 1195 'span', 1196 array( 'class' => 'error' ), 1197 $form->msg( 'badsig' )->text() 1198 ); 1199 } else { 1200 return true; 1201 } 1202 } 1203 1204 /** 1205 * @param string $signature 1206 * @param array $alldata 1207 * @param HTMLForm $form 1208 * @return string 1209 */ 1210 static function cleanSignature( $signature, $alldata, $form ) { 1211 if ( isset( $alldata['fancysig'] ) && $alldata['fancysig'] ) { 1212 global $wgParser; 1213 $signature = $wgParser->cleanSig( $signature ); 1214 } else { 1215 // When no fancy sig used, make sure ~{3,5} get removed. 1216 $signature = Parser::cleanSigInSig( $signature ); 1217 } 1218 1219 return $signature; 1220 } 1221 1222 /** 1223 * @param User $user 1224 * @param IContextSource $context 1225 * @param string $formClass 1226 * @param array $remove Array of items to remove 1227 * @return HtmlForm 1228 */ 1229 static function getFormObject( 1230 $user, 1231 IContextSource $context, 1232 $formClass = 'PreferencesForm', 1233 array $remove = array() 1234 ) { 1235 $formDescriptor = Preferences::getPreferences( $user, $context ); 1236 if ( count( $remove ) ) { 1237 $removeKeys = array_flip( $remove ); 1238 $formDescriptor = array_diff_key( $formDescriptor, $removeKeys ); 1239 } 1240 1241 // Remove type=api preferences. They are not intended for rendering in the form. 1242 foreach ( $formDescriptor as $name => $info ) { 1243 if ( isset( $info['type'] ) && $info['type'] === 'api' ) { 1244 unset( $formDescriptor[$name] ); 1245 } 1246 } 1247 1248 /** 1249 * @var $htmlForm PreferencesForm 1250 */ 1251 $htmlForm = new $formClass( $formDescriptor, $context, 'prefs' ); 1252 1253 $htmlForm->setModifiedUser( $user ); 1254 $htmlForm->setId( 'mw-prefs-form' ); 1255 $htmlForm->setSubmitText( $context->msg( 'saveprefs' )->text() ); 1256 # Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save' 1257 $htmlForm->setSubmitTooltip( 'preferences-save' ); 1258 $htmlForm->setSubmitID( 'prefsubmit' ); 1259 $htmlForm->setSubmitCallback( array( 'Preferences', 'tryFormSubmit' ) ); 1260 1261 return $htmlForm; 1262 } 1263 1264 /** 1265 * @param IContextSource $context 1266 * @return array 1267 */ 1268 static function getTimezoneOptions( IContextSource $context ) { 1269 $opt = array(); 1270 1271 $localTZoffset = $context->getConfig()->get( 'LocalTZoffset' ); 1272 $timestamp = MWTimestamp::getLocalInstance(); 1273 // Check that the LocalTZoffset is the same as the local time zone offset 1274 if ( $localTZoffset == $timestamp->format( 'Z' ) / 60 ) { 1275 $server_tz_msg = $context->msg( 1276 'timezoneuseserverdefault', 1277 $timestamp->getTimezone()->getName() 1278 )->text(); 1279 } else { 1280 $tzstring = sprintf( 1281 '%+03d:%02d', 1282 floor( $localTZoffset / 60 ), 1283 abs( $localTZoffset ) % 60 1284 ); 1285 $server_tz_msg = $context->msg( 'timezoneuseserverdefault', $tzstring )->text(); 1286 } 1287 $opt[$server_tz_msg] = "System|$localTZoffset"; 1288 $opt[$context->msg( 'timezoneuseoffset' )->text()] = 'other'; 1289 $opt[$context->msg( 'guesstimezone' )->text()] = 'guess'; 1290 1291 if ( function_exists( 'timezone_identifiers_list' ) ) { 1292 # Read timezone list 1293 $tzs = timezone_identifiers_list(); 1294 sort( $tzs ); 1295 1296 $tzRegions = array(); 1297 $tzRegions['Africa'] = $context->msg( 'timezoneregion-africa' )->text(); 1298 $tzRegions['America'] = $context->msg( 'timezoneregion-america' )->text(); 1299 $tzRegions['Antarctica'] = $context->msg( 'timezoneregion-antarctica' )->text(); 1300 $tzRegions['Arctic'] = $context->msg( 'timezoneregion-arctic' )->text(); 1301 $tzRegions['Asia'] = $context->msg( 'timezoneregion-asia' )->text(); 1302 $tzRegions['Atlantic'] = $context->msg( 'timezoneregion-atlantic' )->text(); 1303 $tzRegions['Australia'] = $context->msg( 'timezoneregion-australia' )->text(); 1304 $tzRegions['Europe'] = $context->msg( 'timezoneregion-europe' )->text(); 1305 $tzRegions['Indian'] = $context->msg( 'timezoneregion-indian' )->text(); 1306 $tzRegions['Pacific'] = $context->msg( 'timezoneregion-pacific' )->text(); 1307 asort( $tzRegions ); 1308 1309 $prefill = array_fill_keys( array_values( $tzRegions ), array() ); 1310 $opt = array_merge( $opt, $prefill ); 1311 1312 $now = date_create( 'now' ); 1313 1314 foreach ( $tzs as $tz ) { 1315 $z = explode( '/', $tz, 2 ); 1316 1317 # timezone_identifiers_list() returns a number of 1318 # backwards-compatibility entries. This filters them out of the 1319 # list presented to the user. 1320 if ( count( $z ) != 2 || !array_key_exists( $z[0], $tzRegions ) ) { 1321 continue; 1322 } 1323 1324 # Localize region 1325 $z[0] = $tzRegions[$z[0]]; 1326 1327 $minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 ); 1328 1329 $display = str_replace( '_', ' ', $z[0] . '/' . $z[1] ); 1330 $value = "ZoneInfo|$minDiff|$tz"; 1331 1332 $opt[$z[0]][$display] = $value; 1333 } 1334 } 1335 return $opt; 1336 } 1337 1338 /** 1339 * @param string $value 1340 * @param array $alldata 1341 * @return int 1342 */ 1343 static function filterIntval( $value, $alldata ) { 1344 return intval( $value ); 1345 } 1346 1347 /** 1348 * @param string $tz 1349 * @param array $alldata 1350 * @return string 1351 */ 1352 static function filterTimezoneInput( $tz, $alldata ) { 1353 $data = explode( '|', $tz, 3 ); 1354 switch ( $data[0] ) { 1355 case 'ZoneInfo': 1356 case 'System': 1357 return $tz; 1358 default: 1359 $data = explode( ':', $tz, 2 ); 1360 if ( count( $data ) == 2 ) { 1361 $data[0] = intval( $data[0] ); 1362 $data[1] = intval( $data[1] ); 1363 $minDiff = abs( $data[0] ) * 60 + $data[1]; 1364 if ( $data[0] < 0 ) { 1365 $minDiff = - $minDiff; 1366 } 1367 } else { 1368 $minDiff = intval( $data[0] ) * 60; 1369 } 1370 1371 # Max is +14:00 and min is -12:00, see: 1372 # http://en.wikipedia.org/wiki/Timezone 1373 $minDiff = min( $minDiff, 840 ); # 14:00 1374 $minDiff = max( $minDiff, - 720 ); # -12:00 1375 return 'Offset|' . $minDiff; 1376 } 1377 } 1378 1379 /** 1380 * Handle the form submission if everything validated properly 1381 * 1382 * @param array $formData 1383 * @param PreferencesForm $form 1384 * @return bool|Status|string 1385 */ 1386 static function tryFormSubmit( $formData, $form ) { 1387 global $wgAuth; 1388 1389 $user = $form->getModifiedUser(); 1390 $hiddenPrefs = $form->getConfig()->get( 'HiddenPrefs' ); 1391 $result = true; 1392 1393 if ( !$user->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) { 1394 return Status::newFatal( 'mypreferencesprotected' ); 1395 } 1396 1397 // Filter input 1398 foreach ( array_keys( $formData ) as $name ) { 1399 if ( isset( self::$saveFilters[$name] ) ) { 1400 $formData[$name] = 1401 call_user_func( self::$saveFilters[$name], $formData[$name], $formData ); 1402 } 1403 } 1404 1405 // Fortunately, the realname field is MUCH simpler 1406 // (not really "private", but still shouldn't be edited without permission) 1407 if ( !in_array( 'realname', $hiddenPrefs ) 1408 && $user->isAllowed( 'editmyprivateinfo' ) 1409 && array_key_exists( 'realname', $formData ) 1410 ) { 1411 $realName = $formData['realname']; 1412 $user->setRealName( $realName ); 1413 } 1414 1415 if ( $user->isAllowed( 'editmyoptions' ) ) { 1416 foreach ( self::$saveBlacklist as $b ) { 1417 unset( $formData[$b] ); 1418 } 1419 1420 # If users have saved a value for a preference which has subsequently been disabled 1421 # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference 1422 # is subsequently re-enabled 1423 foreach ( $hiddenPrefs as $pref ) { 1424 # If the user has not set a non-default value here, the default will be returned 1425 # and subsequently discarded 1426 $formData[$pref] = $user->getOption( $pref, null, true ); 1427 } 1428 1429 // Keep old preferences from interfering due to back-compat code, etc. 1430 $user->resetOptions( 'unused', $form->getContext() ); 1431 1432 foreach ( $formData as $key => $value ) { 1433 $user->setOption( $key, $value ); 1434 } 1435 1436 wfRunHooks( 'PreferencesFormPreSave', array( $formData, $form, $user, &$result ) ); 1437 $user->saveSettings(); 1438 } 1439 1440 $wgAuth->updateExternalDB( $user ); 1441 1442 return $result; 1443 } 1444 1445 /** 1446 * @param array $formData 1447 * @param PreferencesForm $form 1448 * @return Status 1449 */ 1450 public static function tryUISubmit( $formData, $form ) { 1451 $res = self::tryFormSubmit( $formData, $form ); 1452 1453 if ( $res ) { 1454 $urlOptions = array( 'success' => 1 ); 1455 1456 if ( $res === 'eauth' ) { 1457 $urlOptions['eauth'] = 1; 1458 } 1459 1460 $urlOptions += $form->getExtraSuccessRedirectParameters(); 1461 1462 $url = $form->getTitle()->getFullURL( $urlOptions ); 1463 1464 $form->getContext()->getOutput()->redirect( $url ); 1465 } 1466 1467 return Status::newGood(); 1468 } 1469 1470 /** 1471 * Try to set a user's email address. 1472 * This does *not* try to validate the address. 1473 * Caller is responsible for checking $wgAuth and 'editmyprivateinfo' 1474 * right. 1475 * 1476 * @deprecated since 1.20; use User::setEmailWithConfirmation() instead. 1477 * @param User $user 1478 * @param string $newaddr New email address 1479 * @return array (true on success or Status on failure, info string) 1480 */ 1481 public static function trySetUserEmail( User $user, $newaddr ) { 1482 wfDeprecated( __METHOD__, '1.20' ); 1483 1484 $result = $user->setEmailWithConfirmation( $newaddr ); 1485 if ( $result->isGood() ) { 1486 return array( true, $result->value ); 1487 } else { 1488 return array( $result, 'mailerror' ); 1489 } 1490 } 1491 } 1492 1493 /** Some tweaks to allow js prefs to work */ 1494 class PreferencesForm extends HTMLForm { 1495 // Override default value from HTMLForm 1496 protected $mSubSectionBeforeFields = false; 1497 1498 private $modifiedUser; 1499 1500 /** 1501 * @param User $user 1502 */ 1503 public function setModifiedUser( $user ) { 1504 $this->modifiedUser = $user; 1505 } 1506 1507 /** 1508 * @return User 1509 */ 1510 public function getModifiedUser() { 1511 if ( $this->modifiedUser === null ) { 1512 return $this->getUser(); 1513 } else { 1514 return $this->modifiedUser; 1515 } 1516 } 1517 1518 /** 1519 * Get extra parameters for the query string when redirecting after 1520 * successful save. 1521 * 1522 * @return array() 1523 */ 1524 public function getExtraSuccessRedirectParameters() { 1525 return array(); 1526 } 1527 1528 /** 1529 * @param string $html 1530 * @return string 1531 */ 1532 function wrapForm( $html ) { 1533 $html = Xml::tags( 'div', array( 'id' => 'preferences' ), $html ); 1534 1535 return parent::wrapForm( $html ); 1536 } 1537 1538 /** 1539 * @return string 1540 */ 1541 function getButtons() { 1542 global $wgUseMediaWikiUIEverywhere; 1543 1544 $attrs = array( 'id' => 'mw-prefs-restoreprefs' ); 1545 if ( $wgUseMediaWikiUIEverywhere ) { 1546 $attrs['class'] = 'mw-ui-button mw-ui-quiet'; 1547 } 1548 1549 if ( !$this->getModifiedUser()->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) { 1550 return ''; 1551 } 1552 1553 $html = parent::getButtons(); 1554 1555 if ( $this->getModifiedUser()->isAllowed( 'editmyoptions' ) ) { 1556 $t = SpecialPage::getTitleFor( 'Preferences', 'reset' ); 1557 1558 $html .= "\n" . Linker::link( $t, $this->msg( 'restoreprefs' )->escaped(), 1559 $attrs ); 1560 1561 $html = Xml::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html ); 1562 } 1563 1564 return $html; 1565 } 1566 1567 /** 1568 * Separate multi-option preferences into multiple preferences, since we 1569 * have to store them separately 1570 * @param array $data 1571 * @return array 1572 */ 1573 function filterDataForSubmit( $data ) { 1574 foreach ( $this->mFlatFields as $fieldname => $field ) { 1575 if ( $field instanceof HTMLNestedFilterable ) { 1576 $info = $field->mParams; 1577 $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $fieldname; 1578 foreach ( $field->filterDataForSubmit( $data[$fieldname] ) as $key => $value ) { 1579 $data["$prefix$key"] = $value; 1580 } 1581 unset( $data[$fieldname] ); 1582 } 1583 } 1584 1585 return $data; 1586 } 1587 1588 /** 1589 * Get the whole body of the form. 1590 * @return string 1591 */ 1592 function getBody() { 1593 return $this->displaySection( $this->mFieldTree, '', 'mw-prefsection-' ); 1594 } 1595 1596 /** 1597 * Get the "<legend>" for a given section key. Normally this is the 1598 * prefs-$key message but we'll allow extensions to override it. 1599 * @param string $key 1600 * @return string 1601 */ 1602 function getLegend( $key ) { 1603 $legend = parent::getLegend( $key ); 1604 wfRunHooks( 'PreferencesGetLegend', array( $this, $key, &$legend ) ); 1605 return $legend; 1606 } 1607 }
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 |