r101602 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r101601‎ | r101602 | r101603 >
Date:09:33, 2 November 2011
Author:neilk
Status:ok (Comments)
Tags:todo 
Comment:
validation of custom license wikitext, including basic parsing, checking that templates exist, and that they are descendants of a particular category
Modified paths:
  • /trunk/extensions/UploadWizard/UploadWizard.config.php (modified) (history)
  • /trunk/extensions/UploadWizard/UploadWizard.i18n.php (modified) (history)
  • /trunk/extensions/UploadWizard/UploadWizardHooks.php (modified) (history)
  • /trunk/extensions/UploadWizard/resources/mw.UploadWizardLicenseInput.js (modified) (history)

Diff [purge]

Index: trunk/extensions/UploadWizard/UploadWizard.config.php
@@ -278,8 +278,8 @@
279279 ),
280280 array(
281281 'head' => 'mwe-upwiz-license-custom-head',
 282+ 'special' => 'custom',
282283 'licenses' => array( 'custom' ),
283 - 'prependTemplates' => array( 'custom-badness' )
284284 ),
285285 ),
286286 'defaults' => array( 'none' ),
@@ -330,6 +330,12 @@
331331 // Max number of uploads for a given form
332332 'maxUploads' => 50,
333333
 334+ // Minimum length of custom wikitext for a license, if used
 335+ 'minCustomLicenseLength' => 6,
 336+
 337+ // Maximum length of custom wikitext for a license
 338+ 'maxCustomLicenseLength' => 10000,
 339+
334340 // not for use with all wikis.
335341 // The ISO 639 code for the language tagalog is "tl".
336342 // Normally we name templates for languages by the ISO 639 code.
Index: trunk/extensions/UploadWizard/UploadWizardHooks.php
@@ -293,6 +293,10 @@
294294 'mwe-upwiz-error-title-fileexists-shared-forbidden',
295295 'mwe-upwiz-error-title-double-apostrophe',
296296 'mwe-upwiz-error-title-extension',
 297+ 'mwe-upwiz-error-license-wikitext-missing',
 298+ 'mwe-upwiz-error-license-wikitext-too-short',
 299+ 'mwe-upwiz-error-license-wikitext-too-long',
 300+ 'mwe-upwiz-error-license-wikitext-invalid',
297301 'mwe-upwiz-details-error-count',
298302 'mwe-upwiz-license-cc-by-sa-3.0',
299303 'mwe-upwiz-license-cc-by-sa-3.0-at',
Index: trunk/extensions/UploadWizard/UploadWizard.i18n.php
@@ -208,6 +208,10 @@
209209 'mwe-upwiz-error-title-fileexists-shared-forbidden' => 'This title is reserved by a file on a remote shared repository. Choose another name.',
210210 'mwe-upwiz-error-title-double-apostrophe' => 'This title contains a double apostrophe; please remove it.',
211211 'mwe-upwiz-error-title-extension' => 'You do not need to add a file extension. Just make a human readable title and the application will take care of the rest.',
 212+ 'mwe-upwiz-error-license-wikitext-missing' => 'You selected an option which requires you to enter wikitext.',
 213+ 'mwe-upwiz-error-license-wikitext-too-short' => 'The wikitext here is too short to be a license',
 214+ 'mwe-upwiz-error-license-wikitext-too-long' => 'The wikitext you entered is too long.',
 215+ 'mwe-upwiz-error-license-wikitext-invalid' => 'This does not seem to be valid wikitext, or does not contain a license.',
212216 'mwe-upwiz-details-error-count' => 'There {{PLURAL:$1|is one error|are $1 errors}} with the {{PLURAL:$2|form|forms}} above. Correct the errors, and try submitting again.',
213217
214218 /* LICENSES & combinations of licenses */
Index: trunk/extensions/UploadWizard/resources/mw.UploadWizardLicenseInput.js
@@ -28,7 +28,7 @@
2929 }
3030
3131 _this.$selector = $j( selector );
32 - _this.$selector.append( $j( '<div class="mwe-error"></div>' ) );
 32+ _this.$selector.append( $j( '<div class="mwe-error mwe-error-main"></div>' ) );
3333
3434 _this.type = config.type === 'or' ? 'radio' : 'checkbox';
3535
@@ -37,14 +37,64 @@
3838 mw.UploadWizardLicenseInput.prototype.count++;
3939 _this.name = 'license' + mw.UploadWizardLicenseInput.prototype.count;
4040
41 -
 41+ // the jquery wrapped inputs (checkboxes or radio buttons) for this licenseInput.
 42+ _this.inputs = [];
 43+
 44+ // create inputs and licenses from config
 45+ if ( mw.isDefined( config['licenseGroups'] ) ) {
 46+ _this.createGroupedInputs( _this.$selector, config['licenseGroups'] );
 47+ } else {
 48+ _this.createInputs( _this.$selector, config );
 49+ }
 50+
 51+ // set values of the whole license input
 52+ if ( values ) {
 53+ _this.setValues( values );
 54+ }
 55+
 56+ return _this;
 57+};
 58+
 59+mw.UploadWizardLicenseInput.prototype = {
 60+ count: 0,
 61+
4262 /**
43 - * Define the licenses this input will show:
 63+ * Creates the license input interface in toggleable groups.
 64+ * @param jQuery selector
 65+ * @param license input configuration groups
4466 */
45 - _this.licenses = [];
46 - _this.inputs = [];
 67+ createGroupedInputs: function( $el, configGroups ) {
 68+ var _this = this;
 69+ $j.each( configGroups, function( i, group ) {
 70+ var $body, $toggler;
 71+ var $group = $j( '<div></div>' ).addClass( 'mwe-upwiz-deed-license-group' );
 72+ if ( mw.isDefined( group['head'] ) ) {
 73+ // if there is a header, make a toggle-to-expand div and append inputs there.
 74+ var $head = $j( '<div></div>' ).append(
 75+ $j( '<a>' )
 76+ .addClass( 'mwe-upwiz-deed-license-group-head mwe-upwiz-toggler' )
 77+ .msg( group.head, _this.count )
 78+ );
 79+ $body = $j( '<div></div>' ).addClass( 'mwe-upwiz-toggler-content' ).css( { 'marginBottom': '1em' } );
 80+ $toggler = $group.append( $head, $body ).collapseToggle();
 81+ } else {
 82+ // if there is no header, just append licenses to the group div.
 83+ $body = $group;
 84+ }
 85+ if ( mw.isDefined( group['subhead'] ) ) {
 86+ $body.append( $j( '<div></div>' ).addClass( 'mwe-upwiz-deed-license-group-subhead' ).msg( group.subhead, _this.count ) );
 87+ }
 88+ var $licensesDiv = $j( '<div></div>' ).addClass( 'mwe-upwiz-deed-license' );
 89+ _this.createInputs( $licensesDiv, group, $toggler );
 90+ $body.append( $licensesDiv );
 91+ _this.$selector.append( $group );
 92+ } );
 93+ },
 94+
4795 /**
48 - * append defined license inputs to element; also records licenses and inputs in _this
 96+ * append defined license inputs to element.
 97+ * SIDE EFFECT: also records licenses and inputs in _this
 98+ *
4999 * Abstracts out simple lists of licenses, more complex groups with layout
50100 * @param {jQuery} selector to add inputs to
51101 * @param {Array} license configuration, which must have a 'licenses' property, which is an array of license names
@@ -57,22 +107,22 @@
58108 * methods in its data.
59109 *
60110 */
61 - function appendLicenses( $el, config, groupToggler ) {
62 -
 111+ createInputs: function( $el, config, $groupToggler ) {
 112+ var _this = this;
63113 if ( !mw.isDefined( config['licenses'] && typeof config['licenses'] === 'object' ) ) {
64114 throw new Error( "improper license config" );
65115 }
66116 $j.each( config['licenses'], function( i, licenseName ) {
67117 if ( mw.isDefined( mw.UploadWizard.config.licenses[licenseName] ) ) {
68118 var license = { name: licenseName, props: mw.UploadWizard.config.licenses[licenseName] };
69 - _this.licenses.push( license );
70119
71 - var templates = _this.getTemplatesForLicense( license );
 120+ var templates = mw.isDefined( license.props['templates'] ) ? license.props.templates.slice(0) : [ license.name ];
72121
73 - var $input = _this.getInputElement( templates, config );
 122+ var $input = _this.createInputElement( templates, config );
74123 _this.inputs.push( $input );
75124
76 - var $label = _this.getInputElementLabel( license, $input );
 125+ var $label = _this.createInputElementLabel( license, $input );
 126+
77127 $el.append( $input, $label, $j( '<br/>' ) );
78128 // TODO add popup help?
79129
@@ -80,56 +130,123 @@
81131 $input.data( 'licenseName', licenseName );
82132
83133 // this is so if a single input in a group changes, we open the entire "toggler" that was hiding them
84 - $input.data( 'groupToggler', groupToggler );
 134+ $input.data( 'groupToggler', $groupToggler );
85135
86 - if ( licenseName === 'custom' ) {
87 - $el.append( _this.getInputElementRelatedTextarea( $input ) );
 136+ if ( config['special'] === 'custom' ) {
 137+ var $customDiv = _this.createCustomWikiTextInterface( $input );
 138+ $el.append( $customDiv );
 139+ $input.data( 'textarea', $customDiv.find( 'textarea' ) );
88140 }
89141 }
90142 } );
91 - }
92 -
 143+ },
93144
94 - if ( mw.isDefined( config['licenseGroups'] ) ) {
95 - $j.each( config['licenseGroups'], function( i, group ) {
96 - var toggler;
97 - var $group = $j( '<div></div>' ).addClass( 'mwe-upwiz-deed-license-group' );
98 - // if there is no header, just append licenses to the group div.
99 - var $body = $group;
100 - // if there is a header, make a toggle-to-expand div and append to that instead.
101 - if ( mw.isDefined( group['head'] ) ) {
102 - var $head = $j( '<div></div>' ).append(
103 - $j( '<a>' )
104 - .addClass( 'mwe-upwiz-deed-license-group-head mwe-upwiz-toggler' )
105 - .msg( group.head, _this.count )
106 - );
107 - $body = $j( '<div></div>' ).addClass( 'mwe-upwiz-toggler-content' ).css( { 'marginBottom': '1em' } );
108 - toggler = $group.append( $head, $body ).collapseToggle();
109 - }
110 - if ( mw.isDefined( group['subhead'] ) ) {
111 - $body.append( $j( '<div></div>' ).addClass( 'mwe-upwiz-deed-license-group-subhead' ).msg( group.subhead, _this.count ) );
112 - }
113 - var $licensesDiv = $j( '<div></div>' ).addClass( 'mwe-upwiz-deed-license' );
114 - appendLicenses( $licensesDiv, group, toggler );
115 - $body.append( $licensesDiv );
116 - _this.$selector.append( $group );
 145+ /**
 146+ * License templates are these abstract ideas like cc-by-sa. In general they map directly to a license template.
 147+ * However, configuration for a particular option can add other templates or transform the templates,
 148+ * such as wrapping templates in an outer "self" template for own-work
 149+ * @param {Array} of license template names
 150+ * @param {Object}, license input configuration
 151+ * @return {String} of wikitext
 152+ */
 153+ createInputValueFromTemplateConfig: function( templates, config ) {
 154+ if ( mw.isDefined( config['prependTemplates'] ) ) {
 155+ $j.each( config['prependTemplates'], function( i, template ) {
 156+ templates.unshift( template );
 157+ } );
 158+ }
 159+ if ( mw.isDefined( config['filterTemplate'] ) ) {
 160+ templates.unshift( config['filterTemplate'] );
 161+ templates = [ templates.join( '|' ) ];
 162+ }
 163+ var wikiTexts = $j.map( templates, function(t) { return '{{' + t + '}}'; } );
 164+ return wikiTexts.join( '' );
 165+ },
 166+
 167+ /**
 168+ * Return a radio button or checkbox with appropriate values, depending on config
 169+ * @param {Array} of template strings
 170+ * @param {Object} config for this license input
 171+ * @return {jQuery} wrapped input
 172+ */
 173+ createInputElement: function( templates, config ) {
 174+ var _this = this;
 175+
 176+ var attrs = {
 177+ id: _this.name + '_' + _this.inputs.length, // unique id
 178+ name: _this.name, // name of input, shared among all checkboxes or radio buttons.
 179+ type: _this.type, // kind of input
 180+ value: _this.createInputValueFromTemplateConfig( templates, config )
 181+ };
 182+
 183+ var inputHtml = '<input ' +
 184+ $j.map( attrs, function(val, key) {
 185+ return key + '="' + val.toString().replace( '"', '' ) + '"';
 186+ } ).join( " " )
 187+ + ' />';
 188+
 189+ // Note we aren't using $('<input>').attr( { ... } ) . We construct a string of HTML.
 190+ // IE6 is idiotic about radio buttons; you have to create them as HTML or clicks aren't recorded
 191+ return $j( inputHtml ).click( function() {
 192+ _this.$selector.trigger( 'changeLicenses' );
117193 } );
 194+ },
118195
 196+ /**
 197+ * Get a label for the form element
 198+ * @param {Object} license definition from global config. Will tell us the messages, and maybe icons.
 199+ * @param {jQuery} wrapped input
 200+ * @return {jQuery} wrapped label referring to that input, with appropriate HTML, decorations, etc.
 201+ */
 202+ createInputElementLabel: function( license, $input ) {
 203+ var messageKey = mw.isDefined( license.props['msg'] ) ? license.props.msg : '[missing msg for ' + license.name + ']';
 204+ var $icons = $j( '<span></span>' );
 205+ if ( mw.isDefined( license.props['icons'] ) ) {
 206+ $j.each( license.props.icons, function( i, icon ) {
 207+ $icons.append( $j( '<span></span>' ).addClass( 'mwe-upwiz-license-icon mwe-upwiz-' + icon + '-icon' ) );
 208+ } );
 209+ }
 210+ return $j( '<label />' )
 211+ .attr( { 'for': $input.attr('id') } )
 212+ .msg( messageKey, this.count )
 213+ .append( $icons );
 214+ },
119215
120 - } else {
121 - appendLicenses( _this.$selector, config );
122 - }
 216+ /**
 217+ * Given an input, return another textarea to be appended below.
 218+ * When text entered here, auto-selects the input.
 219+ * @param {jQuery} wrapped input
 220+ * @return {jQuery} wrapped textarea
 221+ */
 222+ createCustomWikiTextInterface: function( $input ) {
 223+ var _this = this;
123224
124 - if ( values ) {
125 - _this.setValues( values );
126 - }
 225+ var nameId = $input.attr( 'id' ) + '_custom';
 226+ var $textarea = $j( '<textarea></textarea>' )
 227+ .attr( { id: nameId, name: nameId } )
 228+ .growTextArea()
 229+ .focus( function() { _this.setInput( $input, true ); } )
 230+ .keydown( function() { _this.$selector.trigger( 'changeLicenses' ); } )
 231+ .css( {
 232+ 'width': '100%',
 233+ 'font-family': 'monospace'
 234+ } );
127235
128 - return _this;
129 -};
 236+ var $button = $j( '<span></span>' )
 237+ .button( { label: gM( 'mwe-upwiz-license-custom-preview' ) } )
 238+ .css( { 'width': '8em' } )
 239+ .click( function() { _this.showPreview( $textarea.val() ); } );
130240
131 -mw.UploadWizardLicenseInput.prototype = {
132 - count: 0,
 241+ return $j( '<div></div>' ).css( { 'width': '100%' } ).append(
 242+ $j( '<div><label for="' + nameId + '" class="mwe-error mwe-error-textarea"></label></div>' ),
 243+ $j( '<div></div>' ).css( { 'float': 'right', 'width': '9em', 'padding-left': '1em' } ).append( $button ),
 244+ $j( '<div></div>' ).css( { 'margin-right': '10em' } ).append( $textarea ),
 245+ $j( '<div></div>' ).css( { 'clear':'both' } )
 246+ );
 247+ },
133248
 249+ /* ---- end creational stuff ----- */
 250+
134251 // Set the input value. If it is part of a group, and this is being turned on, pop open the group so we can see this input.
135252 setInput: function( $input, val ) {
136253 var _this = this;
@@ -140,6 +257,7 @@
141258 if ( bool !== oldVal ) {
142259 _this.$selector.trigger( 'changeLicenses' );
143260 }
 261+
144262 // pop open the 'toggle' group if is now on. Do nothing if it is now off.
145263 if ( bool && $input.data( 'groupToggler' ) ) {
146264 $input.data( 'groupToggler' ).data( 'open' )();
@@ -215,23 +333,40 @@
216334 },
217335
218336 /**
219 - * Gets the wikitext associated with all selected inputs.
220 - * Anything from a text input is automatically suspect, because it might not be valid. So we append a template to double check.
221 - * This is a bit of a hack, in the ideal case we'd make all the inputs into objects that returned their own wikitext, but
222 - * it is easier to extend the current interface (which assumes form input value is all we want).
 337+ * Gets the wikitext associated with all selected inputs. Some inputs also have associated textareas so we append their contents too.
223338 * @return string of wikitext (empty string if no inputs set)
224 - */
 339+ */
225340 getWikiText: function() {
 341+ var _this = this;
226342 var wikiTexts = this.getSelectedInputs().map(
227343 function() {
228 - return this.val() + "\n";
229 - }
 344+ return _this.getInputWikiText( this );
 345+ }
230346 );
231347 // need to use makeArray because a jQuery-returned set of things won't have .join
232348 return $j.makeArray( wikiTexts ).join( '' );
233349 },
234350
235351 /**
 352+ * Get the value of a particular input
 353+ */
 354+ getInputWikiText: function( $input) {
 355+ return $input.val() + "\n" + this.getInputTextAreaVal($input);
 356+ },
 357+
 358+ /**
 359+ * Get the value of the associated textarea, if any
 360+ * @return {String}
 361+ */
 362+ getInputTextAreaVal: function( $input ) {
 363+ var extra = '';
 364+ if ( $input.data( 'textarea' ) ) {
 365+ extra = $j.trim( $input.data( 'textarea' ).val() );
 366+ }
 367+ return extra;
 368+ },
 369+
 370+ /**
236371 * Gets which inputs have user-entered values
237372 * @return {jQuery Array} of inputs
238373 */
@@ -242,31 +377,67 @@
243378
244379 /**
245380 * Check if a valid value is set, also look for incompatible choices.
246 - * Side effect: if no valid value, add notes to the interface. Add listeners to interface, to revalidate and remove notes.
247 - * @return boolean; true if a value set, false otherwise
 381+ * Side effect: if no valid value, add error notices to the interface. Add listeners to interface, to revalidate and remove notices
 382+ * If I was sufficiently clever, most of these could just be dynamically added & subtracted validation rules.
 383+ * Instead this is a bit of a recapitulation of jquery.validate
 384+ * @return boolean; true if a value set and all is well, false otherwise
248385 */
249386 valid: function() {
250387 var _this = this;
251 - var isValid = true;
252388
253 - if ( ! _this.isSet() ) {
254 - isValid = false;
255 - errorHtml = gM( 'mwe-upwiz-deeds-need-license' );
 389+ var errors = [];
 390+
 391+ var selectedInputs = this.getSelectedInputs();
 392+
 393+ if ( selectedInputs.length === 0 ) {
 394+ errors.push( [ this.$selector.find( '.mwe-error-head' ), 'mwe-upwiz-deeds-need-license' ] );
 395+
 396+ } else {
 397+ // It's pretty hard to screw up a radio button, so if even one of them is selected it's okay.
 398+ // But also check that associated textareas are filled for if the input is selected, and that
 399+ // they are the appropriate size.
 400+ $j.each( selectedInputs, function(i, $input) {
 401+ if ( ! $input.data( 'textarea' ) ) {
 402+ return;
 403+ }
 404+
 405+ var textAreaName = $input.data( 'textarea' ).attr( 'name' );
 406+ var $errorEl = $( 'label[for=' + textAreaName + '].mwe-error' );
 407+
 408+ var text = _this.getInputTextAreaVal( $input );
 409+
 410+ if ( text === '' ) {
 411+ errors.push( [ $errorEl, 'mwe-upwiz-error-license-wikitext-missing' ] );
 412+ } else if ( text.length < mw.UploadWizard.config.minCustomLicenseLength ) {
 413+ errors.push( [ $errorEl, 'mwe-upwiz-error-license-wikitext-too-short' ] );
 414+ } else if ( text.length > mw.UploadWizard.config.maxCustomLicenseLength ) {
 415+ errors.push( [ $errorEl, 'mwe-upwiz-error-license-wikitext-too-long' ] );
 416+ } else if ( !_this.validateWikiText( text ) ) {
 417+ errors.push( [ $errorEl, 'mwe-upwiz-error-license-wikitext-invalid' ] );
 418+ }
 419+
 420+ } );
256421 }
 422+
 423+ // clear out the errors if we are now valid
 424+ if ( errors.length === 0 ) {
 425+ this.$selector.find( '.mwe-error' ).fadeOut();
 426+ } else {
 427+ // show the errors
 428+ $j.each( errors, function( i, err ) {
 429+ var $el = err[0],
 430+ msg = err[1];
 431+ $el.msg( msg ).show();
 432+ } );
257433
258 - var $errorEl = this.$selector.find( '.mwe-error' );
259 - if (isValid) {
260 - $errorEl.fadeOut();
261 - } else {
262 - // we bind to $selector because unbind() doesn't work on non-DOM objects
 434+ // and watch for any change at all in the license to revalidate.
263435 _this.$selector.bind( 'changeLicenses.valid', function() {
264436 _this.$selector.unbind( 'changeLicenses.valid' );
265437 _this.valid();
266 - } );
267 - $errorEl.html( errorHtml ).show();
 438+ } );
268439 }
269440
270 - return isValid;
 441+ return errors.length === 0;
271442 },
272443
273444
@@ -278,120 +449,76 @@
279450 return this.getSelectedInputs().length > 0;
280451 },
281452
 453+
282454 /**
283 - * Given a license name, return template names
284 - * Note we return copies, so as not to perturb the configuration itself
285 - * @param {String} license name
286 - * @return {Array} of strings of template names
 455+ * Attempt to determine if wikitext parses... and maybe does it contain a license tag
 456+ * @return boolean
287457 */
288 - getTemplatesForLicense: function( license ) {
289 - return mw.isDefined( license.props['templates'] ) ? license.props.templates.slice(0) : [ license.name ];
290 - },
 458+ validateWikiText: function( text ) {
 459+ var parser = new mw.language.parser(),
 460+ _this = this,
 461+ ast;
291462
292 - /**
293 - * License templates are these abstract ideas like cc-by-sa. In general they map directly to a license template.
294 - * However, configuration for a particular option can add other templates or transform the templates,
295 - * such as wrapping templates in an outer "self" template for own-work
296 - * @param {Array} of license template names
297 - * @param {Object}, license input configuration
298 - * @return {String} of wikitext
299 - */
300 - getWikiTextForTemplates: function( templates, config ) {
301 - if ( mw.isDefined( config['prependTemplates'] ) ) {
302 - $j.each( config['prependTemplates'], function( i, template ) {
303 - templates.unshift( template );
304 - } );
 463+ try {
 464+ ast = parser.wikiTextToAst( text );
 465+ } catch (e) {
 466+ return false;
305467 }
306 - if ( mw.isDefined( config['filterTemplate'] ) ) {
307 - templates.unshift( config['filterTemplate'] );
308 - templates = [ templates.join( '|' ) ];
 468+
 469+ function accumTemplates( node, templates ) {
 470+ if ( typeof node === 'object' ) {
 471+ var operation = node[0].toLowerCase();
 472+ if ( typeof mw.language.htmlEmitter.prototype[operation] !== 'function' ) {
 473+ templates.push( operation );
 474+ }
 475+ $j.map( node.slice( 1 ), function( n ) {
 476+ accumTemplates( n, templates );
 477+ } );
 478+ }
309479 }
310 - return $j.map( templates, function(t) { return '{{' + t + '}}'; } ).join( '' );
311 - },
 480+ var templates = [];
 481+ accumTemplates( ast, templates );
312482
313 - /**
314 - * Return a radio button or checkbox with appropriate values, depending on config
315 - * @param {Array} of template strings
316 - * @param {Object} config for this license input
317 - * @return {jQuery} wrapped input
318 - */
319 - getInputElement: function( templates, config ) {
320 - var _this = this;
321 -
322 - var attrs = {
323 - id: _this.name + '_' + _this.inputs.length, // unique id
324 - name: _this.name, // name of input, shared among all checkboxes or radio buttons.
325 - type: _this.type, // kind of input
326 - value: _this.getWikiTextForTemplates( templates, config )
327 - };
 483+ var topCat = new mw.Title( 'License tags', 'category' );
328484
329 - var inputHtml = '<input ' +
330 - $j.map( attrs, function(val, key) {
331 - return key + '="' + val.toString().replace( '"', '' ) + '"';
332 - } ).join( " " )
333 - + ' />';
334 -
335 - // Note we aren't using $('<input>').attr( { ... } ) . We construct a string of HTML.
336 - // IE6 is idiotic about radio buttons; you have to create them as HTML or clicks aren't recorded
337 - return $j( inputHtml ).click( function() {
338 - _this.$selector.trigger( 'changeLicenses' );
 485+ var found = false;
 486+ function recurseCategories( desiredCatTitle, title, depthToContinue ) {
 487+ if ( depthToContinue === 0 ) {
 488+ return;
 489+ }
 490+ var ok = function(cats) {
 491+ if ( cats !== false ) {
 492+ $.each( cats, function( i, catTitle ) {
 493+ if ( catTitle.getNameText() === desiredCatTitle.getNameText() ) {
 494+ found = true;
 495+ return false;
 496+ }
 497+ recurseCategories( desiredCatTitle, catTitle, depthToContinue - 1 );
 498+ return true;
 499+ } );
 500+ }
 501+ };
 502+ var err = function() {};
 503+ // this proceeds synchronously, so we pick up in the next line
 504+ _this.api.getCategories( title, ok, err, false );
 505+ }
 506+
 507+ $.each( templates, function( i, t ) {
 508+ var title = new mw.Title( t, 'template' );
 509+ recurseCategories( topCat, title, 5 );
 510+ if ( found ) {
 511+ return false;
 512+ }
339513 } );
 514+
 515+ return found;
340516 },
341517
342518 /**
343 - * Get a label for the form element
344 - * @param {Object} license definition from global config. Will tell us the messages, and maybe icons.
345 - * @param {jQuery} wrapped input
346 - * @return {jQuery} wrapped label referring to that input, with appropriate HTML, decorations, etc.
 519+ * Preview license from a particular input, in a popup window
 520+ * @param {jQuery} an input
347521 */
348 - getInputElementLabel: function( license, $input ) {
349 - var messageKey = mw.isDefined( license.props['msg'] ) ? license.props.msg : '[missing msg for ' + license.name + ']';
350 - var $icons = $j( '<span></span>' );
351 - if ( mw.isDefined( license.props['icons'] ) ) {
352 - $j.each( license.props.icons, function( i, icon ) {
353 - $icons.append( $j( '<span></span>' ).addClass( 'mwe-upwiz-license-icon mwe-upwiz-' + icon + '-icon' ) );
354 - } );
355 - }
356 - return $j( '<label />' )
357 - .attr( { 'for': $input.attr('id') } )
358 - .msg( messageKey, this.count )
359 - .append( $icons );
360 - },
361 -
362 - /**
363 - * Given an input, return another textarea to be appended below.
364 - * When text entered here, auto-selects the input.
365 - * @param {jQuery} wrapped input
366 - * @return {jQuery} wrapped textarea
367 - */
368 - getInputElementRelatedTextarea: function( $input ) {
369 - var _this = this;
370 -
371 - var $textarea = $j( '<textarea></textarea>' )
372 - .attr( { id: $input.attr( 'id' ) + '_custom' } )
373 - .growTextArea()
374 - .focus( function() { _this.setInput( $input, true ); } )
375 - .css( {
376 - 'width': '100%',
377 - 'font-family': 'monospace'
378 - } );
379 -
380 - var $button = $j( '<span></span>' )
381 - .button( { label: gM( 'mwe-upwiz-license-custom-preview' ) } )
382 - .css( { 'width': '8em' } )
383 - .click( function() { _this.showPreview( $textarea.val() ); } );
384 -
385 - return $j( '<div></div>' ).css( { 'width': '100%' } ).append(
386 - $j( '<div></div>' ).css( { 'float': 'right', 'width': '9em', 'padding-left': '1em' } ).append( $button ),
387 - $j( '<div></div>' ).css( { 'margin-right': '10em' } ).append( $textarea ),
388 - $j( '<div></div>' ).css( { 'clear':'both' } )
389 - );
390 - },
391 -
392 - /**
393 - * Preview license
394 - */
395 - showPreview: function() {
 522+ showPreview: function( $input ) {
396523 // do stuff with this.api
397524 }
398525

Follow-up revisions

RevisionCommit summaryAuthorDate
r102731add documentation to configneilk01:28, 11 November 2011

Comments

#Comment by Siebrand (talk | contribs)   09:38, 2 November 2011

Please add message documentation for the newly added messages. Thanks.

#Comment by Raindrift (talk | contribs)   18:58, 10 November 2011
'minCustomLicenseLength' => 6,

Please explain that in more detail. Why 6?

#Comment by NeilK (talk | contribs)   01:29, 11 November 2011

Status & tagging log