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