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