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