4 General information about this file:
5 We're now using the HTMLForm object with some customisation to generate the Preferences
6 form. This object handles generic submission, CSRF protection, layout and other logic
7 in a reusable manner. We subclass it as a PreferencesForm to make some minor
9 In order to generate the form, the HTMLForm object needs an array structure detailing the
10 form fields available, and that's what this class is for. Each element of the array is
11 a basic property-list, including the type of field, the label it is to be given in the
12 form, callbacks for validation and 'filtering', and other pertinent information. Note that
13 the 'default' field is named for generic forms, and does not represent the preference's
14 default (which is stored in $wgDefaultUserOptions), but the default for the form field,
15 which should be whatever the user has set for that preference. There is no need to
16 override it unless you have some special storage logic (for instance, those not presently
17 stored as options, but which are best set from the user preferences view).
18 Field types are implemented as subclasses of the generic HTMLFormField object, and
19 typically implement at least getInputHTML, which generates the HTML for the input field
20 to be placed in the table.
21 Once fields have been retrieved and validated, submission logic is handed over to the
22 tryUISubmit static method of this class.
26 static $defaultPreferences = null;
29 'timecorrection' => array( 'Preferences', 'filterTimezoneInput' ),
32 static function getPreferences( $user ) {
33 if ( self
::$defaultPreferences )
34 return self
::$defaultPreferences;
38 $defaultPreferences = array();
40 self
::profilePreferences( $user, $defaultPreferences );
41 self
::skinPreferences( $user, $defaultPreferences );
42 self
::filesPreferences( $user, $defaultPreferences );
43 self
::mathPreferences( $user, $defaultPreferences );
44 self
::datetimePreferences( $user, $defaultPreferences );
45 self
::renderingPreferences( $user, $defaultPreferences );
46 self
::editingPreferences( $user, $defaultPreferences );
47 self
::rcPreferences( $user, $defaultPreferences );
48 self
::watchlistPreferences( $user, $defaultPreferences );
49 self
::searchPreferences( $user, $defaultPreferences );
50 self
::miscPreferences( $user, $defaultPreferences );
52 wfRunHooks( 'GetPreferences', array( $user, &$defaultPreferences ) );
54 ## Remove preferences that wikis don't want to use
55 global $wgHiddenPrefs;
56 foreach ( $wgHiddenPrefs as $pref ) {
57 if ( isset( $defaultPreferences[$pref] ) ) {
58 unset( $defaultPreferences[$pref] );
62 ## Prod in defaults from the user
63 global $wgDefaultUserOptions;
64 foreach( $defaultPreferences as $name => &$info ) {
65 $prefFromUser = self
::getOptionFromUser( $name, $info, $user );
66 $field = HTMLForm
::loadInputFromParameters( $info ); // For validation
67 $defaultOptions = User
::getDefaultOptions();
68 $globalDefault = isset( $defaultOptions[$name] )
69 ?
$defaultOptions[$name]
72 // If it validates, set it as the default
73 if ( isset( $info['default'] ) ) {
74 // Already set, no problem
76 } elseif ( !is_null( $prefFromUser ) && // Make sure we're not just pulling nothing
77 $field->validate( $prefFromUser, $user->mOptions
) === true ) {
78 $info['default'] = $prefFromUser;
79 } elseif( $field->validate( $globalDefault, $user->mOptions
) === true ) {
80 $info['default'] = $globalDefault;
82 throw new MWException( "Global default '$globalDefault' is invalid for field $name" );
86 self
::$defaultPreferences = $defaultPreferences;
88 return $defaultPreferences;
91 // Pull option from a user account. Handles stuff like array-type preferences.
92 static function getOptionFromUser( $name, $info, $user ) {
93 $val = $user->getOption( $name );
95 // Handling for array-type preferences
96 if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
97 ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
99 $options = HTMLFormField
::flattenOptions( $info['options'] );
100 $prefix = isset( $info['prefix'] ) ?
$info['prefix'] : $name;
103 foreach( $options as $label => $value ) {
104 if( $user->getOption( "$prefix$value" ) ) {
113 static function profilePreferences( $user, &$defaultPreferences ) {
115 ## User info #####################################
117 $defaultPreferences['username'] =
120 'label-message' => 'username',
121 'default' => $user->getName(),
122 'section' => 'personal/info',
125 $defaultPreferences['userid'] =
128 'label-message' => 'uid',
129 'default' => $user->getId(),
130 'section' => 'personal/info',
133 # Get groups to which the user belongs
134 $userEffectiveGroups = $user->getEffectiveGroups();
135 $userGroups = $userMembers = array();
136 foreach( $userEffectiveGroups as $ueg ) {
138 // Skip the default * group, seems useless here
141 $groupName = User
::getGroupName( $ueg );
142 $userGroups[] = User
::makeGroupLinkHTML( $ueg, $groupName );
144 $memberName = User
::getGroupMember( $ueg );
145 $userMembers[] = User
::makeGroupLinkHTML( $ueg, $memberName );
147 asort( $userGroups );
148 asort( $userMembers );
150 $defaultPreferences['usergroups'] =
153 'label' => wfMsgExt( 'prefs-memberingroups', 'parseinline',
154 $wgLang->formatNum( count($userGroups) ) ),
155 'default' => wfMsgExt( 'prefs-memberingroups-type', array(),
156 $wgLang->commaList( $userGroups ),
157 $wgLang->commaList( $userMembers )
160 'section' => 'personal/info',
163 $defaultPreferences['editcount'] =
166 'label-message' => 'prefs-edits',
167 'default' => $wgLang->formatNum( $user->getEditCount() ),
168 'section' => 'personal/info',
171 if( $user->getRegistration() ) {
172 $defaultPreferences['registrationdate'] =
175 'label-message' => 'prefs-registration',
176 'default' => wfMsgExt( 'prefs-registration-date-time', 'parsemag',
177 $wgLang->timeanddate( $user->getRegistration(), true ),
178 $wgLang->date( $user->getRegistration(), true ),
179 $wgLang->time( $user->getRegistration(), true ) ),
180 'section' => 'personal/info',
184 // Actually changeable stuff
186 $defaultPreferences['realname'] =
188 'type' => $wgAuth->allowPropChange( 'realname' ) ?
'text' : 'info',
189 'default' => $user->getRealName(),
190 'section' => 'personal/info',
191 'label-message' => 'yourrealname',
192 'help-message' => 'prefs-help-realname',
195 $defaultPreferences['gender'] =
198 'section' => 'personal/info',
200 wfMsg( 'gender-male' ) => 'male',
201 wfMsg( 'gender-female' ) => 'female',
202 wfMsg( 'gender-unknown' ) => 'unknown',
204 'label-message' => 'yourgender',
205 'help-message' => 'prefs-help-gender',
208 if( $wgAuth->allowPasswordChange() ) {
209 global $wgUser; // For skin.
210 $link = $wgUser->getSkin()->link( SpecialPage
::getTitleFor( 'Resetpass' ),
211 wfMsgHtml( 'prefs-resetpass' ), array(),
212 array( 'returnto' => SpecialPage
::getTitleFor( 'Preferences' ) ) );
214 $defaultPreferences['password'] =
219 'label-message' => 'yourpassword',
220 'section' => 'personal/info',
224 $defaultPreferences['rememberpassword'] =
227 'label-message' => 'tog-rememberpassword',
228 'section' => 'personal/info',
232 global $wgContLanguageCode;
233 $languages = array_reverse( Language
::getLanguageNames( false ) );
234 if( !array_key_exists( $wgContLanguageCode, $languages ) ) {
235 $languages[$wgContLanguageCode] = $wgContLanguageCode;
240 foreach( $languages as $code => $name ) {
241 $display = wfBCP47( $code ) . ' - ' . $name;
242 $options[$display] = $code;
244 $defaultPreferences['language'] =
247 'section' => 'personal/i18n',
248 'options' => $options,
249 'label-message' => 'yourlanguage',
252 global $wgContLang, $wgDisableLangConversion;
253 global $wgDisableTitleConversion;
254 /* see if there are multiple language variants to choose from*/
255 $variantArray = array();
256 if( !$wgDisableLangConversion ) {
257 $variants = $wgContLang->getVariants();
259 $languages = Language
::getLanguageNames( true );
260 foreach( $variants as $v ) {
261 $v = str_replace( '_', '-', strtolower( $v ) );
262 if( array_key_exists( $v, $languages ) ) {
263 // If it doesn't have a name, we'll pretend it doesn't exist
264 $variantArray[$v] = $languages[$v];
269 foreach( $variantArray as $code => $name ) {
270 $display = wfBCP47( $code ) . ' - ' . $name;
271 $options[$display] = $code;
274 if( count( $variantArray ) > 1 ) {
275 $defaultPreferences['variant'] =
277 'label-message' => 'yourvariant',
279 'options' => $options,
280 'section' => 'personal/i18n',
285 if( count( $variantArray ) > 1 && !$wgDisableLangConversion && !$wgDisableTitleConversion ) {
286 $defaultPreferences['noconvertlink'] =
289 'section' => 'personal/i18n',
290 'label-message' => 'tog-noconvertlink',
294 global $wgMaxSigChars, $wgOut, $wgParser;
296 // show a preview of the old signature first
297 $oldsigWikiText = $wgParser->preSaveTransform( "~~~", new Title
, $user, new ParserOptions
);
298 $oldsigHTML = $wgOut->parseInline( $oldsigWikiText );
299 $defaultPreferences['oldsig'] =
303 'label-message' => 'tog-oldsig',
304 'default' => $oldsigHTML,
305 'section' => 'personal/signature',
307 $defaultPreferences['nickname'] =
309 'type' => $wgAuth->allowPropChange( 'nickname' ) ?
'text' : 'info',
310 'maxlength' => $wgMaxSigChars,
311 'label-message' => 'yournick',
312 'validation-callback' =>
313 array( 'Preferences', 'validateSignature' ),
314 'section' => 'personal/signature',
315 'filter-callback' => array( 'Preferences', 'cleanSignature' ),
317 $defaultPreferences['fancysig'] =
320 'label-message' => 'tog-fancysig',
321 'help-message' => 'prefs-help-signature', // show general help about signature at the bottom of the section
322 'section' => 'personal/signature'
327 global $wgEnableEmail;
328 if ($wgEnableEmail) {
330 global $wgEmailConfirmToEdit;
332 $defaultPreferences['emailaddress'] =
334 'type' => $wgAuth->allowPropChange( 'emailaddress' ) ?
'email' : 'info',
335 'default' => $user->getEmail(),
336 'section' => 'personal/email',
337 'label-message' => 'youremail',
338 'help-message' => $wgEmailConfirmToEdit
339 ?
'prefs-help-email-required'
340 : 'prefs-help-email',
341 'validation-callback' => array( 'Preferences', 'validateEmail' ),
344 global $wgEnableUserEmail, $wgEmailAuthentication;
346 $disableEmailPrefs = false;
348 if ( $wgEmailAuthentication ) {
349 if ( $user->getEmail() ) {
350 if( $user->getEmailAuthenticationTimestamp() ) {
351 // date and time are separate parameters to facilitate localisation.
352 // $time is kept for backward compat reasons.
353 // 'emailauthenticated' is also used in SpecialConfirmemail.php
354 $time = $wgLang->timeAndDate( $user->getEmailAuthenticationTimestamp(), true );
355 $d = $wgLang->date( $user->getEmailAuthenticationTimestamp(), true );
356 $t = $wgLang->time( $user->getEmailAuthenticationTimestamp(), true );
357 $emailauthenticated = wfMsgExt( 'emailauthenticated', 'parseinline',
358 array($time, $d, $t ) ) . '<br />';
359 $disableEmailPrefs = false;
361 $disableEmailPrefs = true;
362 global $wgUser; // wgUser is okay here, it's for display
363 $skin = $wgUser->getSkin();
364 $emailauthenticated = wfMsgExt( 'emailnotauthenticated', 'parseinline' ) . '<br />' .
366 SpecialPage
::getTitleFor( 'Confirmemail' ),
367 wfMsg( 'emailconfirmlink' ),
370 array( 'known', 'noclasses' )
374 $disableEmailPrefs = true;
375 $emailauthenticated = wfMsgHtml( 'noemailprefs' );
378 $defaultPreferences['emailauthentication'] =
382 'section' => 'personal/email',
383 'label-message' => 'prefs-emailconfirm-label',
384 'default' => $emailauthenticated,
389 if( $wgEnableUserEmail ) {
390 $defaultPreferences['disablemail'] =
394 'section' => 'personal/email',
395 'label-message' => 'allowemail',
396 'disabled' => $disableEmailPrefs,
398 $defaultPreferences['ccmeonemails'] =
401 'section' => 'personal/email',
402 'label-message' => 'tog-ccmeonemails',
403 'disabled' => $disableEmailPrefs,
407 global $wgEnotifWatchlist;
408 if ( $wgEnotifWatchlist ) {
409 $defaultPreferences['enotifwatchlistpages'] =
412 'section' => 'personal/email',
413 'label-message' => 'tog-enotifwatchlistpages',
414 'disabled' => $disableEmailPrefs,
417 global $wgEnotifUserTalk;
418 if( $wgEnotifUserTalk ) {
419 $defaultPreferences['enotifusertalkpages'] =
422 'section' => 'personal/email',
423 'label-message' => 'tog-enotifusertalkpages',
424 'disabled' => $disableEmailPrefs,
427 if( $wgEnotifUserTalk ||
$wgEnotifWatchlist ) {
428 $defaultPreferences['enotifminoredits'] =
431 'section' => 'personal/email',
432 'label-message' => 'tog-enotifminoredits',
433 'disabled' => $disableEmailPrefs,
436 $defaultPreferences['enotifrevealaddr'] =
439 'section' => 'personal/email',
440 'label-message' => 'tog-enotifrevealaddr',
441 'disabled' => $disableEmailPrefs,
446 static function skinPreferences( $user, &$defaultPreferences ) {
447 ## Skin #####################################
448 $defaultPreferences['skin'] =
451 'options' => self
::generateSkinOptions( $user ),
453 'section' => 'rendering/skin',
456 $selectedSkin = $user->getOption( 'skin' );
457 if ( in_array( $selectedSkin, array( 'cologneblue', 'standard' ) ) ) {
459 $settings = array_flip( $wgLang->getQuickbarSettings() );
461 $defaultPreferences['quickbar'] =
464 'options' => $settings,
465 'section' => 'rendering/skin',
466 'label-message' => 'qbsettings',
471 static function mathPreferences( $user, &$defaultPreferences ) {
472 ## Math #####################################
473 global $wgUseTeX, $wgLang;
475 $defaultPreferences['math'] =
479 array_flip( array_map( 'wfMsgHtml', $wgLang->getMathNames() ) ),
481 'section' => 'rendering/math',
486 static function filesPreferences( $user, &$defaultPreferences ) {
487 ## Files #####################################
488 $defaultPreferences['imagesize'] =
491 'options' => self
::getImageSizes(),
492 'label-message' => 'imagemaxsize',
493 'section' => 'rendering/files',
495 $defaultPreferences['thumbsize'] =
498 'options' => self
::getThumbSizes(),
499 'label-message' => 'thumbsize',
500 'section' => 'rendering/files',
504 static function datetimePreferences( $user, &$defaultPreferences ) {
507 ## Date and time #####################################
508 $dateOptions = self
::getDateOptions();
510 $defaultPreferences['date'] =
513 'options' => $dateOptions,
515 'section' => 'datetime/dateformat',
520 $nowlocal = Xml
::element( 'span', array( 'id' => 'wpLocalTime' ),
521 $wgLang->time( $now = wfTimestampNow(), true ) );
522 $nowserver = $wgLang->time( $now, false ) .
523 Xml
::hidden( 'wpServerTime', substr( $now, 8, 2 ) * 60 +
substr( $now, 10, 2 ) );
525 $defaultPreferences['nowserver'] =
529 'label-message' => 'servertime',
530 'default' => $nowserver,
531 'section' => 'datetime/timeoffset',
534 $defaultPreferences['nowlocal'] =
538 'label-message' => 'localtime',
539 'default' => $nowlocal,
540 'section' => 'datetime/timeoffset',
543 // Grab existing pref.
544 $tzOffset = $user->getOption( 'timecorrection' );
545 $tz = explode( '|', $tzOffset, 2 );
547 $tzSetting = $tzOffset;
548 if( count( $tz ) > 1 && $tz[0] == 'Offset' ) {
550 $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff/60 ), abs( $minDiff )%60
);
553 $defaultPreferences['timecorrection'] =
555 'class' => 'HTMLSelectOrOtherField',
556 'label-message' => 'timezonelegend',
557 'options' => self
::getTimezoneOptions(),
558 'default' => $tzSetting,
559 'section' => 'datetime/timeoffset',
563 static function renderingPreferences( $user, &$defaultPreferences ) {
564 ## Page Rendering ##############################
565 $defaultPreferences['underline'] =
569 wfMsg( 'underline-never' ) => 0,
570 wfMsg( 'underline-always' ) => 1,
571 wfMsg( 'underline-default' ) => 2,
573 'label-message' => 'tog-underline',
574 'section' => 'rendering/advancedrendering',
577 $stubThresholdValues = array( 0, 50, 100, 500, 1000, 2000, 5000, 10000 );
578 $stubThresholdOptions = array();
579 foreach( $stubThresholdValues as $value ) {
580 $stubThresholdOptions[wfMsg( 'size-bytes', $value )] = $value;
583 $defaultPreferences['stubthreshold'] =
585 'type' => 'selectorother',
586 'section' => 'rendering/advancedrendering',
587 'options' => $stubThresholdOptions,
588 'label' => wfMsg( 'stub-threshold' ), // Raw HTML message. Yay?
590 $defaultPreferences['highlightbroken'] =
593 'section' => 'rendering/advancedrendering',
594 'label' => wfMsg( 'tog-highlightbroken' ), // Raw HTML
596 $defaultPreferences['showtoc'] =
599 'section' => 'rendering/advancedrendering',
600 'label-message' => 'tog-showtoc',
602 $defaultPreferences['nocache'] =
605 'label-message' => 'tog-nocache',
606 'section' => 'rendering/advancedrendering',
608 $defaultPreferences['showhiddencats'] =
611 'section' => 'rendering/advancedrendering',
612 'label-message' => 'tog-showhiddencats'
614 $defaultPreferences['showjumplinks'] =
617 'section' => 'rendering/advancedrendering',
618 'label-message' => 'tog-showjumplinks',
620 $defaultPreferences['justify'] =
623 'section' => 'rendering/advancedrendering',
624 'label-message' => 'tog-justify',
626 $defaultPreferences['numberheadings'] =
629 'section' => 'rendering/advancedrendering',
630 'label-message' => 'tog-numberheadings',
634 static function editingPreferences( $user, &$defaultPreferences ) {
635 global $wgUseExternalEditor, $wgLivePreview;
637 ## Editing #####################################
638 $defaultPreferences['cols'] =
641 'label-message' => 'columns',
642 'section' => 'editing/textboxsize',
646 $defaultPreferences['rows'] =
649 'label-message' => 'rows',
650 'section' => 'editing/textboxsize',
655 $defaultPreferences['editfont'] =
658 'section' => 'editing/advancedediting',
659 'label-message' => 'editfont-style',
661 wfMsg( 'editfont-default' ) => 'default',
662 wfMsg( 'editfont-monospace' ) => 'monospace',
663 wfMsg( 'editfont-sansserif' ) => 'sans-serif',
664 wfMsg( 'editfont-serif' ) => 'serif',
667 $defaultPreferences['previewontop'] =
670 'section' => 'editing/advancedediting',
671 'label-message' => 'tog-previewontop',
673 $defaultPreferences['previewonfirst'] =
676 'section' => 'editing/advancedediting',
677 'label-message' => 'tog-previewonfirst',
679 $defaultPreferences['editsection'] =
682 'section' => 'editing/advancedediting',
683 'label-message' => 'tog-editsection',
685 $defaultPreferences['editsectiononrightclick'] =
688 'section' => 'editing/advancedediting',
689 'label-message' => 'tog-editsectiononrightclick',
691 $defaultPreferences['editondblclick'] =
694 'section' => 'editing/advancedediting',
695 'label-message' => 'tog-editondblclick',
697 $defaultPreferences['editwidth'] =
700 'section' => 'editing/advancedediting',
701 'label-message' => 'tog-editwidth',
703 $defaultPreferences['showtoolbar'] =
706 'section' => 'editing/advancedediting',
707 'label-message' => 'tog-showtoolbar',
709 $defaultPreferences['minordefault'] =
712 'section' => 'editing/advancedediting',
713 'label-message' => 'tog-minordefault',
716 if ( $wgUseExternalEditor ) {
717 $defaultPreferences['externaleditor'] =
720 'section' => 'editing/advancedediting',
721 'label-message' => 'tog-externaleditor',
723 $defaultPreferences['externaldiff'] =
726 'section' => 'editing/advancedediting',
727 'label-message' => 'tog-externaldiff',
731 $defaultPreferences['forceeditsummary'] =
734 'section' => 'editing/advancedediting',
735 'label-message' => 'tog-forceeditsummary',
737 if ( $wgLivePreview ) {
738 $defaultPreferences['uselivepreview'] =
741 'section' => 'editing/advancedediting',
742 'label-message' => 'tog-uselivepreview',
747 static function rcPreferences( $user, &$defaultPreferences ) {
748 global $wgRCMaxAge, $wgUseRCPatrol, $wgLang;
749 ## RecentChanges #####################################
750 $defaultPreferences['rcdays'] =
753 'label-message' => 'recentchangesdays',
754 'section' => 'rc/display',
756 'max' => ceil( $wgRCMaxAge / ( 3600*24 ) ),
757 'help' => wfMsgExt( 'recentchangesdays-max', array( 'parsemag' ), $wgLang->formatNum( ceil( $wgRCMaxAge / ( 3600*24 ) ) ) ),
759 $defaultPreferences['rclimit'] =
762 'label-message' => 'recentchangescount',
763 'help-message' => 'prefs-help-recentchangescount',
764 'section' => 'rc/display',
766 $defaultPreferences['usenewrc'] =
769 'label-message' => 'tog-usenewrc',
770 'section' => 'rc/advancedrc',
772 $defaultPreferences['hideminor'] =
775 'label-message' => 'tog-hideminor',
776 'section' => 'rc/advancedrc',
779 global $wgUseRCPatrol;
780 if( $wgUseRCPatrol ) {
781 $defaultPreferences['hidepatrolled'] =
784 'section' => 'rc/advancedrc',
785 'label-message' => 'tog-hidepatrolled',
787 $defaultPreferences['newpageshidepatrolled'] =
790 'section' => 'rc/advancedrc',
791 'label-message' => 'tog-newpageshidepatrolled',
795 global $wgRCShowWatchingUsers;
796 if( $wgRCShowWatchingUsers ) {
797 $defaultPreferences['shownumberswatching'] =
800 'section' => 'rc/advancedrc',
801 'label-message' => 'tog-shownumberswatching',
806 static function watchlistPreferences( $user, &$defaultPreferences ) {
807 global $wgUseRCPatrol, $wgEnableAPI;
808 ## Watchlist #####################################
809 $defaultPreferences['watchlistdays'] =
814 'section' => 'watchlist/display',
815 'help' => wfMsgHtml( 'prefs-watchlist-days-max' ),
816 'label-message' => 'prefs-watchlist-days',
818 $defaultPreferences['wllimit'] =
823 'label-message' => 'prefs-watchlist-edits',
824 'help' => wfMsgHtml( 'prefs-watchlist-edits-max' ),
825 'section' => 'watchlist/display',
827 $defaultPreferences['extendwatchlist'] =
830 'section' => 'watchlist/advancedwatchlist',
831 'label-message' => 'tog-extendwatchlist',
833 $defaultPreferences['watchlisthideminor'] =
836 'section' => 'watchlist/advancedwatchlist',
837 'label-message' => 'tog-watchlisthideminor',
839 $defaultPreferences['watchlisthidebots'] =
842 'section' => 'watchlist/advancedwatchlist',
843 'label-message' => 'tog-watchlisthidebots',
845 $defaultPreferences['watchlisthideown'] =
848 'section' => 'watchlist/advancedwatchlist',
849 'label-message' => 'tog-watchlisthideown',
851 $defaultPreferences['watchlisthideanons'] =
854 'section' => 'watchlist/advancedwatchlist',
855 'label-message' => 'tog-watchlisthideanons',
857 $defaultPreferences['watchlisthideliu'] =
860 'section' => 'watchlist/advancedwatchlist',
861 'label-message' => 'tog-watchlisthideliu',
863 if ( $wgEnableAPI ) {
864 # Some random gibberish as a proposed default
865 $hash = sha1( mt_rand() . microtime( true ) );
866 $defaultPreferences['watchlisttoken'] =
869 'section' => 'watchlist/advancedwatchlist',
870 'label-message' => 'prefs-watchlist-token',
871 'help' => wfMsgHtml( 'prefs-help-watchlist-token', $hash )
875 if ( $wgUseRCPatrol ) {
876 $defaultPreferences['watchlisthidepatrolled'] =
879 'section' => 'watchlist/advancedwatchlist',
880 'label-message' => 'tog-watchlisthidepatrolled',
885 'edit' => 'watchdefault',
886 'move' => 'watchmoves',
887 'delete' => 'watchdeletion'
891 if( $user->isAllowed( 'createpage' ) ||
$user->isAllowed( 'createtalk' ) ) {
892 $watchTypes['read'] = 'watchcreations';
895 foreach( $watchTypes as $action => $pref ) {
896 if ( $user->isAllowed( $action ) ) {
897 $defaultPreferences[$pref] = array(
899 'section' => 'watchlist/advancedwatchlist',
900 'label-message' => "tog-$pref",
906 static function searchPreferences( $user, &$defaultPreferences ) {
909 ## Search #####################################
910 $defaultPreferences['searchlimit'] =
913 'label-message' => 'resultsperpage',
914 'section' => 'searchoptions/display',
917 $defaultPreferences['contextlines'] =
920 'label-message' => 'contextlines',
921 'section' => 'searchoptions/display',
924 $defaultPreferences['contextchars'] =
927 'label-message' => 'contextchars',
928 'section' => 'searchoptions/display',
931 global $wgEnableMWSuggest;
932 if( $wgEnableMWSuggest ) {
933 $defaultPreferences['disablesuggest'] =
936 'label-message' => 'mwsuggest-disable',
937 'section' => 'searchoptions/display',
941 $defaultPreferences['searcheverything'] =
944 'label-message' => 'searcheverything-enable',
945 'section' => 'searchoptions/advancedsearchoptions',
948 // Searchable namespaces back-compat with old format
949 $searchableNamespaces = SearchEngine
::searchableNamespaces();
951 $nsOptions = array();
952 foreach( $wgContLang->getNamespaces() as $ns => $name ) {
953 if( $ns < 0 ) continue;
954 $displayNs = str_replace( '_', ' ', $name );
956 if( !$displayNs ) $displayNs = wfMsg( 'blanknamespace' );
958 $displayNs = htmlspecialchars( $displayNs );
959 $nsOptions[$displayNs] = $ns;
962 $defaultPreferences['searchnamespaces'] =
964 'type' => 'multiselect',
965 'label-message' => 'defaultns',
966 'options' => $nsOptions,
967 'section' => 'searchoptions/advancedsearchoptions',
968 'prefix' => 'searchNs',
972 static function miscPreferences( $user, &$defaultPreferences ) {
973 ## Misc #####################################
974 $defaultPreferences['diffonly'] =
977 'section' => 'misc/diffs',
978 'label-message' => 'tog-diffonly',
980 $defaultPreferences['norollbackdiff'] =
983 'section' => 'misc/diffs',
984 'label-message' => 'tog-norollbackdiff',
987 // Stuff from Language::getExtraUserToggles()
990 $toggles = $wgContLang->getExtraUserToggles();
992 foreach( $toggles as $toggle ) {
993 $defaultPreferences[$toggle] =
996 'section' => 'personal/i18n',
997 'label-message' => "tog-$toggle",
1002 static function generateSkinOptions( $user ) {
1003 global $wgDefaultSkin;
1006 $mptitle = Title
::newMainPage();
1007 $previewtext = wfMsgHtml( 'skin-preview' );
1008 # Only show members of Skin::getSkinNames() rather than
1009 # $skinNames (skins is all skin names from Language.php)
1010 $validSkinNames = Skin
::getUsableSkins();
1011 # Sort by UI skin name. First though need to update validSkinNames as sometimes
1012 # the skinkey & UI skinname differ (e.g. "standard" skinkey is "Classic" in the UI).
1013 foreach ( $validSkinNames as $skinkey => &$skinname ) {
1014 $msgName = "skinname-{$skinkey}";
1015 $localisedSkinName = wfMsg( $msgName );
1016 if ( !wfEmptyMsg( $msgName, $localisedSkinName ) ) {
1017 $skinname = htmlspecialchars( $localisedSkinName );
1020 asort( $validSkinNames );
1021 $sk = $user->getSkin();
1023 foreach( $validSkinNames as $skinkey => $sn ) {
1024 $mplink = htmlspecialchars( $mptitle->getLocalURL( "useskin=$skinkey" ) );
1025 $previewlink = "(<a target='_blank' href=\"$mplink\">$previewtext</a>)";
1027 global $wgAllowUserCss, $wgAllowUserJs;
1028 if( $wgAllowUserCss ) {
1029 $cssPage = Title
::makeTitleSafe( NS_USER
, $user->getName() . '/' . $skinkey . '.css' );
1030 $customCSS = $sk->link( $cssPage, wfMsgHtml( 'prefs-custom-css' ) );
1031 $extraLinks .= " ($customCSS)";
1033 if( $wgAllowUserJs ) {
1034 $jsPage = Title
::makeTitleSafe( NS_USER
, $user->getName() . '/' . $skinkey . '.js' );
1035 $customJS = $sk->link( $jsPage, wfMsgHtml( 'prefs-custom-js' ) );
1036 $extraLinks .= " ($customJS)";
1038 if( $skinkey == $wgDefaultSkin )
1039 $sn .= ' (' . wfMsgHtml( 'default' ) . ')';
1040 $display = "$sn $previewlink{$extraLinks}";
1041 $ret[$display] = $skinkey;
1047 static function getDateOptions() {
1049 $dateopts = $wgLang->getDatePreferences();
1054 if ( !in_array( 'default', $dateopts ) ) {
1055 $dateopts[] = 'default'; // Make sure default is always valid
1060 $epoch = '20010115161234'; # Wikipedia day
1061 foreach( $dateopts as $key ) {
1062 if( $key == 'default' ) {
1063 $formatted = wfMsgHtml( 'datedefault' );
1065 $formatted = htmlspecialchars( $wgLang->timeanddate( $epoch, false, $key ) );
1067 $ret[$formatted] = $key;
1073 static function getImageSizes() {
1074 global $wgImageLimits;
1078 foreach ( $wgImageLimits as $index => $limits ) {
1079 $display = "{$limits[0]}×{$limits[1]}" . wfMsg( 'unit-pixel' );
1080 $ret[$display] = $index;
1086 static function getThumbSizes() {
1087 global $wgThumbLimits;
1091 foreach ( $wgThumbLimits as $index => $size ) {
1092 $display = $size . wfMsg( 'unit-pixel' );
1093 $ret[$display] = $index;
1099 static function validateSignature( $signature, $alldata ) {
1100 global $wgParser, $wgMaxSigChars, $wgLang;
1101 if( mb_strlen( $signature ) > $wgMaxSigChars ) {
1103 Xml
::element( 'span', array( 'class' => 'error' ),
1104 wfMsgExt( 'badsiglength', 'parsemag',
1105 $wgLang->formatNum( $wgMaxSigChars )
1108 } elseif( !empty( $alldata['fancysig'] ) &&
1109 false === $wgParser->validateSig( $signature ) ) {
1110 return Xml
::element( 'span', array( 'class' => 'error' ), wfMsg( 'badsig' ) );
1116 static function cleanSignature( $signature, $alldata ) {
1118 if( $alldata['fancysig'] ) {
1119 $signature = $wgParser->cleanSig( $signature );
1121 // When no fancy sig used, make sure ~{3,5} get removed.
1122 $signature = $wgParser->cleanSigInSig( $signature );
1128 static function validateEmail( $email, $alldata ) {
1129 if ( $email && !User
::isValidEmailAddr( $email ) ) {
1130 return wfMsgExt( 'invalidemailaddress', 'parseinline' );
1133 global $wgEmailConfirmToEdit;
1134 if( $wgEmailConfirmToEdit && !$email ) {
1135 return wfMsgExt( 'noemailtitle', 'parseinline' );
1140 static function getFormObject( $user ) {
1141 $formDescriptor = Preferences
::getPreferences( $user );
1142 $htmlForm = new PreferencesForm( $formDescriptor, 'prefs' );
1144 $htmlForm->setSubmitText( wfMsg( 'saveprefs' ) );
1145 $htmlForm->setTitle( SpecialPage
::getTitleFor( 'Preferences' ) );
1146 $htmlForm->setSubmitID( 'prefsubmit' );
1147 $htmlForm->setSubmitCallback( array( 'Preferences', 'tryFormSubmit' ) );
1152 static function getTimezoneOptions() {
1155 global $wgLocalTZoffset;
1157 $opt[wfMsg( 'timezoneuseserverdefault' )] = "System|$wgLocalTZoffset";
1158 $opt[wfMsg( 'timezoneuseoffset' )] = 'other';
1159 $opt[wfMsg( 'guesstimezone' )] = 'guess';
1161 if ( function_exists( 'timezone_identifiers_list' ) ) {
1162 # Read timezone list
1163 $tzs = timezone_identifiers_list();
1166 $tzRegions = array();
1167 $tzRegions['Africa'] = wfMsg( 'timezoneregion-africa' );
1168 $tzRegions['America'] = wfMsg( 'timezoneregion-america' );
1169 $tzRegions['Antarctica'] = wfMsg( 'timezoneregion-antarctica' );
1170 $tzRegions['Arctic'] = wfMsg( 'timezoneregion-arctic' );
1171 $tzRegions['Asia'] = wfMsg( 'timezoneregion-asia' );
1172 $tzRegions['Atlantic'] = wfMsg( 'timezoneregion-atlantic' );
1173 $tzRegions['Australia'] = wfMsg( 'timezoneregion-australia' );
1174 $tzRegions['Europe'] = wfMsg( 'timezoneregion-europe' );
1175 $tzRegions['Indian'] = wfMsg( 'timezoneregion-indian' );
1176 $tzRegions['Pacific'] = wfMsg( 'timezoneregion-pacific' );
1177 asort( $tzRegions );
1179 $prefill = array_fill_keys( array_values( $tzRegions ), array() );
1180 $opt = array_merge( $opt, $prefill );
1182 $now = date_create( 'now' );
1184 foreach ( $tzs as $tz ) {
1185 $z = explode( '/', $tz, 2 );
1187 # timezone_identifiers_list() returns a number of
1188 # backwards-compatibility entries. This filters them out of the
1189 # list presented to the user.
1190 if ( count( $z ) != 2 ||
!array_key_exists( $z[0], $tzRegions ) )
1194 $z[0] = $tzRegions[$z[0]];
1196 $minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 );
1198 $display = str_replace( '_', ' ', $z[0] . '/' . $z[1] );
1199 $value = "ZoneInfo|$minDiff|$tz";
1201 $opt[$z[0]][$display] = $value;
1207 static function filterTimezoneInput( $tz, $alldata ) {
1208 $data = explode( '|', $tz, 3 );
1209 switch ( $data[0] ) {
1214 $data = explode( ':', $tz, 2 );
1216 if( count( $data ) == 2 ) {
1217 $data[0] = intval( $data[0] );
1218 $data[1] = intval( $data[1] );
1219 $minDiff = abs( $data[0] ) * 60 +
$data[1];
1220 if ( $data[0] < 0 ) $minDiff = -$minDiff;
1222 $minDiff = intval( $data[0] ) * 60;
1225 # Max is +14:00 and min is -12:00, see:
1226 # http://en.wikipedia.org/wiki/Timezone
1227 $minDiff = min( $minDiff, 840 ); # 14:00
1228 $minDiff = max( $minDiff, -720 ); # -12:00
1229 return 'Offset|'.$minDiff;
1233 static function tryFormSubmit( $formData, $entryPoint = 'internal' ) {
1234 global $wgUser, $wgEmailAuthentication, $wgEnableEmail;
1239 foreach( array_keys( $formData ) as $name ) {
1240 if ( isset( self
::$saveFilters[$name] ) ) {
1242 call_user_func( self
::$saveFilters[$name], $formData[$name], $formData );
1246 // Stuff that shouldn't be saved as a preference.
1247 $saveBlacklist = array(
1252 if( $wgEnableEmail ) {
1253 $newadr = $formData['emailaddress'];
1254 $oldadr = $wgUser->getEmail();
1255 if( ( $newadr != '' ) && ( $newadr != $oldadr ) ) {
1256 # the user has supplied a new email address on the login page
1257 # new behaviour: set this new emailaddr from login-page into user database record
1258 $wgUser->setEmail( $newadr );
1259 # but flag as "dirty" = unauthenticated
1260 $wgUser->invalidateEmail();
1261 if( $wgEmailAuthentication ) {
1262 # Mail a temporary password to the dirty address.
1263 # User can come back through the confirmation URL to re-enable email.
1264 $result = $wgUser->sendConfirmationMail();
1265 if( WikiError
::isError( $result ) ) {
1266 return wfMsg( 'mailerror', htmlspecialchars( $result->getMessage() ) );
1267 } elseif( $entryPoint == 'ui' ) {
1272 $wgUser->setEmail( $newadr );
1274 if( $oldadr != $newadr ) {
1275 wfRunHooks( 'PrefsEmailAudit', array( $wgUser, $oldadr, $newadr ) );
1279 // Fortunately, the realname field is MUCH simpler
1280 global $wgHiddenPrefs;
1281 if ( !in_array( 'realname', $wgHiddenPrefs ) ) {
1282 $realName = $formData['realname'];
1283 $wgUser->setRealName( $realName );
1286 foreach( $saveBlacklist as $b )
1287 unset( $formData[$b] );
1289 // Keeps old preferences from interfering due to back-compat
1291 $wgUser->resetOptions();
1293 foreach( $formData as $key => $value ) {
1294 $wgUser->setOption( $key, $value );
1297 $wgUser->saveSettings();
1302 public static function tryUISubmit( $formData ) {
1303 $res = self
::tryFormSubmit( $formData, 'ui' );
1306 $urlOptions = array( 'success' );
1307 if( $res === 'eauth' )
1308 $urlOptions[] = 'eauth';
1310 $queryString = implode( '&', $urlOptions );
1312 $url = SpecialPage
::getTitleFor( 'Preferences' )->getFullURL( $queryString );
1314 $wgOut->redirect( $url );
1320 public static function loadOldSearchNs( $user ) {
1321 $searchableNamespaces = SearchEngine
::searchableNamespaces();
1322 // Back compat with old format
1325 foreach( $searchableNamespaces as $ns => $name ) {
1326 if( $user->getOption( 'searchNs' . $ns ) ) {
1335 /** Some tweaks to allow js prefs to work */
1336 class PreferencesForm
extends HTMLForm
{
1338 function wrapForm( $html ) {
1339 $html = Xml
::tags( 'div', array( 'id' => 'preferences' ), $html );
1341 return parent
::wrapForm( $html );
1344 function getButtons() {
1345 $html = parent
::getButtons();
1349 $sk = $wgUser->getSkin();
1350 $t = SpecialPage
::getTitleFor( 'Preferences', 'reset' );
1352 $html .= "\n" . $sk->link( $t, wfMsgHtml( 'restoreprefs' ) );
1354 $html = Xml
::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html );
1359 function filterDataForSubmit( $data ) {
1360 // Support for separating MultiSelect preferences into multiple preferences
1361 // Due to lack of array support.
1362 foreach( $this->mFlatFields
as $fieldname => $field ) {
1363 $info = $field->mParams
;
1364 if( $field instanceof HTMLMultiSelectField
) {
1365 $options = HTMLFormField
::flattenOptions( $info['options'] );
1366 $prefix = isset( $info['prefix'] ) ?
$info['prefix'] : $fieldname;
1368 foreach( $options as $opt ) {
1369 $data["$prefix$opt"] = in_array( $opt, $data[$fieldname] );
1372 unset( $data[$fieldname] );