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