http://www.mediawiki.org/wiki/Special:Code/MediaWiki/91863
Revision: 91863 Author: salvatoreingala Date: 2011-07-11 08:43:00 +0000 (Mon, 11 Jul 2011) Log Message: ----------- Changed preference descriptions format. 'fields' is now an array of preference descriptions instead of a map with the prefName => prefDescription (and preference name is now a field in description). This is cleaner and more flexible (for example, fields that don't describe a gadget preference may now be added). Modified Paths: -------------- branches/salvatoreingala/Gadgets/Gadgets_tests.php branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js Modified: branches/salvatoreingala/Gadgets/Gadgets_tests.php =================================================================== --- branches/salvatoreingala/Gadgets/Gadgets_tests.php 2011-07-11 08:27:07 UTC (rev 91862) +++ branches/salvatoreingala/Gadgets/Gadgets_tests.php 2011-07-11 08:43:00 UTC (rev 91863) @@ -89,7 +89,8 @@ //Test with stdClass instead if array $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( (object)array( 'fields' => array( - 'testBoolean' => array( + array( + 'name' => 'testBoolean', 'type' => 'boolean', 'label' => 'foo', 'default' => 'bar' @@ -100,7 +101,8 @@ //Test with wrong type $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( array( 'fields' => array( - 'testUnexisting' => array( + array( + 'name' => 'testUnexisting', 'type' => 'unexistingtype', 'label' => 'foo', 'default' => 'bar' @@ -108,10 +110,22 @@ ) ) ) ); + //Test with missing name + $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( array( + 'fields' => array( + array( + 'type' => 'boolean', + 'label' => 'foo', + 'default' => true + ) + ) + ) ) ); + //Test with wrong preference name $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( array( 'fields' => array( - 'testWrongN@me' => array( + array( + 'name' => 'testWrongN@me', 'type' => 'boolean', 'label' => 'foo', 'default' => true @@ -119,10 +133,41 @@ ) ) ) ); + //Test with two fields with the same name + $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( array( + 'fields' => array( + array( + 'name' => 'testBoolean', + 'type' => 'boolean', + 'label' => 'foo', + 'default' => true + ), + array( + 'name' => 'testBoolean', + 'type' => 'string', + 'label' => 'foo', + 'default' => 'bar' + ) + ) + ) ) ); + + //Test with fields encoded as associative array instead of regular array + $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( array( + 'fields' => array( + 'testBoolean' => array( + 'name' => 'testBoolean', + 'type' => 'string', + 'label' => 'foo', + 'default' => 'bar' + ) + ) + ) ) ); + //Test with too long preference name (41 characters) $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( array( 'fields' => array( - 'aPreferenceNameExceedingTheLimitOf40Chars' => array( + array( + 'name' => 'aPreferenceNameExceedingTheLimitOf40Chars', 'type' => 'boolean', 'label' => 'foo', 'default' => true @@ -133,7 +178,8 @@ //This must pass, instead (40 characters is fine) $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( array( 'fields' => array( - 'otherPreferenceNameThatS40CharactersLong' => array( + array( + 'name' => 'otherPreferenceNameThatS40CharactersLong', 'type' => 'boolean', 'label' => 'foo', 'default' => true @@ -145,7 +191,8 @@ //Test with an unexisting field parameter $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( array( 'fields' => array( - 'testBoolean' => array( + array( + 'name' => 'testBoolean', 'type' => 'boolean', 'label' => 'foo', 'default' => true, @@ -159,7 +206,8 @@ function testPrefsDescriptionsBoolean() { $correct = array( 'fields' => array( - 'testBoolean' => array( + array( + 'name' => 'testBoolean', 'type' => 'boolean', 'label' => 'some label', 'default' => true @@ -171,7 +219,8 @@ $correct2 = array( 'fields' => array( - 'testBoolean' => array( + array( + 'name' => 'testBoolean', 'type' => 'boolean', 'label' => 'some label', 'default' => false @@ -184,7 +233,7 @@ //Tests with wrong default values $wrong = $correct; foreach ( array( 0, 1, '', 'false', 'true', null, array() ) as $def ) { - $wrong['fields']['testBoolean']['default'] = $def; + $wrong['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); } } @@ -193,7 +242,8 @@ function testPrefsDescriptionsString() { $correct = array( 'fields' => array( - 'testString' => array( + array( + 'name' => 'testString', 'type' => 'string', 'label' => 'some label', 'minlength' => 6, @@ -209,14 +259,14 @@ //Tests with wrong default values $wrong = $correct; foreach ( array( null, true, false, 0, 1, array(), 'short', 'veryverylongstring' ) as $def ) { - $wrong['fields']['testString']['default'] = $def; + $wrong['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); } //Tests with correct default values (when required is false) $correct2 = $correct; foreach ( array( '', '6chars', '1234567890' ) as $def ) { - $correct2['fields']['testString']['default'] = $def; + $correct2['fields'][0]['default'] = $def; $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); } @@ -231,7 +281,8 @@ function testPrefsDescriptionsNumber() { $correctFloat = array( 'fields' => array( - 'testNumber' => array( + array( + 'name' => 'testNumber', 'type' => 'number', 'label' => 'some label', 'min' => -15, @@ -244,7 +295,8 @@ $correctInt = array( 'fields' => array( - 'testNumber' => array( + array( + 'name' => 'testNumber', 'type' => 'number', 'label' => 'some label', 'min' => -15, @@ -262,20 +314,20 @@ //Tests with wrong default values (with 'required' = true) $wrongFloat = $correctFloat; foreach ( array( '', false, true, null, array(), -100, +100 ) as $def ) { - $wrongFloat['fields']['testNumber']['default'] = $def; + $wrongFloat['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrongFloat ) ); } $wrongInt = $correctInt; foreach ( array( '', false, true, null, array(), -100, +100, 2.7182818 ) as $def ) { - $wrongInt['fields']['testNumber']['default'] = $def; + $wrongInt['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrongInt ) ); } //If required=false, default=null must be accepted, too foreach ( array( $correctFloat, $correctInt ) as $correct ) { - $correct['fields']['testNumber']['required'] = false; - $correct['fields']['testNumber']['default'] = null; + $correct['fields'][0]['required'] = false; + $correct['fields'][0]['default'] = null; $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct ) ); } } @@ -284,7 +336,8 @@ function testPrefsDescriptionsSelect() { $correct = array( 'fields' => array( - 'testSelect' => array( + array( + 'name' => 'testSelect', 'type' => 'select', 'label' => 'some label', 'default' => 3, @@ -302,14 +355,14 @@ //Tests with correct default values $correct2 = $correct; foreach ( array( null, true, 3, 'test' ) as $def ) { - $correct2['fields']['testSelect']['default'] = $def; + $correct2['fields'][0]['default'] = $def; $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); } //Tests with wrong default values $wrong = $correct; foreach ( array( '', 'true', 'null', false, array(), 0, 1, 3.0001 ) as $def ) { - $wrong['fields']['testSelect']['default'] = $def; + $wrong['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); } } @@ -318,7 +371,8 @@ function testPrefsDescriptionsRange() { $correct = array( 'fields' => array( - 'testRange' => array( + array( + 'name' => 'testRange', 'type' => 'range', 'label' => 'some label', 'default' => 35, @@ -331,27 +385,28 @@ //Tests with correct default values $correct2 = $correct; foreach ( array( 15, 33, 45 ) as $def ) { - $correct2['fields']['testRange']['default'] = $def; + $correct2['fields'][0]['default'] = $def; $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); } //Tests with wrong default values $wrong = $correct; foreach ( array( '', true, false, null, array(), '35', 14, 46, 30.2 ) as $def ) { - $wrong['fields']['testRange']['default'] = $def; + $wrong['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); } //Test with max not in the set min + k*step (step not given, so it's 1) $wrong = $correct; - $wrong['fields']['testRange']['max'] = 45.5; + $wrong['fields'][0]['max'] = 45.5; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); //Tests with floating point min, max and step $correct = array( 'fields' => array( - 'testRange' => array( + array( + 'name' => 'testRange', 'type' => 'range', 'label' => 'some label', 'default' => 0.20, @@ -367,14 +422,14 @@ //Tests with correct default values $correct2 = $correct; foreach ( array( -2.8, -2.55, 0.20, 4.2 ) as $def ) { - $correct2['fields']['testRange']['default'] = $def; + $correct2['fields'][0]['default'] = $def; $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); } //Tests with wrong default values $wrong = $correct; foreach ( array( '', true, false, null, array(), '0.20', -2.7, 0, 4.199999 ) as $def ) { - $wrong['fields']['testRange']['default'] = $def; + $wrong['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); } } @@ -383,7 +438,8 @@ function testPrefsDescriptionsDate() { $correct = array( 'fields' => array( - 'testDate' => array( + array( + 'name' => 'testDate', 'type' => 'date', 'label' => 'some label', 'default' => null @@ -400,7 +456,7 @@ '2011-12-31T23:59:59Z', ) as $def ) { - $correct2['fields']['testDate']['default'] = $def; + $correct2['fields'][0]['default'] = $def; $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); } @@ -423,7 +479,7 @@ '2011:07-05T15:00:00Z' ) as $def ) { - $wrong['fields']['testDate']['default'] = $def; + $wrong['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); } } @@ -432,7 +488,8 @@ function testPrefsDescriptionsColor() { $correct = array( 'fields' => array( - 'testColor' => array( + array( + 'name' => 'testColor', 'type' => 'color', 'label' => 'some label', 'default' => '#123456' @@ -448,7 +505,7 @@ '#8ed36e', ) as $def ) { - $correct2['fields']['testColor']['default'] = $def; + $correct2['fields'][0]['default'] = $def; $this->assertTrue( GadgetPrefs::isPrefsDescriptionValid( $correct2 ) ); } @@ -465,7 +522,7 @@ 'red', //syntax not allowed ) as $def ) { - $wrong['fields']['testColor']['default'] = $def; + $wrong['fields'][0]['default'] = $def; $this->assertFalse( GadgetPrefs::isPrefsDescriptionValid( $wrong ) ); } } @@ -476,31 +533,36 @@ return array( array( array( 'fields' => array( - 'testBoolean' => array( + array( + 'name' => 'testBoolean', 'type' => 'boolean', 'label' => '@foo', 'default' => true ), - 'testBoolean2' => array( + array( + 'name' => 'testBoolean2', 'type' => 'boolean', 'label' => '@@foo2', 'default' => true ), - 'testNumber' => array( + array( + 'name' => 'testNumber', 'type' => 'number', 'label' => '@foo3', 'min' => 2.3, 'max' => 13.94, 'default' => 7 ), - 'testNumber2' => array( + array( + 'name' => 'testNumber2', 'type' => 'number', 'label' => 'foo4', 'min' => 2.3, 'max' => 13.94, 'default' => 7 ), - 'testSelect' => array( + array( + 'name' => 'testSelect', 'type' => 'select', 'label' => 'foo', 'default' => 3, @@ -511,7 +573,8 @@ '@opt4' => 'opt4value' ) ), - 'testSelect2' => array( + array( + 'name' => 'testSelect2', 'type' => 'select', 'label' => 'foo', 'default' => 3, @@ -557,9 +620,9 @@ $this->assertEquals( $prefs2['testNumber'], $prefs['testNumber'] ); $this->assertEquals( $prefs2['testSelect'], $prefs['testSelect'] ); - $this->assertEquals( $prefs2['testBoolean2'], $prefsDescription['fields']['testBoolean2']['default'] ); - $this->assertEquals( $prefs2['testNumber2'], $prefsDescription['fields']['testNumber2']['default'] ); - $this->assertEquals( $prefs2['testSelect2'], $prefsDescription['fields']['testSelect2']['default'] ); + $this->assertEquals( $prefs2['testBoolean2'], $prefsDescription['fields'][1]['default'] ); + $this->assertEquals( $prefs2['testNumber2'], $prefsDescription['fields'][3]['default'] ); + $this->assertEquals( $prefs2['testSelect2'], $prefsDescription['fields'][5]['default'] ); $g = $this->create( '*foo[ResourceLoader]| foo.css|foo.js|foo.bar' ); $g->setPrefsDescription( $prefsDescription ); Modified: branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php =================================================================== --- branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php 2011-07-11 08:27:07 UTC (rev 91862) +++ branches/salvatoreingala/Gadgets/api/ApiGetGadgetPrefs.php 2011-07-11 08:43:00 UTC (rev 91863) @@ -53,8 +53,9 @@ } //Add user preferences to preference description - foreach ( $userPrefs as $pref => $value ) { - $prefsDescription['fields'][$pref]['value'] = $value; + foreach ( $prefsDescription['fields'] as $prefIdx => $prefDescription ) { + $prefName = $prefDescription['name']; + $prefsDescription['fields'][$prefIdx]['value'] = $userPrefs[$prefName]; } $this->getResult()->addValue( null, $this->getModuleName(), $prefsDescription ); Modified: branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php =================================================================== --- branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php 2011-07-11 08:27:07 UTC (rev 91862) +++ branches/salvatoreingala/Gadgets/backend/GadgetPrefs.php 2011-07-11 08:43:00 UTC (rev 91863) @@ -238,12 +238,17 @@ public static function isPrefsDescriptionValid( $prefsDescription ) { if ( !is_array( $prefsDescription ) || !isset( $prefsDescription['fields'] ) - || !is_array( $prefsDescription['fields'] ) - || count( $prefsDescription['fields'] ) == 0 ) + || !is_array( $prefsDescription['fields'] ) ) { return false; } - + + //Check if 'fields' is a regular (not-associative) array, and that it is not empty + $count = count( $prefsDescription['fields'] ); + if ( $count == 0 || array_keys( $prefsDescription['fields'] ) !== range( 0, $count - 1 ) ) { + return false; + } + //Count of mandatory members for each type $mandatoryCount = array(); foreach ( self::$prefsDescriptionSpecifications as $type => $typeSpec ) { @@ -256,22 +261,35 @@ } //TODO: validation of members other than $prefs['fields'] + + //Map of encountered names + $names = array(); - foreach ( $prefsDescription['fields'] as $option => $optionDefinition ) { + foreach ( $prefsDescription['fields'] as $optionDefinition ) { - //Check if 'type' is set and valid - if ( !isset( $optionDefinition['type'] ) ) { + //Check if 'name' and 'type' are set + if ( !isset( $optionDefinition['type'] ) || !isset( $optionDefinition['name'] ) ) { return false; } $type = $optionDefinition['type']; + //check if 'type' is valid if ( !isset( self::$prefsDescriptionSpecifications[$type] ) ) { return false; } - //check $option name compliance - if ( strlen( $option ) > 40 + $option = $optionDefinition['name']; + + //check that it's different from previous names + if ( isset( $names[$option] ) ) { + return false; + } + + $names[$option] = true; + + //check option name compliance + if ( strlen( $option ) > 40 || !preg_match( '/^[a-zA-Z_][a-zA-Z0-9_]*$/', $option ) ) { return false; @@ -283,8 +301,8 @@ $count = 0; //count of present mandatory members foreach ( $optionDefinition as $fieldName => $fieldValue ) { - if ( $fieldName == 'type' ) { - continue; //'type' must not be checked + if ( $fieldName == 'type' || $fieldName == 'name' ) { + continue; //'type' and 'name' must not be checked } if ( !isset( $typeDescription[$fieldName] ) ) { @@ -468,16 +486,19 @@ * @return boolean true if $prefs passes validation against $prefsDescription, false otherwise. */ public static function checkPrefsAgainstDescription( $prefsDescription, $prefs ) { + $validPrefs = array(); //Check that all the given preferences pass validation - foreach ( $prefsDescription['fields'] as $prefName => $prefDescription ) { + foreach ( $prefsDescription['fields'] as $prefDescription ) { + $prefName = $prefDescription['name']; if ( !self::checkSinglePref( $prefDescription, $prefs, $prefName ) ) { return false; } + $validPrefs[$prefName] = true; } //Check that $prefs contains no preferences that are not described in $prefsDescription foreach ( $prefs as $prefName => $value ) { - if ( !isset( $prefsDescription['fields'][$prefName] ) ) { + if ( !isset( $validPrefs[$prefName] ) ) { return false; } } @@ -493,19 +514,23 @@ * @param &$prefs Array: reference of the array of preferences to match. */ public static function matchPrefsWithDescription( $prefsDescription, &$prefs ) { + $validPrefs = array(); + + //Fix preferences that fail validation, by replacing their value with default + foreach ( $prefsDescription['fields'] as $prefDescription ) { + $prefName = $prefDescription['name']; + if ( !self::checkSinglePref( $prefDescription, $prefs, $prefName ) ) { + $prefs[$prefName] = $prefDescription['default']; + } + $validPrefs[$prefName] = true; + } + //Remove unexisting preferences from $prefs foreach ( $prefs as $prefName => $value ) { - if ( !isset( $prefsDescription['fields'][$prefName] ) ) { + if ( !isset( $validPrefs[$prefName] ) ) { unset( $prefs[$prefName] ); } } - - //Fix preferences that fail validation - foreach ( $prefsDescription['fields'] as $prefName => $prefDescription ) { - if ( !self::checkSinglePref( $prefDescription, $prefs, $prefName ) ) { - $prefs[$prefName] = $prefDescription['default']; - } - } } /** Modified: branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js =================================================================== --- branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js 2011-07-11 08:27:07 UTC (rev 91862) +++ branches/salvatoreingala/Gadgets/ui/resources/jquery.formBuilder.js 2011-07-11 08:43:00 UTC (rev 91863) @@ -556,37 +556,35 @@ var settings = {}; //validator settings - for ( var fieldName in description.fields ) { - if ( description.fields.hasOwnProperty( fieldName )) { - //TODO: validate fieldName - var field = description.fields[fieldName]; + for ( var i = 0; i < description.fields.length; i++ ) { + //TODO: validate fieldName + var field = description.fields[i], + fieldName = field.name, + FieldConstructor = validFieldTypes[field.type]; - var FieldConstructor = validFieldTypes[field.type]; + if ( typeof FieldConstructor != 'function' ) { + mw.log( "field with invalid type: " + field.type ); + return null; + } - if ( typeof FieldConstructor != 'function' ) { - mw.log( "field with invalid type: " + field.type ); - return null; - } - - var f; - try { - f = new FieldConstructor( $form, fieldName, field ); - } catch ( e ) { - mw.log( e ); - return null; //constructor failed, wrong syntax in field description - } - - $form.append( f.getElement() ); - - //If this field has validation rules, add them to settings - var fieldSettings = f.getValidationSettings(); - - if ( fieldSettings ) { - $.extend( true, settings, fieldSettings ); - } - - fields.push( f ); + var f; + try { + f = new FieldConstructor( $form, fieldName, field ); + } catch ( e ) { + mw.log( e ); + return null; //constructor failed, wrong syntax in field description } + + $form.append( f.getElement() ); + + //If this field has validation rules, add them to settings + var fieldSettings = f.getValidationSettings(); + + if ( fieldSettings ) { + $.extend( true, settings, fieldSettings ); + } + + fields.push( f ); } var validator = $form.validate( settings ); _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs