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, $wgParser;
296 // show a preview of the old signature first
297 $oldsigtext = $wgParser->preSaveTransform( "~~~", new Title
, $user, new ParserOptions
);
298 $oldsig = $wgParser->parse( $oldsigtext, new Title
, new ParserOptions
);
299 $m = array(); // remove <p> created by the parser (looks better without <p>)
300 if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $oldsig->mText
, $m ) ) $oldsig->mText
= $m[1];
302 $defaultPreferences['oldsig'] =
306 'label-message' => 'tog-oldsig',
307 'default' => $oldsig->mText
,
308 'section' => 'personal/signature',
310 $defaultPreferences['nickname'] =
312 'type' => $wgAuth->allowPropChange( 'nickname' ) ?
'text' : 'info',
313 'maxlength' => $wgMaxSigChars,
314 'label-message' => 'yournick',
315 'validation-callback' =>
316 array( 'Preferences', 'validateSignature' ),
317 'section' => 'personal/signature',
318 'filter-callback' => array( 'Preferences', 'cleanSignature' ),
320 $defaultPreferences['fancysig'] =
323 'label-message' => 'tog-fancysig',
324 'help-message' => 'prefs-help-signature', // show general help about signature at the bottom of the section
325 'section' => 'personal/signature'
330 global $wgEnableEmail;
331 if ($wgEnableEmail) {
333 global $wgEmailConfirmToEdit;
335 $defaultPreferences['emailaddress'] =
337 'type' => $wgAuth->allowPropChange( 'emailaddress' ) ?
'text' : 'info',
338 'default' => $user->getEmail(),
339 'section' => 'personal/email',
340 'label-message' => 'youremail',
341 'help-message' => $wgEmailConfirmToEdit
342 ?
'prefs-help-email-required'
343 : 'prefs-help-email',
344 'validation-callback' => array( 'Preferences', 'validateEmail' ),
347 global $wgEnableUserEmail, $wgEmailAuthentication;
349 $disableEmailPrefs = false;
351 if ( $wgEmailAuthentication ) {
352 if ( $user->getEmail() ) {
353 if( $user->getEmailAuthenticationTimestamp() ) {
354 // date and time are separate parameters to facilitate localisation.
355 // $time is kept for backward compat reasons.
356 // 'emailauthenticated' is also used in SpecialConfirmemail.php
357 $time = $wgLang->timeAndDate( $user->getEmailAuthenticationTimestamp(), true );
358 $d = $wgLang->date( $user->getEmailAuthenticationTimestamp(), true );
359 $t = $wgLang->time( $user->getEmailAuthenticationTimestamp(), true );
360 $emailauthenticated = wfMsgExt( 'emailauthenticated', 'parseinline',
361 array($time, $d, $t ) ) . '<br />';
362 $disableEmailPrefs = false;
364 $disableEmailPrefs = true;
365 global $wgUser; // wgUser is okay here, it's for display
366 $skin = $wgUser->getSkin();
367 $emailauthenticated = wfMsgExt( 'emailnotauthenticated', 'parseinline' ) . '<br />' .
369 SpecialPage
::getTitleFor( 'Confirmemail' ),
370 wfMsg( 'emailconfirmlink' ),
373 array( 'known', 'noclasses' )
377 $disableEmailPrefs = true;
378 $emailauthenticated = wfMsgHtml( 'noemailprefs' );
381 $defaultPreferences['emailauthentication'] =
385 'section' => 'personal/email',
386 'label-message' => 'prefs-emailconfirm-label',
387 'default' => $emailauthenticated,
392 if( $wgEnableUserEmail ) {
393 $defaultPreferences['disablemail'] =
397 'section' => 'personal/email',
398 'label-message' => 'allowemail',
399 'disabled' => $disableEmailPrefs,
401 $defaultPreferences['ccmeonemails'] =
404 'section' => 'personal/email',
405 'label-message' => 'tog-ccmeonemails',
406 'disabled' => $disableEmailPrefs,
410 global $wgEnotifWatchlist;
411 if ( $wgEnotifWatchlist ) {
412 $defaultPreferences['enotifwatchlistpages'] =
415 'section' => 'personal/email',
416 'label-message' => 'tog-enotifwatchlistpages',
417 'disabled' => $disableEmailPrefs,
420 global $wgEnotifUserTalk;
421 if( $wgEnotifUserTalk ) {
422 $defaultPreferences['enotifusertalkpages'] =
425 'section' => 'personal/email',
426 'label-message' => 'tog-enotifusertalkpages',
427 'disabled' => $disableEmailPrefs,
430 if( $wgEnotifUserTalk ||
$wgEnotifWatchlist ) {
431 $defaultPreferences['enotifminoredits'] =
434 'section' => 'personal/email',
435 'label-message' => 'tog-enotifminoredits',
436 'disabled' => $disableEmailPrefs,
439 $defaultPreferences['enotifrevealaddr'] =
442 'section' => 'personal/email',
443 'label-message' => 'tog-enotifrevealaddr',
444 'disabled' => $disableEmailPrefs,
449 static function skinPreferences( $user, &$defaultPreferences ) {
450 ## Skin #####################################
451 $defaultPreferences['skin'] =
454 'options' => self
::generateSkinOptions( $user ),
456 'section' => 'rendering/skin',
459 $selectedSkin = $user->getOption( 'skin' );
460 if ( in_array( $selectedSkin, array( 'cologneblue', 'standard' ) ) ) {
462 $settings = array_flip( $wgLang->getQuickbarSettings() );
464 $defaultPreferences['quickbar'] =
467 'options' => $settings,
468 'section' => 'rendering/skin',
469 'label-message' => 'qbsettings',
474 static function mathPreferences( $user, &$defaultPreferences ) {
475 ## Math #####################################
476 global $wgUseTeX, $wgLang;
478 $defaultPreferences['math'] =
482 array_flip( array_map( 'wfMsgHtml', $wgLang->getMathNames() ) ),
484 'section' => 'rendering/math',
489 static function filesPreferences( $user, &$defaultPreferences ) {
490 ## Files #####################################
491 $defaultPreferences['imagesize'] =
494 'options' => self
::getImageSizes(),
495 'label-message' => 'imagemaxsize',
496 'section' => 'rendering/files',
498 $defaultPreferences['thumbsize'] =
501 'options' => self
::getThumbSizes(),
502 'label-message' => 'thumbsize',
503 'section' => 'rendering/files',
507 static function datetimePreferences( $user, &$defaultPreferences ) {
510 ## Date and time #####################################
511 $dateOptions = self
::getDateOptions();
513 $defaultPreferences['date'] =
516 'options' => $dateOptions,
518 'section' => 'datetime/dateformat',
523 $nowlocal = Xml
::element( 'span', array( 'id' => 'wpLocalTime' ),
524 $wgLang->time( $now = wfTimestampNow(), true ) );
525 $nowserver = $wgLang->time( $now, false ) .
526 Xml
::hidden( 'wpServerTime', substr( $now, 8, 2 ) * 60 +
substr( $now, 10, 2 ) );
528 $defaultPreferences['nowserver'] =
532 'label-message' => 'servertime',
533 'default' => $nowserver,
534 'section' => 'datetime/timeoffset',
537 $defaultPreferences['nowlocal'] =
541 'label-message' => 'localtime',
542 'default' => $nowlocal,
543 'section' => 'datetime/timeoffset',
546 // Grab existing pref.
547 $tzOffset = $user->getOption( 'timecorrection' );
548 $tz = explode( '|', $tzOffset, 2 );
550 $tzSetting = $tzOffset;
551 if( count( $tz ) > 1 && $tz[0] == 'Offset' ) {
553 $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff/60 ), abs( $minDiff )%60
);
556 $defaultPreferences['timecorrection'] =
558 'class' => 'HTMLSelectOrOtherField',
559 'label-message' => 'timezonelegend',
560 'options' => self
::getTimezoneOptions(),
561 'default' => $tzSetting,
562 'section' => 'datetime/timeoffset',
566 static function renderingPreferences( $user, &$defaultPreferences ) {
567 ## Page Rendering ##############################
568 $defaultPreferences['underline'] =
572 wfMsg( 'underline-never' ) => 0,
573 wfMsg( 'underline-always' ) => 1,
574 wfMsg( 'underline-default' ) => 2,
576 'label-message' => 'tog-underline',
577 'section' => 'rendering/advancedrendering',
580 $stubThresholdValues = array( 0, 50, 100, 500, 1000, 2000, 5000, 10000 );
581 $stubThresholdOptions = array();
582 foreach( $stubThresholdValues as $value ) {
583 $stubThresholdOptions[wfMsg( 'size-bytes', $value )] = $value;
586 $defaultPreferences['stubthreshold'] =
588 'type' => 'selectorother',
589 'section' => 'rendering/advancedrendering',
590 'options' => $stubThresholdOptions,
591 'label' => wfMsg( 'stub-threshold' ), // Raw HTML message. Yay?
593 $defaultPreferences['highlightbroken'] =
596 'section' => 'rendering/advancedrendering',
597 'label' => wfMsg( 'tog-highlightbroken' ), // Raw HTML
599 $defaultPreferences['showtoc'] =
602 'section' => 'rendering/advancedrendering',
603 'label-message' => 'tog-showtoc',
605 $defaultPreferences['nocache'] =
608 'label-message' => 'tog-nocache',
609 'section' => 'rendering/advancedrendering',
611 $defaultPreferences['showhiddencats'] =
614 'section' => 'rendering/advancedrendering',
615 'label-message' => 'tog-showhiddencats'
617 $defaultPreferences['showjumplinks'] =
620 'section' => 'rendering/advancedrendering',
621 'label-message' => 'tog-showjumplinks',
623 $defaultPreferences['justify'] =
626 'section' => 'rendering/advancedrendering',
627 'label-message' => 'tog-justify',
629 $defaultPreferences['numberheadings'] =
632 'section' => 'rendering/advancedrendering',
633 'label-message' => 'tog-numberheadings',
637 static function editingPreferences( $user, &$defaultPreferences ) {
638 global $wgUseExternalEditor, $wgLivePreview;
640 ## Editing #####################################
641 $defaultPreferences['cols'] =
644 'label-message' => 'columns',
645 'section' => 'editing/textboxsize',
649 $defaultPreferences['rows'] =
652 'label-message' => 'rows',
653 'section' => 'editing/textboxsize',
658 $defaultPreferences['editfont'] =
661 'section' => 'editing/advancedediting',
662 'label-message' => 'editfont-style',
664 wfMsg( 'editfont-default' ) => 'default',
665 wfMsg( 'editfont-monospace' ) => 'monospace',
666 wfMsg( 'editfont-sansserif' ) => 'sans-serif',
667 wfMsg( 'editfont-serif' ) => 'serif',
670 $defaultPreferences['previewontop'] =
673 'section' => 'editing/advancedediting',
674 'label-message' => 'tog-previewontop',
676 $defaultPreferences['previewonfirst'] =
679 'section' => 'editing/advancedediting',
680 'label-message' => 'tog-previewonfirst',
682 $defaultPreferences['editsection'] =
685 'section' => 'editing/advancedediting',
686 'label-message' => 'tog-editsection',
688 $defaultPreferences['editsectiononrightclick'] =
691 'section' => 'editing/advancedediting',
692 'label-message' => 'tog-editsectiononrightclick',
694 $defaultPreferences['editondblclick'] =
697 'section' => 'editing/advancedediting',
698 'label-message' => 'tog-editondblclick',
700 $defaultPreferences['editwidth'] =
703 'section' => 'editing/advancedediting',
704 'label-message' => 'tog-editwidth',
706 $defaultPreferences['showtoolbar'] =
709 'section' => 'editing/advancedediting',
710 'label-message' => 'tog-showtoolbar',
712 $defaultPreferences['minordefault'] =
715 'section' => 'editing/advancedediting',
716 'label-message' => 'tog-minordefault',
719 if ( $wgUseExternalEditor ) {
720 $defaultPreferences['externaleditor'] =
723 'section' => 'editing/advancedediting',
724 'label-message' => 'tog-externaleditor',
726 $defaultPreferences['externaldiff'] =
729 'section' => 'editing/advancedediting',
730 'label-message' => 'tog-externaldiff',
734 $defaultPreferences['forceeditsummary'] =
737 'section' => 'editing/advancedediting',
738 'label-message' => 'tog-forceeditsummary',
740 if ( $wgLivePreview ) {
741 $defaultPreferences['uselivepreview'] =
744 'section' => 'editing/advancedediting',
745 'label-message' => 'tog-uselivepreview',
750 static function rcPreferences( $user, &$defaultPreferences ) {
751 global $wgRCMaxAge, $wgUseRCPatrol, $wgLang;
752 ## RecentChanges #####################################
753 $defaultPreferences['rcdays'] =
756 'label-message' => 'recentchangesdays',
757 'section' => 'rc/display',
759 'max' => ceil( $wgRCMaxAge / ( 3600*24 ) ),
760 'help' => wfMsgExt( 'recentchangesdays-max', array( 'parsemag' ), $wgLang->formatNum( ceil( $wgRCMaxAge / ( 3600*24 ) ) ) ),
762 $defaultPreferences['rclimit'] =
765 'label-message' => 'recentchangescount',
766 'help-message' => 'prefs-help-recentchangescount',
767 'section' => 'rc/display',
769 $defaultPreferences['usenewrc'] =
772 'label-message' => 'tog-usenewrc',
773 'section' => 'rc/advancedrc',
775 $defaultPreferences['hideminor'] =
778 'label-message' => 'tog-hideminor',
779 'section' => 'rc/advancedrc',
782 global $wgUseRCPatrol;
783 if( $wgUseRCPatrol ) {
784 $defaultPreferences['hidepatrolled'] =
787 'section' => 'rc/advancedrc',
788 'label-message' => 'tog-hidepatrolled',
790 $defaultPreferences['newpageshidepatrolled'] =
793 'section' => 'rc/advancedrc',
794 'label-message' => 'tog-newpageshidepatrolled',
798 global $wgRCShowWatchingUsers;
799 if( $wgRCShowWatchingUsers ) {
800 $defaultPreferences['shownumberswatching'] =
803 'section' => 'rc/advancedrc',
804 'label-message' => 'tog-shownumberswatching',
809 static function watchlistPreferences( $user, &$defaultPreferences ) {
810 global $wgUseRCPatrol, $wgEnableAPI;
811 ## Watchlist #####################################
812 $defaultPreferences['watchlistdays'] =
817 'section' => 'watchlist/display',
818 'help' => wfMsgHtml( 'prefs-watchlist-days-max' ),
819 'label-message' => 'prefs-watchlist-days',
821 $defaultPreferences['wllimit'] =
826 'label-message' => 'prefs-watchlist-edits',
827 'help' => wfMsgHtml( 'prefs-watchlist-edits-max' ),
828 'section' => 'watchlist/display',
830 $defaultPreferences['extendwatchlist'] =
833 'section' => 'watchlist/advancedwatchlist',
834 'label-message' => 'tog-extendwatchlist',
836 $defaultPreferences['watchlisthideminor'] =
839 'section' => 'watchlist/advancedwatchlist',
840 'label-message' => 'tog-watchlisthideminor',
842 $defaultPreferences['watchlisthidebots'] =
845 'section' => 'watchlist/advancedwatchlist',
846 'label-message' => 'tog-watchlisthidebots',
848 $defaultPreferences['watchlisthideown'] =
851 'section' => 'watchlist/advancedwatchlist',
852 'label-message' => 'tog-watchlisthideown',
854 $defaultPreferences['watchlisthideanons'] =
857 'section' => 'watchlist/advancedwatchlist',
858 'label-message' => 'tog-watchlisthideanons',
860 $defaultPreferences['watchlisthideliu'] =
863 'section' => 'watchlist/advancedwatchlist',
864 'label-message' => 'tog-watchlisthideliu',
866 if ( $wgEnableAPI ) {
867 # Some random gibberish as a proposed default
868 $hash = sha1( mt_rand() . microtime( true ) );
869 $defaultPreferences['watchlisttoken'] =
872 'section' => 'watchlist/advancedwatchlist',
873 'label-message' => 'prefs-watchlist-token',
874 'help' => wfMsgHtml( 'prefs-help-watchlist-token', $hash )
878 if ( $wgUseRCPatrol ) {
879 $defaultPreferences['watchlisthidepatrolled'] =
882 'section' => 'watchlist/advancedwatchlist',
883 'label-message' => 'tog-watchlisthidepatrolled',
888 'edit' => 'watchdefault',
889 'move' => 'watchmoves',
890 'delete' => 'watchdeletion'
894 if( $user->isAllowed( 'createpage' ) ||
$user->isAllowed( 'createtalk' ) ) {
895 $watchTypes['read'] = 'watchcreations';
898 foreach( $watchTypes as $action => $pref ) {
899 if ( $user->isAllowed( $action ) ) {
900 $defaultPreferences[$pref] = array(
902 'section' => 'watchlist/advancedwatchlist',
903 'label-message' => "tog-$pref",
909 static function searchPreferences( $user, &$defaultPreferences ) {
912 ## Search #####################################
913 $defaultPreferences['searchlimit'] =
916 'label-message' => 'resultsperpage',
917 'section' => 'searchoptions/display',
920 $defaultPreferences['contextlines'] =
923 'label-message' => 'contextlines',
924 'section' => 'searchoptions/display',
927 $defaultPreferences['contextchars'] =
930 'label-message' => 'contextchars',
931 'section' => 'searchoptions/display',
934 global $wgEnableMWSuggest;
935 if( $wgEnableMWSuggest ) {
936 $defaultPreferences['disablesuggest'] =
939 'label-message' => 'mwsuggest-disable',
940 'section' => 'searchoptions/display',
944 $defaultPreferences['searcheverything'] =
947 'label-message' => 'searcheverything-enable',
948 'section' => 'searchoptions/advancedsearchoptions',
951 // Searchable namespaces back-compat with old format
952 $searchableNamespaces = SearchEngine
::searchableNamespaces();
954 $nsOptions = array();
955 foreach( $wgContLang->getNamespaces() as $ns => $name ) {
956 if( $ns < 0 ) continue;
957 $displayNs = str_replace( '_', ' ', $name );
959 if( !$displayNs ) $displayNs = wfMsg( 'blanknamespace' );
961 $displayNs = htmlspecialchars( $displayNs );
962 $nsOptions[$displayNs] = $ns;
965 $defaultPreferences['searchnamespaces'] =
967 'type' => 'multiselect',
968 'label-message' => 'defaultns',
969 'options' => $nsOptions,
970 'section' => 'searchoptions/advancedsearchoptions',
971 'prefix' => 'searchNs',
975 static function miscPreferences( $user, &$defaultPreferences ) {
976 ## Misc #####################################
977 $defaultPreferences['diffonly'] =
980 'section' => 'misc/diffs',
981 'label-message' => 'tog-diffonly',
983 $defaultPreferences['norollbackdiff'] =
986 'section' => 'misc/diffs',
987 'label-message' => 'tog-norollbackdiff',
990 // Stuff from Language::getExtraUserToggles()
993 $toggles = $wgContLang->getExtraUserToggles();
995 foreach( $toggles as $toggle ) {
996 $defaultPreferences[$toggle] =
999 'section' => 'personal/i18n',
1000 'label-message' => "tog-$toggle",
1005 static function generateSkinOptions( $user ) {
1006 global $wgDefaultSkin;
1009 $mptitle = Title
::newMainPage();
1010 $previewtext = wfMsgHtml( 'skin-preview' );
1011 # Only show members of Skin::getSkinNames() rather than
1012 # $skinNames (skins is all skin names from Language.php)
1013 $validSkinNames = Skin
::getUsableSkins();
1014 # Sort by UI skin name. First though need to update validSkinNames as sometimes
1015 # the skinkey & UI skinname differ (e.g. "standard" skinkey is "Classic" in the UI).
1016 foreach ( $validSkinNames as $skinkey => &$skinname ) {
1017 $msgName = "skinname-{$skinkey}";
1018 $localisedSkinName = wfMsg( $msgName );
1019 if ( !wfEmptyMsg( $msgName, $localisedSkinName ) ) {
1020 $skinname = htmlspecialchars( $localisedSkinName );
1023 asort( $validSkinNames );
1024 $sk = $user->getSkin();
1026 foreach( $validSkinNames as $skinkey => $sn ) {
1027 $mplink = htmlspecialchars( $mptitle->getLocalURL( "useskin=$skinkey" ) );
1028 $previewlink = "(<a target='_blank' href=\"$mplink\">$previewtext</a>)";
1030 global $wgAllowUserCss, $wgAllowUserJs;
1031 if( $wgAllowUserCss ) {
1032 $cssPage = Title
::makeTitleSafe( NS_USER
, $user->getName() . '/' . $skinkey . '.css' );
1033 $customCSS = $sk->link( $cssPage, wfMsgHtml( 'prefs-custom-css' ) );
1034 $extraLinks .= " ($customCSS)";
1036 if( $wgAllowUserJs ) {
1037 $jsPage = Title
::makeTitleSafe( NS_USER
, $user->getName() . '/' . $skinkey . '.js' );
1038 $customJS = $sk->link( $jsPage, wfMsgHtml( 'prefs-custom-js' ) );
1039 $extraLinks .= " ($customJS)";
1041 if( $skinkey == $wgDefaultSkin )
1042 $sn .= ' (' . wfMsgHtml( 'default' ) . ')';
1043 $display = "$sn $previewlink{$extraLinks}";
1044 $ret[$display] = $skinkey;
1050 static function getDateOptions() {
1052 $dateopts = $wgLang->getDatePreferences();
1057 if ( !in_array( 'default', $dateopts ) ) {
1058 $dateopts[] = 'default'; // Make sure default is always valid
1063 $epoch = '20010115161234'; # Wikipedia day
1064 foreach( $dateopts as $key ) {
1065 if( $key == 'default' ) {
1066 $formatted = wfMsgHtml( 'datedefault' );
1068 $formatted = htmlspecialchars( $wgLang->timeanddate( $epoch, false, $key ) );
1070 $ret[$formatted] = $key;
1076 static function getImageSizes() {
1077 global $wgImageLimits;
1081 foreach ( $wgImageLimits as $index => $limits ) {
1082 $display = "{$limits[0]}×{$limits[1]}" . wfMsg( 'unit-pixel' );
1083 $ret[$display] = $index;
1089 static function getThumbSizes() {
1090 global $wgThumbLimits;
1094 foreach ( $wgThumbLimits as $index => $size ) {
1095 $display = $size . wfMsg( 'unit-pixel' );
1096 $ret[$display] = $index;
1102 static function validateSignature( $signature, $alldata ) {
1103 global $wgParser, $wgMaxSigChars, $wgLang;
1104 if( mb_strlen( $signature ) > $wgMaxSigChars ) {
1106 Xml
::element( 'span', array( 'class' => 'error' ),
1107 wfMsgExt( 'badsiglength', 'parsemag',
1108 $wgLang->formatNum( $wgMaxSigChars )
1111 } elseif( !empty( $alldata['fancysig'] ) &&
1112 false === $wgParser->validateSig( $signature ) ) {
1113 return Xml
::element( 'span', array( 'class' => 'error' ), wfMsg( 'badsig' ) );
1119 static function cleanSignature( $signature, $alldata ) {
1121 if( $alldata['fancysig'] ) {
1122 $signature = $wgParser->cleanSig( $signature );
1124 // When no fancy sig used, make sure ~{3,5} get removed.
1125 $signature = $wgParser->cleanSigInSig( $signature );
1131 static function validateEmail( $email, $alldata ) {
1132 if ( $email && !User
::isValidEmailAddr( $email ) ) {
1133 return wfMsgExt( 'invalidemailaddress', 'parseinline' );
1136 global $wgEmailConfirmToEdit;
1137 if( $wgEmailConfirmToEdit && !$email ) {
1138 return wfMsgExt( 'noemailtitle', 'parseinline' );
1143 static function getFormObject( $user ) {
1144 $formDescriptor = Preferences
::getPreferences( $user );
1145 $htmlForm = new PreferencesForm( $formDescriptor, 'prefs' );
1147 $htmlForm->setSubmitText( wfMsg( 'saveprefs' ) );
1148 $htmlForm->setTitle( SpecialPage
::getTitleFor( 'Preferences' ) );
1149 $htmlForm->setSubmitID( 'prefsubmit' );
1150 $htmlForm->setSubmitCallback( array( 'Preferences', 'tryFormSubmit' ) );
1155 static function getTimezoneOptions() {
1158 global $wgLocalTZoffset;
1160 $opt[wfMsg( 'timezoneuseserverdefault' )] = "System|$wgLocalTZoffset";
1161 $opt[wfMsg( 'timezoneuseoffset' )] = 'other';
1162 $opt[wfMsg( 'guesstimezone' )] = 'guess';
1164 if ( function_exists( 'timezone_identifiers_list' ) ) {
1165 # Read timezone list
1166 $tzs = timezone_identifiers_list();
1169 $tzRegions = array();
1170 $tzRegions['Africa'] = wfMsg( 'timezoneregion-africa' );
1171 $tzRegions['America'] = wfMsg( 'timezoneregion-america' );
1172 $tzRegions['Antarctica'] = wfMsg( 'timezoneregion-antarctica' );
1173 $tzRegions['Arctic'] = wfMsg( 'timezoneregion-arctic' );
1174 $tzRegions['Asia'] = wfMsg( 'timezoneregion-asia' );
1175 $tzRegions['Atlantic'] = wfMsg( 'timezoneregion-atlantic' );
1176 $tzRegions['Australia'] = wfMsg( 'timezoneregion-australia' );
1177 $tzRegions['Europe'] = wfMsg( 'timezoneregion-europe' );
1178 $tzRegions['Indian'] = wfMsg( 'timezoneregion-indian' );
1179 $tzRegions['Pacific'] = wfMsg( 'timezoneregion-pacific' );
1180 asort( $tzRegions );
1182 $prefill = array_fill_keys( array_values( $tzRegions ), array() );
1183 $opt = array_merge( $opt, $prefill );
1185 $now = date_create( 'now' );
1187 foreach ( $tzs as $tz ) {
1188 $z = explode( '/', $tz, 2 );
1190 # timezone_identifiers_list() returns a number of
1191 # backwards-compatibility entries. This filters them out of the
1192 # list presented to the user.
1193 if ( count( $z ) != 2 ||
!array_key_exists( $z[0], $tzRegions ) )
1197 $z[0] = $tzRegions[$z[0]];
1199 $minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 );
1201 $display = str_replace( '_', ' ', $z[0] . '/' . $z[1] );
1202 $value = "ZoneInfo|$minDiff|$tz";
1204 $opt[$z[0]][$display] = $value;
1210 static function filterTimezoneInput( $tz, $alldata ) {
1211 $data = explode( '|', $tz, 3 );
1212 switch ( $data[0] ) {
1217 $data = explode( ':', $tz, 2 );
1219 if( count( $data ) == 2 ) {
1220 $data[0] = intval( $data[0] );
1221 $data[1] = intval( $data[1] );
1222 $minDiff = abs( $data[0] ) * 60 +
$data[1];
1223 if ( $data[0] < 0 ) $minDiff = -$minDiff;
1225 $minDiff = intval( $data[0] ) * 60;
1228 # Max is +14:00 and min is -12:00, see:
1229 # http://en.wikipedia.org/wiki/Timezone
1230 $minDiff = min( $minDiff, 840 ); # 14:00
1231 $minDiff = max( $minDiff, -720 ); # -12:00
1232 return 'Offset|'.$minDiff;
1236 static function tryFormSubmit( $formData, $entryPoint = 'internal' ) {
1237 global $wgUser, $wgEmailAuthentication, $wgEnableEmail;
1242 foreach( array_keys( $formData ) as $name ) {
1243 if ( isset( self
::$saveFilters[$name] ) ) {
1245 call_user_func( self
::$saveFilters[$name], $formData[$name], $formData );
1249 // Stuff that shouldn't be saved as a preference.
1250 $saveBlacklist = array(
1255 if( $wgEnableEmail ) {
1256 $newadr = $formData['emailaddress'];
1257 $oldadr = $wgUser->getEmail();
1258 if( ( $newadr != '' ) && ( $newadr != $oldadr ) ) {
1259 # the user has supplied a new email address on the login page
1260 # new behaviour: set this new emailaddr from login-page into user database record
1261 $wgUser->setEmail( $newadr );
1262 # but flag as "dirty" = unauthenticated
1263 $wgUser->invalidateEmail();
1264 if( $wgEmailAuthentication ) {
1265 # Mail a temporary password to the dirty address.
1266 # User can come back through the confirmation URL to re-enable email.
1267 $result = $wgUser->sendConfirmationMail();
1268 if( WikiError
::isError( $result ) ) {
1269 return wfMsg( 'mailerror', htmlspecialchars( $result->getMessage() ) );
1270 } elseif( $entryPoint == 'ui' ) {
1275 $wgUser->setEmail( $newadr );
1277 if( $oldadr != $newadr ) {
1278 wfRunHooks( 'PrefsEmailAudit', array( $wgUser, $oldadr, $newadr ) );
1282 // Fortunately, the realname field is MUCH simpler
1283 global $wgHiddenPrefs;
1284 if ( !in_array( 'realname', $wgHiddenPrefs ) ) {
1285 $realName = $formData['realname'];
1286 $wgUser->setRealName( $realName );
1289 foreach( $saveBlacklist as $b )
1290 unset( $formData[$b] );
1292 // Keeps old preferences from interfering due to back-compat
1294 $wgUser->resetOptions();
1296 foreach( $formData as $key => $value ) {
1297 $wgUser->setOption( $key, $value );
1300 $wgUser->saveSettings();
1305 public static function tryUISubmit( $formData ) {
1306 $res = self
::tryFormSubmit( $formData, 'ui' );
1309 $urlOptions = array( 'success' );
1310 if( $res === 'eauth' )
1311 $urlOptions[] = 'eauth';
1313 $queryString = implode( '&', $urlOptions );
1315 $url = SpecialPage
::getTitleFor( 'Preferences' )->getFullURL( $queryString );
1317 $wgOut->redirect( $url );
1323 public static function loadOldSearchNs( $user ) {
1324 $searchableNamespaces = SearchEngine
::searchableNamespaces();
1325 // Back compat with old format
1328 foreach( $searchableNamespaces as $ns => $name ) {
1329 if( $user->getOption( 'searchNs' . $ns ) ) {
1338 /** Some tweaks to allow js prefs to work */
1339 class PreferencesForm
extends HTMLForm
{
1341 function wrapForm( $html ) {
1342 $html = Xml
::tags( 'div', array( 'id' => 'preferences' ), $html );
1344 return parent
::wrapForm( $html );
1347 function getButtons() {
1348 $html = parent
::getButtons();
1352 $sk = $wgUser->getSkin();
1353 $t = SpecialPage
::getTitleFor( 'Preferences', 'reset' );
1355 $html .= "\n" . $sk->link( $t, wfMsgHtml( 'restoreprefs' ) );
1357 $html = Xml
::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html );
1362 function filterDataForSubmit( $data ) {
1363 // Support for separating MultiSelect preferences into multiple preferences
1364 // Due to lack of array support.
1365 foreach( $this->mFlatFields
as $fieldname => $field ) {
1366 $info = $field->mParams
;
1367 if( $field instanceof HTMLMultiSelectField
) {
1368 $options = HTMLFormField
::flattenOptions( $info['options'] );
1369 $prefix = isset( $info['prefix'] ) ?
$info['prefix'] : $fieldname;
1371 foreach( $options as $opt ) {
1372 $data["$prefix$opt"] = in_array( $opt, $data[$fieldname] );
1375 unset( $data[$fieldname] );