Merge "Rename Skin::getUsableSkins() to Skin::getAllowedSkins()"
[lhc/web/wiklou.git] / includes / Preferences.php
index df80894..1825cce 100644 (file)
@@ -56,6 +56,19 @@ class Preferences {
                        'searchlimit' => array( 'Preferences', 'filterIntval' ),
        );
 
+       // Stuff that shouldn't be saved as a preference.
+       private static $saveBlacklist = array(
+               'realname',
+               'emailaddress',
+       );
+
+       /**
+        * @return array
+        */
+       static function getSaveBlacklist() {
+               return self::$saveBlacklist;
+       }
+
        /**
         * @throws MWException
         * @param $user User
@@ -71,8 +84,8 @@ class Preferences {
 
                self::profilePreferences( $user, $context, $defaultPreferences );
                self::skinPreferences( $user, $context, $defaultPreferences );
-               self::filesPreferences( $user, $context, $defaultPreferences );
                self::datetimePreferences( $user, $context, $defaultPreferences );
+               self::filesPreferences( $user, $context, $defaultPreferences );
                self::renderingPreferences( $user, $context, $defaultPreferences );
                self::editingPreferences( $user, $context, $defaultPreferences );
                self::rcPreferences( $user, $context, $defaultPreferences );
@@ -93,9 +106,14 @@ class Preferences {
                ## Make sure that form fields have their parent set. See bug 41337.
                $dummyForm = new HTMLForm( array(), $context );
 
+               $disable = !$user->isAllowed( 'editmyoptions' );
+
                ## Prod in defaults from the user
                foreach ( $defaultPreferences as $name => &$info ) {
                        $prefFromUser = self::getOptionFromUser( $name, $info, $user );
+                       if ( $disable && !in_array( $name, self::$saveBlacklist ) ) {
+                               $info['disabled'] = 'disabled';
+                       }
                        $field = HTMLForm::loadInputFromParameters( $name, $info ); // For validation
                        $field->mParent = $dummyForm;
                        $defaultOptions = User::getDefaultOptions();
@@ -175,9 +193,10 @@ class Preferences {
         */
        static function profilePreferences( $user, IContextSource $context, &$defaultPreferences ) {
                global $wgAuth, $wgContLang, $wgParser, $wgCookieExpiration, $wgLanguageCode,
-                       $wgDisableTitleConversion, $wgDisableLangConversion, $wgMaxSigChars,
+                       $wgDisableLangConversion, $wgMaxSigChars,
                        $wgEnableEmail, $wgEmailConfirmToEdit, $wgEnableUserEmail, $wgEmailAuthentication,
-                       $wgEnotifWatchlist, $wgEnotifUserTalk, $wgEnotifRevealEditorAddress;
+                       $wgEnotifWatchlist, $wgEnotifUserTalk, $wgEnotifRevealEditorAddress,
+                       $wgSecureLogin;
 
                // retrieving user name for GENDER and misc.
                $userName = $user->getName();
@@ -229,10 +248,14 @@ class Preferences {
                        'section' => 'personal/info',
                );
 
+               $editCount = Linker::link( SpecialPage::getTitleFor( "Contributions", $userName ),
+                       $lang->formatNum( $user->getEditCount() ) );
+
                $defaultPreferences['editcount'] = array(
                        'type' => 'info',
+                       'raw' => true,
                        'label-message' => 'prefs-edits',
-                       'default' => $lang->formatNum( $user->getEditCount() ),
+                       'default' => $editCount,
                        'section' => 'personal/info',
                );
 
@@ -252,28 +275,20 @@ class Preferences {
                        );
                }
 
+               $canViewPrivateInfo = $user->isAllowed( 'viewmyprivateinfo' );
+               $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' );
+
                // Actually changeable stuff
                $defaultPreferences['realname'] = array(
-                       'type' => $wgAuth->allowPropChange( 'realname' ) ? 'text' : 'info',
+                       // (not really "private", but still shouldn't be edited without permission)
+                       'type' => $canEditPrivateInfo && $wgAuth->allowPropChange( 'realname' ) ? 'text' : 'info',
                        'default' => $user->getRealName(),
                        'section' => 'personal/info',
                        'label-message' => 'yourrealname',
                        'help-message' => 'prefs-help-realname',
                );
 
-               $defaultPreferences['gender'] = array(
-                       'type' => 'select',
-                       'section' => 'personal/info',
-                       'options' => array(
-                               $context->msg( 'gender-male' )->text() => 'male',
-                               $context->msg( 'gender-female' )->text() => 'female',
-                               $context->msg( 'gender-unknown' )->text() => 'unknown',
-                       ),
-                       'label-message' => 'yourgender',
-                       'help-message' => 'prefs-help-gender',
-               );
-
-               if ( $wgAuth->allowPasswordChange() ) {
+               if ( $canEditPrivateInfo && $wgAuth->allowPasswordChange() ) {
                        $link = Linker::link( SpecialPage::getTitleFor( 'ChangePassword' ),
                                $context->msg( 'prefs-resetpass' )->escaped(), array(),
                                array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) );
@@ -294,6 +309,15 @@ class Preferences {
                                'section' => 'personal/info',
                        );
                }
+               // Only show prefershttps if secure login is turned on
+               if ( $wgSecureLogin && wfCanIPUseHTTPS( $context->getRequest()->getIP() ) ) {
+                       $defaultPreferences['prefershttps'] = array(
+                               'type' => 'toggle',
+                               'label-message' => 'tog-prefershttps',
+                               'help-message' => 'prefs-help-prefershttps',
+                               'section' => 'personal/info'
+                       );
+               }
 
                // Language
                $languages = Language::fetchLanguageNames( null, 'mw' );
@@ -314,44 +338,76 @@ class Preferences {
                        'label-message' => 'yourlanguage',
                );
 
+               $defaultPreferences['gender'] = array(
+                       'type' => 'radio',
+                       'section' => 'personal/i18n',
+                       'options' => array(
+                               $context->msg( 'parentheses',
+                                       $context->msg( 'gender-unknown' )->text()
+                               )->text() => 'unknown',
+                               $context->msg( 'gender-female' )->text() => 'female',
+                               $context->msg( 'gender-male' )->text() => 'male',
+                       ),
+                       'label-message' => 'yourgender',
+                       'help-message' => 'prefs-help-gender',
+               );
+
                // see if there are multiple language variants to choose from
                if ( !$wgDisableLangConversion ) {
-                       $variants = $wgContLang->getVariants();
+                       foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
+                               if ( $langCode == $wgContLang->getCode() ) {
+                                       $variants = $wgContLang->getVariants();
 
-                       if ( count( $variants ) > 1 ) {
-                               $variantArray = array();
-                               foreach ( $variants as $v ) {
-                                       $v = str_replace( '_', '-', strtolower( $v ) );
-                                       $variantArray[$v] = $wgContLang->getVariantname( $v, false );
-                               }
+                                       if ( count( $variants ) <= 1 ) {
+                                               continue;
+                                       }
 
-                               $options = array();
-                               foreach ( $variantArray as $code => $name ) {
-                                       $display = wfBCP47( $code ) . ' - ' . $name;
-                                       $options[$display] = $code;
-                               }
+                                       $variantArray = array();
+                                       foreach ( $variants as $v ) {
+                                               $v = str_replace( '_', '-', strtolower( $v ) );
+                                               $variantArray[$v] = $lang->getVariantname( $v, false );
+                                       }
 
-                               $defaultPreferences['variant'] = array(
-                                       'label-message' => 'yourvariant',
-                                       'type' => 'select',
-                                       'options' => $options,
-                                       'section' => 'personal/i18n',
-                                       'help-message' => 'prefs-help-variant',
-                               );
+                                       $options = array();
+                                       foreach ( $variantArray as $code => $name ) {
+                                               $display = wfBCP47( $code ) . ' - ' . $name;
+                                               $options[$display] = $code;
+                                       }
 
-                               if ( !$wgDisableTitleConversion ) {
-                                       $defaultPreferences['noconvertlink'] =
-                                               array(
-                                               'type' => 'toggle',
+                                       $defaultPreferences['variant'] = array(
+                                               'label-message' => 'yourvariant',
+                                               'type' => 'select',
+                                               'options' => $options,
                                                'section' => 'personal/i18n',
-                                               'label-message' => 'tog-noconvertlink',
+                                               'help-message' => 'prefs-help-variant',
+                                       );
+                               } else {
+                                       $defaultPreferences["variant-$langCode"] = array(
+                                               'type' => 'api',
                                        );
                                }
                        }
                }
 
+               // Stuff from Language::getExtraUserToggles()
+               // FIXME is this dead code? $extraUserToggles doesn't seem to be defined for any language
+               $toggles = $wgContLang->getExtraUserToggles();
+
+               foreach ( $toggles as $toggle ) {
+                       $defaultPreferences[$toggle] = array(
+                               'type' => 'toggle',
+                               'section' => 'personal/i18n',
+                               'label-message' => "tog-$toggle",
+                       );
+               }
+
                // show a preview of the old signature first
-               $oldsigWikiText = $wgParser->preSaveTransform( "~~~", $context->getTitle(), $user, ParserOptions::newFromContext( $context ) );
+               $oldsigWikiText = $wgParser->preSaveTransform(
+                       '~~~',
+                       $context->getTitle(),
+                       $user,
+                       ParserOptions::newFromContext( $context )
+               );
                $oldsigHTML = $context->getOutput()->parseInline( $oldsigWikiText, true, true );
                $defaultPreferences['oldsig'] = array(
                        'type' => 'info',
@@ -371,46 +427,49 @@ class Preferences {
                $defaultPreferences['fancysig'] = array(
                        'type' => 'toggle',
                        'label-message' => 'tog-fancysig',
-                       'help-message' => 'prefs-help-signature', // show general help about signature at the bottom of the section
+                       // show general help about signature at the bottom of the section
+                       'help-message' => 'prefs-help-signature',
                        'section' => 'personal/signature'
                );
 
                ## Email stuff
 
                if ( $wgEnableEmail ) {
-                       $helpMessages[] = $wgEmailConfirmToEdit
-                                       ? 'prefs-help-email-required'
-                                       : 'prefs-help-email';
-
-                       if ( $wgEnableUserEmail ) {
-                               // additional messages when users can send email to each other
-                               $helpMessages[] = 'prefs-help-email-others';
-                       }
+                       if ( $canViewPrivateInfo ) {
+                               $helpMessages[] = $wgEmailConfirmToEdit
+                                               ? 'prefs-help-email-required'
+                                               : 'prefs-help-email';
+
+                               if ( $wgEnableUserEmail ) {
+                                       // additional messages when users can send email to each other
+                                       $helpMessages[] = 'prefs-help-email-others';
+                               }
 
-                       $link = Linker::link(
-                               SpecialPage::getTitleFor( 'ChangeEmail' ),
-                               $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->escaped(),
-                               array(),
-                               array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) );
+                               $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : '';
+                               if ( $canEditPrivateInfo && $wgAuth->allowPropChange( 'emailaddress' ) ) {
+                                       $link = Linker::link(
+                                               SpecialPage::getTitleFor( 'ChangeEmail' ),
+                                               $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->escaped(),
+                                               array(),
+                                               array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) );
+
+                                       $emailAddress .= $emailAddress == '' ? $link : (
+                                               $context->msg( 'word-separator' )->plain()
+                                               . $context->msg( 'parentheses' )->rawParams( $link )->plain()
+                                       );
+                               }
 
-                       $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : '';
-                       if ( $wgAuth->allowPropChange( 'emailaddress' ) ) {
-                               $emailAddress .= $emailAddress == '' ? $link : (
-                                       $context->msg( 'word-separator' )->plain()
-                                       . $context->msg( 'parentheses' )->rawParams( $link )->plain()
+                               $defaultPreferences['emailaddress'] = array(
+                                       'type' => 'info',
+                                       'raw' => true,
+                                       'default' => $emailAddress,
+                                       'label-message' => 'youremail',
+                                       'section' => 'personal/email',
+                                       'help-messages' => $helpMessages,
+                                       # 'cssclass' chosen below
                                );
                        }
 
-                       $defaultPreferences['emailaddress'] = array(
-                               'type' => 'info',
-                               'raw' => true,
-                               'default' => $emailAddress,
-                               'label-message' => 'youremail',
-                               'section' => 'personal/email',
-                               'help-messages' => $helpMessages,
-                               # 'cssclass' chosen below
-                       );
-
                        $disableEmailPrefs = false;
 
                        if ( $wgEmailAuthentication ) {
@@ -444,16 +503,18 @@ class Preferences {
                                        $emailauthenticationclass = 'mw-email-none';
                                }
 
-                               $defaultPreferences['emailauthentication'] = array(
-                                       'type' => 'info',
-                                       'raw' => true,
-                                       'section' => 'personal/email',
-                                       'label-message' => 'prefs-emailconfirm-label',
-                                       'default' => $emailauthenticated,
-                                       # Apply the same CSS class used on the input to the message:
-                                       'cssclass' => $emailauthenticationclass,
-                               );
-                               $defaultPreferences['emailaddress']['cssclass'] = $emailauthenticationclass;
+                               if ( $canViewPrivateInfo ) {
+                                       $defaultPreferences['emailauthentication'] = array(
+                                               'type' => 'info',
+                                               'raw' => true,
+                                               'section' => 'personal/email',
+                                               'label-message' => 'prefs-emailconfirm-label',
+                                               'default' => $emailauthenticated,
+                                               # Apply the same CSS class used on the input to the message:
+                                               'cssclass' => $emailauthenticationclass,
+                                       );
+                                       $defaultPreferences['emailaddress']['cssclass'] = $emailauthenticationclass;
+                               }
                        }
 
                        if ( $wgEnableUserEmail && $user->isAllowed( 'sendemail' ) ) {
@@ -587,7 +648,7 @@ class Preferences {
                                'type' => 'radio',
                                'options' => $dateOptions,
                                'label' => '&#160;',
-                               'section' => 'datetime/dateformat',
+                               'section' => 'rendering/dateformat',
                        );
                }
 
@@ -604,7 +665,7 @@ class Preferences {
                        'raw' => 1,
                        'label-message' => 'servertime',
                        'default' => $nowserver,
-                       'section' => 'datetime/timeoffset',
+                       'section' => 'rendering/timeoffset',
                );
 
                $defaultPreferences['nowlocal'] = array(
@@ -612,7 +673,7 @@ class Preferences {
                        'raw' => 1,
                        'label-message' => 'localtime',
                        'default' => $nowlocal,
-                       'section' => 'datetime/timeoffset',
+                       'section' => 'rendering/timeoffset',
                );
 
                // Grab existing pref.
@@ -626,8 +687,8 @@ class Preferences {
                        $minDiff = $tz[1];
                        $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff / 60 ), abs( $minDiff ) % 60 );
                } elseif ( count( $tz ) > 1 && $tz[0] == 'ZoneInfo' &&
-                       !in_array( $tzOffset, HTMLFormField::flattenOptions( $tzOptions ) ) )
-               {
+                       !in_array( $tzOffset, HTMLFormField::flattenOptions( $tzOptions ) )
+               {
                        # Timezone offset can vary with DST
                        $userTZ = timezone_open( $tz[2] );
                        if ( $userTZ !== false ) {
@@ -642,7 +703,7 @@ class Preferences {
                        'options' => $tzOptions,
                        'default' => $tzSetting,
                        'size' => 20,
-                       'section' => 'datetime/timeoffset',
+                       'section' => 'rendering/timeoffset',
                );
        }
 
@@ -652,6 +713,18 @@ class Preferences {
         * @param $defaultPreferences Array
         */
        static function renderingPreferences( $user, IContextSource $context, &$defaultPreferences ) {
+               ## Diffs ####################################
+               $defaultPreferences['diffonly'] = array(
+                       'type' => 'toggle',
+                       'section' => 'rendering/diffs',
+                       'label-message' => 'tog-diffonly',
+               );
+               $defaultPreferences['norollbackdiff'] = array(
+                       'type' => 'toggle',
+                       'section' => 'rendering/diffs',
+                       'label-message' => 'tog-norollbackdiff',
+               );
+
                ## Page Rendering ##############################
                global $wgAllowUserCssPrefs;
                if ( $wgAllowUserCssPrefs ) {
@@ -674,43 +747,18 @@ class Preferences {
                }
 
                $defaultPreferences['stubthreshold'] = array(
-                       'type' => 'selectorother',
+                       'type' => 'select',
                        'section' => 'rendering/advancedrendering',
                        'options' => $stubThresholdOptions,
                        'size' => 20,
                        'label-raw' => $context->msg( 'stub-threshold' )->text(), // Raw HTML message. Yay?
                );
 
-               if ( $wgAllowUserCssPrefs ) {
-                       $defaultPreferences['showtoc'] = array(
-                               'type' => 'toggle',
-                               'section' => 'rendering/advancedrendering',
-                               'label-message' => 'tog-showtoc',
-                       );
-               }
-               $defaultPreferences['nocache'] = array(
-                       'type' => 'toggle',
-                       'label-message' => 'tog-nocache',
-                       'section' => 'rendering/advancedrendering',
-               );
                $defaultPreferences['showhiddencats'] = array(
                        'type' => 'toggle',
                        'section' => 'rendering/advancedrendering',
                        'label-message' => 'tog-showhiddencats'
                );
-               $defaultPreferences['showjumplinks'] = array(
-                       'type' => 'toggle',
-                       'section' => 'rendering/advancedrendering',
-                       'label-message' => 'tog-showjumplinks',
-               );
-
-               if ( $wgAllowUserCssPrefs ) {
-                       $defaultPreferences['justify'] = array(
-                               'type' => 'toggle',
-                               'section' => 'rendering/advancedrendering',
-                               'label-message' => 'tog-justify',
-                       );
-               }
 
                $defaultPreferences['numberheadings'] = array(
                        'type' => 'toggle',
@@ -728,25 +776,21 @@ class Preferences {
                global $wgAllowUserCssPrefs;
 
                ## Editing #####################################
-               $defaultPreferences['cols'] = array(
-                       'type' => 'int',
-                       'label-message' => 'columns',
-                       'section' => 'editing/textboxsize',
-                       'min' => 4,
-                       'max' => 1000,
+               $defaultPreferences['editsectiononrightclick'] = array(
+                       'type' => 'toggle',
+                       'section' => 'editing/advancedediting',
+                       'label-message' => 'tog-editsectiononrightclick',
                );
-               $defaultPreferences['rows'] = array(
-                       'type' => 'int',
-                       'label-message' => 'rows',
-                       'section' => 'editing/textboxsize',
-                       'min' => 4,
-                       'max' => 1000,
+               $defaultPreferences['editondblclick'] = array(
+                       'type' => 'toggle',
+                       'section' => 'editing/advancedediting',
+                       'label-message' => 'tog-editondblclick',
                );
 
                if ( $wgAllowUserCssPrefs ) {
                        $defaultPreferences['editfont'] = array(
                                'type' => 'select',
-                               'section' => 'editing/advancedediting',
+                               'section' => 'editing/editor',
                                'label-message' => 'editfont-style',
                                'options' => array(
                                        $context->msg( 'editfont-default' )->text() => 'default',
@@ -756,64 +800,57 @@ class Preferences {
                                )
                        );
                }
-               $defaultPreferences['previewontop'] = array(
-                       'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
-                       'label-message' => 'tog-previewontop',
+               $defaultPreferences['cols'] = array(
+                       'type' => 'int',
+                       'label-message' => 'columns',
+                       'section' => 'editing/editor',
+                       'min' => 4,
+                       'max' => 1000,
                );
-               $defaultPreferences['previewonfirst'] = array(
-                       'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
-                       'label-message' => 'tog-previewonfirst',
+               $defaultPreferences['rows'] = array(
+                       'type' => 'int',
+                       'label-message' => 'rows',
+                       'section' => 'editing/editor',
+                       'min' => 4,
+                       'max' => 1000,
                );
-
-               if ( $wgAllowUserCssPrefs ) {
-                       $defaultPreferences['editsection'] = array(
+               if ( $user->isAllowed( 'minoredit' ) ) {
+                       $defaultPreferences['minordefault'] = array(
                                'type' => 'toggle',
-                               'section' => 'editing/advancedediting',
-                               'label-message' => 'tog-editsection',
+                               'section' => 'editing/editor',
+                               'label-message' => 'tog-minordefault',
                        );
                }
-               $defaultPreferences['editsectiononrightclick'] = array(
+               $defaultPreferences['forceeditsummary'] = array(
                        'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
-                       'label-message' => 'tog-editsectiononrightclick',
+                       'section' => 'editing/editor',
+                       'label-message' => 'tog-forceeditsummary',
                );
-               $defaultPreferences['editondblclick'] = array(
+               $defaultPreferences['useeditwarning'] = array(
                        'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
-                       'label-message' => 'tog-editondblclick',
+                       'section' => 'editing/editor',
+                       'label-message' => 'tog-useeditwarning',
                );
                $defaultPreferences['showtoolbar'] = array(
                        'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
+                       'section' => 'editing/editor',
                        'label-message' => 'tog-showtoolbar',
                );
 
-               if ( $user->isAllowed( 'minoredit' ) ) {
-                       $defaultPreferences['minordefault'] = array(
-                               'type' => 'toggle',
-                               'section' => 'editing/advancedediting',
-                               'label-message' => 'tog-minordefault',
-                       );
-               }
-
-               $defaultPreferences['forceeditsummary'] = array(
+               $defaultPreferences['previewonfirst'] = array(
                        'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
-                       'label-message' => 'tog-forceeditsummary',
+                       'section' => 'editing/preview',
+                       'label-message' => 'tog-previewonfirst',
                );
-
-               $defaultPreferences['uselivepreview'] = array(
+               $defaultPreferences['previewontop'] = array(
                        'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
-                       'label-message' => 'tog-uselivepreview',
+                       'section' => 'editing/preview',
+                       'label-message' => 'tog-previewontop',
                );
-
-               $defaultPreferences['useeditwarning'] = array(
+               $defaultPreferences['uselivepreview'] = array(
                        'type' => 'toggle',
-                       'section' => 'editing/advancedediting',
-                       'label-message' => 'tog-useeditwarning',
+                       'section' => 'editing/preview',
+                       'label-message' => 'tog-uselivepreview',
                );
 
        }
@@ -942,19 +979,6 @@ class Preferences {
                        );
                }
 
-               if ( $wgEnableAPI ) {
-                       # Some random gibberish as a proposed default
-                       // @todo Fixme: this should use CryptRand but we may not want to read urandom on every view
-                       $hash = sha1( mt_rand() . microtime( true ) );
-
-                       $defaultPreferences['watchlisttoken'] = array(
-                               'type' => 'text',
-                               'section' => 'watchlist/advancedwatchlist',
-                               'label-message' => 'prefs-watchlist-token',
-                               'help' => $context->msg( 'prefs-help-watchlist-token', $hash )->escaped()
-                       );
-               }
-
                $watchTypes = array(
                        'edit' => 'watchdefault',
                        'move' => 'watchmoves',
@@ -968,6 +992,8 @@ class Preferences {
 
                foreach ( $watchTypes as $action => $pref ) {
                        if ( $user->isAllowed( $action ) ) {
+                               // Messages:
+                               // tog-watchdefault, tog-watchmoves, tog-watchdeletion, tog-watchcreations
                                $defaultPreferences[$pref] = array(
                                        'type' => 'toggle',
                                        'section' => 'watchlist/advancedwatchlist',
@@ -975,6 +1001,19 @@ class Preferences {
                                );
                        }
                }
+
+               if ( $wgEnableAPI ) {
+                       $defaultPreferences['watchlisttoken'] = array(
+                               'type' => 'api',
+                       );
+                       $defaultPreferences['watchlisttoken-info'] = array(
+                               'type' => 'info',
+                               'section' => 'watchlist/tokenwatchlist',
+                               'label-message' => 'prefs-watchlist-token',
+                               'default' => $user->getTokenFromOption( 'watchlisttoken' ),
+                               'help-message' => 'prefs-help-watchlist-token2',
+                       );
+               }
        }
 
        /**
@@ -983,29 +1022,7 @@ class Preferences {
         * @param $defaultPreferences Array
         */
        static function searchPreferences( $user, IContextSource $context, &$defaultPreferences ) {
-               global $wgContLang, $wgVectorUseSimpleSearch;
-
-               ## Search #####################################
-               $defaultPreferences['searchlimit'] = array(
-                       'type' => 'int',
-                       'label-message' => 'resultsperpage',
-                       'section' => 'searchoptions/displaysearchoptions',
-                       'min' => 0,
-               );
-
-               if ( $wgVectorUseSimpleSearch ) {
-                       $defaultPreferences['vector-simplesearch'] = array(
-                               'type' => 'toggle',
-                               'label-message' => 'vector-simplesearch-preference',
-                               'section' => 'searchoptions/displaysearchoptions',
-                       );
-               }
-
-               $defaultPreferences['disablesuggest'] = array(
-                       'type' => 'toggle',
-                       'label-message' => 'mwsuggest-disable',
-                       'section' => 'searchoptions/displaysearchoptions',
-               );
+               global $wgContLang;
 
                $defaultPreferences['searcheverything'] = array(
                        'type' => 'toggle',
@@ -1031,35 +1048,9 @@ class Preferences {
        }
 
        /**
-        * @param $user User
-        * @param $context IContextSource
-        * @param $defaultPreferences Array
+        * Dummy, kept for backwards-compatibility.
         */
        static function miscPreferences( $user, IContextSource $context, &$defaultPreferences ) {
-               global $wgContLang;
-
-               ## Misc #####################################
-               $defaultPreferences['diffonly'] = array(
-                       'type' => 'toggle',
-                       'section' => 'misc/diffs',
-                       'label-message' => 'tog-diffonly',
-               );
-               $defaultPreferences['norollbackdiff'] = array(
-                       'type' => 'toggle',
-                       'section' => 'misc/diffs',
-                       'label-message' => 'tog-norollbackdiff',
-               );
-
-               // Stuff from Language::getExtraUserToggles()
-               $toggles = $wgContLang->getExtraUserToggles();
-
-               foreach ( $toggles as $toggle ) {
-                       $defaultPreferences[$toggle] = array(
-                               'type' => 'toggle',
-                               'section' => 'personal/i18n',
-                               'label-message' => "tog-$toggle",
-                       );
-               }
        }
 
        /**
@@ -1110,7 +1101,10 @@ class Preferences {
                                $linkTools[] = Linker::link( $jsPage, $context->msg( 'prefs-custom-js' )->escaped() );
                        }
 
-                       $display = $sn . ' ' . $context->msg( 'parentheses', $context->getLanguage()->pipeList( $linkTools ) )->text();
+                       $display = $sn . ' ' . $context->msg(
+                               'parentheses',
+                               $context->getLanguage()->pipeList( $linkTools )
+                       )->text();
                        $ret[$display] = $skinkey;
                }
 
@@ -1201,8 +1195,13 @@ class Preferences {
                                $form->msg( 'badsiglength' )->numParams( $wgMaxSigChars )->text() );
                } elseif ( isset( $alldata['fancysig'] ) &&
                                $alldata['fancysig'] &&
-                               false === $wgParser->validateSig( $signature ) ) {
-                       return Xml::element( 'span', array( 'class' => 'error' ), $form->msg( 'badsig' )->text() );
+                               $wgParser->validateSig( $signature ) === false
+               ) {
+                       return Xml::element(
+                               'span',
+                               array( 'class' => 'error' ),
+                               $form->msg( 'badsig' )->text()
+                       );
                } else {
                        return true;
                }
@@ -1233,7 +1232,12 @@ class Preferences {
         * @param array $remove array of items to remove
         * @return HtmlForm
         */
-       static function getFormObject( $user, IContextSource $context, $formClass = 'PreferencesForm', array $remove = array() ) {
+       static function getFormObject(
+               $user,
+               IContextSource $context,
+               $formClass = 'PreferencesForm',
+               array $remove = array()
+       ) {
                $formDescriptor = Preferences::getPreferences( $user, $context );
                if ( count( $remove ) ) {
                        $removeKeys = array_flip( $remove );
@@ -1270,12 +1274,20 @@ class Preferences {
        static function getTimezoneOptions( IContextSource $context ) {
                $opt = array();
 
-               global $wgLocalTZoffset, $wgLocaltimezone;
-               // Check that $wgLocalTZoffset is the same as $wgLocaltimezone
-               if ( $wgLocalTZoffset == date( 'Z' ) / 60 ) {
-                       $server_tz_msg = $context->msg( 'timezoneuseserverdefault', $wgLocaltimezone )->text();
+               global $wgLocalTZoffset;
+               $timestamp = MWTimestamp::getLocalInstance();
+               // Check that $wgLocalTZoffset is the same as the local time zone offset
+               if ( $wgLocalTZoffset == $timestamp->format( 'Z' ) / 60 ) {
+                       $server_tz_msg = $context->msg(
+                               'timezoneuseserverdefault',
+                               $timestamp->getTimezone()->getName()
+                       )->text();
                } else {
-                       $tzstring = sprintf( '%+03d:%02d', floor( $wgLocalTZoffset / 60 ), abs( $wgLocalTZoffset ) % 60 );
+                       $tzstring = sprintf(
+                               '%+03d:%02d',
+                               floor( $wgLocalTZoffset / 60 ),
+                               abs( $wgLocalTZoffset ) % 60
+                       );
                        $server_tz_msg = $context->msg( 'timezoneuseserverdefault', $tzstring )->text();
                }
                $opt[$server_tz_msg] = "System|$wgLocalTZoffset";
@@ -1375,15 +1387,18 @@ class Preferences {
         *
         * @param $formData
         * @param $form PreferencesForm
-        * @param $entryPoint string
         * @return bool|Status|string
         */
-       static function tryFormSubmit( $formData, $form, $entryPoint = 'internal' ) {
+       static function tryFormSubmit( $formData, $form ) {
                global $wgHiddenPrefs, $wgAuth;
 
                $user = $form->getModifiedUser();
                $result = true;
 
+               if ( !$user->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) {
+                       return Status::newFatal( 'mypreferencesprotected' );
+               }
+
                // Filter input
                foreach ( array_keys( $formData ) as $name ) {
                        if ( isset( self::$saveFilters[$name] ) ) {
@@ -1392,40 +1407,37 @@ class Preferences {
                        }
                }
 
-               // Stuff that shouldn't be saved as a preference.
-               $saveBlacklist = array(
-                       'realname',
-                       'emailaddress',
-               );
-
                // Fortunately, the realname field is MUCH simpler
-               if ( !in_array( 'realname', $wgHiddenPrefs ) ) {
+               // (not really "private", but still shouldn't be edited without permission)
+               if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->isAllowed( 'editmyprivateinfo' ) && array_key_exists( 'realname', $formData ) ) {
                        $realName = $formData['realname'];
                        $user->setRealName( $realName );
                }
 
-               foreach ( $saveBlacklist as $b ) {
-                       unset( $formData[$b] );
-               }
+               if ( $user->isAllowed( 'editmyoptions' ) ) {
+                       foreach ( self::$saveBlacklist as $b ) {
+                               unset( $formData[$b] );
+                       }
 
-               # If users have saved a value for a preference which has subsequently been disabled
-               # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
-               # is subsequently re-enabled
-               # TODO: maintenance script to actually delete these
-               foreach ( $wgHiddenPrefs as $pref ) {
-                       # If the user has not set a non-default value here, the default will be returned
-                       # and subsequently discarded
-                       $formData[$pref] = $user->getOption( $pref, null, true );
-               }
+                       # If users have saved a value for a preference which has subsequently been disabled
+                       # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
+                       # is subsequently re-enabled
+                       foreach ( $wgHiddenPrefs as $pref ) {
+                               # If the user has not set a non-default value here, the default will be returned
+                               # and subsequently discarded
+                               $formData[$pref] = $user->getOption( $pref, null, true );
+                       }
 
-               // Keep old preferences from interfering due to back-compat code, etc.
-               $user->resetOptions( 'unused', $form->getContext() );
+                       // Keep old preferences from interfering due to back-compat code, etc.
+                       $user->resetOptions( 'unused', $form->getContext() );
 
-               foreach ( $formData as $key => $value ) {
-                       $user->setOption( $key, $value );
-               }
+                       foreach ( $formData as $key => $value ) {
+                               $user->setOption( $key, $value );
+                       }
 
-               $user->saveSettings();
+                       wfRunHooks( 'PreferencesFormPreSave', array( $formData, $form, $user, &$result ) );
+                       $user->saveSettings();
+               }
 
                $wgAuth->updateExternalDB( $user );
 
@@ -1438,7 +1450,7 @@ class Preferences {
         * @return Status
         */
        public static function tryUISubmit( $formData, $form ) {
-               $res = self::tryFormSubmit( $formData, $form, 'ui' );
+               $res = self::tryFormSubmit( $formData, $form );
 
                if ( $res ) {
                        $urlOptions = array( 'success' => 1 );
@@ -1460,7 +1472,8 @@ class Preferences {
        /**
         * Try to set a user's email address.
         * This does *not* try to validate the address.
-        * Caller is responsible for checking $wgAuth.
+        * Caller is responsible for checking $wgAuth and 'editmyprivateinfo'
+        * right.
         *
         * @deprecated in 1.20; use User::setEmailWithConfirmation() instead.
         * @param $user User
@@ -1549,13 +1562,19 @@ class PreferencesForm extends HTMLForm {
         * @return String
         */
        function getButtons() {
+               if ( !$this->getModifiedUser()->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) {
+                       return '';
+               }
+
                $html = parent::getButtons();
 
-               $t = SpecialPage::getTitleFor( 'Preferences', 'reset' );
+               if ( $this->getModifiedUser()->isAllowed( 'editmyoptions' ) ) {
+                       $t = SpecialPage::getTitleFor( 'Preferences', 'reset' );
 
-               $html .= "\n" . Linker::link( $t, $this->msg( 'restoreprefs' )->escaped() );
+                       $html .= "\n" . Linker::link( $t, $this->msg( 'restoreprefs' )->escaped() );
 
-               $html = Xml::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html );
+                       $html = Xml::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html );
+               }
 
                return $html;
        }