r90215 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r90214‎ | r90215 | r90216 >
Date:17:03, 16 June 2011
Author:salvatoreingala
Status:deferred
Tags:
Comment:
- Added UserSaveOptions hook handler to manage gadget preference saving.
- Replaced static methods Gadget::setUserPrefs and ::getUserPrefs with non-static members ::getPrefs and ::setPrefs.
DB is no more accessed directly, gadget preferences are now built on top of user preferences.
- Gadget::getPrefsDescription now returns an array instead of JSON.
- Added documentation for some methods.
- Removed bug in ResourceLoaderGadgetModule, which was using wrong timestamp format.
Modified paths:
  • /branches/salvatoreingala/Gadgets/ApiGetGadgetPrefs.php (modified) (history)
  • /branches/salvatoreingala/Gadgets/ApiSetGadgetPrefs.php (modified) (history)
  • /branches/salvatoreingala/Gadgets/Gadgets.php (modified) (history)
  • /branches/salvatoreingala/Gadgets/Gadgets_body.php (modified) (history)

Diff [purge]

Index: branches/salvatoreingala/Gadgets/Gadgets_body.php
@@ -160,19 +160,84 @@
161161 * @param &$options
162162 */
163163 public static function userLoadOptions( $user, &$options ) {
 164+ //Only if it's current user
 165+ $curUser = RequestContext::getMain()->getUser();
 166+ if ( $curUser->getID() !== $user->getID() ) {
 167+ return true;
 168+ }
 169+
164170 //Remove gadget-*-config options, since they must not be delivered
165171 //via mw.user.options like other user preferences
166 - foreach ( $options as $option => $value ){
167 - //TODO: Regexp not coherent with current gadget's naming rules
168 - if ( preg_match( '/gadget-[a-zA-Z][a-zA-Z0-9_]*-config/', $option ) ) {
169 - //TODO: cache them before unsetting
 172+ $gadgets = Gadget::loadList();
 173+
 174+ if ( !$gadgets ) {
 175+ return true;
 176+ }
 177+
 178+ $existingPrefs = array();
 179+ foreach ( $options as $option => $value ) {
 180+ $m = array();
 181+ if ( preg_match( '/gadget-([a-zA-Z](?:[-_:.\w\d ]*[a-zA-Z0-9])?)-config/', $option, $m ) ) {
 182+ $gadgetName = $m[1];
 183+ $existingPrefs[$gadgetName] = $value;
170184 unset( $options[$option] );
171185 }
172186 }
 187+
 188+ foreach ( $gadgets as $gadget ) {
 189+ $prefsDescription = $gadget->getPrefsDescription();
 190+
 191+ if ( $prefsDescription === null ) {
 192+ continue;
 193+ }
 194+
 195+ if ( isset( $existingPrefs[$gadget->getName()] ) ) {
 196+ $userPrefs = FormatJson::decode( $existingPrefs[$gadget->getName()], true );
 197+ }
 198+
 199+ if ( !isset( $userPrefs ) ) {
 200+ $userPrefs = array(); //no saved prefs, use defaults
 201+ }
 202+
 203+ Gadget::matchPrefsWithDescription( $prefsDescription, $userPrefs );
 204+
 205+ $gadget->setPrefs( $userPrefs );
 206+ }
 207+
173208 return true;
174209 }
175210
 211+ /**
 212+ * UserSaveOptions hook handler.
 213+ * @param $user
 214+ * @param &$options
 215+ */
 216+ public static function userSaveOptions( $user, &$options ) {
 217+ //Only if it's current user
 218+ $curUser = RequestContext::getMain()->getUser();
 219+ if ( $curUser->getID() !== $user->getID() ) {
 220+ return true;
 221+ }
 222+
 223+ //Reinsert gadget-*-config options, so they can be saved back
 224+ $gadgets = Gadget::loadList();
 225+
 226+ if ( !$gadgets ) {
 227+ return true;
 228+ }
 229+
 230+ foreach ( $gadgets as $gadget ) {
 231+ if ( $gadget->getPrefs() !== null ) {
 232+ //TODO: should remove prefs that equal their default
176233
 234+ $prefsJson = FormatJson::encode( $gadget->getPrefs() );
 235+ $options["gadget-{$gadget->getName()}-config"] = $prefsJson;
 236+ }
 237+ }
 238+
 239+ return true;
 240+ }
 241+
177242 /**
178243 * Adds one legacy script to output.
179244 *
@@ -229,6 +294,12 @@
230295 $category;
231296
232297
 298+ //Map from gadget names to preferences of current user.
 299+ //This is needed because gadget preferences are retrieved and saved
 300+ //in UserLoadOptions and UserSaveOptions hooks handlers instead than
 301+ //in Gadget class constructor.
 302+ private static $preferencesCache = array();
 303+
233304 //Syntax specifications of preference description language
234305 private static $prefsDescriptionSpecifications = array(
235306 'boolean' => array(
@@ -682,6 +753,9 @@
683754 wfProfileIn( __METHOD__ );
684755 $key = wfMemcKey( 'gadgets-definition', self::GADGET_CLASS_VERSION );
685756
 757+ //Force loading user options
 758+ RequestContext::getMain()->getUser()->getOptions();
 759+
686760 if ( $forceNewText === null ) {
687761 //cached?
688762 $gadgets = $wgMemc->get( $key );
@@ -807,29 +881,32 @@
808882 return true;
809883 }
810884
811 - //Gets preferences for gadget $gadget;
812 - // returns * '' if the gadget exists but doesn't have any preferences (or provided ones are not valid)
813 - // * the preference description in JSON format, otherwise
 885+ /**
 886+ * Gets description of preferences for this gadget.
 887+ *
 888+ * @return Mixed null if the gadget exists but doesn't have any preferences or if provided ones are not valid,
 889+ * an array with the description of preferences otherwise.
 890+ */
814891 public function getPrefsDescription() {
815 - $prefsMsg = "Gadget-{$this->name}.preferences";
 892+ $prefsDescriptionMsg = "Gadget-{$this->name}.preferences";
816893
817894 //TODO: use cache
818895
819 - $prefsJson = wfMsgForContentNoTrans( $prefsMsg );
820 - if ( wfEmptyMsg( $prefsMsg, $prefsJson ) ||
821 - !self::isGadgetPrefsDescriptionValid( $prefsJson ) )
 896+ $prefsDescriptionJson = wfMsgForContentNoTrans( $prefsDescriptionMsg );
 897+ if ( wfEmptyMsg( $prefsDescriptionMsg, $prefsDescriptionJson ) ||
 898+ !self::isGadgetPrefsDescriptionValid( $prefsDescriptionJson ) )
822899 {
823 - return '';
 900+ return null;
824901 }
825902
826 - return $prefsJson;
 903+ return FormatJson::decode( $prefsDescriptionJson, true );
827904 }
828905
829906 //Check if a preference is valid, according to description
830907 //NOTE: we pass both $prefs and $prefName (instead of just $prefs[$prefName])
831908 // to allow checking for null.
832909 private static function checkSinglePref( &$prefDescription, &$prefs, $prefName ) {
833 -
 910+
834911 //isset( $prefs[$prefName] ) would return false for null values
835912 if ( !array_key_exists( $prefName, $prefs ) ) {
836913 return false;
@@ -911,8 +988,15 @@
912989 }
913990 }
914991
915 - //Returns true if $prefs is an array of preferences that passes validation
916 - private static function checkPrefsAgainstDescription( &$prefsDescription, &$prefs ) {
 992+ /**
 993+ * Checks if $prefs is an array of preferences that passes validation
 994+ *
 995+ * @param $prefsDescription Array: the preferences description to use.
 996+ * @param &$prefs Array: reference of the array of preferences to check.
 997+ *
 998+ * @return boolean true if $prefs passes validation against $prefsDescription, false otherwise.
 999+ */
 1000+ public static function checkPrefsAgainstDescription( $prefsDescription, $prefs ) {
9171001 foreach ( $prefsDescription['fields'] as $prefName => $prefDescription ) {
9181002 if ( !self::checkSinglePref( $prefDescription, $prefs, $prefName ) ) {
9191003 return false;
@@ -921,9 +1005,14 @@
9221006 return true;
9231007 }
9241008
925 - //Fixes $prefs so that it matches the description given by $prefsDescription.
926 - //All values of $prefs that fail validation are replaced with default values.
927 - private static function matchPrefsWithDescription( &$prefsDescription, &$prefs ) {
 1009+ /**
 1010+ * Fixes $prefs so that it matches the description given by $prefsDescription.
 1011+ * All values of $prefs that fail validation are replaced with default values.
 1012+ *
 1013+ * @param $prefsDescription Array: the preferences description to use.
 1014+ * @param &$prefs Array: reference of the array of preferences to match.
 1015+ */
 1016+ public static function matchPrefsWithDescription( $prefsDescription, &$prefs ) {
9281017 //Remove unexisting preferences from $prefs
9291018 foreach ( $prefs as $prefName => $value ) {
9301019 if ( !isset( $prefsDescription['fields'][$prefName] ) ) {
@@ -939,107 +1028,52 @@
9401029 }
9411030 }
9421031
943 - //Get user's preferences for this gadget
944 - public function getUserPrefs( $user ) {
945 - //TODO: cache!
 1032+ /**
 1033+ * Returns current user's preferences for this gadget.
 1034+ *
 1035+ * @return Mixed the array of preferences if they have been set, null otherwise.
 1036+ */
 1037+ public function getPrefs() {
 1038+ $prefs = self::$preferencesCache[$this->getName()];
 1039+ return self::$preferencesCache[$this->getName()];
 1040+ }
9461041
947 - $prefsDescriptionJson = $this->getPrefsDescription();
 1042+ /**
 1043+ * Sets current user's preferences for this gadget, after validating them.
 1044+ *
 1045+ * @param $prefs Array: the array of preferences.
 1046+ * @param $savePrefs boolean: if true, preferences are also saved back to the Database.
 1047+ *
 1048+ * @return boolean: true if validation is passed, false otherwise.
 1049+ */
 1050+ public function setPrefs( $prefs, $savePrefs = false ) {
9481051
949 - if ( $prefsDescriptionJson === '' ) {
950 - return null;
 1052+ if ( is_string( $prefs ) ) {
 1053+ $prefs = FormatJson::decode( $prefs, true );
9511054 }
9521055
953 - $prefsDescription = FormatJson::decode( $prefsDescriptionJson, true );
954 -
955 -
956 - $dbr = wfGetDB( DB_SLAVE );
957 -
958 - $id = $user->getId();
959 - $property = "gadget-{$this->name}-config";
960 -
961 - $res = $dbr->selectRow(
962 - 'user_properties',
963 - array('up_value'),
964 - array(
965 - "up_user={$id}",
966 - "up_property='{$property}'"
967 - ),
968 - __METHOD__);
969 -
970 -
971 - if ( !$res ) {
972 - $userPrefs = array(); //No prefs in DB, will just get defaults
973 - } else {
974 - $userPrefsJson = $res->up_value;
975 - $userPrefs = FormatJson::decode( $userPrefsJson, true );
 1056+ if ( $prefs === null || !is_array( $prefs ) ) {
 1057+ throw new MWException( __METHOD__ . ': $prefs must be an array or valid JSON' );
9761058 }
977 -
978 - self::matchPrefsWithDescription( $prefsDescription, $userPrefs );
979 -
980 - return $userPrefs;
981 - }
9821059
983 - //Set user's preferences for this gadget.
984 - //Returns false if preferences are rejected (that is, they don't pass validation)
985 - public function setUserPrefs( $user, &$preferences ) {
986 - $prefsDescriptionJson = $this->getPrefsDescription();
 1060+ $prefsDescription = $this->getPrefsDescription();
9871061
988 - if ( $prefsDescriptionJson === '' ) {
 1062+ if ( $prefsDescription === null ) {
9891063 return false; //nothing to save
9901064 }
9911065
992 - $prefsDescription = FormatJson::decode( $prefsDescriptionJson, true );
993 -
994 - if ( !self::checkPrefsAgainstDescription( $prefsDescription, $preferences ) ) {
 1066+ if ( !self::checkPrefsAgainstDescription( $prefsDescription, $prefs ) ) {
9951067 return false; //validation failed
9961068 }
9971069
998 - //TODO: should remove preferences that are equal to their default?
999 -
1000 - //Save preferences to DB
1001 -
1002 - $preferencesJson = FormatJson::encode( $preferences );
1003 -
1004 - $dbw = wfGetDB( DB_MASTER );
1005 -
1006 - $id = $user->getId();
1007 - $property = "gadget-{$this->name}-config";
1008 -
1009 - $row = array(
1010 - 'up_user' => $id,
1011 - 'up_property' => $property,
1012 - 'up_value' => $preferencesJson
1013 - );
1014 -
1015 - //Could probably be done with the "ON DUPLICATE KEY UPDATE" syntax
1016 -
1017 - $res = $dbw->update(
1018 - 'user_properties',
1019 - $row,
1020 - array(
1021 - "up_user={$id}",
1022 - "up_property='{$property}'"
1023 - ),
1024 - __METHOD__
1025 - );
 1070+ self::$preferencesCache[$this->getName()] = $prefs;
10261071
1027 -
1028 - $rc = $dbw->affectedRows();
1029 - if ( $rc == 0 ) {
1030 - $dbw->insert(
1031 - 'user_properties',
1032 - $row,
1033 - __METHOD__,
1034 - array( 'IGNORE' ) //ignore insertions without any changes
1035 - );
 1072+ if ( $savePrefs ) {
 1073+ $user = RequestContext::getMain()->getUser();
 1074+ $user->saveSettings();
10361075 }
1037 -
1038 - //Invalidate cache and update user_touched
1039 - $user->invalidateCache( true );
1040 -
10411076 return true;
10421077 }
1043 -
10441078 }
10451079
10461080 /**
@@ -1085,10 +1119,8 @@
10861120 $gadgets = Gadget::loadList();
10871121 $gadget = $gadgets[$gadgetName];
10881122
1089 - $user = RequestContext::getMain()->getUser();
 1123+ $prefs = $gadget->getPrefs();
10901124
1091 - $prefs = $gadget->getUserPrefs( $user );
1092 -
10931125 //Enclose gadget's code in a closure, with "this" bound to the
10941126 //configuration object (or to "window" for non-configurable gadgets)
10951127 $header = '(function(){';
@@ -1114,7 +1146,7 @@
11151147 public function getModifiedTime( ResourceLoaderContext $context ) {
11161148 $touched = RequestContext::getMain()->getUser()->getTouched();
11171149
1118 - return max( parent::getModifiedTime( $context ), $touched );
 1150+ return max( parent::getModifiedTime( $context ), wfTimestamp( TS_UNIX, $touched ) );
11191151 }
11201152 }
11211153
@@ -1129,7 +1161,7 @@
11301162 foreach ( $gadgetsList as $section => $gadgets ) {
11311163 foreach ( $gadgets as $gadgetName => $gadget ) {
11321164 $prefs = $gadget->getPrefsDescription();
1133 - if ( $prefs !== '' ) {
 1165+ if ( $prefs !== null ) {
11341166 $configurableGadgets[] = $gadget->getName();
11351167 }
11361168 }
@@ -1140,5 +1172,3 @@
11411173 return $script;
11421174 }
11431175 }
1144 -
1145 -
Index: branches/salvatoreingala/Gadgets/Gadgets.php
@@ -35,6 +35,7 @@
3636 $wgHooks['ResourceLoaderRegisterModules'][] = 'GadgetHooks::registerModules';
3737 $wgHooks['UnitTestsList'][] = 'GadgetHooks::unitTestsList';
3838 $wgHooks['UserLoadOptions'][] = 'GadgetHooks::userLoadOptions';
 39+$wgHooks['UserSaveOptions'][] = 'GadgetHooks::userSaveOptions';
3940
4041 $dir = dirname(__FILE__) . '/';
4142 $wgExtensionMessagesFiles['Gadgets'] = $dir . 'Gadgets.i18n.php';
Index: branches/salvatoreingala/Gadgets/ApiSetGadgetPrefs.php
@@ -51,7 +51,7 @@
5252 $this->dieUsage( 'The \'pref\' parameter must be valid JSON', 'notjson' );
5353 }
5454
55 - $result = $gadget->setUserPrefs( $user, $prefs );
 55+ $result = $gadget->setPrefs( $prefs, true );
5656
5757 if ( $result === true ) {
5858 $this->getResult()->addValue(
Index: branches/salvatoreingala/Gadgets/ApiGetGadgetPrefs.php
@@ -39,14 +39,13 @@
4040 $this->dieUsage( 'Gadget not found', 'notfound' );
4141 }
4242
43 - $prefsDescriptionJson = $gadget->getPrefsDescription();
44 - $prefsDescription = FormatJson::decode( $prefsDescriptionJson, true );
 43+ $prefsDescription = $gadget->getPrefsDescription();
4544
4645 if ( $prefsDescription === null ) {
4746 $this->dieUsage( 'Gadget ' . $gadget->getName() . ' does not have any preference.', 'noprefs' );
4847 }
4948
50 - $userPrefs = $gadget->getUserPrefs( $user );
 49+ $userPrefs = $gadget->getPrefs();
5150
5251 //Add user preferences to preference description
5352 foreach ( $userPrefs as $pref => $value ) {

Status & tagging log