Index: branches/RL2/extensions/Gadgets/Gadgets.i18n.php |
— | — | @@ -1,37 +1,36 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * Internationalisation file for extension Gadgets. |
| 4 | + * Internationalisation file for Gadgets extension. |
5 | 5 | * |
6 | 6 | * @file |
7 | 7 | * @ingroup Extensions |
8 | | - * @author Daniel Kinzler, brightbyte.de |
9 | | - * @copyright © 2007 Daniel Kinzler |
10 | | - * @license GNU General Public Licence 2.0 or later |
11 | 8 | */ |
12 | 9 | |
13 | 10 | $messages = array(); |
14 | 11 | |
15 | 12 | /** English |
16 | 13 | * @author Daniel Kinzler, brightbyte.de |
| 14 | + * @author Roan Kattouw |
| 15 | + * @author Timo Tijhof |
17 | 16 | */ |
18 | 17 | $messages['en'] = array( |
19 | 18 | # For Special:Version |
20 | | - 'gadgets-desc' => 'Lets users select custom [[Special:Gadgets|CSS and JavaScript gadgets]] in their [[Special:Preferences|preferences]]', |
| 19 | + 'gadgets-desc' => 'Lets users select custom [[Special:Gadgets|CSS and JavaScript gadgets]] in their [[Special:Preferences#mw-prefsection-gadgets|preferences]].', |
21 | 20 | |
22 | 21 | # For Special:Preferences |
23 | 22 | 'prefs-gadgets' => 'Gadgets', |
24 | | - 'gadgets-prefstext' => 'Below is a list of special gadgets you can enable for your account. |
| 23 | + 'gadgets-prefstext' => 'Below is a list of gadgets you can enable for your account. |
25 | 24 | These gadgets are mostly based on JavaScript, so JavaScript has to be enabled in your browser for them to work. |
26 | 25 | Note that these gadgets will have no effect on this preferences page. |
27 | 26 | |
28 | | -Also note that these special gadgets are not part of the MediaWiki software, and are usually developed and maintained by users on your local wiki. |
29 | | -Local administrators can edit the [[MediaWiki:Gadgets-definition|definitions]] and [[Special:Gadgets|descriptions]] of available gadgets.', |
| 27 | +Also note that these gadgets are not part of the MediaWiki software, and are usually developed and maintained by users of the wiki. |
| 28 | +Administrators manage to the [[Special:GadgetManager|gadget definitions]] and the [[Special:Gadgets|titles and descriptions]] of available gadgets.', |
30 | 29 | 'gadgets-preference-description' => '$1: $2', |
31 | 30 | |
32 | | - # For Special:Gadgets |
| 31 | + # For Special:Gadgets (overview for users and people with editinterface) |
33 | 32 | 'gadgets' => 'Gadgets', |
34 | 33 | 'gadgets-title' => 'Gadgets', |
35 | | - 'gadgets-pagetext' => "Below is a list of special gadgets users can enable on their [[Special:Preferences|preferences page]], as defined by the [[MediaWiki:Gadgets-definition|definitions]]. |
| 34 | + 'gadgets-pagetext' => "Below is a list of special gadgets users can enable on their [[Special:Preferences#mw-prefsection-gadgets|preferences page]], as defined by the [[MediaWiki:Gadgets-definition|definitions]]. |
36 | 35 | This overview provides easy access to the system message pages that define each gadget's description and code.", |
37 | 36 | 'gadgets-uses' => 'Uses', |
38 | 37 | 'gadgets-required-rights' => 'Requires the {{PLURAL:$2|$1 right|following rights: $1}}.', |
— | — | @@ -44,17 +43,23 @@ |
45 | 44 | <pre>$2</pre> |
46 | 45 | You must have appropriate permissions on destination wiki (including the right to edit system messages) and import from file uploads must be enabled.', |
47 | 46 | 'gadgets-export-download' => 'Download', |
48 | | - |
49 | | - # For Special:GadgetManager |
| 47 | + |
| 48 | + # For Special:GadgetManager (for gadget meta-data management) |
50 | 49 | 'gadgetmanager' => 'Gadget manager', |
51 | 50 | 'gadgetmanager-title' => 'Gadget manager', |
52 | | - 'gadgetmanager-pagetext' => 'Below is an overview of all gadgets defined on this wiki. Users can opt in or opt out of these through their [[Special:Preferences|preferences page]].', |
| 51 | + 'gadgetmanager-pagetext' => 'Below is an overview of all gadgets defined on this wiki. Users can opt in or opt out of these through their [[Special:Preferences#mw-prefsection-gadgets|preferences page]].', |
53 | 52 | 'gadgetmanager-nogadgets' => 'This wiki currently has no gadgets defined.', |
54 | 53 | 'gadgetmanager-uncategorized' => 'Uncategorized', |
55 | 54 | 'gadgetmanager-tablehead-title' => 'Gadget title', |
56 | 55 | 'gadgetmanager-tablehead-default' => 'Default', |
57 | 56 | 'gadgetmanager-tablehead-hidden' => 'Hidden', |
58 | 57 | 'gadgetmanager-tablehead-shared' => 'Shared', |
| 58 | + 'gadgetmanager-tablehead-lastmod' => 'Last modified', |
| 59 | + 'gadgetmanager-tablecell-lastmod' => '$1 by $2', |
| 60 | + 'gadgetmanager-editor-title' => 'Editing $1:', |
| 61 | + 'gadgetmanager-editor-removeprop-tooltip' => 'Remove this item', |
| 62 | + 'gadgetmanager-editor-save' => 'Save gadget', |
| 63 | + 'gadgetmanager-editor-cancel' => 'Cancel', |
59 | 64 | 'gadgetmanager-propsgroup-settings' => 'Gadget settings', |
60 | 65 | 'gadgetmanager-propsgroup-module' => 'Module properties', |
61 | 66 | 'gadgetmanager-prop-scripts' => 'Scripts', |
— | — | @@ -62,7 +67,10 @@ |
63 | 68 | 'gadgetmanager-prop-dependencies' => 'Dependencies', |
64 | 69 | 'gadgetmanager-prop-messages' => 'Messages', |
65 | 70 | 'gadgetmanager-prop-category' => 'Category', |
66 | | - 'gadgetmanager-prop-rights' => 'Rights', |
| 71 | + 'gadgetmanager-prop-rights' => 'Required user rights', |
| 72 | + 'gadgetmanager-prop-default' => 'Enable by default', |
| 73 | + 'gadgetmanager-prop-hidden' => 'Hide gadget', |
| 74 | + 'gadgetmanager-prop-shared' => 'Share gadget', |
67 | 75 | 'gadgetmanager-prop-default-yes' => 'This gadget is loaded by default.', |
68 | 76 | 'gadgetmanager-prop-hidden-yes' => 'This is a hidden gadget.', |
69 | 77 | 'gadgetmanager-prop-shared-yes' => 'This gadget is shared.', |
— | — | @@ -70,7 +78,7 @@ |
71 | 79 | 'gadgetmanager-modifylink-tooltip' => 'Modify this gadget', |
72 | 80 | 'gadgetmanager-deletelink' => 'delete', |
73 | 81 | 'gadgetmanager-deletelink-tooltip' => 'Delete ths gadget', |
74 | | - |
| 82 | + |
75 | 83 | # Permissions |
76 | 84 | 'gadgets-cant-create' => 'You do not have the right to create new Gadgets.', |
77 | 85 | 'gadgets-cant-delete' => 'You do not have the right to delete Gadgets.', |
— | — | @@ -92,6 +100,7 @@ |
93 | 101 | * @author SPQRobin |
94 | 102 | * @author Siebrand |
95 | 103 | * @author The Evil IP address |
| 104 | + * @author Timo Tijhof |
96 | 105 | */ |
97 | 106 | $messages['qqq'] = array( |
98 | 107 | 'gadgets-desc' => '{{desc}}', |
— | — | @@ -112,6 +121,13 @@ |
113 | 122 | {{Identical|Export}}', |
114 | 123 | 'gadgets-export-download' => 'Use the verb for this message. Submit button. |
115 | 124 | {{Identical|Download}}', |
| 125 | + 'gadgetmanager-tablehead-lastmodified' => '{{Identical|Last modified}}', |
| 126 | + 'gadgetmanager-tablecell-lastmod' => 'This message is used on Special:GadgetManager to indicate the last modified date, time and user for gadget definitions. |
| 127 | +* $1 is a time and date (duplicated in $3 and $4) |
| 128 | +* $2 is a link to a user page with a user name as link text, followed by a series of related links |
| 129 | +* $3 is the date |
| 130 | +* $4 is the time |
| 131 | +* $5 is the user name which can be used with GENDER' |
116 | 132 | ); |
117 | 133 | |
118 | 134 | /** Afrikaans (Afrikaans) |
Index: branches/RL2/extensions/Gadgets/Gadgets.php |
— | — | @@ -99,6 +99,7 @@ |
100 | 100 | $wgHooks['ArticleUndelete'][] = 'GadgetHooks::gadgetDefinitionUndelete'; |
101 | 101 | $wgHooks['ArticleUndelete'][] = 'GadgetHooks::cssOrJsPageUndelete'; |
102 | 102 | $wgHooks['BeforePageDisplay'][] = 'GadgetHooks::beforePageDisplay'; |
| 103 | +$wgHooks['MakeGlobalVariablesScript'][] = 'GadgetHooks::makeGlobalVariablesScript'; |
103 | 104 | $wgHooks['CanonicalNamespaces'][] = 'GadgetHooks::canonicalNamespaces'; |
104 | 105 | $wgHooks['GetPreferences'][] = 'GadgetHooks::getPreferences'; |
105 | 106 | $wgHooks['LoadExtensionSchemaUpdates'][] = 'GadgetHooks::loadExtensionSchemaUpdates'; |
— | — | @@ -148,4 +149,44 @@ |
149 | 150 | 'styles' => 'ext.gadgets.gadgetmanager.prejs.css', |
150 | 151 | 'position' => 'top', |
151 | 152 | ), |
| 153 | + // Method to interact with API |
| 154 | + 'ext.gadgets.gadgetmanager.api' => $gadResourceTemplate + array( |
| 155 | + 'scripts' => 'ext.gadgets.gadgetmanager.api.js', |
| 156 | + 'dependencies' => 'mediawiki.util', |
| 157 | + ), |
| 158 | + // jQuery plugin |
| 159 | + 'jquery.createPropCloud' => $gadResourceTemplate + array( |
| 160 | + 'scripts' => 'jquery.createPropCloud.js', |
| 161 | + 'messages' => array( |
| 162 | + 'gadgetmanager-editor-prop-remove', |
| 163 | + ), |
| 164 | + ), |
| 165 | + // Event handling, UI components, initiates on document ready |
| 166 | + 'ext.gadgets.gadgetmanager.ui' => $gadResourceTemplate + array( |
| 167 | + 'scripts' => 'ext.gadgets.gadgetmanager.ui.js', |
| 168 | + 'styles' => 'ext.gadgets.gadgetmanager.ui.css', |
| 169 | + 'dependencies' => array( |
| 170 | + 'ext.gadgets.gadgetmanager.api', |
| 171 | + 'jquery.localize', |
| 172 | + 'jquery.ui.autocomplete', |
| 173 | + 'jquery.ui.dialog', |
| 174 | + 'mediawiki.Title', |
| 175 | + 'jquery.createPropCloud', |
| 176 | + ), |
| 177 | + 'messages' => array( |
| 178 | + 'gadgetmanager-editor-title', |
| 179 | + 'gadgetmanager-editor-removeprop-tooltip', |
| 180 | + 'gadgetmanager-editor-save', |
| 181 | + 'gadgetmanager-editor-cancel', |
| 182 | + 'gadgetmanager-prop-scripts', |
| 183 | + 'gadgetmanager-prop-styles', |
| 184 | + 'gadgetmanager-prop-dependencies', |
| 185 | + 'gadgetmanager-prop-messages', |
| 186 | + 'gadgetmanager-prop-category', |
| 187 | + 'gadgetmanager-prop-rights', |
| 188 | + 'gadgetmanager-prop-default', |
| 189 | + 'gadgetmanager-prop-hidden', |
| 190 | + 'gadgetmanager-prop-shared', |
| 191 | + ), |
| 192 | + ), |
152 | 193 | ); |
Index: branches/RL2/extensions/Gadgets/GadgetHooks.php |
— | — | @@ -289,6 +289,23 @@ |
290 | 290 | } |
291 | 291 | |
292 | 292 | /** |
| 293 | + * MakeGlobalVariablesScript hook handler |
| 294 | + * @param $vars Array: Key/value pars for mw.config.set on this page. |
| 295 | + * @param $out OutputPage |
| 296 | + */ |
| 297 | + public static function makeGlobalVariablesScript( &$vars, $out ) { |
| 298 | + if ( $out->getTitle()->equals( SpecialPage::getTitleFor( 'GadgetManager' ) ) ) { |
| 299 | + global $wgGadgetEnableSharing; |
| 300 | + |
| 301 | + $vars['gadgetManagerConf'] = array( |
| 302 | + 'enableSharing' => $wgGadgetEnableSharing, |
| 303 | + 'allRights' => User::getAllRights(), |
| 304 | + ); |
| 305 | + } |
| 306 | + return true; |
| 307 | + } |
| 308 | + |
| 309 | + /** |
293 | 310 | * UnitTestsList hook handler |
294 | 311 | * @param $files Array: List of extension test files |
295 | 312 | */ |
Index: branches/RL2/extensions/Gadgets/backend/Gadget.php |
— | — | @@ -8,7 +8,7 @@ |
9 | 9 | * // The rights required to be able to enable/load this gadget |
10 | 10 | * "rights": ["delete", "block"], |
11 | 11 | * // Whether this gadget is enabled by default |
12 | | - * "default": true |
| 12 | + * "default": true, |
13 | 13 | * // Whether this gadget is hidden from preferences |
14 | 14 | * "hidden": false, |
15 | 15 | * // Whether this gadget is shared |
Index: branches/RL2/extensions/Gadgets/modules/jquery.createPropCloud.js |
— | — | @@ -0,0 +1,107 @@ |
| 2 | +/** |
| 3 | + * jQuery PropCloud plugin |
| 4 | + * @author Timo Tijhof, 2011 |
| 5 | + */ |
| 6 | +( function() { |
| 7 | + |
| 8 | + function newPropHtml( label, o ) { |
| 9 | + return $( '<span class="' + o.prefix + 'prop"></span>' ) |
| 10 | + .append( |
| 11 | + $( '<span class="' + o.prefix + 'prop-label"></span>' ).text( label ) |
| 12 | + ) |
| 13 | + .append( |
| 14 | + $( '<span class="' + o.prefix + 'prop-delete"></span> ') |
| 15 | + .attr( 'title', o.removeTooltip ) |
| 16 | + .click( function() { |
| 17 | + $(this).parent().remove(); |
| 18 | + o.onRemove( label ); |
| 19 | + } |
| 20 | + ) |
| 21 | + ); |
| 22 | + } |
| 23 | + |
| 24 | + /** |
| 25 | + * Create prop cloud around an input field. |
| 26 | + * |
| 27 | + * @example This is the HTML structure being created: |
| 28 | + * |
| 29 | + * <div class="editor-propcloud"> |
| 30 | + * <div class="editor-propcontainer"> |
| 31 | + * <span editor="jquery-prop"> |
| 32 | + * <span class="editor-prop-label"> .. </span> |
| 33 | + * <span class="editor-prop-delete" title="Remove this item"></span> |
| 34 | + * </span> |
| 35 | + * <span class="editor-prop"> |
| 36 | + * <span class="editor-prop-label"> .. </span> |
| 37 | + * <span class="editor-prop-delete" title="Remove this item"></span> |
| 38 | + * </span> |
| 39 | + * </div> |
| 40 | + * <input class="editor-propinput" /> |
| 41 | + * </div> |
| 42 | + * |
| 43 | + * @context {jQuery} |
| 44 | + * @param o {Object} All optional |
| 45 | + * - prefix {String} Class name prefix |
| 46 | + * - props {Array} Array of properties to start with |
| 47 | + * - autocompleteSource {Function|Array} Source of autocomplete suggestions (required) |
| 48 | + * See also http://jqueryui.com/demos/autocomplete/#options (source) |
| 49 | + * - onAdd {Function} Callback when an item is added |
| 50 | + * - onRemove {Function} Callback when an item is deleted |
| 51 | + * - removeTooltip {String} Tooltip for the remove-icon |
| 52 | + * |
| 53 | + * @return {jQuery} prop cloud (input field inside) |
| 54 | + */ |
| 55 | + $.fn.createPropCloud = function( o ) { |
| 56 | + // Some defaults |
| 57 | + o = $.extend({ |
| 58 | + prefix: 'editor', |
| 59 | + props: [], |
| 60 | + autocompleteSource: [], |
| 61 | + onAdd: function( prop ) {}, |
| 62 | + onRemove: function( prop ) {}, |
| 63 | + removeTooltip: 'Remove this item' |
| 64 | + }, o ); |
| 65 | + o.prefix = o.prefix + '-'; |
| 66 | + |
| 67 | + var $el = this.eq(0), |
| 68 | + $input = $el.addClass( o.prefix + 'propinput' ), |
| 69 | + $cloud = $input.wrap( '<div class="' + o.prefix + 'propcloud"></div>' ).parent(), |
| 70 | + $container = $( '<div class="' + o.prefix + 'propcontainer"></div>' ); |
| 71 | + |
| 72 | + // Append while container is still off the dom |
| 73 | + // This is faster and prevents visible build up |
| 74 | + for ( var i = 0, props = o.props, len = props.length; i < len; i++ ) { |
| 75 | + $container.append( newPropHtml( '' + props[i], o ) ); |
| 76 | + } |
| 77 | + |
| 78 | + $input.autocomplete( { |
| 79 | + // The source is entirly up to you |
| 80 | + source: o.autocompleteSource, |
| 81 | + |
| 82 | + // A value is choosen |
| 83 | + // (ie. by pressing return/tab, clicking on suggestion, etc.) |
| 84 | + select: function( e, data ){ |
| 85 | + var val = data.item.value; |
| 86 | + |
| 87 | + // Prevent duplicate values |
| 88 | + if ( o.props.indexOf( val ) === -1 ) { |
| 89 | + $container.append( newPropHtml( val, o ) ); |
| 90 | + o.onAdd( val ); |
| 91 | + } |
| 92 | + |
| 93 | + // Clear input whether duplicate (and ignored), |
| 94 | + // or unique (and added to the PropCloud by now) |
| 95 | + $input.val( '' ); |
| 96 | + |
| 97 | + // Return false, |
| 98 | + // otherwise jQuery UI calls .val( val ) again |
| 99 | + return false; |
| 100 | + } |
| 101 | + }); |
| 102 | + |
| 103 | + $cloud.prepend( $container ); |
| 104 | + |
| 105 | + return $cloud; |
| 106 | + }; |
| 107 | + |
| 108 | +})(); |
\ No newline at end of file |
Index: branches/RL2/extensions/Gadgets/modules/ext.gadgets.gadgetmanager.api.js |
— | — | @@ -0,0 +1,187 @@ |
| 2 | +/** |
| 3 | + * Implement the editing API for the gadget manager. |
| 4 | + * |
| 5 | + * @author Timo Tijhof |
| 6 | + * @copyright © 2011 Timo Tijhof |
| 7 | + * @license GNU General Public Licence 2.0 or later |
| 8 | + */ |
| 9 | +( function() { |
| 10 | + |
| 11 | + var |
| 12 | + /** |
| 13 | + * @var {Object} Keyed by gadget id, contains the metadata as an object. |
| 14 | + */ |
| 15 | + gadgetCache = {}, |
| 16 | + /** |
| 17 | + * @var {Object|Null} If cache, object with category ids and the member counts */ |
| 18 | + gadgetCategoryCache = null; |
| 19 | + |
| 20 | + /* Local functions */ |
| 21 | + |
| 22 | + /** |
| 23 | + * For most returns from api.* functions, a clone is made when data from |
| 24 | + * cached is used. This is to avoid situations where later modifications |
| 25 | + * (ie. by the ajax editor) on the object affecting the cache (because |
| 26 | + * the object would otherwise be passed by reference). |
| 27 | + */ |
| 28 | + function objClone( obj ) { |
| 29 | + /** |
| 30 | + * A normal `$.extend( {}, obj );` is not suffecient, |
| 31 | + * it has to be recursive, because the values of this |
| 32 | + * object are also refererenes to objects. |
| 33 | + * Consider: |
| 34 | + * <code> |
| 35 | + * var a = { words: [ 'foo', 'bar','baz' ] }; |
| 36 | + * var b = $.extend( {}, a ); |
| 37 | + * b.words.push( 'quux' ); |
| 38 | + * a.words[4]; // quux ! |
| 39 | + * </code> |
| 40 | + */ |
| 41 | + return $.extend( true /* resursive */, {}, obj ); |
| 42 | + } |
| 43 | + function arrClone( arr ) { |
| 44 | + return arr.slice(); |
| 45 | + } |
| 46 | + |
| 47 | + /* Public functions */ |
| 48 | + |
| 49 | + mw.gadgetManager = { |
| 50 | + |
| 51 | + conf: mw.config.get( 'gadgetManagerConf' ), |
| 52 | + |
| 53 | + api: { |
| 54 | + |
| 55 | + /** |
| 56 | + * Get gadget blob from the API (or from cache if available). |
| 57 | + * |
| 58 | + * @param id {String} Gadget id. |
| 59 | + * @param callback {Function} To be called with an object as first argument, |
| 60 | + * and status as second argument (success or error). |
| 61 | + * @return {jqXHR|Null}: Null if served from cache, otherwise the jqXHR. |
| 62 | + */ |
| 63 | + getGadgetMetadata: function( id, callback ) { |
| 64 | + // Check cache |
| 65 | + if ( id in gadgetCache && gadgetCache[id] !== null ) { |
| 66 | + callback( objClone( gadgetCache[id] ), 'success' ); |
| 67 | + return null; |
| 68 | + } |
| 69 | + // Get from API if not cached |
| 70 | + return $.ajax({ |
| 71 | + url: mw.util.wikiScript( 'api' ), |
| 72 | + data: { |
| 73 | + format: 'json', |
| 74 | + action: 'query', |
| 75 | + list: 'gadgets', |
| 76 | + gaprop: 'name|metadata|desc', |
| 77 | + ganames: id, |
| 78 | + galanguage: mw.config.get( 'wgUserLanguage' ) |
| 79 | + }, |
| 80 | + type: 'GET', |
| 81 | + dataType: 'json', |
| 82 | + success: function( data ) { |
| 83 | + if ( data && data.query && data.query.gadgets && data.query.gadgets[0] ) { |
| 84 | + data = data.query.gadgets[0].metadata; |
| 85 | + // Update cache |
| 86 | + gadgetCache[id] = data; |
| 87 | + callback( objClone( data ), 'success' ); |
| 88 | + } else { |
| 89 | + // Invalidate cache |
| 90 | + gadgetCache[id] = null; |
| 91 | + callback( {}, 'error' ); |
| 92 | + } |
| 93 | + }, |
| 94 | + error: function() { |
| 95 | + // Invalidate cache |
| 96 | + gadgetCache[id] = null; |
| 97 | + callback( {}, 'error' ); |
| 98 | + } |
| 99 | + }); |
| 100 | + }, |
| 101 | + |
| 102 | + /** |
| 103 | + * @param callback {Function} To be called with an array as first argument, |
| 104 | + * and status as second argument (success or error). |
| 105 | + * @return {jqXHR|Null}: Null if served from cache, otherwise the jqXHR. |
| 106 | + */ |
| 107 | + getGadgetCategories: function( callback ) { |
| 108 | + // Check cache |
| 109 | + if ( gadgetCategoryCache !== null ) { |
| 110 | + callback( arrClone( gadgetCategoryCache ) ); |
| 111 | + return null; |
| 112 | + } |
| 113 | + // Get from API if not cached |
| 114 | + return $.ajax({ |
| 115 | + url: mw.util.wikiScript( 'api' ), |
| 116 | + data: { |
| 117 | + format: 'json', |
| 118 | + action: 'query', |
| 119 | + list: 'gadgetcategories', |
| 120 | + gcprop: 'name|title|members', |
| 121 | + gclanguage: mw.config.get( 'wgUserLanguage' ) |
| 122 | + }, |
| 123 | + type: 'GET', |
| 124 | + dataType: 'json', |
| 125 | + success: function( data ) { |
| 126 | + if ( data && data.query && data.query.gadgetcategories |
| 127 | + && data.query.gadgetcategories[0] ) |
| 128 | + { |
| 129 | + data = data.query.gadgetcategories; |
| 130 | + // Update cache |
| 131 | + gadgetCategoryCache = data; |
| 132 | + callback( arrClone( data ), 'success' ); |
| 133 | + } else { |
| 134 | + // Invalidate cache |
| 135 | + gadgetCategoryCache = null; |
| 136 | + callback( [], 'error' ); |
| 137 | + } |
| 138 | + }, |
| 139 | + error: function() { |
| 140 | + // Invalidate cache |
| 141 | + gadgetCategoryCache = null; |
| 142 | + callback( [], 'error' ); |
| 143 | + } |
| 144 | + }); |
| 145 | + }, |
| 146 | + |
| 147 | + /** |
| 148 | + * Creates or edits an existing gadget definition. |
| 149 | + * |
| 150 | + * @param gadget {Object} |
| 151 | + * - id {String} Id of the gadget to modify |
| 152 | + * - blob {Object} Gadget meta data |
| 153 | + * @param callback {Function} Called with two arguments: |
| 154 | + * - status ('ok' or 'error') |
| 155 | + * - msg (localized, something like "Successfull", "Conflict occurred" etc.) |
| 156 | + * @return {jqXHR|Null}: Null if served from cache, otherwise the jqXHR. |
| 157 | + */ |
| 158 | + doModifyGadget: function( gadget, callback ) { |
| 159 | + mw.log( gadget ); |
| 160 | + // @todo |
| 161 | + // Get token |
| 162 | + // JSON.stringify |
| 163 | + // Do with ApiEdit |
| 164 | + // Invalidate cache |
| 165 | + gadgetCache[gadget.id] = null; |
| 166 | + callback( 'error', '@todo: Saving not implemented yet. Check console for object that would be saved.' ); |
| 167 | + return null; |
| 168 | + }, |
| 169 | + |
| 170 | + /** |
| 171 | + * Deletes a gadget definition. |
| 172 | + * |
| 173 | + * @param id {String} Id of the gadget to delete. |
| 174 | + * @param callback {Function} Called with one argument (ok', 'error' or 'conflict'). |
| 175 | + * @return {jqXHR|Null}: Null if served from cache, otherwise the jqXHR. |
| 176 | + */ |
| 177 | + doDeleteGadget: function( id, callback ) { |
| 178 | + // @todo |
| 179 | + // Do with ApiDelete |
| 180 | + // Invalidate cache |
| 181 | + gadgetCache[id] = null; |
| 182 | + callback( 'error' ); |
| 183 | + return null; |
| 184 | + } |
| 185 | + } |
| 186 | + }; |
| 187 | + |
| 188 | +})(); |
Index: branches/RL2/extensions/Gadgets/modules/ext.gadgets.gadgetmanager.ui.css |
— | — | @@ -0,0 +1,67 @@ |
| 2 | +/** |
| 3 | + * Styling for the gadget manager javascript-generated user interface. |
| 4 | + */ |
| 5 | + |
| 6 | +/** |
| 7 | + * Form |
| 8 | + */ |
| 9 | +.mw-gadgetmanager-form fieldset { |
| 10 | + margin: 0; |
| 11 | +} |
| 12 | + |
| 13 | +.mw-gadgetmanager-form table { |
| 14 | + width: 100%; |
| 15 | +} |
| 16 | + |
| 17 | +.mw-gadgetmanager-form td, |
| 18 | +.mw-gadgetmanager-form th { |
| 19 | + vertical-align: top; |
| 20 | +} |
| 21 | + |
| 22 | +/** |
| 23 | + * The PropCloud |
| 24 | + */ |
| 25 | +.mw-gadgetmanager-propcloud { |
| 26 | + background: white; |
| 27 | + border: 1px solid grey; |
| 28 | + overflow: hidden; |
| 29 | +} |
| 30 | + |
| 31 | +.mw-gadgetmanager-propcontainer {} |
| 32 | + |
| 33 | +.mw-gadgetmanager-prop { |
| 34 | + float: left; |
| 35 | + margin: 2px 5px 5px 2px; |
| 36 | + padding: 2px 5px; |
| 37 | + background: #e5eff6; |
| 38 | + border: 1px solid #a4d2fb; |
| 39 | + border-radius: 10px; |
| 40 | + line-height: 1; |
| 41 | +} |
| 42 | + |
| 43 | +.mw-gadgetmanager-prop-label {} |
| 44 | + |
| 45 | +.mw-gadgetmanager-prop-delete { |
| 46 | + display: inline-block; |
| 47 | + width: 10px; |
| 48 | + height: 10px; |
| 49 | + /* @embed */ |
| 50 | + background: url(images/close.png) 50% 50% no-repeat; |
| 51 | + opacity: 0.4; |
| 52 | +} |
| 53 | + |
| 54 | +.mw-gadgetmanager-prop:hover .mw-gadgetmanager-prop-delete { |
| 55 | + opacity: 0.7; |
| 56 | +} |
| 57 | + |
| 58 | +.mw-gadgetmanager-prop:hover .mw-gadgetmanager-prop-delete:hover { |
| 59 | + opacity: 1; |
| 60 | + cursor: pointer; |
| 61 | +} |
| 62 | + |
| 63 | +.mw-gadgetmanager-propinput, |
| 64 | +.mw-gadgetmanager-propinput:focus { |
| 65 | + border: 0; |
| 66 | + outline: 0; |
| 67 | + background: transparent; |
| 68 | +} |
Index: branches/RL2/extensions/Gadgets/modules/images/close.png |
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes on: branches/RL2/extensions/Gadgets/modules/images/close.png |
___________________________________________________________________ |
Added: svn:mime-type |
1 | 69 | + application/octet-stream |
Index: branches/RL2/extensions/Gadgets/modules/ext.gadgets.gadgetmanager.ui.js |
— | — | @@ -0,0 +1,417 @@ |
| 2 | +/** |
| 3 | + * JavaScript to initialize the UI of the gadget manager. |
| 4 | + * |
| 5 | + * @author Timo Tijhof |
| 6 | + * @copyright © 2011 Timo Tijhof |
| 7 | + * @license GNU General Public Licence 2.0 or later |
| 8 | + */ |
| 9 | +( function() { |
| 10 | + |
| 11 | + var |
| 12 | + /** |
| 13 | + * @var {Object} Local alias to gadgetmananger |
| 14 | + */ |
| 15 | + gm = mw.gadgetManager, |
| 16 | + /** |
| 17 | + * @var {Object} HTML fragements |
| 18 | + */ |
| 19 | + tpl = { |
| 20 | + fancyForm: '<form class="mw-gadgetmanager-form">\ |
| 21 | + <fieldset>\ |
| 22 | + <legend>Module properties</legend>\ |
| 23 | + <table>\ |
| 24 | + <tr>\ |
| 25 | + <td><label for="mw-gadgetmanager-input-scripts"><html:msg key="gadgetmanager-prop-scripts"></label></td>\ |
| 26 | + <td><input type="text" id="mw-gadgetmanager-input-scripts" /></td>\ |
| 27 | + </tr>\ |
| 28 | + <tr>\ |
| 29 | + <td><label for="mw-gadgetmanager-input-styles"><html:msg key="gadgetmanager-prop-styles"></label></td>\ |
| 30 | + <td><input type="text" id="mw-gadgetmanager-input-styles" /></td>\ |
| 31 | + </tr>\ |
| 32 | + <tr>\ |
| 33 | + <td><label for="mw-gadgetmanager-input-dependencies"><html:msg key="gadgetmanager-prop-dependencies"></label></td>\ |
| 34 | + <td><input type="text" id="mw-gadgetmanager-input-dependencies" /></td>\ |
| 35 | + </tr>\ |
| 36 | + <tr>\ |
| 37 | + <td><label for="mw-gadgetmanager-input-messages"><html:msg key="gadgetmanager-prop-messages"></label></td>\ |
| 38 | + <td><input type="text" id="mw-gadgetmanager-input-messages" /></td>\ |
| 39 | + </tr>\ |
| 40 | + </table>\ |
| 41 | + </fieldset>\ |
| 42 | + <fieldset>\ |
| 43 | + <legend>Gadget settings</legend>\ |
| 44 | + <table>\ |
| 45 | + <tr>\ |
| 46 | + <td><label for="mw-gadgetmanager-input-category"><html:msg key="gadgetmanager-prop-category"></label></td>\ |
| 47 | + <td><select id="mw-gadgetmanager-input-category"></select></td>\ |
| 48 | + </tr>\ |
| 49 | + <tr>\ |
| 50 | + <td><label for="mw-gadgetmanager-input-rights"><html:msg key="gadgetmanager-prop-rights"></label></td>\ |
| 51 | + <td><input type="text" id="mw-gadgetmanager-input-rights" /></td>\ |
| 52 | + </tr>\ |
| 53 | + <tr>\ |
| 54 | + <td><label for="mw-gadgetmanager-input-default"><html:msg key="gadgetmanager-prop-default"></label></td>\ |
| 55 | + <td><input type="checkbox" id="mw-gadgetmanager-input-default" /></td>\ |
| 56 | + </tr>\ |
| 57 | + <tr>\ |
| 58 | + <td><label for="mw-gadgetmanager-input-hidden"><html:msg key="gadgetmanager-prop-hidden"></label></td>\ |
| 59 | + <td><input type="checkbox" id="mw-gadgetmanager-input-hidden" /></td>\ |
| 60 | + </tr>\ |
| 61 | + ' + ( gm.conf.enableSharing ? '<tr>\ |
| 62 | + <td><label for="mw-gadgetmanager-input-shared"><html:msg key="gadgetmanager-prop-shared"></label></td>\ |
| 63 | + <td><input type="checkbox" id="mw-gadgetmanager-input-shared" /></td>\ |
| 64 | + </tr>\ |
| 65 | + ' : '' ) + '</table>\ |
| 66 | + </fieldset>\ |
| 67 | + </form>' |
| 68 | + }, |
| 69 | + /** |
| 70 | + * @var {Object} Static cache for suggestions by script prefix. |
| 71 | + */ |
| 72 | + suggestCacheScripts = {}, |
| 73 | + /** |
| 74 | + * @var {Object} Static cache for suggestions by style prefix. |
| 75 | + */ |
| 76 | + suggestCacheStyles = {}, |
| 77 | + /** |
| 78 | + * @var {Object} Static cache for suggestions by messages prefix. |
| 79 | + */ |
| 80 | + suggestCacheMsgs = {}, |
| 81 | + /** |
| 82 | + * @var {Object} Complete static cache for module names. Lazy loaded from null. |
| 83 | + */ |
| 84 | + suggestCacheDependencies = null, |
| 85 | + /** |
| 86 | + * @var {Object} Complete static cache for all rights. |
| 87 | + */ |
| 88 | + suggestCacheRights = gm.conf.allRights, |
| 89 | + /** |
| 90 | + * @var {Number} Maximum number of autocomplete suggestions in the gadget editor input fields. |
| 91 | + */ |
| 92 | + suggestLimit = 7, |
| 93 | + /** |
| 94 | + * @var {Array} List of category objects with their name, localized title and member count. |
| 95 | + */ |
| 96 | + gadgetCategoriesCache = []; |
| 97 | + |
| 98 | + /* Local functions */ |
| 99 | + |
| 100 | + /** |
| 101 | + * Remove all occurences of a value from an array. |
| 102 | + * |
| 103 | + * @param arr {Array} Array to be changed |
| 104 | + * @param val {Mixed} Value to be removed from the array |
| 105 | + * @return {Array} May or may not be changed, reference kept |
| 106 | + */ |
| 107 | + function arrayRemove( arr, val ) { |
| 108 | + var i; |
| 109 | + // Parentheses are crucial here. Without them, var i will be a |
| 110 | + // boolean instead of a number, resulting in an infinite loop! |
| 111 | + while ( ( i = arr.indexOf( val ) ) !== -1 ) { |
| 112 | + arr.splice( i, 1 ); |
| 113 | + } |
| 114 | + return arr; |
| 115 | + } |
| 116 | + |
| 117 | + /* Public functions */ |
| 118 | + |
| 119 | + gm.ui = { |
| 120 | + /** |
| 121 | + * Initilizes the the page. For now just binding click handlers |
| 122 | + * to the anchor tags in the table. |
| 123 | + */ |
| 124 | + initUI: function() { |
| 125 | + // Bind trigger to the links |
| 126 | + $( '.mw-gadgetmanager-gadgets .mw-gadgetmanager-gadgets-title a' ) |
| 127 | + .click( function( e ) { |
| 128 | + e.preventDefault(); |
| 129 | + var $el = $( this ); |
| 130 | + var gadget = { |
| 131 | + id: $el.data( 'gadgetname' ), |
| 132 | + displayTitle: $el.text(), |
| 133 | + metadata: null |
| 134 | + }; |
| 135 | + gm.ui.startGadgetEditor( gadget ); |
| 136 | + }); |
| 137 | + }, |
| 138 | + |
| 139 | + /** |
| 140 | + * Initialize the gadget editor dialog. |
| 141 | + * |
| 142 | + * @asynchronous |
| 143 | + * @param id {String} |
| 144 | + * @param displayTitle {String} |
| 145 | + */ |
| 146 | + startGadgetEditor: function( gadget ) { |
| 147 | + // We need two things. Gadget meta-data and category information. |
| 148 | + var done = 0, ready = 2; |
| 149 | + |
| 150 | + gm.api.getGadgetMetadata( gadget.id, function( metadata, status ) { |
| 151 | + // @todo Notification: If status is 'error' |
| 152 | + gadget.metadata = metadata; |
| 153 | + done++; |
| 154 | + if ( done >= ready ) { |
| 155 | + gm.ui.showFancyForm( gadget ); |
| 156 | + } |
| 157 | + }); |
| 158 | + |
| 159 | + gm.api.getGadgetCategories( function( cats ) { |
| 160 | + gadgetCategoriesCache = cats; |
| 161 | + done++; |
| 162 | + if ( done >= ready ) { |
| 163 | + gm.ui.showFancyForm( gadget ); |
| 164 | + } |
| 165 | + }); |
| 166 | + }, |
| 167 | + |
| 168 | + /** |
| 169 | + * Generate form, create a dialog and open it into view. |
| 170 | + * |
| 171 | + * @param gadget {Object} |
| 172 | + * @return {jQuery} The (dialogged) form. |
| 173 | + */ |
| 174 | + showFancyForm: function( gadget ) { |
| 175 | + var $form = gm.ui.getFancyForm( gadget.metadata ); |
| 176 | + var buttons = {}; |
| 177 | + buttons[mw.msg( 'gadgetmanager-editor-save' )] = function() { |
| 178 | + gm.api.doModifyGadget( gadget, function( status, msg ) { |
| 179 | + alert( "Save result: \n- status: " + status + "\n- msg: " + msg ); |
| 180 | + /* @todo Notification |
| 181 | + addNotification( { |
| 182 | + msg: msg, |
| 183 | + type: status !== 'error' ? 'success' : status, |
| 184 | + timedActionDelay: 5, |
| 185 | + timedAction: function(){ |
| 186 | + // refresh page |
| 187 | + } |
| 188 | + }); |
| 189 | + */ |
| 190 | + }); |
| 191 | + }; |
| 192 | + return $form |
| 193 | + .dialog({ |
| 194 | + autoOpen: true, |
| 195 | + width: 800, |
| 196 | + modal: true, |
| 197 | + draggable: false, |
| 198 | + resizable: false, |
| 199 | + title: mw.message( 'gadgetmanager-editor-title', gadget.displayTitle ).escaped(), |
| 200 | + buttons: buttons, |
| 201 | + open: function() { |
| 202 | + // Dialog is ready for action. |
| 203 | + // Push out any notifications if some were queued up already between |
| 204 | + // getting the gadget data and the display of the form. |
| 205 | + /* @todo Notification |
| 206 | + if ( gm.ui.notifications.length ) { |
| 207 | + for ( in ) { |
| 208 | + slice(i,1)_remove; |
| 209 | + gm.ui.addNotification( $form, n[i] ); |
| 210 | + } |
| 211 | + } |
| 212 | + */ |
| 213 | + } |
| 214 | + }); |
| 215 | + }, |
| 216 | + |
| 217 | + /** |
| 218 | + * Generate a <form> for the given module. |
| 219 | + * Also binds events for submission and autocompletion. |
| 220 | + * |
| 221 | + * @param metadata {Object} Object to read and write to, used when saving |
| 222 | + * the gadget metadata back through the API. |
| 223 | + * @return {jQuery} The form. |
| 224 | + */ |
| 225 | + getFancyForm: function( metadata ) { |
| 226 | + var nsGadgetId = mw.config.get( 'wgNamespaceIds' ).gadget, |
| 227 | + $form = $( tpl.fancyForm ).localize(); |
| 228 | + |
| 229 | + // Module properties: scripts |
| 230 | + $form.find( '#mw-gadgetmanager-input-scripts' ).createPropCloud({ |
| 231 | + props: metadata.module.scripts, |
| 232 | + autocompleteSource: function( data, response ) { |
| 233 | + // Use cache if available |
| 234 | + if ( data.term in suggestCacheScripts ) { |
| 235 | + response( suggestCacheScripts[data.term] ); |
| 236 | + return; |
| 237 | + } |
| 238 | + $.getJSON( mw.util.wikiScript( 'api' ), { |
| 239 | + format: 'json', |
| 240 | + action: 'query', |
| 241 | + list: 'gadgetpages', |
| 242 | + gpnamespace: nsGadgetId, |
| 243 | + gpextension: 'js', |
| 244 | + gpprefix: data.term |
| 245 | + }, function( json ) { |
| 246 | + if ( json && json.query && json.query.gadgetpages ) { |
| 247 | + var suggestions = $.map( json.query.gadgetpages, function( val, i ) { |
| 248 | + return val.pagename; |
| 249 | + }); |
| 250 | + suggestions = suggestions.splice( 0, suggestLimit ); |
| 251 | + |
| 252 | + // Update cache |
| 253 | + suggestCacheScripts[data.term] = suggestions; |
| 254 | + |
| 255 | + response( suggestions ); |
| 256 | + } else { |
| 257 | + response( [] ); |
| 258 | + } |
| 259 | + } |
| 260 | + ); |
| 261 | + }, |
| 262 | + prefix: 'mw-gadgetmanager', |
| 263 | + removeTooltip: mw.msg( 'gadgetmanager-editor-removeprop-tooltip' ), |
| 264 | + onAdd: function( prop ) { metadata.module.scripts.push( prop ); }, |
| 265 | + onRemove: function( prop ) { arrayRemove( metadata.module.scripts, prop ); } |
| 266 | + }); |
| 267 | + |
| 268 | + // Module properties: styles |
| 269 | + $form.find( '#mw-gadgetmanager-input-styles' ).createPropCloud({ |
| 270 | + props: metadata.module.styles, |
| 271 | + autocompleteSource: function( data, response ) { |
| 272 | + // Use cache if available |
| 273 | + if ( data.term in suggestCacheStyles ) { |
| 274 | + response( suggestCacheStyles[data.term] ); |
| 275 | + return; |
| 276 | + } |
| 277 | + $.getJSON( mw.util.wikiScript( 'api' ), { |
| 278 | + format: 'json', |
| 279 | + action: 'query', |
| 280 | + list: 'gadgetpages', |
| 281 | + gpnamespace: nsGadgetId, |
| 282 | + gpextension: 'css', |
| 283 | + gpprefix: data.term |
| 284 | + }, function( json ) { |
| 285 | + if ( json && json.query && json.query.gadgetpages ) { |
| 286 | + var suggestions = $.map( json.query.gadgetpages, function( val, i ) { |
| 287 | + return val.pagename; |
| 288 | + }); |
| 289 | + suggestions = suggestions.splice( 0, suggestLimit ); |
| 290 | + |
| 291 | + // Update cache |
| 292 | + suggestCacheStyles[data.term] = suggestions; |
| 293 | + |
| 294 | + response( suggestions ); |
| 295 | + } else { |
| 296 | + response( [] ); |
| 297 | + } |
| 298 | + } |
| 299 | + ); |
| 300 | + }, |
| 301 | + prefix: 'mw-gadgetmanager', |
| 302 | + removeTooltip: mw.msg( 'gadgetmanager-editor-removeprop-tooltip' ), |
| 303 | + onAdd: function( prop ) { metadata.module.styles.push( prop ); }, |
| 304 | + onRemove: function( prop ) { arrayRemove( metadata.module.styles, prop ); } |
| 305 | + }); |
| 306 | + |
| 307 | + // Module properties: dependencies |
| 308 | + $form.find( '#mw-gadgetmanager-input-dependencies' ).createPropCloud({ |
| 309 | + props: metadata.module.dependencies, |
| 310 | + autocompleteSource: function( data, response ) { |
| 311 | + if ( suggestCacheDependencies === null ) { |
| 312 | + suggestCacheDependencies = mw.loader.getModuleNames(); |
| 313 | + } |
| 314 | + var output = $.ui.autocomplete.filter( suggestCacheDependencies, data.term ); |
| 315 | + response( output.slice( 0, suggestLimit ) ); |
| 316 | + }, |
| 317 | + prefix: 'mw-gadgetmanager', |
| 318 | + removeTooltip: mw.msg( 'gadgetmanager-editor-removeprop-tooltip' ), |
| 319 | + onAdd: function( prop ) { metadata.module.dependencies.push( prop ); }, |
| 320 | + onRemove: function( prop ) { arrayRemove( metadata.module.dependencies, prop ); } |
| 321 | + }); |
| 322 | + |
| 323 | + // Module properties: messages |
| 324 | + $form.find( '#mw-gadgetmanager-input-messages' ).createPropCloud({ |
| 325 | + props: metadata.module.messages, |
| 326 | + autocompleteSource: function( data, response ) { |
| 327 | + // Use cache if available |
| 328 | + if ( data.term in suggestCacheMsgs ) { |
| 329 | + response( suggestCacheMsgs[data.term] ); |
| 330 | + return; |
| 331 | + } |
| 332 | + $.getJSON( mw.util.wikiScript( 'api' ), { |
| 333 | + format: 'json', |
| 334 | + action: 'query', |
| 335 | + meta: 'allmessages', |
| 336 | + amprefix: data.term, |
| 337 | + amnocontent: true, |
| 338 | + amlang: mw.config.get( 'wgContentLanguage' ) |
| 339 | + }, function( json ) { |
| 340 | + if ( json && json.query && json.query.allmessages ) { |
| 341 | + var suggestions = $.map( json.query.allmessages, function( val, i ) { |
| 342 | + return val.name; |
| 343 | + }); |
| 344 | + suggestions = suggestions.splice( 0, suggestLimit ); |
| 345 | + |
| 346 | + // Update cache |
| 347 | + suggestCacheMsgs[data.term] = suggestions; |
| 348 | + |
| 349 | + response( suggestions ); |
| 350 | + } else { |
| 351 | + response( [] ); |
| 352 | + } |
| 353 | + } |
| 354 | + ); |
| 355 | + }, |
| 356 | + prefix: 'mw-gadgetmanager', |
| 357 | + removeTooltip: mw.msg( 'gadgetmanager-editor-removeprop-tooltip' ), |
| 358 | + onAdd: function( prop ) { metadata.module.messages.push( prop ); }, |
| 359 | + onRemove: function( prop ) { arrayRemove( metadata.module.messages, prop ); } |
| 360 | + }); |
| 361 | + |
| 362 | + // Gadget settings: category |
| 363 | + $form.find( '#mw-gadgetmanager-input-category' ).append( function() { |
| 364 | + var current = metadata.settings.category, |
| 365 | + opts = '', |
| 366 | + i = 0, |
| 367 | + cat; |
| 368 | + for ( ; i < gadgetCategoriesCache.length; i++ ) { |
| 369 | + cat = gadgetCategoriesCache[i]; |
| 370 | + opts += mw.html.element( 'option', { |
| 371 | + value: cat.name, |
| 372 | + selected: cat.name === current |
| 373 | + }, cat.title ); |
| 374 | + } |
| 375 | + return opts; |
| 376 | + }).change( function() { |
| 377 | + metadata.settings.category = $(this).val(); |
| 378 | + }); |
| 379 | + |
| 380 | + // Gadget settings: rights |
| 381 | + $form.find( '#mw-gadgetmanager-input-rights' ).createPropCloud({ |
| 382 | + props: metadata.settings.rights, |
| 383 | + autocompleteSource: function( data, response ) { |
| 384 | + var output = $.ui.autocomplete.filter( suggestCacheRights, data.term ); |
| 385 | + response( output.slice( 0, suggestLimit ) ); |
| 386 | + }, |
| 387 | + prefix: 'mw-gadgetmanager', |
| 388 | + removeTooltip: mw.msg( 'gadgetmanager-editor-removeprop-tooltip' ), |
| 389 | + onAdd: function( prop ) { metadata.settings.rights.push( prop ); }, |
| 390 | + onRemove: function( prop ) { arrayRemove( metadata.settings.rights, prop ); } |
| 391 | + }); |
| 392 | + |
| 393 | + // Gadget settings: Default |
| 394 | + $form.find( '#mw-gadgetmanager-input-default' ) |
| 395 | + .prop( 'checked', metadata.settings['default'] ) |
| 396 | + .change( function() { |
| 397 | + metadata.settings['default'] = this.checked; |
| 398 | + }); |
| 399 | + |
| 400 | + // Gadget settings: Hidden |
| 401 | + $form.find( '#mw-gadgetmanager-input-hidden' ) |
| 402 | + .prop( 'checked', metadata.settings.hidden ) |
| 403 | + .change( function() { metadata.settings.hidden = this.checked; }); |
| 404 | + |
| 405 | + // Gadget settings: Shared |
| 406 | + $form.find( '#mw-gadgetmanager-input-shared' ) |
| 407 | + .prop( 'checked', metadata.settings.shared ) |
| 408 | + .change( function() { metadata.settings.shared = this.checked; }); |
| 409 | + |
| 410 | + |
| 411 | + return $form; |
| 412 | + } |
| 413 | + }; |
| 414 | + |
| 415 | + // Launch on document ready |
| 416 | + $( document ).ready( gm.ui.initUI ); |
| 417 | + |
| 418 | +})(); |
Index: branches/RL2/extensions/Gadgets/modules/ext.gadgets.gadgetmanager.prejs.css |
— | — | @@ -1,5 +1,6 @@ |
2 | | -.mw-gadgetmanager-gadgets { |
3 | | - width: 100%; |
| 2 | +.mw-gadgetmanager-gadgets.mw-datatable th, |
| 3 | +.mw-gadgetmanager-gadgets.mw-datatable td { |
| 4 | + padding: 2px 5px; |
4 | 5 | } |
5 | 6 | |
6 | 7 | .mw-gadgetmanager-gadgets-default, |
Index: branches/RL2/extensions/Gadgets/SpecialGadgetManager.php |
— | — | @@ -22,6 +22,11 @@ |
23 | 23 | $out->setPagetitle( wfMsg( 'gadgetmanager-title' ) ); |
24 | 24 | $out->addModuleStyles( 'ext.gadgets.gadgetmanager.prejs' ); |
25 | 25 | |
| 26 | + // Only load ajax editor if user is allowed to edit |
| 27 | + if ( $this->getUser()->isAllowed( 'gadgets-definition-edit' ) ) { |
| 28 | + $out->addModules( 'ext.gadgets.gadgetmanager.ui' ); |
| 29 | + } |
| 30 | + |
26 | 31 | // Determine view |
27 | 32 | if ( is_string( $par ) && $par !== '' ) { |
28 | 33 | $html = $this->generateGadgetView( $par ); |
— | — | @@ -91,7 +96,7 @@ |
92 | 97 | if ( $wgGadgetEnableSharing ) { |
93 | 98 | $html .= '<th>' . wfMessage( 'gadgetmanager-tablehead-shared' )->escaped() . '</th>'; |
94 | 99 | } |
95 | | - $html .= '</tr>'; |
| 100 | + $html .= '<th>' . wfMessage( 'gadgetmanager-tablehead-lastmod' )->escaped() . '</tr>'; |
96 | 101 | |
97 | 102 | // Populate table rows for the current category |
98 | 103 | foreach ( $gadgets as $gadgetName => $gadget ) { |
— | — | @@ -105,14 +110,10 @@ |
106 | 111 | ) ); |
107 | 112 | |
108 | 113 | // Title |
109 | | - $titleMsg = wfMessage( $gadget->getTitleMsg() ); |
110 | 114 | $titleLink = Linker::link( |
111 | 115 | $this->getTitle( $gadget->getName() ), |
112 | | - // MediaWiki-message is optional. This is for backwards compatibility (since |
113 | | - // the previous version didn't have titles), and to a allow wikis that only |
114 | | - // care about one language to save from creating NS_MEDIAWIKI pages. |
115 | | - // @todo: Centralize this logic. |
116 | | - $titleMsg->exists() ? $titleMsg->plain() : $this->getLang()->ucfirst( $gadget->getName() ) |
| 116 | + $gadget->getTitleMessage(), |
| 117 | + array( 'data-gadgetname' => $gadget->getName() ) |
117 | 118 | ); |
118 | 119 | $html .= "<td class=\"mw-gadgetmanager-gadgets-title\">$titleLink</td>"; |
119 | 120 | // Default |
— | — | @@ -127,6 +128,39 @@ |
128 | 129 | . ( $gadget->isShared() ? $tickedCheckboxHtml : '' ) . '</td>'; |
129 | 130 | } |
130 | 131 | |
| 132 | + // Last modified |
| 133 | + $lastModText = ''; |
| 134 | + $definitionTitle = Title::newFromText( $gadget->getName() . '.js', NS_GADGET_DEFINITION ); |
| 135 | + if ( $definitionTitle ) { |
| 136 | + $definitionRev = Revision::newFromTitle( $definitionTitle ); |
| 137 | + if ( $definitionRev ) { |
| 138 | + $userLang = $this->getLang(); |
| 139 | + $revTimestamp = $definitionRev->getTimestamp(); |
| 140 | + $userText = $definitionRev->getUserText(); |
| 141 | + $userLinks = |
| 142 | + Linker::userLink( |
| 143 | + $definitionRev->getUser(), |
| 144 | + $userText |
| 145 | + ) . |
| 146 | + Linker::userToolLinks( |
| 147 | + $definitionRev->getUser(), |
| 148 | + $userText |
| 149 | + ); |
| 150 | + $lastModText = wfMsgExt( |
| 151 | + 'gadgetmanager-tablecell-lastmod', |
| 152 | + array( 'replaceafter', 'parseinline' ), |
| 153 | + array( |
| 154 | + $userLang->timeanddate( $revTimestamp, true ), |
| 155 | + $userLinks, |
| 156 | + $userLang->date( $revTimestamp, true ), |
| 157 | + $userLang->time( $revTimestamp, true ), |
| 158 | + $userText |
| 159 | + ) |
| 160 | + ); |
| 161 | + } |
| 162 | + $html .= "<td>$lastModText</td>"; |
| 163 | + } |
| 164 | + |
131 | 165 | $html .= '</tr>'; |
132 | 166 | } |
133 | 167 | |
— | — | @@ -141,6 +175,8 @@ |
142 | 176 | * @return String: HTML |
143 | 177 | */ |
144 | 178 | public function generateGadgetView( $gadgetName ) { |
145 | | - return "Stub page where there will be some info about the gadget ($gadgetName). This is also used for permalinks to a gadget's config page."; |
| 179 | + return 'TODO - This page is about "' |
| 180 | + . htmlspecialchars( $gadgetName ) |
| 181 | + . '". Also used as permalink from other places.'; |
146 | 182 | } |
147 | 183 | } |