MediaWiki  REL1_22
Preferences.php
Go to the documentation of this file.
00001 <?php
00048 class Preferences {
00049     static $defaultPreferences = null;
00050     static $saveFilters = array(
00051             'timecorrection' => array( 'Preferences', 'filterTimezoneInput' ),
00052             'cols' => array( 'Preferences', 'filterIntval' ),
00053             'rows' => array( 'Preferences', 'filterIntval' ),
00054             'rclimit' => array( 'Preferences', 'filterIntval' ),
00055             'wllimit' => array( 'Preferences', 'filterIntval' ),
00056             'searchlimit' => array( 'Preferences', 'filterIntval' ),
00057     );
00058 
00059     // Stuff that shouldn't be saved as a preference.
00060     private static $saveBlacklist = array(
00061         'realname',
00062         'emailaddress',
00063     );
00064 
00071     static function getPreferences( $user, IContextSource $context ) {
00072         if ( self::$defaultPreferences ) {
00073             return self::$defaultPreferences;
00074         }
00075 
00076         $defaultPreferences = array();
00077 
00078         self::profilePreferences( $user, $context, $defaultPreferences );
00079         self::skinPreferences( $user, $context, $defaultPreferences );
00080         self::filesPreferences( $user, $context, $defaultPreferences );
00081         self::datetimePreferences( $user, $context, $defaultPreferences );
00082         self::renderingPreferences( $user, $context, $defaultPreferences );
00083         self::editingPreferences( $user, $context, $defaultPreferences );
00084         self::rcPreferences( $user, $context, $defaultPreferences );
00085         self::watchlistPreferences( $user, $context, $defaultPreferences );
00086         self::searchPreferences( $user, $context, $defaultPreferences );
00087         self::miscPreferences( $user, $context, $defaultPreferences );
00088 
00089         wfRunHooks( 'GetPreferences', array( $user, &$defaultPreferences ) );
00090 
00091         ## Remove preferences that wikis don't want to use
00092         global $wgHiddenPrefs;
00093         foreach ( $wgHiddenPrefs as $pref ) {
00094             if ( isset( $defaultPreferences[$pref] ) ) {
00095                 unset( $defaultPreferences[$pref] );
00096             }
00097         }
00098 
00099         ## Make sure that form fields have their parent set. See bug 41337.
00100         $dummyForm = new HTMLForm( array(), $context );
00101 
00102         $disable = !$user->isAllowed( 'editmyoptions' );
00103 
00104         ## Prod in defaults from the user
00105         foreach ( $defaultPreferences as $name => &$info ) {
00106             $prefFromUser = self::getOptionFromUser( $name, $info, $user );
00107             if ( $disable && !in_array( $name, self::$saveBlacklist ) ) {
00108                 $info['disabled'] = 'disabled';
00109             }
00110             $field = HTMLForm::loadInputFromParameters( $name, $info ); // For validation
00111             $field->mParent = $dummyForm;
00112             $defaultOptions = User::getDefaultOptions();
00113             $globalDefault = isset( $defaultOptions[$name] )
00114                 ? $defaultOptions[$name]
00115                 : null;
00116 
00117             // If it validates, set it as the default
00118             if ( isset( $info['default'] ) ) {
00119                 // Already set, no problem
00120                 continue;
00121             } elseif ( !is_null( $prefFromUser ) && // Make sure we're not just pulling nothing
00122                     $field->validate( $prefFromUser, $user->getOptions() ) === true ) {
00123                 $info['default'] = $prefFromUser;
00124             } elseif ( $field->validate( $globalDefault, $user->getOptions() ) === true ) {
00125                 $info['default'] = $globalDefault;
00126             } else {
00127                 throw new MWException( "Global default '$globalDefault' is invalid for field $name" );
00128             }
00129         }
00130 
00131         self::$defaultPreferences = $defaultPreferences;
00132 
00133         return $defaultPreferences;
00134     }
00135 
00144     static function getOptionFromUser( $name, $info, $user ) {
00145         $val = $user->getOption( $name );
00146 
00147         // Handling for multiselect preferences
00148         if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
00149                 ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
00150             $options = HTMLFormField::flattenOptions( $info['options'] );
00151             $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
00152             $val = array();
00153 
00154             foreach ( $options as $value ) {
00155                 if ( $user->getOption( "$prefix$value" ) ) {
00156                     $val[] = $value;
00157                 }
00158             }
00159         }
00160 
00161         // Handling for checkmatrix preferences
00162         if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
00163                 ( isset( $info['class'] ) && $info['class'] == 'HTMLCheckMatrix' ) ) {
00164             $columns = HTMLFormField::flattenOptions( $info['columns'] );
00165             $rows = HTMLFormField::flattenOptions( $info['rows'] );
00166             $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
00167             $val = array();
00168 
00169             foreach ( $columns as $column ) {
00170                 foreach ( $rows as $row ) {
00171                     if ( $user->getOption( "$prefix$column-$row" ) ) {
00172                         $val[] = "$column-$row";
00173                     }
00174                 }
00175             }
00176         }
00177 
00178         return $val;
00179     }
00180 
00187     static function profilePreferences( $user, IContextSource $context, &$defaultPreferences ) {
00188         global $wgAuth, $wgContLang, $wgParser, $wgCookieExpiration, $wgLanguageCode,
00189             $wgDisableTitleConversion, $wgDisableLangConversion, $wgMaxSigChars,
00190             $wgEnableEmail, $wgEmailConfirmToEdit, $wgEnableUserEmail, $wgEmailAuthentication,
00191             $wgEnotifWatchlist, $wgEnotifUserTalk, $wgEnotifRevealEditorAddress,
00192             $wgSecureLogin;
00193 
00194         // retrieving user name for GENDER and misc.
00195         $userName = $user->getName();
00196 
00197         ## User info #####################################
00198         // Information panel
00199         $defaultPreferences['username'] = array(
00200             'type' => 'info',
00201             'label-message' => array( 'username', $userName ),
00202             'default' => $userName,
00203             'section' => 'personal/info',
00204         );
00205 
00206         $defaultPreferences['userid'] = array(
00207             'type' => 'info',
00208             'label-message' => array( 'uid', $userName ),
00209             'default' => $user->getId(),
00210             'section' => 'personal/info',
00211         );
00212 
00213         # Get groups to which the user belongs
00214         $userEffectiveGroups = $user->getEffectiveGroups();
00215         $userGroups = $userMembers = array();
00216         foreach ( $userEffectiveGroups as $ueg ) {
00217             if ( $ueg == '*' ) {
00218                 // Skip the default * group, seems useless here
00219                 continue;
00220             }
00221             $groupName = User::getGroupName( $ueg );
00222             $userGroups[] = User::makeGroupLinkHTML( $ueg, $groupName );
00223 
00224             $memberName = User::getGroupMember( $ueg, $userName );
00225             $userMembers[] = User::makeGroupLinkHTML( $ueg, $memberName );
00226         }
00227         asort( $userGroups );
00228         asort( $userMembers );
00229 
00230         $lang = $context->getLanguage();
00231 
00232         $defaultPreferences['usergroups'] = array(
00233             'type' => 'info',
00234             'label' => $context->msg( 'prefs-memberingroups' )->numParams(
00235                 count( $userGroups ) )->params( $userName )->parse(),
00236             'default' => $context->msg( 'prefs-memberingroups-type',
00237                 $lang->commaList( $userGroups ),
00238                 $lang->commaList( $userMembers )
00239             )->plain(),
00240             'raw' => true,
00241             'section' => 'personal/info',
00242         );
00243 
00244         $editCount = Linker::link( SpecialPage::getTitleFor( "Contributions", $userName ),
00245             $lang->formatNum( $user->getEditCount() ) );
00246 
00247         $defaultPreferences['editcount'] = array(
00248             'type' => 'info',
00249             'raw' => true,
00250             'label-message' => 'prefs-edits',
00251             'default' => $editCount,
00252             'section' => 'personal/info',
00253         );
00254 
00255         if ( $user->getRegistration() ) {
00256             $displayUser = $context->getUser();
00257             $userRegistration = $user->getRegistration();
00258             $defaultPreferences['registrationdate'] = array(
00259                 'type' => 'info',
00260                 'label-message' => 'prefs-registration',
00261                 'default' => $context->msg(
00262                     'prefs-registration-date-time',
00263                     $lang->userTimeAndDate( $userRegistration, $displayUser ),
00264                     $lang->userDate( $userRegistration, $displayUser ),
00265                     $lang->userTime( $userRegistration, $displayUser )
00266                 )->parse(),
00267                 'section' => 'personal/info',
00268             );
00269         }
00270 
00271         $canViewPrivateInfo = $user->isAllowed( 'viewmyprivateinfo' );
00272         $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' );
00273 
00274         // Actually changeable stuff
00275         $defaultPreferences['realname'] = array(
00276             // (not really "private", but still shouldn't be edited without permission)
00277             'type' => $canEditPrivateInfo && $wgAuth->allowPropChange( 'realname' ) ? 'text' : 'info',
00278             'default' => $user->getRealName(),
00279             'section' => 'personal/info',
00280             'label-message' => 'yourrealname',
00281             'help-message' => 'prefs-help-realname',
00282         );
00283 
00284         if ( $canEditPrivateInfo && $wgAuth->allowPasswordChange() ) {
00285             $link = Linker::link( SpecialPage::getTitleFor( 'ChangePassword' ),
00286                 $context->msg( 'prefs-resetpass' )->escaped(), array(),
00287                 array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) );
00288 
00289             $defaultPreferences['password'] = array(
00290                 'type' => 'info',
00291                 'raw' => true,
00292                 'default' => $link,
00293                 'label-message' => 'yourpassword',
00294                 'section' => 'personal/info',
00295             );
00296         }
00297         if ( $wgCookieExpiration > 0 ) {
00298             $defaultPreferences['rememberpassword'] = array(
00299                 'type' => 'toggle',
00300                 'label' => $context->msg( 'tog-rememberpassword' )->numParams(
00301                     ceil( $wgCookieExpiration / ( 3600 * 24 ) ) )->text(),
00302                 'section' => 'personal/info',
00303             );
00304         }
00305         // Only show preferhttps if secure login is turned on
00306         if ( $wgSecureLogin && wfCanIPUseHTTPS( $context->getRequest()->getIP() ) ) {
00307             $defaultPreferences['prefershttps'] = array(
00308                 'type' => 'toggle',
00309                 'label-message' => 'tog-prefershttps',
00310                 'help-message' => 'prefs-help-prefershttps',
00311                 'section' => 'personal/info'
00312             );
00313         }
00314 
00315         // Language
00316         $languages = Language::fetchLanguageNames( null, 'mw' );
00317         if ( !array_key_exists( $wgLanguageCode, $languages ) ) {
00318             $languages[$wgLanguageCode] = $wgLanguageCode;
00319         }
00320         ksort( $languages );
00321 
00322         $options = array();
00323         foreach ( $languages as $code => $name ) {
00324             $display = wfBCP47( $code ) . ' - ' . $name;
00325             $options[$display] = $code;
00326         }
00327         $defaultPreferences['language'] = array(
00328             'type' => 'select',
00329             'section' => 'personal/i18n',
00330             'options' => $options,
00331             'label-message' => 'yourlanguage',
00332         );
00333 
00334         $defaultPreferences['gender'] = array(
00335             'type' => 'radio',
00336             'section' => 'personal/i18n',
00337             'options' => array(
00338                 $context->msg( 'parentheses',
00339                     $context->msg( 'gender-unknown' )->text()
00340                 )->text() => 'unknown',
00341                 $context->msg( 'gender-female' )->text() => 'female',
00342                 $context->msg( 'gender-male' )->text() => 'male',
00343             ),
00344             'label-message' => 'yourgender',
00345             'help-message' => 'prefs-help-gender',
00346         );
00347 
00348         // see if there are multiple language variants to choose from
00349         if ( !$wgDisableLangConversion ) {
00350             foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
00351                 if ( $langCode == $wgContLang->getCode() ) {
00352                     $variants = $wgContLang->getVariants();
00353 
00354                     if ( count( $variants ) <= 1 ) {
00355                         continue;
00356                     }
00357 
00358                     $variantArray = array();
00359                     foreach ( $variants as $v ) {
00360                         $v = str_replace( '_', '-', strtolower( $v ) );
00361                         $variantArray[$v] = $lang->getVariantname( $v, false );
00362                     }
00363 
00364                     $options = array();
00365                     foreach ( $variantArray as $code => $name ) {
00366                         $display = wfBCP47( $code ) . ' - ' . $name;
00367                         $options[$display] = $code;
00368                     }
00369 
00370                     $defaultPreferences['variant'] = array(
00371                         'label-message' => 'yourvariant',
00372                         'type' => 'select',
00373                         'options' => $options,
00374                         'section' => 'personal/i18n',
00375                         'help-message' => 'prefs-help-variant',
00376                     );
00377 
00378                     if ( !$wgDisableTitleConversion ) {
00379                         $defaultPreferences['noconvertlink'] = array(
00380                             'type' => 'toggle',
00381                             'section' => 'personal/i18n',
00382                             'label-message' => 'tog-noconvertlink',
00383                         );
00384                     }
00385                 } else {
00386                     $defaultPreferences["variant-$langCode"] = array(
00387                         'type' => 'api',
00388                     );
00389                 }
00390             }
00391         }
00392 
00393         // Stuff from Language::getExtraUserToggles()
00394         // FIXME is this dead code? $extraUserToggles doesn't seem to be defined for any language
00395         $toggles = $wgContLang->getExtraUserToggles();
00396 
00397         foreach ( $toggles as $toggle ) {
00398             $defaultPreferences[$toggle] = array(
00399                 'type' => 'toggle',
00400                 'section' => 'personal/i18n',
00401                 'label-message' => "tog-$toggle",
00402             );
00403         }
00404 
00405         // show a preview of the old signature first
00406         $oldsigWikiText = $wgParser->preSaveTransform( "~~~", $context->getTitle(), $user, ParserOptions::newFromContext( $context ) );
00407         $oldsigHTML = $context->getOutput()->parseInline( $oldsigWikiText, true, true );
00408         $defaultPreferences['oldsig'] = array(
00409             'type' => 'info',
00410             'raw' => true,
00411             'label-message' => 'tog-oldsig',
00412             'default' => $oldsigHTML,
00413             'section' => 'personal/signature',
00414         );
00415         $defaultPreferences['nickname'] = array(
00416             'type' => $wgAuth->allowPropChange( 'nickname' ) ? 'text' : 'info',
00417             'maxlength' => $wgMaxSigChars,
00418             'label-message' => 'yournick',
00419             'validation-callback' => array( 'Preferences', 'validateSignature' ),
00420             'section' => 'personal/signature',
00421             'filter-callback' => array( 'Preferences', 'cleanSignature' ),
00422         );
00423         $defaultPreferences['fancysig'] = array(
00424             'type' => 'toggle',
00425             'label-message' => 'tog-fancysig',
00426             'help-message' => 'prefs-help-signature', // show general help about signature at the bottom of the section
00427             'section' => 'personal/signature'
00428         );
00429 
00430         ## Email stuff
00431 
00432         if ( $wgEnableEmail ) {
00433             if ( $canViewPrivateInfo ) {
00434                 $helpMessages[] = $wgEmailConfirmToEdit
00435                         ? 'prefs-help-email-required'
00436                         : 'prefs-help-email';
00437 
00438                 if ( $wgEnableUserEmail ) {
00439                     // additional messages when users can send email to each other
00440                     $helpMessages[] = 'prefs-help-email-others';
00441                 }
00442 
00443                 $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : '';
00444                 if ( $canEditPrivateInfo && $wgAuth->allowPropChange( 'emailaddress' ) ) {
00445                     $link = Linker::link(
00446                         SpecialPage::getTitleFor( 'ChangeEmail' ),
00447                         $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->escaped(),
00448                         array(),
00449                         array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) );
00450 
00451                     $emailAddress .= $emailAddress == '' ? $link : (
00452                         $context->msg( 'word-separator' )->plain()
00453                         . $context->msg( 'parentheses' )->rawParams( $link )->plain()
00454                     );
00455                 }
00456 
00457                 $defaultPreferences['emailaddress'] = array(
00458                     'type' => 'info',
00459                     'raw' => true,
00460                     'default' => $emailAddress,
00461                     'label-message' => 'youremail',
00462                     'section' => 'personal/email',
00463                     'help-messages' => $helpMessages,
00464                     # 'cssclass' chosen below
00465                 );
00466             }
00467 
00468             $disableEmailPrefs = false;
00469 
00470             if ( $wgEmailAuthentication ) {
00471                 $emailauthenticationclass = 'mw-email-not-authenticated';
00472                 if ( $user->getEmail() ) {
00473                     if ( $user->getEmailAuthenticationTimestamp() ) {
00474                         // date and time are separate parameters to facilitate localisation.
00475                         // $time is kept for backward compat reasons.
00476                         // 'emailauthenticated' is also used in SpecialConfirmemail.php
00477                         $displayUser = $context->getUser();
00478                         $emailTimestamp = $user->getEmailAuthenticationTimestamp();
00479                         $time = $lang->userTimeAndDate( $emailTimestamp, $displayUser );
00480                         $d = $lang->userDate( $emailTimestamp, $displayUser );
00481                         $t = $lang->userTime( $emailTimestamp, $displayUser );
00482                         $emailauthenticated = $context->msg( 'emailauthenticated',
00483                             $time, $d, $t )->parse() . '<br />';
00484                         $disableEmailPrefs = false;
00485                         $emailauthenticationclass = 'mw-email-authenticated';
00486                     } else {
00487                         $disableEmailPrefs = true;
00488                         $emailauthenticated = $context->msg( 'emailnotauthenticated' )->parse() . '<br />' .
00489                             Linker::linkKnown(
00490                                 SpecialPage::getTitleFor( 'Confirmemail' ),
00491                                 $context->msg( 'emailconfirmlink' )->escaped()
00492                             ) . '<br />';
00493                         $emailauthenticationclass = "mw-email-not-authenticated";
00494                     }
00495                 } else {
00496                     $disableEmailPrefs = true;
00497                     $emailauthenticated = $context->msg( 'noemailprefs' )->escaped();
00498                     $emailauthenticationclass = 'mw-email-none';
00499                 }
00500 
00501                 if ( $canViewPrivateInfo ) {
00502                     $defaultPreferences['emailauthentication'] = array(
00503                         'type' => 'info',
00504                         'raw' => true,
00505                         'section' => 'personal/email',
00506                         'label-message' => 'prefs-emailconfirm-label',
00507                         'default' => $emailauthenticated,
00508                         # Apply the same CSS class used on the input to the message:
00509                         'cssclass' => $emailauthenticationclass,
00510                     );
00511                     $defaultPreferences['emailaddress']['cssclass'] = $emailauthenticationclass;
00512                 }
00513             }
00514 
00515             if ( $wgEnableUserEmail && $user->isAllowed( 'sendemail' ) ) {
00516                 $defaultPreferences['disablemail'] = array(
00517                     'type' => 'toggle',
00518                     'invert' => true,
00519                     'section' => 'personal/email',
00520                     'label-message' => 'allowemail',
00521                     'disabled' => $disableEmailPrefs,
00522                 );
00523                 $defaultPreferences['ccmeonemails'] = array(
00524                     'type' => 'toggle',
00525                     'section' => 'personal/email',
00526                     'label-message' => 'tog-ccmeonemails',
00527                     'disabled' => $disableEmailPrefs,
00528                 );
00529             }
00530 
00531             if ( $wgEnotifWatchlist ) {
00532                 $defaultPreferences['enotifwatchlistpages'] = array(
00533                     'type' => 'toggle',
00534                     'section' => 'personal/email',
00535                     'label-message' => 'tog-enotifwatchlistpages',
00536                     'disabled' => $disableEmailPrefs,
00537                 );
00538             }
00539             if ( $wgEnotifUserTalk ) {
00540                 $defaultPreferences['enotifusertalkpages'] = array(
00541                     'type' => 'toggle',
00542                     'section' => 'personal/email',
00543                     'label-message' => 'tog-enotifusertalkpages',
00544                     'disabled' => $disableEmailPrefs,
00545                 );
00546             }
00547             if ( $wgEnotifUserTalk || $wgEnotifWatchlist ) {
00548                 $defaultPreferences['enotifminoredits'] = array(
00549                     'type' => 'toggle',
00550                     'section' => 'personal/email',
00551                     'label-message' => 'tog-enotifminoredits',
00552                     'disabled' => $disableEmailPrefs,
00553                 );
00554 
00555                 if ( $wgEnotifRevealEditorAddress ) {
00556                     $defaultPreferences['enotifrevealaddr'] = array(
00557                         'type' => 'toggle',
00558                         'section' => 'personal/email',
00559                         'label-message' => 'tog-enotifrevealaddr',
00560                         'disabled' => $disableEmailPrefs,
00561                     );
00562                 }
00563             }
00564         }
00565     }
00566 
00573     static function skinPreferences( $user, IContextSource $context, &$defaultPreferences ) {
00574         ## Skin #####################################
00575         global $wgAllowUserCss, $wgAllowUserJs;
00576 
00577         $defaultPreferences['skin'] = array(
00578             'type' => 'radio',
00579             'options' => self::generateSkinOptions( $user, $context ),
00580             'label' => '&#160;',
00581             'section' => 'rendering/skin',
00582         );
00583 
00584         # Create links to user CSS/JS pages for all skins
00585         # This code is basically copied from generateSkinOptions().  It'd
00586         # be nice to somehow merge this back in there to avoid redundancy.
00587         if ( $wgAllowUserCss || $wgAllowUserJs ) {
00588             $linkTools = array();
00589             $userName = $user->getName();
00590 
00591             if ( $wgAllowUserCss ) {
00592                 $cssPage = Title::makeTitleSafe( NS_USER, $userName . '/common.css' );
00593                 $linkTools[] = Linker::link( $cssPage, $context->msg( 'prefs-custom-css' )->escaped() );
00594             }
00595 
00596             if ( $wgAllowUserJs ) {
00597                 $jsPage = Title::makeTitleSafe( NS_USER, $userName . '/common.js' );
00598                 $linkTools[] = Linker::link( $jsPage, $context->msg( 'prefs-custom-js' )->escaped() );
00599             }
00600 
00601             $defaultPreferences['commoncssjs'] = array(
00602                 'type' => 'info',
00603                 'raw' => true,
00604                 'default' => $context->getLanguage()->pipeList( $linkTools ),
00605                 'label-message' => 'prefs-common-css-js',
00606                 'section' => 'rendering/skin',
00607             );
00608         }
00609     }
00610 
00616     static function filesPreferences( $user, IContextSource $context, &$defaultPreferences ) {
00617         ## Files #####################################
00618         $defaultPreferences['imagesize'] = array(
00619             'type' => 'select',
00620             'options' => self::getImageSizes( $context ),
00621             'label-message' => 'imagemaxsize',
00622             'section' => 'rendering/files',
00623         );
00624         $defaultPreferences['thumbsize'] = array(
00625             'type' => 'select',
00626             'options' => self::getThumbSizes( $context ),
00627             'label-message' => 'thumbsize',
00628             'section' => 'rendering/files',
00629         );
00630     }
00631 
00638     static function datetimePreferences( $user, IContextSource $context, &$defaultPreferences ) {
00639         ## Date and time #####################################
00640         $dateOptions = self::getDateOptions( $context );
00641         if ( $dateOptions ) {
00642             $defaultPreferences['date'] = array(
00643                 'type' => 'radio',
00644                 'options' => $dateOptions,
00645                 'label' => '&#160;',
00646                 'section' => 'datetime/dateformat',
00647             );
00648         }
00649 
00650         // Info
00651         $now = wfTimestampNow();
00652         $lang = $context->getLanguage();
00653         $nowlocal = Xml::element( 'span', array( 'id' => 'wpLocalTime' ),
00654             $lang->time( $now, true ) );
00655         $nowserver = $lang->time( $now, false ) .
00656             Html::hidden( 'wpServerTime', (int)substr( $now, 8, 2 ) * 60 + (int)substr( $now, 10, 2 ) );
00657 
00658         $defaultPreferences['nowserver'] = array(
00659             'type' => 'info',
00660             'raw' => 1,
00661             'label-message' => 'servertime',
00662             'default' => $nowserver,
00663             'section' => 'datetime/timeoffset',
00664         );
00665 
00666         $defaultPreferences['nowlocal'] = array(
00667             'type' => 'info',
00668             'raw' => 1,
00669             'label-message' => 'localtime',
00670             'default' => $nowlocal,
00671             'section' => 'datetime/timeoffset',
00672         );
00673 
00674         // Grab existing pref.
00675         $tzOffset = $user->getOption( 'timecorrection' );
00676         $tz = explode( '|', $tzOffset, 3 );
00677 
00678         $tzOptions = self::getTimezoneOptions( $context );
00679 
00680         $tzSetting = $tzOffset;
00681         if ( count( $tz ) > 1 && $tz[0] == 'Offset' ) {
00682             $minDiff = $tz[1];
00683             $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff / 60 ), abs( $minDiff ) % 60 );
00684         } elseif ( count( $tz ) > 1 && $tz[0] == 'ZoneInfo' &&
00685             !in_array( $tzOffset, HTMLFormField::flattenOptions( $tzOptions ) ) )
00686         {
00687             # Timezone offset can vary with DST
00688             $userTZ = timezone_open( $tz[2] );
00689             if ( $userTZ !== false ) {
00690                 $minDiff = floor( timezone_offset_get( $userTZ, date_create( 'now' ) ) / 60 );
00691                 $tzSetting = "ZoneInfo|$minDiff|{$tz[2]}";
00692             }
00693         }
00694 
00695         $defaultPreferences['timecorrection'] = array(
00696             'class' => 'HTMLSelectOrOtherField',
00697             'label-message' => 'timezonelegend',
00698             'options' => $tzOptions,
00699             'default' => $tzSetting,
00700             'size' => 20,
00701             'section' => 'datetime/timeoffset',
00702         );
00703     }
00704 
00710     static function renderingPreferences( $user, IContextSource $context, &$defaultPreferences ) {
00711         ## Diffs ####################################
00712         $defaultPreferences['diffonly'] = array(
00713             'type' => 'toggle',
00714             'section' => 'rendering/diffs',
00715             'label-message' => 'tog-diffonly',
00716         );
00717         $defaultPreferences['norollbackdiff'] = array(
00718             'type' => 'toggle',
00719             'section' => 'rendering/diffs',
00720             'label-message' => 'tog-norollbackdiff',
00721         );
00722 
00723         ## Page Rendering ##############################
00724         global $wgAllowUserCssPrefs;
00725         if ( $wgAllowUserCssPrefs ) {
00726             $defaultPreferences['underline'] = array(
00727                 'type' => 'select',
00728                 'options' => array(
00729                     $context->msg( 'underline-never' )->text() => 0,
00730                     $context->msg( 'underline-always' )->text() => 1,
00731                     $context->msg( 'underline-default' )->text() => 2,
00732                 ),
00733                 'label-message' => 'tog-underline',
00734                 'section' => 'rendering/advancedrendering',
00735             );
00736         }
00737 
00738         $stubThresholdValues = array( 50, 100, 500, 1000, 2000, 5000, 10000 );
00739         $stubThresholdOptions = array( $context->msg( 'stub-threshold-disabled' )->text() => 0 );
00740         foreach ( $stubThresholdValues as $value ) {
00741             $stubThresholdOptions[$context->msg( 'size-bytes', $value )->text()] = $value;
00742         }
00743 
00744         $defaultPreferences['stubthreshold'] = array(
00745             'type' => 'selectorother',
00746             'section' => 'rendering/advancedrendering',
00747             'options' => $stubThresholdOptions,
00748             'size' => 20,
00749             'label-raw' => $context->msg( 'stub-threshold' )->text(), // Raw HTML message. Yay?
00750         );
00751 
00752         if ( $wgAllowUserCssPrefs ) {
00753             $defaultPreferences['showtoc'] = array(
00754                 'type' => 'toggle',
00755                 'section' => 'rendering/advancedrendering',
00756                 'label-message' => 'tog-showtoc',
00757             );
00758         }
00759         $defaultPreferences['nocache'] = array(
00760             'type' => 'toggle',
00761             'label-message' => 'tog-nocache',
00762             'section' => 'rendering/advancedrendering',
00763         );
00764         $defaultPreferences['showhiddencats'] = array(
00765             'type' => 'toggle',
00766             'section' => 'rendering/advancedrendering',
00767             'label-message' => 'tog-showhiddencats'
00768         );
00769 
00770         if ( $wgAllowUserCssPrefs ) {
00771             $defaultPreferences['justify'] = array(
00772                 'type' => 'toggle',
00773                 'section' => 'rendering/advancedrendering',
00774                 'label-message' => 'tog-justify',
00775             );
00776         }
00777 
00778         $defaultPreferences['numberheadings'] = array(
00779             'type' => 'toggle',
00780             'section' => 'rendering/advancedrendering',
00781             'label-message' => 'tog-numberheadings',
00782         );
00783     }
00784 
00790     static function editingPreferences( $user, IContextSource $context, &$defaultPreferences ) {
00791         global $wgAllowUserCssPrefs;
00792 
00793         ## Editing #####################################
00794         if ( $wgAllowUserCssPrefs ) {
00795             $defaultPreferences['editsection'] = array(
00796                 'type' => 'toggle',
00797                 'section' => 'editing/advancedediting',
00798                 'label-message' => 'tog-editsection',
00799             );
00800         }
00801         $defaultPreferences['editsectiononrightclick'] = array(
00802             'type' => 'toggle',
00803             'section' => 'editing/advancedediting',
00804             'label-message' => 'tog-editsectiononrightclick',
00805         );
00806         $defaultPreferences['editondblclick'] = array(
00807             'type' => 'toggle',
00808             'section' => 'editing/advancedediting',
00809             'label-message' => 'tog-editondblclick',
00810         );
00811 
00812         if ( $wgAllowUserCssPrefs ) {
00813             $defaultPreferences['editfont'] = array(
00814                 'type' => 'select',
00815                 'section' => 'editing/editor',
00816                 'label-message' => 'editfont-style',
00817                 'options' => array(
00818                     $context->msg( 'editfont-default' )->text() => 'default',
00819                     $context->msg( 'editfont-monospace' )->text() => 'monospace',
00820                     $context->msg( 'editfont-sansserif' )->text() => 'sans-serif',
00821                     $context->msg( 'editfont-serif' )->text() => 'serif',
00822                 )
00823             );
00824         }
00825         $defaultPreferences['cols'] = array(
00826             'type' => 'int',
00827             'label-message' => 'columns',
00828             'section' => 'editing/editor',
00829             'min' => 4,
00830             'max' => 1000,
00831         );
00832         $defaultPreferences['rows'] = array(
00833             'type' => 'int',
00834             'label-message' => 'rows',
00835             'section' => 'editing/editor',
00836             'min' => 4,
00837             'max' => 1000,
00838         );
00839         if ( $user->isAllowed( 'minoredit' ) ) {
00840             $defaultPreferences['minordefault'] = array(
00841                 'type' => 'toggle',
00842                 'section' => 'editing/editor',
00843                 'label-message' => 'tog-minordefault',
00844             );
00845         }
00846         $defaultPreferences['forceeditsummary'] = array(
00847             'type' => 'toggle',
00848             'section' => 'editing/editor',
00849             'label-message' => 'tog-forceeditsummary',
00850         );
00851         $defaultPreferences['useeditwarning'] = array(
00852             'type' => 'toggle',
00853             'section' => 'editing/editor',
00854             'label-message' => 'tog-useeditwarning',
00855         );
00856         $defaultPreferences['showtoolbar'] = array(
00857             'type' => 'toggle',
00858             'section' => 'editing/editor',
00859             'label-message' => 'tog-showtoolbar',
00860         );
00861 
00862         $defaultPreferences['previewonfirst'] = array(
00863             'type' => 'toggle',
00864             'section' => 'editing/preview',
00865             'label-message' => 'tog-previewonfirst',
00866         );
00867         $defaultPreferences['previewontop'] = array(
00868             'type' => 'toggle',
00869             'section' => 'editing/preview',
00870             'label-message' => 'tog-previewontop',
00871         );
00872         $defaultPreferences['uselivepreview'] = array(
00873             'type' => 'toggle',
00874             'section' => 'editing/preview',
00875             'label-message' => 'tog-uselivepreview',
00876         );
00877 
00878     }
00879 
00885     static function rcPreferences( $user, IContextSource $context, &$defaultPreferences ) {
00886         global $wgRCMaxAge, $wgRCShowWatchingUsers;
00887 
00888         ## RecentChanges #####################################
00889         $defaultPreferences['rcdays'] = array(
00890             'type' => 'float',
00891             'label-message' => 'recentchangesdays',
00892             'section' => 'rc/displayrc',
00893             'min' => 1,
00894             'max' => ceil( $wgRCMaxAge / ( 3600 * 24 ) ),
00895             'help' => $context->msg( 'recentchangesdays-max' )->numParams(
00896                 ceil( $wgRCMaxAge / ( 3600 * 24 ) ) )->text()
00897         );
00898         $defaultPreferences['rclimit'] = array(
00899             'type' => 'int',
00900             'label-message' => 'recentchangescount',
00901             'help-message' => 'prefs-help-recentchangescount',
00902             'section' => 'rc/displayrc',
00903         );
00904         $defaultPreferences['usenewrc'] = array(
00905             'type' => 'toggle',
00906             'label-message' => 'tog-usenewrc',
00907             'section' => 'rc/advancedrc',
00908         );
00909         $defaultPreferences['hideminor'] = array(
00910             'type' => 'toggle',
00911             'label-message' => 'tog-hideminor',
00912             'section' => 'rc/advancedrc',
00913         );
00914 
00915         if ( $user->useRCPatrol() ) {
00916             $defaultPreferences['hidepatrolled'] = array(
00917                 'type' => 'toggle',
00918                 'section' => 'rc/advancedrc',
00919                 'label-message' => 'tog-hidepatrolled',
00920             );
00921             $defaultPreferences['newpageshidepatrolled'] = array(
00922                 'type' => 'toggle',
00923                 'section' => 'rc/advancedrc',
00924                 'label-message' => 'tog-newpageshidepatrolled',
00925             );
00926         }
00927 
00928         if ( $wgRCShowWatchingUsers ) {
00929             $defaultPreferences['shownumberswatching'] = array(
00930                 'type' => 'toggle',
00931                 'section' => 'rc/advancedrc',
00932                 'label-message' => 'tog-shownumberswatching',
00933             );
00934         }
00935     }
00936 
00942     static function watchlistPreferences( $user, IContextSource $context, &$defaultPreferences ) {
00943         global $wgUseRCPatrol, $wgEnableAPI, $wgRCMaxAge;
00944 
00945         $watchlistdaysMax = ceil( $wgRCMaxAge / ( 3600 * 24 ) );
00946 
00947         ## Watchlist #####################################
00948         $defaultPreferences['watchlistdays'] = array(
00949             'type' => 'float',
00950             'min' => 0,
00951             'max' => $watchlistdaysMax,
00952             'section' => 'watchlist/displaywatchlist',
00953             'help' => $context->msg( 'prefs-watchlist-days-max' )->numParams(
00954                 $watchlistdaysMax )->text(),
00955             'label-message' => 'prefs-watchlist-days',
00956         );
00957         $defaultPreferences['wllimit'] = array(
00958             'type' => 'int',
00959             'min' => 0,
00960             'max' => 1000,
00961             'label-message' => 'prefs-watchlist-edits',
00962             'help' => $context->msg( 'prefs-watchlist-edits-max' )->escaped(),
00963             'section' => 'watchlist/displaywatchlist',
00964         );
00965         $defaultPreferences['extendwatchlist'] = array(
00966             'type' => 'toggle',
00967             'section' => 'watchlist/advancedwatchlist',
00968             'label-message' => 'tog-extendwatchlist',
00969         );
00970         $defaultPreferences['watchlisthideminor'] = array(
00971             'type' => 'toggle',
00972             'section' => 'watchlist/advancedwatchlist',
00973             'label-message' => 'tog-watchlisthideminor',
00974         );
00975         $defaultPreferences['watchlisthidebots'] = array(
00976             'type' => 'toggle',
00977             'section' => 'watchlist/advancedwatchlist',
00978             'label-message' => 'tog-watchlisthidebots',
00979         );
00980         $defaultPreferences['watchlisthideown'] = array(
00981             'type' => 'toggle',
00982             'section' => 'watchlist/advancedwatchlist',
00983             'label-message' => 'tog-watchlisthideown',
00984         );
00985         $defaultPreferences['watchlisthideanons'] = array(
00986             'type' => 'toggle',
00987             'section' => 'watchlist/advancedwatchlist',
00988             'label-message' => 'tog-watchlisthideanons',
00989         );
00990         $defaultPreferences['watchlisthideliu'] = array(
00991             'type' => 'toggle',
00992             'section' => 'watchlist/advancedwatchlist',
00993             'label-message' => 'tog-watchlisthideliu',
00994         );
00995 
00996         if ( $wgUseRCPatrol ) {
00997             $defaultPreferences['watchlisthidepatrolled'] = array(
00998                 'type' => 'toggle',
00999                 'section' => 'watchlist/advancedwatchlist',
01000                 'label-message' => 'tog-watchlisthidepatrolled',
01001             );
01002         }
01003 
01004         $watchTypes = array(
01005             'edit' => 'watchdefault',
01006             'move' => 'watchmoves',
01007             'delete' => 'watchdeletion'
01008         );
01009 
01010         // Kinda hacky
01011         if ( $user->isAllowed( 'createpage' ) || $user->isAllowed( 'createtalk' ) ) {
01012             $watchTypes['read'] = 'watchcreations';
01013         }
01014 
01015         foreach ( $watchTypes as $action => $pref ) {
01016             if ( $user->isAllowed( $action ) ) {
01017                 // Messages:
01018                 // tog-watchdefault, tog-watchmoves, tog-watchdeletion, tog-watchcreations
01019                 $defaultPreferences[$pref] = array(
01020                     'type' => 'toggle',
01021                     'section' => 'watchlist/advancedwatchlist',
01022                     'label-message' => "tog-$pref",
01023                 );
01024             }
01025         }
01026 
01027         if ( $wgEnableAPI ) {
01028             $defaultPreferences['watchlisttoken'] = array(
01029                 'type' => 'api',
01030             );
01031             $defaultPreferences['watchlisttoken-info'] = array(
01032                 'type' => 'info',
01033                 'section' => 'watchlist/tokenwatchlist',
01034                 'label-message' => 'prefs-watchlist-token',
01035                 'default' => $user->getTokenFromOption( 'watchlisttoken' ),
01036                 'help-message' => 'prefs-help-watchlist-token2',
01037             );
01038         }
01039     }
01040 
01046     static function searchPreferences( $user, IContextSource $context, &$defaultPreferences ) {
01047         global $wgContLang, $wgVectorUseSimpleSearch;
01048 
01049         ## Search #####################################
01050         $defaultPreferences['searchlimit'] = array(
01051             'type' => 'int',
01052             'label-message' => 'resultsperpage',
01053             'section' => 'searchoptions/displaysearchoptions',
01054             'min' => 0,
01055         );
01056 
01057         if ( $wgVectorUseSimpleSearch ) {
01058             $defaultPreferences['vector-simplesearch'] = array(
01059                 'type' => 'toggle',
01060                 'label-message' => 'vector-simplesearch-preference',
01061                 'section' => 'searchoptions/displaysearchoptions',
01062             );
01063         }
01064 
01065         $defaultPreferences['disablesuggest'] = array(
01066             'type' => 'toggle',
01067             'label-message' => 'mwsuggest-disable',
01068             'section' => 'searchoptions/displaysearchoptions',
01069         );
01070 
01071         $defaultPreferences['searcheverything'] = array(
01072             'type' => 'toggle',
01073             'label-message' => 'searcheverything-enable',
01074             'section' => 'searchoptions/advancedsearchoptions',
01075         );
01076 
01077         $nsOptions = $wgContLang->getFormattedNamespaces();
01078         $nsOptions[0] = $context->msg( 'blanknamespace' )->text();
01079         foreach ( $nsOptions as $ns => $name ) {
01080             if ( $ns < 0 ) {
01081                 unset( $nsOptions[$ns] );
01082             }
01083         }
01084 
01085         $defaultPreferences['searchnamespaces'] = array(
01086             'type' => 'multiselect',
01087             'label-message' => 'defaultns',
01088             'options' => array_flip( $nsOptions ),
01089             'section' => 'searchoptions/advancedsearchoptions',
01090             'prefix' => 'searchNs',
01091         );
01092     }
01093 
01097     static function miscPreferences( $user, IContextSource $context, &$defaultPreferences ) {
01098     }
01099 
01105     static function generateSkinOptions( $user, IContextSource $context ) {
01106         global $wgDefaultSkin, $wgAllowUserCss, $wgAllowUserJs;
01107         $ret = array();
01108 
01109         $mptitle = Title::newMainPage();
01110         $previewtext = $context->msg( 'skin-preview' )->text();
01111 
01112         # Only show members of Skin::getSkinNames() rather than
01113         # $skinNames (skins is all skin names from Language.php)
01114         $validSkinNames = Skin::getUsableSkins();
01115 
01116         # Sort by UI skin name. First though need to update validSkinNames as sometimes
01117         # the skinkey & UI skinname differ (e.g. "standard" skinkey is "Classic" in the UI).
01118         foreach ( $validSkinNames as $skinkey => &$skinname ) {
01119             $msg = $context->msg( "skinname-{$skinkey}" );
01120             if ( $msg->exists() ) {
01121                 $skinname = htmlspecialchars( $msg->text() );
01122             }
01123         }
01124         asort( $validSkinNames );
01125 
01126         foreach ( $validSkinNames as $skinkey => $sn ) {
01127             $linkTools = array();
01128 
01129             # Mark the default skin
01130             if ( $skinkey == $wgDefaultSkin ) {
01131                 $linkTools[] = $context->msg( 'default' )->escaped();
01132             }
01133 
01134             # Create preview link
01135             $mplink = htmlspecialchars( $mptitle->getLocalURL( array( 'useskin' => $skinkey ) ) );
01136             $linkTools[] = "<a target='_blank' href=\"$mplink\">$previewtext</a>";
01137 
01138             # Create links to user CSS/JS pages
01139             if ( $wgAllowUserCss ) {
01140                 $cssPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.css' );
01141                 $linkTools[] = Linker::link( $cssPage, $context->msg( 'prefs-custom-css' )->escaped() );
01142             }
01143 
01144             if ( $wgAllowUserJs ) {
01145                 $jsPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.js' );
01146                 $linkTools[] = Linker::link( $jsPage, $context->msg( 'prefs-custom-js' )->escaped() );
01147             }
01148 
01149             $display = $sn . ' ' . $context->msg( 'parentheses', $context->getLanguage()->pipeList( $linkTools ) )->text();
01150             $ret[$display] = $skinkey;
01151         }
01152 
01153         return $ret;
01154     }
01155 
01160     static function getDateOptions( IContextSource $context ) {
01161         $lang = $context->getLanguage();
01162         $dateopts = $lang->getDatePreferences();
01163 
01164         $ret = array();
01165 
01166         if ( $dateopts ) {
01167             if ( !in_array( 'default', $dateopts ) ) {
01168                 $dateopts[] = 'default'; // Make sure default is always valid
01169                                         // Bug 19237
01170             }
01171 
01172             // KLUGE: site default might not be valid for user language
01173             global $wgDefaultUserOptions;
01174             if ( !in_array( $wgDefaultUserOptions['date'], $dateopts ) ) {
01175                 $wgDefaultUserOptions['date'] = 'default';
01176             }
01177 
01178             $epoch = wfTimestampNow();
01179             foreach ( $dateopts as $key ) {
01180                 if ( $key == 'default' ) {
01181                     $formatted = $context->msg( 'datedefault' )->escaped();
01182                 } else {
01183                     $formatted = htmlspecialchars( $lang->timeanddate( $epoch, false, $key ) );
01184                 }
01185                 $ret[$formatted] = $key;
01186             }
01187         }
01188         return $ret;
01189     }
01190 
01195     static function getImageSizes( IContextSource $context ) {
01196         global $wgImageLimits;
01197 
01198         $ret = array();
01199         $pixels = $context->msg( 'unit-pixel' )->text();
01200 
01201         foreach ( $wgImageLimits as $index => $limits ) {
01202             $display = "{$limits[0]}×{$limits[1]}" . $pixels;
01203             $ret[$display] = $index;
01204         }
01205 
01206         return $ret;
01207     }
01208 
01213     static function getThumbSizes( IContextSource $context ) {
01214         global $wgThumbLimits;
01215 
01216         $ret = array();
01217         $pixels = $context->msg( 'unit-pixel' )->text();
01218 
01219         foreach ( $wgThumbLimits as $index => $size ) {
01220             $display = $size . $pixels;
01221             $ret[$display] = $index;
01222         }
01223 
01224         return $ret;
01225     }
01226 
01233     static function validateSignature( $signature, $alldata, $form ) {
01234         global $wgParser, $wgMaxSigChars;
01235         if ( mb_strlen( $signature ) > $wgMaxSigChars ) {
01236             return Xml::element( 'span', array( 'class' => 'error' ),
01237                 $form->msg( 'badsiglength' )->numParams( $wgMaxSigChars )->text() );
01238         } elseif ( isset( $alldata['fancysig'] ) &&
01239                 $alldata['fancysig'] &&
01240                 false === $wgParser->validateSig( $signature ) ) {
01241             return Xml::element( 'span', array( 'class' => 'error' ), $form->msg( 'badsig' )->text() );
01242         } else {
01243             return true;
01244         }
01245     }
01246 
01253     static function cleanSignature( $signature, $alldata, $form ) {
01254         if ( isset( $alldata['fancysig'] ) && $alldata['fancysig'] ) {
01255             global $wgParser;
01256             $signature = $wgParser->cleanSig( $signature );
01257         } else {
01258             // When no fancy sig used, make sure ~{3,5} get removed.
01259             $signature = Parser::cleanSigInSig( $signature );
01260         }
01261 
01262         return $signature;
01263     }
01264 
01272     static function getFormObject( $user, IContextSource $context, $formClass = 'PreferencesForm', array $remove = array() ) {
01273         $formDescriptor = Preferences::getPreferences( $user, $context );
01274         if ( count( $remove ) ) {
01275             $removeKeys = array_flip( $remove );
01276             $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
01277         }
01278 
01279         // Remove type=api preferences. They are not intended for rendering in the form.
01280         foreach ( $formDescriptor as $name => $info ) {
01281             if ( isset( $info['type'] ) && $info['type'] === 'api' ) {
01282                 unset( $formDescriptor[$name] );
01283             }
01284         }
01285 
01289         $htmlForm = new $formClass( $formDescriptor, $context, 'prefs' );
01290 
01291         $htmlForm->setModifiedUser( $user );
01292         $htmlForm->setId( 'mw-prefs-form' );
01293         $htmlForm->setSubmitText( $context->msg( 'saveprefs' )->text() );
01294         # Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save'
01295         $htmlForm->setSubmitTooltip( 'preferences-save' );
01296         $htmlForm->setSubmitID( 'prefsubmit' );
01297         $htmlForm->setSubmitCallback( array( 'Preferences', 'tryFormSubmit' ) );
01298 
01299         return $htmlForm;
01300     }
01301 
01306     static function getTimezoneOptions( IContextSource $context ) {
01307         $opt = array();
01308 
01309         global $wgLocalTZoffset;
01310         $timestamp = MWTimestamp::getLocalInstance();
01311         // Check that $wgLocalTZoffset is the same as the local time zone offset
01312         if ( $wgLocalTZoffset == $timestamp->format( 'Z' ) / 60 ) {
01313             $server_tz_msg = $context->msg( 'timezoneuseserverdefault', $timestamp->getTimezone()->getName() )->text();
01314         } else {
01315             $tzstring = sprintf( '%+03d:%02d', floor( $wgLocalTZoffset / 60 ), abs( $wgLocalTZoffset ) % 60 );
01316             $server_tz_msg = $context->msg( 'timezoneuseserverdefault', $tzstring )->text();
01317         }
01318         $opt[$server_tz_msg] = "System|$wgLocalTZoffset";
01319         $opt[$context->msg( 'timezoneuseoffset' )->text()] = 'other';
01320         $opt[$context->msg( 'guesstimezone' )->text()] = 'guess';
01321 
01322         if ( function_exists( 'timezone_identifiers_list' ) ) {
01323             # Read timezone list
01324             $tzs = timezone_identifiers_list();
01325             sort( $tzs );
01326 
01327             $tzRegions = array();
01328             $tzRegions['Africa'] = $context->msg( 'timezoneregion-africa' )->text();
01329             $tzRegions['America'] = $context->msg( 'timezoneregion-america' )->text();
01330             $tzRegions['Antarctica'] = $context->msg( 'timezoneregion-antarctica' )->text();
01331             $tzRegions['Arctic'] = $context->msg( 'timezoneregion-arctic' )->text();
01332             $tzRegions['Asia'] = $context->msg( 'timezoneregion-asia' )->text();
01333             $tzRegions['Atlantic'] = $context->msg( 'timezoneregion-atlantic' )->text();
01334             $tzRegions['Australia'] = $context->msg( 'timezoneregion-australia' )->text();
01335             $tzRegions['Europe'] = $context->msg( 'timezoneregion-europe' )->text();
01336             $tzRegions['Indian'] = $context->msg( 'timezoneregion-indian' )->text();
01337             $tzRegions['Pacific'] = $context->msg( 'timezoneregion-pacific' )->text();
01338             asort( $tzRegions );
01339 
01340             $prefill = array_fill_keys( array_values( $tzRegions ), array() );
01341             $opt = array_merge( $opt, $prefill );
01342 
01343             $now = date_create( 'now' );
01344 
01345             foreach ( $tzs as $tz ) {
01346                 $z = explode( '/', $tz, 2 );
01347 
01348                 # timezone_identifiers_list() returns a number of
01349                 # backwards-compatibility entries. This filters them out of the
01350                 # list presented to the user.
01351                 if ( count( $z ) != 2 || !array_key_exists( $z[0], $tzRegions ) ) {
01352                     continue;
01353                 }
01354 
01355                 # Localize region
01356                 $z[0] = $tzRegions[$z[0]];
01357 
01358                 $minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 );
01359 
01360                 $display = str_replace( '_', ' ', $z[0] . '/' . $z[1] );
01361                 $value = "ZoneInfo|$minDiff|$tz";
01362 
01363                 $opt[$z[0]][$display] = $value;
01364             }
01365         }
01366         return $opt;
01367     }
01368 
01374     static function filterIntval( $value, $alldata ) {
01375         return intval( $value );
01376     }
01377 
01383     static function filterTimezoneInput( $tz, $alldata ) {
01384         $data = explode( '|', $tz, 3 );
01385         switch ( $data[0] ) {
01386             case 'ZoneInfo':
01387             case 'System':
01388                 return $tz;
01389             default:
01390                 $data = explode( ':', $tz, 2 );
01391                 if ( count( $data ) == 2 ) {
01392                     $data[0] = intval( $data[0] );
01393                     $data[1] = intval( $data[1] );
01394                     $minDiff = abs( $data[0] ) * 60 + $data[1];
01395                     if ( $data[0] < 0 ) {
01396                         $minDiff = - $minDiff;
01397                     }
01398                 } else {
01399                     $minDiff = intval( $data[0] ) * 60;
01400                 }
01401 
01402                 # Max is +14:00 and min is -12:00, see:
01403                 # http://en.wikipedia.org/wiki/Timezone
01404                 $minDiff = min( $minDiff, 840 );  # 14:00
01405                 $minDiff = max( $minDiff, - 720 ); # -12:00
01406                 return 'Offset|' . $minDiff;
01407         }
01408     }
01409 
01418     static function tryFormSubmit( $formData, $form, $entryPoint = 'internal' ) {
01419         global $wgHiddenPrefs, $wgAuth;
01420 
01421         $user = $form->getModifiedUser();
01422         $result = true;
01423 
01424         if ( !$user->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) {
01425             return Status::newFatal( 'mypreferencesprotected' );
01426         }
01427 
01428         // Filter input
01429         foreach ( array_keys( $formData ) as $name ) {
01430             if ( isset( self::$saveFilters[$name] ) ) {
01431                 $formData[$name] =
01432                     call_user_func( self::$saveFilters[$name], $formData[$name], $formData );
01433             }
01434         }
01435 
01436         // Fortunately, the realname field is MUCH simpler
01437         // (not really "private", but still shouldn't be edited without permission)
01438         if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->isAllowed( 'editmyprivateinfo' ) ) {
01439             $realName = $formData['realname'];
01440             $user->setRealName( $realName );
01441         }
01442 
01443         if ( $user->isAllowed( 'editmyoptions' ) ) {
01444             foreach ( self::$saveBlacklist as $b ) {
01445                 unset( $formData[$b] );
01446             }
01447 
01448             # If users have saved a value for a preference which has subsequently been disabled
01449             # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
01450             # is subsequently re-enabled
01451             # TODO: maintenance script to actually delete these
01452             foreach ( $wgHiddenPrefs as $pref ) {
01453                 # If the user has not set a non-default value here, the default will be returned
01454                 # and subsequently discarded
01455                 $formData[$pref] = $user->getOption( $pref, null, true );
01456             }
01457 
01458             // Keep old preferences from interfering due to back-compat code, etc.
01459             $user->resetOptions( 'unused', $form->getContext() );
01460 
01461             foreach ( $formData as $key => $value ) {
01462                 $user->setOption( $key, $value );
01463             }
01464 
01465             $user->saveSettings();
01466         }
01467 
01468         $wgAuth->updateExternalDB( $user );
01469 
01470         return $result;
01471     }
01472 
01478     public static function tryUISubmit( $formData, $form ) {
01479         $res = self::tryFormSubmit( $formData, $form, 'ui' );
01480 
01481         if ( $res ) {
01482             $urlOptions = array( 'success' => 1 );
01483 
01484             if ( $res === 'eauth' ) {
01485                 $urlOptions['eauth'] = 1;
01486             }
01487 
01488             $urlOptions += $form->getExtraSuccessRedirectParameters();
01489 
01490             $url = $form->getTitle()->getFullURL( $urlOptions );
01491 
01492             $form->getContext()->getOutput()->redirect( $url );
01493         }
01494 
01495         return Status::newGood();
01496     }
01497 
01509     public static function trySetUserEmail( User $user, $newaddr ) {
01510         wfDeprecated( __METHOD__, '1.20' );
01511 
01512         $result = $user->setEmailWithConfirmation( $newaddr );
01513         if ( $result->isGood() ) {
01514             return array( true, $result->value );
01515         } else {
01516             return array( $result, 'mailerror' );
01517         }
01518     }
01519 
01525     public static function loadOldSearchNs( $user ) {
01526         wfDeprecated( __METHOD__, '1.19' );
01527 
01528         $searchableNamespaces = SearchEngine::searchableNamespaces();
01529         // Back compat with old format
01530         $arr = array();
01531 
01532         foreach ( $searchableNamespaces as $ns => $name ) {
01533             if ( $user->getOption( 'searchNs' . $ns ) ) {
01534                 $arr[] = $ns;
01535             }
01536         }
01537 
01538         return $arr;
01539     }
01540 }
01541 
01543 class PreferencesForm extends HTMLForm {
01544     // Override default value from HTMLForm
01545     protected $mSubSectionBeforeFields = false;
01546 
01547     private $modifiedUser;
01548 
01552     public function setModifiedUser( $user ) {
01553         $this->modifiedUser = $user;
01554     }
01555 
01559     public function getModifiedUser() {
01560         if ( $this->modifiedUser === null ) {
01561             return $this->getUser();
01562         } else {
01563             return $this->modifiedUser;
01564         }
01565     }
01566 
01573     public function getExtraSuccessRedirectParameters() {
01574         return array();
01575     }
01576 
01581     function wrapForm( $html ) {
01582         $html = Xml::tags( 'div', array( 'id' => 'preferences' ), $html );
01583 
01584         return parent::wrapForm( $html );
01585     }
01586 
01590     function getButtons() {
01591         if ( !$this->getModifiedUser()->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) {
01592             return '';
01593         }
01594 
01595         $html = parent::getButtons();
01596 
01597         if ( $this->getModifiedUser()->isAllowed( 'editmyoptions' ) ) {
01598             $t = SpecialPage::getTitleFor( 'Preferences', 'reset' );
01599 
01600             $html .= "\n" . Linker::link( $t, $this->msg( 'restoreprefs' )->escaped() );
01601 
01602             $html = Xml::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html );
01603         }
01604 
01605         return $html;
01606     }
01607 
01614     function filterDataForSubmit( $data ) {
01615         foreach ( $this->mFlatFields as $fieldname => $field ) {
01616             if ( $field instanceof HTMLNestedFilterable ) {
01617                 $info = $field->mParams;
01618                 $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $fieldname;
01619                 foreach ( $field->filterDataForSubmit( $data[$fieldname] ) as $key => $value ) {
01620                     $data["$prefix$key"] = $value;
01621                 }
01622                 unset( $data[$fieldname] );
01623             }
01624         }
01625 
01626         return $data;
01627     }
01628 
01633     function getBody() {
01634         return $this->displaySection( $this->mFieldTree, '', 'mw-prefsection-' );
01635     }
01636 
01643     function getLegend( $key ) {
01644         $legend = parent::getLegend( $key );
01645         wfRunHooks( 'PreferencesGetLegend', array( $this, $key, &$legend ) );
01646         return $legend;
01647     }
01648 }