Adding signature preview on Special:Preferences and introducing help text for signatu...
[lhc/web/wiklou.git] / includes / Preferences.php
1 <?php
2
3 /**
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
8 customisations.
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.
23 */
24
25 class Preferences {
26 static $defaultPreferences = null;
27 static $saveFilters =
28 array(
29 'timecorrection' => array( 'Preferences', 'filterTimezoneInput' ),
30 );
31
32 static function getPreferences( $user ) {
33 if ( self::$defaultPreferences )
34 return self::$defaultPreferences;
35
36 global $wgRCMaxAge;
37
38 $defaultPreferences = array();
39
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 );
51
52 wfRunHooks( 'GetPreferences', array( $user, &$defaultPreferences ) );
53
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] );
59 }
60 }
61
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]
70 : null;
71
72 // If it validates, set it as the default
73 if ( isset( $info['default'] ) ) {
74 // Already set, no problem
75 continue;
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;
81 } else {
82 throw new MWException( "Global default '$globalDefault' is invalid for field $name" );
83 }
84 }
85
86 self::$defaultPreferences = $defaultPreferences;
87
88 return $defaultPreferences;
89 }
90
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 );
94
95 // Handling for array-type preferences
96 if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
97 ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
98
99 $options = HTMLFormField::flattenOptions( $info['options'] );
100 $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
101 $val = array();
102
103 foreach( $options as $label => $value ) {
104 if( $user->getOption( "$prefix$value" ) ) {
105 $val[] = $value;
106 }
107 }
108 }
109
110 return $val;
111 }
112
113 static function profilePreferences( $user, &$defaultPreferences ) {
114 global $wgLang;
115 ## User info #####################################
116 // Information panel
117 $defaultPreferences['username'] =
118 array(
119 'type' => 'info',
120 'label-message' => 'username',
121 'default' => $user->getName(),
122 'section' => 'personal/info',
123 );
124
125 $defaultPreferences['userid'] =
126 array(
127 'type' => 'info',
128 'label-message' => 'uid',
129 'default' => $user->getId(),
130 'section' => 'personal/info',
131 );
132
133 # Get groups to which the user belongs
134 $userEffectiveGroups = $user->getEffectiveGroups();
135 $userGroups = $userMembers = array();
136 foreach( $userEffectiveGroups as $ueg ) {
137 if( $ueg == '*' ) {
138 // Skip the default * group, seems useless here
139 continue;
140 }
141 $groupName = User::getGroupName( $ueg );
142 $userGroups[] = User::makeGroupLinkHTML( $ueg, $groupName );
143
144 $memberName = User::getGroupMember( $ueg );
145 $userMembers[] = User::makeGroupLinkHTML( $ueg, $memberName );
146 }
147 asort( $userGroups );
148 asort( $userMembers );
149
150 $defaultPreferences['usergroups'] =
151 array(
152 'type' => 'info',
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 )
158 ),
159 'raw' => true,
160 'section' => 'personal/info',
161 );
162
163 $defaultPreferences['editcount'] =
164 array(
165 'type' => 'info',
166 'label-message' => 'prefs-edits',
167 'default' => $wgLang->formatNum( $user->getEditCount() ),
168 'section' => 'personal/info',
169 );
170
171 if( $user->getRegistration() ) {
172 $defaultPreferences['registrationdate'] =
173 array(
174 'type' => 'info',
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',
181 );
182 }
183
184 // Actually changeable stuff
185 global $wgAuth;
186 $defaultPreferences['realname'] =
187 array(
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',
193 );
194
195 $defaultPreferences['gender'] =
196 array(
197 'type' => 'select',
198 'section' => 'personal/info',
199 'options' => array(
200 wfMsg( 'gender-male' ) => 'male',
201 wfMsg( 'gender-female' ) => 'female',
202 wfMsg( 'gender-unknown' ) => 'unknown',
203 ),
204 'label-message' => 'yourgender',
205 'help-message' => 'prefs-help-gender',
206 );
207
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' ) ) );
213
214 $defaultPreferences['password'] =
215 array(
216 'type' => 'info',
217 'raw' => true,
218 'default' => $link,
219 'label-message' => 'yourpassword',
220 'section' => 'personal/info',
221 );
222 }
223
224 $defaultPreferences['rememberpassword'] =
225 array(
226 'type' => 'toggle',
227 'label-message' => 'tog-rememberpassword',
228 'section' => 'personal/info',
229 );
230
231 // Language
232 global $wgContLanguageCode;
233 $languages = array_reverse( Language::getLanguageNames( false ) );
234 if( !array_key_exists( $wgContLanguageCode, $languages ) ) {
235 $languages[$wgContLanguageCode] = $wgContLanguageCode;
236 }
237 ksort( $languages );
238
239 $options = array();
240 foreach( $languages as $code => $name ) {
241 $display = wfBCP47( $code ) . ' - ' . $name;
242 $options[$display] = $code;
243 }
244 $defaultPreferences['language'] =
245 array(
246 'type' => 'select',
247 'section' => 'personal/i18n',
248 'options' => $options,
249 'label-message' => 'yourlanguage',
250 );
251
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();
258
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];
265 }
266 }
267
268 $options = array();
269 foreach( $variantArray as $code => $name ) {
270 $display = wfBCP47( $code ) . ' - ' . $name;
271 $options[$display] = $code;
272 }
273
274 if( count( $variantArray ) > 1 ) {
275 $defaultPreferences['variant'] =
276 array(
277 'label-message' => 'yourvariant',
278 'type' => 'select',
279 'options' => $options,
280 'section' => 'personal/i18n',
281 );
282 }
283 }
284
285 if( count( $variantArray ) > 1 && !$wgDisableLangConversion && !$wgDisableTitleConversion ) {
286 $defaultPreferences['noconvertlink'] =
287 array(
288 'type' => 'toggle',
289 'section' => 'personal/i18n',
290 'label-message' => 'tog-noconvertlink',
291 );
292 }
293
294 global $wgMaxSigChars, $wgParser;
295
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];
301
302 $defaultPreferences['oldsig'] =
303 array(
304 'type' => 'info',
305 'raw' => true,
306 'label-message' => 'tog-oldsig',
307 'default' => $oldsig->mText,
308 'section' => 'personal/signature',
309 );
310 $defaultPreferences['nickname'] =
311 array(
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' ),
319 );
320 $defaultPreferences['fancysig'] =
321 array(
322 'type' => 'toggle',
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'
326 );
327
328 ## Email stuff
329
330 global $wgEnableEmail;
331 if ($wgEnableEmail) {
332
333 global $wgEmailConfirmToEdit;
334
335 $defaultPreferences['emailaddress'] =
336 array(
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' ),
345 );
346
347 global $wgEnableUserEmail, $wgEmailAuthentication;
348
349 $disableEmailPrefs = false;
350
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;
363 } else {
364 $disableEmailPrefs = true;
365 global $wgUser; // wgUser is okay here, it's for display
366 $skin = $wgUser->getSkin();
367 $emailauthenticated = wfMsgExt( 'emailnotauthenticated', 'parseinline' ) . '<br />' .
368 $skin->link(
369 SpecialPage::getTitleFor( 'Confirmemail' ),
370 wfMsg( 'emailconfirmlink' ),
371 array(),
372 array(),
373 array( 'known', 'noclasses' )
374 ) . '<br />';
375 }
376 } else {
377 $disableEmailPrefs = true;
378 $emailauthenticated = wfMsgHtml( 'noemailprefs' );
379 }
380
381 $defaultPreferences['emailauthentication'] =
382 array(
383 'type' => 'info',
384 'raw' => true,
385 'section' => 'personal/email',
386 'label-message' => 'prefs-emailconfirm-label',
387 'default' => $emailauthenticated,
388 );
389
390 }
391
392 if( $wgEnableUserEmail ) {
393 $defaultPreferences['disablemail'] =
394 array(
395 'type' => 'toggle',
396 'invert' => true,
397 'section' => 'personal/email',
398 'label-message' => 'allowemail',
399 'disabled' => $disableEmailPrefs,
400 );
401 $defaultPreferences['ccmeonemails'] =
402 array(
403 'type' => 'toggle',
404 'section' => 'personal/email',
405 'label-message' => 'tog-ccmeonemails',
406 'disabled' => $disableEmailPrefs,
407 );
408 }
409
410 global $wgEnotifWatchlist;
411 if ( $wgEnotifWatchlist ) {
412 $defaultPreferences['enotifwatchlistpages'] =
413 array(
414 'type' => 'toggle',
415 'section' => 'personal/email',
416 'label-message' => 'tog-enotifwatchlistpages',
417 'disabled' => $disableEmailPrefs,
418 );
419 }
420 global $wgEnotifUserTalk;
421 if( $wgEnotifUserTalk ) {
422 $defaultPreferences['enotifusertalkpages'] =
423 array(
424 'type' => 'toggle',
425 'section' => 'personal/email',
426 'label-message' => 'tog-enotifusertalkpages',
427 'disabled' => $disableEmailPrefs,
428 );
429 }
430 if( $wgEnotifUserTalk || $wgEnotifWatchlist ) {
431 $defaultPreferences['enotifminoredits'] =
432 array(
433 'type' => 'toggle',
434 'section' => 'personal/email',
435 'label-message' => 'tog-enotifminoredits',
436 'disabled' => $disableEmailPrefs,
437 );
438 }
439 $defaultPreferences['enotifrevealaddr'] =
440 array(
441 'type' => 'toggle',
442 'section' => 'personal/email',
443 'label-message' => 'tog-enotifrevealaddr',
444 'disabled' => $disableEmailPrefs,
445 );
446 }
447 }
448
449 static function skinPreferences( $user, &$defaultPreferences ) {
450 ## Skin #####################################
451 $defaultPreferences['skin'] =
452 array(
453 'type' => 'radio',
454 'options' => self::generateSkinOptions( $user ),
455 'label' => '&nbsp;',
456 'section' => 'rendering/skin',
457 );
458
459 $selectedSkin = $user->getOption( 'skin' );
460 if ( in_array( $selectedSkin, array( 'cologneblue', 'standard' ) ) ) {
461 global $wgLang;
462 $settings = array_flip( $wgLang->getQuickbarSettings() );
463
464 $defaultPreferences['quickbar'] =
465 array(
466 'type' => 'radio',
467 'options' => $settings,
468 'section' => 'rendering/skin',
469 'label-message' => 'qbsettings',
470 );
471 }
472 }
473
474 static function mathPreferences( $user, &$defaultPreferences ) {
475 ## Math #####################################
476 global $wgUseTeX, $wgLang;
477 if( $wgUseTeX ) {
478 $defaultPreferences['math'] =
479 array(
480 'type' => 'radio',
481 'options' =>
482 array_flip( array_map( 'wfMsgHtml', $wgLang->getMathNames() ) ),
483 'label' => '&nbsp;',
484 'section' => 'rendering/math',
485 );
486 }
487 }
488
489 static function filesPreferences( $user, &$defaultPreferences ) {
490 ## Files #####################################
491 $defaultPreferences['imagesize'] =
492 array(
493 'type' => 'select',
494 'options' => self::getImageSizes(),
495 'label-message' => 'imagemaxsize',
496 'section' => 'rendering/files',
497 );
498 $defaultPreferences['thumbsize'] =
499 array(
500 'type' => 'select',
501 'options' => self::getThumbSizes(),
502 'label-message' => 'thumbsize',
503 'section' => 'rendering/files',
504 );
505 }
506
507 static function datetimePreferences( $user, &$defaultPreferences ) {
508 global $wgLang;
509
510 ## Date and time #####################################
511 $dateOptions = self::getDateOptions();
512 if( $dateOptions ) {
513 $defaultPreferences['date'] =
514 array(
515 'type' => 'radio',
516 'options' => $dateOptions,
517 'label' => '&nbsp;',
518 'section' => 'datetime/dateformat',
519 );
520 }
521
522 // Info
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 ) );
527
528 $defaultPreferences['nowserver'] =
529 array(
530 'type' => 'info',
531 'raw' => 1,
532 'label-message' => 'servertime',
533 'default' => $nowserver,
534 'section' => 'datetime/timeoffset',
535 );
536
537 $defaultPreferences['nowlocal'] =
538 array(
539 'type' => 'info',
540 'raw' => 1,
541 'label-message' => 'localtime',
542 'default' => $nowlocal,
543 'section' => 'datetime/timeoffset',
544 );
545
546 // Grab existing pref.
547 $tzOffset = $user->getOption( 'timecorrection' );
548 $tz = explode( '|', $tzOffset, 2 );
549
550 $tzSetting = $tzOffset;
551 if( count( $tz ) > 1 && $tz[0] == 'Offset' ) {
552 $minDiff = $tz[1];
553 $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff/60 ), abs( $minDiff )%60 );
554 }
555
556 $defaultPreferences['timecorrection'] =
557 array(
558 'class' => 'HTMLSelectOrOtherField',
559 'label-message' => 'timezonelegend',
560 'options' => self::getTimezoneOptions(),
561 'default' => $tzSetting,
562 'section' => 'datetime/timeoffset',
563 );
564 }
565
566 static function renderingPreferences( $user, &$defaultPreferences ) {
567 ## Page Rendering ##############################
568 $defaultPreferences['underline'] =
569 array(
570 'type' => 'select',
571 'options' => array(
572 wfMsg( 'underline-never' ) => 0,
573 wfMsg( 'underline-always' ) => 1,
574 wfMsg( 'underline-default' ) => 2,
575 ),
576 'label-message' => 'tog-underline',
577 'section' => 'rendering/advancedrendering',
578 );
579
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;
584 }
585
586 $defaultPreferences['stubthreshold'] =
587 array(
588 'type' => 'selectorother',
589 'section' => 'rendering/advancedrendering',
590 'options' => $stubThresholdOptions,
591 'label' => wfMsg( 'stub-threshold' ), // Raw HTML message. Yay?
592 );
593 $defaultPreferences['highlightbroken'] =
594 array(
595 'type' => 'toggle',
596 'section' => 'rendering/advancedrendering',
597 'label' => wfMsg( 'tog-highlightbroken' ), // Raw HTML
598 );
599 $defaultPreferences['showtoc'] =
600 array(
601 'type' => 'toggle',
602 'section' => 'rendering/advancedrendering',
603 'label-message' => 'tog-showtoc',
604 );
605 $defaultPreferences['nocache'] =
606 array(
607 'type' => 'toggle',
608 'label-message' => 'tog-nocache',
609 'section' => 'rendering/advancedrendering',
610 );
611 $defaultPreferences['showhiddencats'] =
612 array(
613 'type' => 'toggle',
614 'section' => 'rendering/advancedrendering',
615 'label-message' => 'tog-showhiddencats'
616 );
617 $defaultPreferences['showjumplinks'] =
618 array(
619 'type' => 'toggle',
620 'section' => 'rendering/advancedrendering',
621 'label-message' => 'tog-showjumplinks',
622 );
623 $defaultPreferences['justify'] =
624 array(
625 'type' => 'toggle',
626 'section' => 'rendering/advancedrendering',
627 'label-message' => 'tog-justify',
628 );
629 $defaultPreferences['numberheadings'] =
630 array(
631 'type' => 'toggle',
632 'section' => 'rendering/advancedrendering',
633 'label-message' => 'tog-numberheadings',
634 );
635 }
636
637 static function editingPreferences( $user, &$defaultPreferences ) {
638 global $wgUseExternalEditor, $wgLivePreview;
639
640 ## Editing #####################################
641 $defaultPreferences['cols'] =
642 array(
643 'type' => 'int',
644 'label-message' => 'columns',
645 'section' => 'editing/textboxsize',
646 'min' => 4,
647 'max' => 1000,
648 );
649 $defaultPreferences['rows'] =
650 array(
651 'type' => 'int',
652 'label-message' => 'rows',
653 'section' => 'editing/textboxsize',
654 'min' => 4,
655 'max' => 1000,
656 );
657
658 $defaultPreferences['editfont'] =
659 array(
660 'type' => 'select',
661 'section' => 'editing/advancedediting',
662 'label-message' => 'editfont-style',
663 'options' => array(
664 wfMsg( 'editfont-default' ) => 'default',
665 wfMsg( 'editfont-monospace' ) => 'monospace',
666 wfMsg( 'editfont-sansserif' ) => 'sans-serif',
667 wfMsg( 'editfont-serif' ) => 'serif',
668 )
669 );
670 $defaultPreferences['previewontop'] =
671 array(
672 'type' => 'toggle',
673 'section' => 'editing/advancedediting',
674 'label-message' => 'tog-previewontop',
675 );
676 $defaultPreferences['previewonfirst'] =
677 array(
678 'type' => 'toggle',
679 'section' => 'editing/advancedediting',
680 'label-message' => 'tog-previewonfirst',
681 );
682 $defaultPreferences['editsection'] =
683 array(
684 'type' => 'toggle',
685 'section' => 'editing/advancedediting',
686 'label-message' => 'tog-editsection',
687 );
688 $defaultPreferences['editsectiononrightclick'] =
689 array(
690 'type' => 'toggle',
691 'section' => 'editing/advancedediting',
692 'label-message' => 'tog-editsectiononrightclick',
693 );
694 $defaultPreferences['editondblclick'] =
695 array(
696 'type' => 'toggle',
697 'section' => 'editing/advancedediting',
698 'label-message' => 'tog-editondblclick',
699 );
700 $defaultPreferences['editwidth'] =
701 array(
702 'type' => 'toggle',
703 'section' => 'editing/advancedediting',
704 'label-message' => 'tog-editwidth',
705 );
706 $defaultPreferences['showtoolbar'] =
707 array(
708 'type' => 'toggle',
709 'section' => 'editing/advancedediting',
710 'label-message' => 'tog-showtoolbar',
711 );
712 $defaultPreferences['minordefault'] =
713 array(
714 'type' => 'toggle',
715 'section' => 'editing/advancedediting',
716 'label-message' => 'tog-minordefault',
717 );
718
719 if ( $wgUseExternalEditor ) {
720 $defaultPreferences['externaleditor'] =
721 array(
722 'type' => 'toggle',
723 'section' => 'editing/advancedediting',
724 'label-message' => 'tog-externaleditor',
725 );
726 $defaultPreferences['externaldiff'] =
727 array(
728 'type' => 'toggle',
729 'section' => 'editing/advancedediting',
730 'label-message' => 'tog-externaldiff',
731 );
732 }
733
734 $defaultPreferences['forceeditsummary'] =
735 array(
736 'type' => 'toggle',
737 'section' => 'editing/advancedediting',
738 'label-message' => 'tog-forceeditsummary',
739 );
740 if ( $wgLivePreview ) {
741 $defaultPreferences['uselivepreview'] =
742 array(
743 'type' => 'toggle',
744 'section' => 'editing/advancedediting',
745 'label-message' => 'tog-uselivepreview',
746 );
747 }
748 }
749
750 static function rcPreferences( $user, &$defaultPreferences ) {
751 global $wgRCMaxAge, $wgUseRCPatrol, $wgLang;
752 ## RecentChanges #####################################
753 $defaultPreferences['rcdays'] =
754 array(
755 'type' => 'float',
756 'label-message' => 'recentchangesdays',
757 'section' => 'rc/display',
758 'min' => 1,
759 'max' => ceil( $wgRCMaxAge / ( 3600*24 ) ),
760 'help' => wfMsgExt( 'recentchangesdays-max', array( 'parsemag' ), $wgLang->formatNum( ceil( $wgRCMaxAge / ( 3600*24 ) ) ) ),
761 );
762 $defaultPreferences['rclimit'] =
763 array(
764 'type' => 'int',
765 'label-message' => 'recentchangescount',
766 'help-message' => 'prefs-help-recentchangescount',
767 'section' => 'rc/display',
768 );
769 $defaultPreferences['usenewrc'] =
770 array(
771 'type' => 'toggle',
772 'label-message' => 'tog-usenewrc',
773 'section' => 'rc/advancedrc',
774 );
775 $defaultPreferences['hideminor'] =
776 array(
777 'type' => 'toggle',
778 'label-message' => 'tog-hideminor',
779 'section' => 'rc/advancedrc',
780 );
781
782 global $wgUseRCPatrol;
783 if( $wgUseRCPatrol ) {
784 $defaultPreferences['hidepatrolled'] =
785 array(
786 'type' => 'toggle',
787 'section' => 'rc/advancedrc',
788 'label-message' => 'tog-hidepatrolled',
789 );
790 $defaultPreferences['newpageshidepatrolled'] =
791 array(
792 'type' => 'toggle',
793 'section' => 'rc/advancedrc',
794 'label-message' => 'tog-newpageshidepatrolled',
795 );
796 }
797
798 global $wgRCShowWatchingUsers;
799 if( $wgRCShowWatchingUsers ) {
800 $defaultPreferences['shownumberswatching'] =
801 array(
802 'type' => 'toggle',
803 'section' => 'rc/advancedrc',
804 'label-message' => 'tog-shownumberswatching',
805 );
806 }
807 }
808
809 static function watchlistPreferences( $user, &$defaultPreferences ) {
810 global $wgUseRCPatrol, $wgEnableAPI;
811 ## Watchlist #####################################
812 $defaultPreferences['watchlistdays'] =
813 array(
814 'type' => 'float',
815 'min' => 0,
816 'max' => 7,
817 'section' => 'watchlist/display',
818 'help' => wfMsgHtml( 'prefs-watchlist-days-max' ),
819 'label-message' => 'prefs-watchlist-days',
820 );
821 $defaultPreferences['wllimit'] =
822 array(
823 'type' => 'int',
824 'min' => 0,
825 'max' => 1000,
826 'label-message' => 'prefs-watchlist-edits',
827 'help' => wfMsgHtml( 'prefs-watchlist-edits-max' ),
828 'section' => 'watchlist/display',
829 );
830 $defaultPreferences['extendwatchlist'] =
831 array(
832 'type' => 'toggle',
833 'section' => 'watchlist/advancedwatchlist',
834 'label-message' => 'tog-extendwatchlist',
835 );
836 $defaultPreferences['watchlisthideminor'] =
837 array(
838 'type' => 'toggle',
839 'section' => 'watchlist/advancedwatchlist',
840 'label-message' => 'tog-watchlisthideminor',
841 );
842 $defaultPreferences['watchlisthidebots'] =
843 array(
844 'type' => 'toggle',
845 'section' => 'watchlist/advancedwatchlist',
846 'label-message' => 'tog-watchlisthidebots',
847 );
848 $defaultPreferences['watchlisthideown'] =
849 array(
850 'type' => 'toggle',
851 'section' => 'watchlist/advancedwatchlist',
852 'label-message' => 'tog-watchlisthideown',
853 );
854 $defaultPreferences['watchlisthideanons'] =
855 array(
856 'type' => 'toggle',
857 'section' => 'watchlist/advancedwatchlist',
858 'label-message' => 'tog-watchlisthideanons',
859 );
860 $defaultPreferences['watchlisthideliu'] =
861 array(
862 'type' => 'toggle',
863 'section' => 'watchlist/advancedwatchlist',
864 'label-message' => 'tog-watchlisthideliu',
865 );
866 if ( $wgEnableAPI ) {
867 # Some random gibberish as a proposed default
868 $hash = sha1( mt_rand() . microtime( true ) );
869 $defaultPreferences['watchlisttoken'] =
870 array(
871 'type' => 'text',
872 'section' => 'watchlist/advancedwatchlist',
873 'label-message' => 'prefs-watchlist-token',
874 'help' => wfMsgHtml( 'prefs-help-watchlist-token', $hash )
875 );
876 }
877
878 if ( $wgUseRCPatrol ) {
879 $defaultPreferences['watchlisthidepatrolled'] =
880 array(
881 'type' => 'toggle',
882 'section' => 'watchlist/advancedwatchlist',
883 'label-message' => 'tog-watchlisthidepatrolled',
884 );
885 }
886
887 $watchTypes = array(
888 'edit' => 'watchdefault',
889 'move' => 'watchmoves',
890 'delete' => 'watchdeletion'
891 );
892
893 // Kinda hacky
894 if( $user->isAllowed( 'createpage' ) || $user->isAllowed( 'createtalk' ) ) {
895 $watchTypes['read'] = 'watchcreations';
896 }
897
898 foreach( $watchTypes as $action => $pref ) {
899 if ( $user->isAllowed( $action ) ) {
900 $defaultPreferences[$pref] = array(
901 'type' => 'toggle',
902 'section' => 'watchlist/advancedwatchlist',
903 'label-message' => "tog-$pref",
904 );
905 }
906 }
907 }
908
909 static function searchPreferences( $user, &$defaultPreferences ) {
910 global $wgContLang;
911
912 ## Search #####################################
913 $defaultPreferences['searchlimit'] =
914 array(
915 'type' => 'int',
916 'label-message' => 'resultsperpage',
917 'section' => 'searchoptions/display',
918 'min' => 0,
919 );
920 $defaultPreferences['contextlines'] =
921 array(
922 'type' => 'int',
923 'label-message' => 'contextlines',
924 'section' => 'searchoptions/display',
925 'min' => 0,
926 );
927 $defaultPreferences['contextchars'] =
928 array(
929 'type' => 'int',
930 'label-message' => 'contextchars',
931 'section' => 'searchoptions/display',
932 'min' => 0,
933 );
934 global $wgEnableMWSuggest;
935 if( $wgEnableMWSuggest ) {
936 $defaultPreferences['disablesuggest'] =
937 array(
938 'type' => 'toggle',
939 'label-message' => 'mwsuggest-disable',
940 'section' => 'searchoptions/display',
941 );
942 }
943
944 $defaultPreferences['searcheverything'] =
945 array(
946 'type' => 'toggle',
947 'label-message' => 'searcheverything-enable',
948 'section' => 'searchoptions/advancedsearchoptions',
949 );
950
951 // Searchable namespaces back-compat with old format
952 $searchableNamespaces = SearchEngine::searchableNamespaces();
953
954 $nsOptions = array();
955 foreach( $wgContLang->getNamespaces() as $ns => $name ) {
956 if( $ns < 0 ) continue;
957 $displayNs = str_replace( '_', ' ', $name );
958
959 if( !$displayNs ) $displayNs = wfMsg( 'blanknamespace' );
960
961 $displayNs = htmlspecialchars( $displayNs );
962 $nsOptions[$displayNs] = $ns;
963 }
964
965 $defaultPreferences['searchnamespaces'] =
966 array(
967 'type' => 'multiselect',
968 'label-message' => 'defaultns',
969 'options' => $nsOptions,
970 'section' => 'searchoptions/advancedsearchoptions',
971 'prefix' => 'searchNs',
972 );
973 }
974
975 static function miscPreferences( $user, &$defaultPreferences ) {
976 ## Misc #####################################
977 $defaultPreferences['diffonly'] =
978 array(
979 'type' => 'toggle',
980 'section' => 'misc/diffs',
981 'label-message' => 'tog-diffonly',
982 );
983 $defaultPreferences['norollbackdiff'] =
984 array(
985 'type' => 'toggle',
986 'section' => 'misc/diffs',
987 'label-message' => 'tog-norollbackdiff',
988 );
989
990 // Stuff from Language::getExtraUserToggles()
991 global $wgContLang;
992
993 $toggles = $wgContLang->getExtraUserToggles();
994
995 foreach( $toggles as $toggle ) {
996 $defaultPreferences[$toggle] =
997 array(
998 'type' => 'toggle',
999 'section' => 'personal/i18n',
1000 'label-message' => "tog-$toggle",
1001 );
1002 }
1003 }
1004
1005 static function generateSkinOptions( $user ) {
1006 global $wgDefaultSkin;
1007 $ret = array();
1008
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 );
1021 }
1022 }
1023 asort( $validSkinNames );
1024 $sk = $user->getSkin();
1025
1026 foreach( $validSkinNames as $skinkey => $sn ) {
1027 $mplink = htmlspecialchars( $mptitle->getLocalURL( "useskin=$skinkey" ) );
1028 $previewlink = "(<a target='_blank' href=\"$mplink\">$previewtext</a>)";
1029 $extraLinks = '';
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)";
1035 }
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)";
1040 }
1041 if( $skinkey == $wgDefaultSkin )
1042 $sn .= ' (' . wfMsgHtml( 'default' ) . ')';
1043 $display = "$sn $previewlink{$extraLinks}";
1044 $ret[$display] = $skinkey;
1045 }
1046
1047 return $ret;
1048 }
1049
1050 static function getDateOptions() {
1051 global $wgLang;
1052 $dateopts = $wgLang->getDatePreferences();
1053
1054 $ret = array();
1055
1056 if( $dateopts ) {
1057 if ( !in_array( 'default', $dateopts ) ) {
1058 $dateopts[] = 'default'; // Make sure default is always valid
1059 // Bug 19237
1060 }
1061
1062 $idCnt = 0;
1063 $epoch = '20010115161234'; # Wikipedia day
1064 foreach( $dateopts as $key ) {
1065 if( $key == 'default' ) {
1066 $formatted = wfMsgHtml( 'datedefault' );
1067 } else {
1068 $formatted = htmlspecialchars( $wgLang->timeanddate( $epoch, false, $key ) );
1069 }
1070 $ret[$formatted] = $key;
1071 }
1072 }
1073 return $ret;
1074 }
1075
1076 static function getImageSizes() {
1077 global $wgImageLimits;
1078
1079 $ret = array();
1080
1081 foreach ( $wgImageLimits as $index => $limits ) {
1082 $display = "{$limits[0]}×{$limits[1]}" . wfMsg( 'unit-pixel' );
1083 $ret[$display] = $index;
1084 }
1085
1086 return $ret;
1087 }
1088
1089 static function getThumbSizes() {
1090 global $wgThumbLimits;
1091
1092 $ret = array();
1093
1094 foreach ( $wgThumbLimits as $index => $size ) {
1095 $display = $size . wfMsg( 'unit-pixel' );
1096 $ret[$display] = $index;
1097 }
1098
1099 return $ret;
1100 }
1101
1102 static function validateSignature( $signature, $alldata ) {
1103 global $wgParser, $wgMaxSigChars, $wgLang;
1104 if( mb_strlen( $signature ) > $wgMaxSigChars ) {
1105 return
1106 Xml::element( 'span', array( 'class' => 'error' ),
1107 wfMsgExt( 'badsiglength', 'parsemag',
1108 $wgLang->formatNum( $wgMaxSigChars )
1109 )
1110 );
1111 } elseif( !empty( $alldata['fancysig'] ) &&
1112 false === $wgParser->validateSig( $signature ) ) {
1113 return Xml::element( 'span', array( 'class' => 'error' ), wfMsg( 'badsig' ) );
1114 } else {
1115 return true;
1116 }
1117 }
1118
1119 static function cleanSignature( $signature, $alldata ) {
1120 global $wgParser;
1121 if( $alldata['fancysig'] ) {
1122 $signature = $wgParser->cleanSig( $signature );
1123 } else {
1124 // When no fancy sig used, make sure ~{3,5} get removed.
1125 $signature = $wgParser->cleanSigInSig( $signature );
1126 }
1127
1128 return $signature;
1129 }
1130
1131 static function validateEmail( $email, $alldata ) {
1132 if ( $email && !User::isValidEmailAddr( $email ) ) {
1133 return wfMsgExt( 'invalidemailaddress', 'parseinline' );
1134 }
1135
1136 global $wgEmailConfirmToEdit;
1137 if( $wgEmailConfirmToEdit && !$email ) {
1138 return wfMsgExt( 'noemailtitle', 'parseinline' );
1139 }
1140 return true;
1141 }
1142
1143 static function getFormObject( $user ) {
1144 $formDescriptor = Preferences::getPreferences( $user );
1145 $htmlForm = new PreferencesForm( $formDescriptor, 'prefs' );
1146
1147 $htmlForm->setSubmitText( wfMsg( 'saveprefs' ) );
1148 $htmlForm->setTitle( SpecialPage::getTitleFor( 'Preferences' ) );
1149 $htmlForm->setSubmitID( 'prefsubmit' );
1150 $htmlForm->setSubmitCallback( array( 'Preferences', 'tryFormSubmit' ) );
1151
1152 return $htmlForm;
1153 }
1154
1155 static function getTimezoneOptions() {
1156 $opt = array();
1157
1158 global $wgLocalTZoffset;
1159
1160 $opt[wfMsg( 'timezoneuseserverdefault' )] = "System|$wgLocalTZoffset";
1161 $opt[wfMsg( 'timezoneuseoffset' )] = 'other';
1162 $opt[wfMsg( 'guesstimezone' )] = 'guess';
1163
1164 if ( function_exists( 'timezone_identifiers_list' ) ) {
1165 # Read timezone list
1166 $tzs = timezone_identifiers_list();
1167 sort( $tzs );
1168
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 );
1181
1182 $prefill = array_fill_keys( array_values( $tzRegions ), array() );
1183 $opt = array_merge( $opt, $prefill );
1184
1185 $now = date_create( 'now' );
1186
1187 foreach ( $tzs as $tz ) {
1188 $z = explode( '/', $tz, 2 );
1189
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 ) )
1194 continue;
1195
1196 # Localize region
1197 $z[0] = $tzRegions[$z[0]];
1198
1199 $minDiff = floor( timezone_offset_get( timezone_open( $tz ), $now ) / 60 );
1200
1201 $display = str_replace( '_', ' ', $z[0] . '/' . $z[1] );
1202 $value = "ZoneInfo|$minDiff|$tz";
1203
1204 $opt[$z[0]][$display] = $value;
1205 }
1206 }
1207 return $opt;
1208 }
1209
1210 static function filterTimezoneInput( $tz, $alldata ) {
1211 $data = explode( '|', $tz, 3 );
1212 switch ( $data[0] ) {
1213 case 'ZoneInfo':
1214 case 'System':
1215 return $tz;
1216 default:
1217 $data = explode( ':', $tz, 2 );
1218 $minDiff = 0;
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;
1224 } else {
1225 $minDiff = intval( $data[0] ) * 60;
1226 }
1227
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;
1233 }
1234 }
1235
1236 static function tryFormSubmit( $formData, $entryPoint = 'internal' ) {
1237 global $wgUser, $wgEmailAuthentication, $wgEnableEmail;
1238
1239 $result = true;
1240
1241 // Filter input
1242 foreach( array_keys( $formData ) as $name ) {
1243 if ( isset( self::$saveFilters[$name] ) ) {
1244 $formData[$name] =
1245 call_user_func( self::$saveFilters[$name], $formData[$name], $formData );
1246 }
1247 }
1248
1249 // Stuff that shouldn't be saved as a preference.
1250 $saveBlacklist = array(
1251 'realname',
1252 'emailaddress',
1253 );
1254
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' ) {
1271 $result = 'eauth';
1272 }
1273 }
1274 } else {
1275 $wgUser->setEmail( $newadr );
1276 }
1277 if( $oldadr != $newadr ) {
1278 wfRunHooks( 'PrefsEmailAudit', array( $wgUser, $oldadr, $newadr ) );
1279 }
1280 }
1281
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 );
1287 }
1288
1289 foreach( $saveBlacklist as $b )
1290 unset( $formData[$b] );
1291
1292 // Keeps old preferences from interfering due to back-compat
1293 // code, etc.
1294 $wgUser->resetOptions();
1295
1296 foreach( $formData as $key => $value ) {
1297 $wgUser->setOption( $key, $value );
1298 }
1299
1300 $wgUser->saveSettings();
1301
1302 return $result;
1303 }
1304
1305 public static function tryUISubmit( $formData ) {
1306 $res = self::tryFormSubmit( $formData, 'ui' );
1307
1308 if( $res ) {
1309 $urlOptions = array( 'success' );
1310 if( $res === 'eauth' )
1311 $urlOptions[] = 'eauth';
1312
1313 $queryString = implode( '&', $urlOptions );
1314
1315 $url = SpecialPage::getTitleFor( 'Preferences' )->getFullURL( $queryString );
1316 global $wgOut;
1317 $wgOut->redirect( $url );
1318 }
1319
1320 return true;
1321 }
1322
1323 public static function loadOldSearchNs( $user ) {
1324 $searchableNamespaces = SearchEngine::searchableNamespaces();
1325 // Back compat with old format
1326 $arr = array();
1327
1328 foreach( $searchableNamespaces as $ns => $name ) {
1329 if( $user->getOption( 'searchNs' . $ns ) ) {
1330 $arr[] = $ns;
1331 }
1332 }
1333
1334 return $arr;
1335 }
1336 }
1337
1338 /** Some tweaks to allow js prefs to work */
1339 class PreferencesForm extends HTMLForm {
1340
1341 function wrapForm( $html ) {
1342 $html = Xml::tags( 'div', array( 'id' => 'preferences' ), $html );
1343
1344 return parent::wrapForm( $html );
1345 }
1346
1347 function getButtons() {
1348 $html = parent::getButtons();
1349
1350 global $wgUser;
1351
1352 $sk = $wgUser->getSkin();
1353 $t = SpecialPage::getTitleFor( 'Preferences', 'reset' );
1354
1355 $html .= "\n" . $sk->link( $t, wfMsgHtml( 'restoreprefs' ) );
1356
1357 $html = Xml::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html );
1358
1359 return $html;
1360 }
1361
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;
1370
1371 foreach( $options as $opt ) {
1372 $data["$prefix$opt"] = in_array( $opt, $data[$fieldname] );
1373 }
1374
1375 unset( $data[$fieldname] );
1376 }
1377 }
1378
1379 return $data;
1380 }
1381 }