| 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 | } |