Index: branches/preferences-work/phase3/includes/User.php |
— | — | @@ -1921,25 +1921,19 @@ |
1922 | 1922 | */ |
1923 | 1923 | function setOption( $oname, $val ) { |
1924 | 1924 | $this->load(); |
1925 | | - if ( is_null( $this->mOptions ) ) { |
1926 | | - $this->mOptions = User::getDefaultOptions(); |
1927 | | - } |
| 1925 | + $this->loadOptions(); |
| 1926 | + |
1928 | 1927 | if ( $oname == 'skin' ) { |
1929 | 1928 | # Clear cached skin, so the new one displays immediately in Special:Preferences |
1930 | 1929 | unset( $this->mSkin ); |
1931 | 1930 | } |
1932 | | - // Filter out any newlines that may have passed through input validation. |
1933 | | - // Newlines are used to separate items in the options blob. |
1934 | | - if( $val ) { |
1935 | | - $val = str_replace( "\r\n", "\n", $val ); |
1936 | | - $val = str_replace( "\r", "\n", $val ); |
1937 | | - $val = str_replace( "\n", " ", $val ); |
1938 | | - } |
| 1931 | + |
1939 | 1932 | // Explicitly NULL values should refer to defaults |
1940 | 1933 | global $wgDefaultUserOptions; |
1941 | 1934 | if( is_null($val) && isset($wgDefaultUserOptions[$oname]) ) { |
1942 | 1935 | $val = $wgDefaultUserOptions[$oname]; |
1943 | 1936 | } |
| 1937 | + |
1944 | 1938 | $this->mOptions[$oname] = $val; |
1945 | 1939 | } |
1946 | 1940 | |
— | — | @@ -2288,24 +2282,6 @@ |
2289 | 2283 | } |
2290 | 2284 | |
2291 | 2285 | /** |
2292 | | - * Encode this user's options as a string |
2293 | | - * @return \string Encoded options |
2294 | | - * @private |
2295 | | - */ |
2296 | | - function encodeOptions() { |
2297 | | - $this->load(); |
2298 | | - if ( is_null( $this->mOptions ) ) { |
2299 | | - $this->mOptions = User::getDefaultOptions(); |
2300 | | - } |
2301 | | - $a = array(); |
2302 | | - foreach ( $this->mOptions as $oname => $oval ) { |
2303 | | - array_push( $a, $oname.'='.$oval ); |
2304 | | - } |
2305 | | - $s = implode( "\n", $a ); |
2306 | | - return $s; |
2307 | | - } |
2308 | | - |
2309 | | - /** |
2310 | 2286 | * Set this user's options from an encoded string |
2311 | 2287 | * @param $str \string Encoded options to import |
2312 | 2288 | * @private |
— | — | @@ -3431,6 +3407,8 @@ |
3432 | 3408 | while( $row = $dbr->fetchObject( $res ) ) { |
3433 | 3409 | $this->mOptions[$row->up_property] = unserialize( $row->up_value ); |
3434 | 3410 | } |
| 3411 | + |
| 3412 | + $this->mOptionsLoaded = true; |
3435 | 3413 | } |
3436 | 3414 | |
3437 | 3415 | protected function saveOptions() { |
Index: branches/preferences-work/phase3/includes/HTMLForm.php |
— | — | @@ -18,6 +18,7 @@ |
19 | 19 | 'check' => 'HTMLCheckField', |
20 | 20 | 'toggle' => 'HTMLCheckField', |
21 | 21 | 'int' => 'HTMLIntField', |
| 22 | + 'info' => 'HTMLInfoField', |
22 | 23 | ); |
23 | 24 | |
24 | 25 | function __construct( $descriptor, $messagePrefix ) { |
— | — | @@ -107,6 +108,7 @@ |
108 | 109 | |
109 | 110 | // Check for validation |
110 | 111 | foreach( $this->mFlatFields as $fieldname => $field ) { |
| 112 | + if ( !empty($field->mParams['nodata']) ) continue; |
111 | 113 | if ( $field->validate( $this->mFieldData[$fieldname], |
112 | 114 | $this->mFieldData ) !== true ) { |
113 | 115 | return isset($this->mValidationErrorMessage) ? |
— | — | @@ -213,7 +215,10 @@ |
214 | 216 | |
215 | 217 | foreach( $fields as $key => $value ) { |
216 | 218 | if ( is_object( $value ) ) { |
217 | | - $tableHtml .= $value->getTableRow( $this->mFieldData[$key] ); |
| 219 | + $v = empty($value->mParams['nodata']) |
| 220 | + ? $this->mFieldData[$key] |
| 221 | + : $value->getDefault(); |
| 222 | + $tableHtml .= $value->getTableRow( $v ); |
218 | 223 | } elseif ( is_array( $value ) ) { |
219 | 224 | $section = $this->displaySection( $value ); |
220 | 225 | $legend = wfMsg( "{$this->mMessagePrefix}-$key" ); |
— | — | @@ -232,9 +237,16 @@ |
233 | 238 | $fieldData = array(); |
234 | 239 | |
235 | 240 | foreach( $this->mFlatFields as $fieldname => $field ) { |
| 241 | + if ( !empty($field->mParams['nodata']) ) continue; |
236 | 242 | $fieldData[$fieldname] = $field->loadDataFromRequest( $wgRequest ); |
237 | 243 | } |
238 | 244 | |
| 245 | + // Filter data. |
| 246 | + foreach( $fieldData as $name => &$value ) { |
| 247 | + $field = $this->mFlatFields[$name]; |
| 248 | + $value = $field->filter( $value, $this->mFlatFields ); |
| 249 | + } |
| 250 | + |
239 | 251 | $this->mFieldData = $fieldData; |
240 | 252 | } |
241 | 253 | } |
— | — | @@ -250,6 +262,14 @@ |
251 | 263 | return true; |
252 | 264 | } |
253 | 265 | |
| 266 | + function filter( $value, $alldata ) { |
| 267 | + if( isset( $this->mFilterCallback ) ) { |
| 268 | + $value = call_user_func( $this->mFilterCallback, $value, $alldata ); |
| 269 | + } |
| 270 | + |
| 271 | + return $value; |
| 272 | + } |
| 273 | + |
254 | 274 | function loadDataFromRequest( $request ) { |
255 | 275 | if ($request->getCheck( $this->mName ) ) { |
256 | 276 | return $request->getText( $this->mName ); |
— | — | @@ -292,6 +312,10 @@ |
293 | 313 | if ( isset( $params['validation-callback'] ) ) { |
294 | 314 | $this->mValidationCallback = $params['validation-callback']; |
295 | 315 | } |
| 316 | + |
| 317 | + if ( isset( $params['filter-callback'] ) ) { |
| 318 | + $this->mFilterCallback = $params['filter-callback']; |
| 319 | + } |
296 | 320 | } |
297 | 321 | |
298 | 322 | function getTableRow( $value ) { |
— | — | @@ -441,6 +465,8 @@ |
442 | 466 | function validate( $value, $alldata ) { |
443 | 467 | $p = parent::validate( $value, $alldata ); |
444 | 468 | if ($p !== true) return $p; |
| 469 | + |
| 470 | + if (!is_array($value)) return false; |
445 | 471 | // If all options are valid, array_intersect of the valid options and the provided |
446 | 472 | // options will return the provided options. |
447 | 473 | $validValues = array_intersect( $value, array_keys($this->mParams['options']) ); |
— | — | @@ -453,6 +479,7 @@ |
454 | 480 | function getInputHTML( $value ) { |
455 | 481 | $html = ''; |
456 | 482 | foreach( $this->mParams['options'] as $key => $label ) { |
| 483 | + global $wgRequest; |
457 | 484 | $checkbox = Xml::check( $this->mName.'[]', in_array( $key, $value ), |
458 | 485 | array( 'id' => $this->mID, 'value' => $key ) ); |
459 | 486 | $checkbox .= ' ' . Xml::tags( 'label', array( 'for' => $this->mID ), $label ); |
— | — | @@ -466,11 +493,24 @@ |
467 | 494 | function loadDataFromRequest( $request ) { |
468 | 495 | // won't work with getCheck |
469 | 496 | if ($request->getCheck( 'wpEditToken' ) ) { |
470 | | - return $request->getArray( $this->mName ); |
| 497 | + $arr = $request->getArray( $this->mName ); |
| 498 | + |
| 499 | + if (!$arr) |
| 500 | + $arr = array(); |
| 501 | + |
| 502 | + return $arr; |
471 | 503 | } else { |
472 | 504 | return $this->getDefault(); |
473 | 505 | } |
474 | 506 | } |
| 507 | + |
| 508 | + function getDefault() { |
| 509 | + if ( isset( $this->mDefault ) ) { |
| 510 | + return $this->mDefault; |
| 511 | + } else { |
| 512 | + return array(); |
| 513 | + } |
| 514 | + } |
475 | 515 | } |
476 | 516 | |
477 | 517 | class HTMLRadioField extends HTMLFormField { |
— | — | @@ -497,3 +537,15 @@ |
498 | 538 | return $html; |
499 | 539 | } |
500 | 540 | } |
| 541 | + |
| 542 | +class HTMLInfoField extends HTMLFormField { |
| 543 | + function __construct( $info ) { |
| 544 | + $info['nodata'] = true; |
| 545 | + |
| 546 | + parent::__construct($info); |
| 547 | + } |
| 548 | + |
| 549 | + function getInputHTML( $value ) { |
| 550 | + return !empty($this->mParams['raw']) ? $value : htmlspecialchars($value); |
| 551 | + } |
| 552 | +} |
Index: branches/preferences-work/phase3/includes/Preferences.php |
— | — | @@ -3,15 +3,95 @@ |
4 | 4 | class Preferences { |
5 | 5 | static $defaultPreferences = null; |
6 | 6 | |
7 | | - static function getPreferences() { |
| 7 | + static function getPreferences( $user ) { |
8 | 8 | if (self::$defaultPreferences) |
9 | 9 | return self::$defaultPreferences; |
10 | 10 | |
11 | | - global $wgLang, $wgRCMaxAge, $wgUser; |
| 11 | + global $wgLang, $wgRCMaxAge; |
12 | 12 | |
13 | 13 | $defaultPreferences = array(); |
14 | 14 | |
15 | 15 | ## User info ##################################### |
| 16 | + // Information panel |
| 17 | + $defaultPreferences['username'] = |
| 18 | + array( |
| 19 | + 'type' => 'info', |
| 20 | + 'label-message' => 'username', |
| 21 | + 'default' => $user->getName(), |
| 22 | + 'section' => 'user', |
| 23 | + ); |
| 24 | + |
| 25 | + $defaultPreferences['userid'] = |
| 26 | + array( |
| 27 | + 'type' => 'info', |
| 28 | + 'label-message' => 'uid', |
| 29 | + 'default' => $user->getId(), |
| 30 | + 'section' => 'user', |
| 31 | + ); |
| 32 | + |
| 33 | + # Get groups to which the user belongs |
| 34 | + $userEffectiveGroups = $user->getEffectiveGroups(); |
| 35 | + $userEffectiveGroupsArray = array(); |
| 36 | + foreach( $userEffectiveGroups as $ueg ) { |
| 37 | + if( $ueg == '*' ) { |
| 38 | + // Skip the default * group, seems useless here |
| 39 | + continue; |
| 40 | + } |
| 41 | + $userEffectiveGroupsArray[] = User::makeGroupLinkHTML( $ueg ); |
| 42 | + } |
| 43 | + asort( $userEffectiveGroupsArray ); |
| 44 | + |
| 45 | + $defaultPreferences['usergroups'] = |
| 46 | + array( |
| 47 | + 'type' => 'info', |
| 48 | + 'label' => wfMsgExt( 'prefs-memberingroups', 'parseinline', |
| 49 | + count($userEffectiveGroupsArray) ), |
| 50 | + 'default' => $wgLang->commaList( $userEffectiveGroupsArray ), |
| 51 | + 'raw' => true, |
| 52 | + 'section' => 'user', |
| 53 | + ); |
| 54 | + |
| 55 | + $defaultPreferences['editcount'] = |
| 56 | + array( |
| 57 | + 'type' => 'info', |
| 58 | + 'label-message' => 'prefs-edits', |
| 59 | + 'default' => $user->getEditCount(), |
| 60 | + 'section' => 'user', |
| 61 | + ); |
| 62 | + |
| 63 | + if ($user->getRegistration()) { |
| 64 | + $defaultPreferences['registrationdate'] = |
| 65 | + array( |
| 66 | + 'type' => 'info', |
| 67 | + 'label-message' => 'prefs-registration', |
| 68 | + 'default' => $wgLang->timeanddate( $user->getRegistration() ), |
| 69 | + 'section' => 'user', |
| 70 | + ); |
| 71 | + } |
| 72 | + |
| 73 | + // Actually changeable stuff |
| 74 | + $defaultPreferences['realname'] = |
| 75 | + array( |
| 76 | + 'type' => 'text', |
| 77 | + 'default' => $user->getRealName(), |
| 78 | + 'section' => 'user', |
| 79 | + 'label-message' => 'yourrealname', |
| 80 | + 'help-message' => 'prefs-help-realname', |
| 81 | + ); |
| 82 | + |
| 83 | + global $wgEmailConfirmToEdit; |
| 84 | + |
| 85 | + $defaultPreferences['emailaddress'] = |
| 86 | + array( |
| 87 | + 'type' => 'text', |
| 88 | + 'default' => $user->getEmail(), |
| 89 | + 'section' => 'user', |
| 90 | + 'label-message' => 'youremail', |
| 91 | + 'help-message' => $wgEmailConfirmToEdit |
| 92 | + ? 'prefs-help-email-required' |
| 93 | + : 'prefs-help-email', |
| 94 | + ); |
| 95 | + |
16 | 96 | $defaultPreferences['gender'] = |
17 | 97 | array( |
18 | 98 | 'type' => 'select', |
— | — | @@ -22,8 +102,30 @@ |
23 | 103 | 'unknown' => wfMsg('gender-unknown'), |
24 | 104 | ), |
25 | 105 | 'label-message' => 'yourgender', |
| 106 | + 'help-message' => 'prefs-help-gender', |
26 | 107 | ); |
27 | 108 | |
| 109 | + // Language |
| 110 | + global $wgContLanguageCode; |
| 111 | + $languages = Language::getLanguageNames( false ); |
| 112 | + if( !array_key_exists( $wgContLanguageCode, $languages ) ) { |
| 113 | + $languages[$wgContLanguageCode] = $wgContLanguageCode; |
| 114 | + } |
| 115 | + ksort( $languages ); |
| 116 | + |
| 117 | + $options = array(); |
| 118 | + foreach( $languages as $code => $name ) { |
| 119 | + $options[$code] = "$code - $name"; |
| 120 | + } |
| 121 | + $defaultPreferences['language'] = |
| 122 | + array( |
| 123 | + 'type' => 'select', |
| 124 | + 'section' => 'user', |
| 125 | + 'options' => $options, |
| 126 | + 'default' => $wgContLanguageCode, |
| 127 | + 'label-message' => 'yourlanguage', |
| 128 | + ); |
| 129 | + |
28 | 130 | global $wgContLang, $wgDisableLangConversion; |
29 | 131 | /* see if there are multiple language variants to choose from*/ |
30 | 132 | $variantArray = array(); |
— | — | @@ -70,9 +172,10 @@ |
71 | 173 | 'type' => 'text', |
72 | 174 | 'maxlength' => $wgMaxSigChars, |
73 | 175 | 'label-message' => 'yournick', |
74 | | - 'validation' => |
75 | | - array( 'DefaultPreferences', 'validateSignature' ), |
| 176 | + 'validation-callback' => |
| 177 | + array( 'Preferences', 'validateSignature' ), |
76 | 178 | 'section' => 'user', |
| 179 | + 'filter-callback' => array( 'Preferences', 'cleanSignature' ), |
77 | 180 | ); |
78 | 181 | $defaultPreferences['fancysig'] = |
79 | 182 | array( |
— | — | @@ -80,8 +183,6 @@ |
81 | 184 | 'label-message' => 'tog-fancysig', |
82 | 185 | 'section' => 'user' |
83 | 186 | ); |
84 | | - |
85 | | - ## TODO STUFF STORED IN USER ROW |
86 | 187 | |
87 | 188 | |
88 | 189 | ## Skin ##################################### |
— | — | @@ -348,12 +449,12 @@ |
349 | 450 | 'delete' => 'watchdeletion' ); |
350 | 451 | |
351 | 452 | // Kinda hacky |
352 | | - if( $wgUser->isAllowed( 'createpage' ) || $wgUser->isAllowed( 'createtalk' ) ) { |
| 453 | + if( $user->isAllowed( 'createpage' ) || $user->isAllowed( 'createtalk' ) ) { |
353 | 454 | $watchTypes['read'] = 'watchcreations'; |
354 | 455 | } |
355 | 456 | |
356 | 457 | foreach( $watchTypes as $action => $pref ) { |
357 | | - if ( $wgUser->isAllowed( $action ) ) { |
| 458 | + if ( $user->isAllowed( $action ) ) { |
358 | 459 | $defaultPreferences[$pref] = array( |
359 | 460 | 'type' => 'toggle', |
360 | 461 | 'section' => 'watchlist', |
— | — | @@ -384,9 +485,25 @@ |
385 | 486 | 'section' => 'search', |
386 | 487 | 'min' => 0, |
387 | 488 | ); |
| 489 | + |
| 490 | + $nsOptions = array(); |
| 491 | + foreach( $wgContLang->getNamespaces() as $ns => $name ) { |
| 492 | + if ($ns < 0) continue; |
| 493 | + $displayNs = str_replace( '_', ' ', $name ); |
| 494 | + |
| 495 | + if (!$displayNs) $displayNs = wfMsg( 'blanknamespace' ); |
| 496 | + |
| 497 | + $nsOptions[$ns] = $displayNs; |
| 498 | + } |
| 499 | + |
| 500 | + $defaultPreferences['searchNs'] = |
| 501 | + array( |
| 502 | + 'type' => 'multiselect', |
| 503 | + 'label-message' => 'defaultns', |
| 504 | + 'options' => $nsOptions, |
| 505 | + 'section' => 'search', |
| 506 | + ); |
388 | 507 | |
389 | | - ## TODO Searchable namespaces. |
390 | | - |
391 | 508 | global $wgEnableMWSuggest; |
392 | 509 | if ($wgEnableMWSuggest) { |
393 | 510 | $defaultPreferences['disablesuggest'] = |
— | — | @@ -530,7 +647,26 @@ |
531 | 648 | ); |
532 | 649 | } |
533 | 650 | |
534 | | - ## Unsorted -------------------------------------------------------------- |
| 651 | + ## Prod in defaults from the user |
| 652 | + global $wgDefaultUserOptions; |
| 653 | + foreach( $defaultPreferences as $name => &$info ) { |
| 654 | + $prefFromUser = $user->getOption( $name ); |
| 655 | + $field = HTMLForm::loadInputFromParameters( $info ); // For validation |
| 656 | + $globalDefault = isset($wgDefaultUserOptions[$name]) |
| 657 | + ? $wgDefaultUserOptions[$name] |
| 658 | + : null; |
| 659 | + |
| 660 | + // If it validates, set it as the default |
| 661 | + if ( isset( $user->mOptions[$name] ) && // Make sure we're not just pulling nothing |
| 662 | + $field->validate( $prefFromUser, $user->mOptions ) ) { |
| 663 | + $info['default'] = $prefFromUser; |
| 664 | + } elseif ( isset($info['default']) ) { |
| 665 | + // Already set, no problem |
| 666 | + continue; |
| 667 | + } elseif( $field->validate( $globalDefault, $user->mOptions ) ) { |
| 668 | + $info['default'] = $globalDefault; |
| 669 | + } |
| 670 | + } |
535 | 671 | |
536 | 672 | wfRunHooks( 'GetPreferences', array( &$defaultPreferences ) ); |
537 | 673 | |
— | — | @@ -539,6 +675,10 @@ |
540 | 676 | return $defaultPreferences; |
541 | 677 | } |
542 | 678 | |
| 679 | + static function savePreferencesFromForm( $data, $user ) { |
| 680 | + |
| 681 | + } |
| 682 | + |
543 | 683 | static function generateSkinOptions() { |
544 | 684 | global $wgDefaultSkin; |
545 | 685 | $ret = array(); |
— | — | @@ -565,12 +705,12 @@ |
566 | 706 | $extraLinks = ''; |
567 | 707 | global $wgAllowUserCss, $wgAllowUserJs; |
568 | 708 | if( $wgAllowUserCss ) { |
569 | | - $cssPage = Title::makeTitleSafe( NS_USER, $wgUser->getName().'/'.$skinkey.'.css' ); |
| 709 | + $cssPage = Title::makeTitleSafe( NS_USER, $user->getName().'/'.$skinkey.'.css' ); |
570 | 710 | $customCSS = $sk->makeLinkObj( $cssPage, wfMsgExt('prefs-custom-css', array() ) ); |
571 | 711 | $extraLinks .= " ($customCSS)"; |
572 | 712 | } |
573 | 713 | if( $wgAllowUserJs ) { |
574 | | - $jsPage = Title::makeTitleSafe( NS_USER, $wgUser->getName().'/'.$skinkey.'.js' ); |
| 714 | + $jsPage = Title::makeTitleSafe( NS_USER, $user->getName().'/'.$skinkey.'.js' ); |
575 | 715 | $customJS = $sk->makeLinkObj( $jsPage, wfMsgHtml('prefs-custom-js') ); |
576 | 716 | $extraLinks .= " ($customJS)"; |
577 | 717 | } |
— | — | @@ -643,4 +783,16 @@ |
644 | 784 | return true; |
645 | 785 | } |
646 | 786 | } |
| 787 | + |
| 788 | + static function cleanSignature( $signature, $alldata ) { |
| 789 | + global $wgParser; |
| 790 | + if( $alldata['fancysig'] ) { |
| 791 | + $signature = $wgParser->cleanSig( $signature ); |
| 792 | + } else { |
| 793 | + // When no fancy sig used, make sure ~{3,5} get removed. |
| 794 | + $signature = $wgParser->cleanSigInSig( $signature ); |
| 795 | + } |
| 796 | + |
| 797 | + return $signature; |
| 798 | + } |
647 | 799 | } |
Index: branches/preferences-work/phase3/languages/messages/MessagesEn.php |
— | — | @@ -907,6 +907,7 @@ |
908 | 908 | 'username' => 'Username:', |
909 | 909 | 'uid' => 'User ID:', |
910 | 910 | 'prefs-memberingroups' => 'Member of {{PLURAL:$1|group|groups}}:', |
| 911 | +'prefs-registration' => 'Registration time:', |
911 | 912 | 'yourrealname' => 'Real name:', |
912 | 913 | 'yourlanguage' => 'Language:', |
913 | 914 | 'yourvariant' => 'Variant:', # only translate this message to other languages if you have to change it |