From 8c27921232e13e086c2b65459092b7ddb13d3e0b Mon Sep 17 00:00:00 2001 From: Moriel Schottlender Date: Fri, 20 Sep 2013 20:46:02 -0700 Subject: [PATCH] Implement edit interface for TemplateData documentation This adds a TemplateData manager to the edit page in the Template namespace. If a tag already exists, the tool will parse it and display it in a visual interface that the user can then manipulate in their own language. If there is no tag, the dialog will appear empty and allow users to add and tweak their desired template parameter data. The tool also allows rudimentary parameter import, which picks up the parameters from a current template into the GUI to make the user's life easier when producing TemplateData. The template documentation editor feature is off by default. To enable it use $wgTemplateDataUseGUI = true in LocalSettings. Bug: 50436 Change-Id: I863a8199c0b08cc52b320ed00dcba912dd2aeefc --- .jshintrc | 3 +- TemplateData.hooks.php | 33 + TemplateData.i18n.php | 84 +- TemplateData.php | 62 ++ modules/ext.templateDataGenerator.core.js | 836 ++++++++++++++++++ modules/ext.templateDataGenerator.css | 87 ++ modules/ext.templateDataGenerator.editPage.js | 43 + tests/ext.templateData.tests.js | 269 ++++++ 8 files changed, 1414 insertions(+), 3 deletions(-) create mode 100644 modules/ext.templateDataGenerator.core.js create mode 100644 modules/ext.templateDataGenerator.css create mode 100644 modules/ext.templateDataGenerator.editPage.js create mode 100644 tests/ext.templateData.tests.js diff --git a/.jshintrc b/.jshintrc index 13f645b3..c9e9f825 100644 --- a/.jshintrc +++ b/.jshintrc @@ -27,6 +27,7 @@ "predef": [ "mediaWiki", - "jQuery" + "jQuery", + "QUnit" ] } diff --git a/TemplateData.hooks.php b/TemplateData.hooks.php index f26056ba..1ceb025b 100644 --- a/TemplateData.hooks.php +++ b/TemplateData.hooks.php @@ -25,6 +25,22 @@ class TemplateDataHooks { return true; } + /** + * Register qunit unit tests + */ + public static function onResourceLoaderTestModules( + array &$testModules, + ResourceLoader &$resourceLoader + ) { + $testModules['qunit']['ext.templateData.test'] = array( + 'scripts' => array( 'tests/ext.templateData.tests.js' ), + 'dependencies' => array( 'ext.templateDataGenerator.core' ), + 'localBasePath' => __DIR__ , + 'remoteExtPath' => 'TemplateData', + ); + return true; + } + /** * @param Page &$page * @param User &$user @@ -65,6 +81,23 @@ class TemplateDataHooks { return true; } + /** + * Parser hook registering the GUI module only in edit pages. + * + * @param EditPage $editPage + * @param OutputPage $output + * @return bool + */ + public static function onEditPage( $editPage, $output ) { + global $wgTemplateDataUseGUI; + if ( $wgTemplateDataUseGUI ) { + if ( $output->getTitle()->getNamespace() === NS_TEMPLATE ) { + $output->addModules( 'ext.templateDataGenerator.editPage' ); + } + } + return true; + } + /** * Parser hook for . * If there is any JSON provided, render the template documentation on the page. diff --git a/TemplateData.i18n.php b/TemplateData.i18n.php index 689d7bf8..8e551a9f 100644 --- a/TemplateData.i18n.php +++ b/TemplateData.i18n.php @@ -32,6 +32,34 @@ $messages['en'] = array( 'templatedata-invalid-unknown' => 'Unexpected property "$1".', 'templatedata-invalid-value' => 'Invalid value for property "$1".', 'templatedata-invalid-length' => 'Data too large to save ({{formatnum:$1}} {{PLURAL:$1|byte|bytes}}, {{PLURAL:$2|limit is}} {{formatnum:$2}})', + + // TemplateData generator GUI: + 'templatedata-editbutton' => 'Manage template documentation', + 'templatedata-errormsg-jsonbadformat' => 'Bad JSON format. Either correct it, or delete the current tags and try again.', + 'templatedata-modal-button-addparam' => 'Add parameter', + 'templatedata-modal-button-apply' => 'Apply', + 'templatedata-modal-button-cancel' => 'Cancel', + 'templatedata-modal-button-delparam' => 'Delete parameter', + 'templatedata-modal-button-importParams' => 'Import parameters', + 'templatedata-modal-errormsg' => 'Errors found. Please make sure there are no empty or duplicate parameter names, and that the parameter name does not include "|", "=" or "}}".', + 'templatedata-modal-errormsg-import-noparams' => 'No new parameters found during import.', + 'templatedata-modal-notice-import-numparams' => '$1 new {{PLURAL:$1|parameter was|parameters were}} imported.', + 'templatedata-modal-table-param-actions' => 'Actions', + 'templatedata-modal-table-param-aliases' => 'Aliases (comma separated)', + 'templatedata-modal-table-param-default' => 'Default', + 'templatedata-modal-table-param-desc' => 'Description', + 'templatedata-modal-table-param-label' => 'Label', + 'templatedata-modal-table-param-name' => 'Name', + 'templatedata-modal-table-param-required' => 'Required', + 'templatedata-modal-table-param-type' => 'Type', + 'templatedata-modal-table-param-type-number' => 'Number', + 'templatedata-modal-table-param-type-page' => 'Page', + 'templatedata-modal-table-param-type-string' => 'String', + 'templatedata-modal-table-param-type-undefined' => 'Undefined', + 'templatedata-modal-table-param-type-user' => 'User', + 'templatedata-modal-title' => 'Template documentation editor', + 'templatedata-modal-title-templatedesc' => 'Template description', + 'templatedata-modal-title-templateparams' => 'Template parameters', ); /** Message documentation (Message documentation) @@ -91,6 +119,32 @@ $messages['qqq'] = array( 'templatedata-invalid-length' => "Error message when generated JSON's length exceed database limits. * $1 - length of generated JSON * $2 - maximal allowed length", + 'templatedata-editbutton' => 'The label of the button to manage templatedata, appearing above the editor field.', + 'templatedata-errormsg-jsonbadformat' => 'Error message that appears in case the JSON string is not possible to parse. The user is asked to either correct the json syntax or delete the values between the <templatedata> tags and try again.', + 'templatedata-modal-button-addparam' => 'Button to add a parameter', + 'templatedata-modal-button-apply' => 'Label of the apply button', + 'templatedata-modal-button-cancel' => 'Label of the cancel button', + 'templatedata-modal-button-delparam' => 'Button to delete a parameter', + 'templatedata-modal-button-importParams' => 'Label of the import button', + 'templatedata-modal-errormsg' => 'Error message that appears in the TemplateData generator GUI in case there are empty, duplicate or invalid parameter names', + 'templatedata-modal-errormsg-import-noparams' => 'message that appears in the TemplateData generator GUI in case no template parameters were found during the import attempt.', + 'templatedata-modal-notice-import-numparams' => 'message that appears in the TemplateData generator GUI showing how many new parameters were imported into the GUI from an existing template.', + 'templatedata-modal-table-param-actions' => 'Label for a table heading: Parameter actions in the table', + 'templatedata-modal-table-param-aliases' => 'Label for a table heading: Aliases of the parameter, instruct the user to separate aliases with commas.', + 'templatedata-modal-table-param-default' => 'Label for a table heading: Default value of the parameter', + 'templatedata-modal-table-param-desc' => 'Label for a table heading: Description of the parameter', + 'templatedata-modal-table-param-label' => 'Label for a table heading: Label of the parameter', + 'templatedata-modal-table-param-name' => 'Label for a table heading: Name of the parameter', + 'templatedata-modal-table-param-required' => 'Label for a table heading: Required status of the parameter', + 'templatedata-modal-table-param-type' => 'Label for a table heading: Type of the parameter', + 'templatedata-modal-table-param-type-number' => 'A possible parameter type: Number', + 'templatedata-modal-table-param-type-page' => 'A possible parameter type: Page', + 'templatedata-modal-table-param-type-string' => 'A possible parameter type: String', + 'templatedata-modal-table-param-type-undefined' => 'A possible parameter type: Undefined', + 'templatedata-modal-table-param-type-user' => 'A possible parameter type: User', + 'templatedata-modal-title' => 'Title of the modal popup.', + 'templatedata-modal-title-templatedesc' => 'The title for the template description textbox', + 'templatedata-modal-title-templateparams' => 'The title for the template parameters table', ); /** Arabic (العربية) @@ -493,12 +547,38 @@ $messages['he'] = array( 'templatedata-doc-param-desc-empty' => 'אין תיאור', 'templatedata-invalid-duplicate-value' => 'המאפיין "$1" (ערך: "$3") זהה למאפיין "$2".', 'templatedata-invalid-parse' => 'שגיאת תחביר ב־JSON.', - 'templatedata-invalid-type' => 'המאפיין "$1" צפוי להיות מסוג "$2".', + 'templatedata-invalid-type' => 'המאפיין "$1" אמור להיות מסוג "$2".', 'templatedata-invalid-missing' => 'המאפיין הדרוש "$1" לא נמצא.', 'templatedata-invalid-empty-array' => 'למאפיין "$1" צריך להיות לפחות ערך אחד במערך שלו.', 'templatedata-invalid-unknown' => 'מאפיין בלתי־צפוי "$1".', 'templatedata-invalid-value' => 'ערך בלתי־תקין למאפיין "$1".', - 'templatedata-invalid-length' => 'הנתונים גדולים מכדי לשמור ({{formatnum:$1}} {{PLURAL:$1|בית|בתים}}, {{PLURAL:$2|המגבלה היא}} {{formatnum:$2}})', + 'templatedata-invalid-length' => 'הנתונים גדולים מכדי לשמור ({{PLURAL:$1|בית אחד|{{formatnum:$1}} בתים}}, {{PLURAL:$2|המגבלה היא}} {{formatnum:$2}})', + 'templatedata-editbutton' => 'יצירת נתוני תבנית', + 'templatedata-errormsg-jsonbadformat' => 'JSON בלתי־תקין. נא לתקן אותו או למחוק את הטקסט בין תגי ולנסות שוב.', + 'templatedata-modal-errormsg' => 'נמצאו שגיאות. נא לוודא ששמות הפרמטרים אינם ריקים ואינם חוזרים על עצמם, ושבשמות הפרמטרים לא מופיעים התווים |, = או }}', + 'templatedata-modal-errormsg-import-noparams' => 'לא נמצאו פרמטרים חדשים בעת הייבוא', + 'templatedata-modal-notice-import-numparams' => '{{PLURAL:$1|יובא פרמטר חדש אחד|יובאו $1 פרמטרים חדשים}}', + 'templatedata-modal-title' => 'מחולל נתוני תבנית', + 'templatedata-modal-title-templatedesc' => 'תיאור התבנית', + 'templatedata-modal-title-templateparams' => 'פרמטרי תבנית', + 'templatedata-modal-table-param-name' => 'שם', + 'templatedata-modal-table-param-aliases' => 'כינויים (מופרדים בפסיק)', + 'templatedata-modal-table-param-label' => 'תווית', + 'templatedata-modal-table-param-desc' => 'תיאור', + 'templatedata-modal-table-param-type' => 'סוג', + 'templatedata-modal-table-param-type-undefined' => 'בלתי־מוגדר', + 'templatedata-modal-table-param-type-number' => 'מספר', + 'templatedata-modal-table-param-type-string' => 'מחרוזת', + 'templatedata-modal-table-param-type-user' => 'משתמש', + 'templatedata-modal-table-param-type-page' => 'דף', + 'templatedata-modal-table-param-default' => 'ערך התחלתי', + 'templatedata-modal-table-param-required' => 'נדרש', + 'templatedata-modal-table-param-actions' => 'פעולות', + 'templatedata-modal-button-addparam' => 'הוספת פרמטר', + 'templatedata-modal-button-delparam' => 'מחיקת פרמטר', + 'templatedata-modal-button-importParams' => 'ייבוא פרמטרים', + 'templatedata-modal-buttons-apply' => 'החלה', + 'templatedata-modal-buttons-cancel' => 'ביטול', ); /** Upper Sorbian (hornjoserbsce) diff --git a/TemplateData.php b/TemplateData.php index 7a6138dc..eb398d95 100644 --- a/TemplateData.php +++ b/TemplateData.php @@ -34,6 +34,8 @@ $wgAutoloadClasses['ApiTemplateData'] = $dir . '/api/ApiTemplateData.php'; $wgHooks['ParserFirstCallInit'][] = 'TemplateDataHooks::onParserFirstCallInit'; $wgHooks['PageContentSave'][] = 'TemplateDataHooks::onPageContentSave'; $wgHooks['UnitTestsList'][] = 'TemplateDataHooks::onUnitTestsList'; +$wgHooks['ResourceLoaderTestModules'][] = 'TemplateDataHooks::onResourceLoaderTestModules'; +$wgHooks['EditPage::showEditForm:initial'][] = 'TemplateDataHooks::onEditPage'; // Register APIs $wgAPIModules['templatedata'] = 'ApiTemplateData'; @@ -48,3 +50,63 @@ $wgResourceModules['ext.templateData'] = array( 'localBasePath' => $dir, 'remoteExtPath' => 'TemplateData', ); + +$wgResourceModules['ext.templateDataGenerator.editPage'] = array( + 'localBasePath' => $dir, + 'remoteExtPath' => 'TemplateData', + 'scripts' => array( + 'modules/ext.templateDataGenerator.editPage.js', + ), + 'dependencies' => array( + 'ext.templateDataGenerator.core', + ), + 'messages' => array( + 'templatedata-editbutton', + 'templatedata-errormsg-jsonbadformat', + ) +); + +$wgResourceModules['ext.templateDataGenerator.core'] = array( + 'localBasePath' => $dir, + 'remoteExtPath' => 'TemplateData', + 'styles' => 'modules/ext.templateDataGenerator.css', + 'scripts' => array( + 'modules/ext.templateDataGenerator.core.js', + ), + 'dependencies' => array( + 'jquery.ui.dialog', + 'jquery.ui.button', + ), + 'messages' => array( + 'templatedata-modal-button-addparam', + 'templatedata-modal-button-apply', + 'templatedata-modal-button-cancel', + 'templatedata-modal-button-delparam', + 'templatedata-modal-button-importParams', + 'templatedata-modal-errormsg', + 'templatedata-modal-errormsg-import-noparams', + 'templatedata-modal-notice-import-numparams', + 'templatedata-modal-table-param-actions', + 'templatedata-modal-table-param-aliases', + 'templatedata-modal-table-param-default', + 'templatedata-modal-table-param-desc', + 'templatedata-modal-table-param-label', + 'templatedata-modal-table-param-name', + 'templatedata-modal-table-param-required', + 'templatedata-modal-table-param-type', + 'templatedata-modal-table-param-type-number', + 'templatedata-modal-table-param-type-page', + 'templatedata-modal-table-param-type-string', + 'templatedata-modal-table-param-type-undefined', + 'templatedata-modal-table-param-type-user', + 'templatedata-modal-title', + 'templatedata-modal-title-templatedesc', + 'templatedata-modal-title-templateparams', + ) +); + +/* Configuration */ + +// Set this to true to use the template documentation +// editor feature +$wgTemplateDataUseGUI = false; diff --git a/modules/ext.templateDataGenerator.core.js b/modules/ext.templateDataGenerator.core.js new file mode 100644 index 00000000..d2e8fe61 --- /dev/null +++ b/modules/ext.templateDataGenerator.core.js @@ -0,0 +1,836 @@ +( function ( $, mw ) { + /** + * TemplateDataGenerator generates the JSON string for templatedata + * or reads existing templatedata string and allows for it to be edited + * with a visual modal GUI. + * + * @author Moriel Schottlender + */ + 'use strict'; + + mw.libs.templateDataGenerator = ( function () { + var paramBase, paramTypes, domObjects, curr; + + /* Private helper functions */ + + /** + * Show an error message in the main Edit screen + * + * @param {string} msg The message to display in the error box + */ + function showErrorEditPage( msg ) { + domObjects.$errorBox.text( msg ).show(); + } + + /** + * Helper function to clean up the aliases string-to-array + * + * @param {string} str Comma separated string + * @returns {string[]} Cleaned-up alias array + */ + function cleanupAliasArray( str ) { + return $.map( str.split( ',' ), function ( item ) { + if ( $.trim( item ).length > 0 ) { + return $.trim( item ); + } + } ); + } + + /** + * Show an error message in the GUI + * + * @param {string} msg The message to be displayed in the error box + */ + function showErrorModal( msg ) { + domObjects.$errorModalBox.text( msg ).show(); + } + + /** + * Create ` object + */ + function createTypeSelect( opts ) { + var op, + $sel = $( '' ) + }, + aliases: { + label: mw.msg( 'templatedata-modal-table-param-aliases' ), + dom: $( '' ) + }, + label: { + label: mw.msg( 'templatedata-modal-table-param-label' ), + dom: $( '' ) + }, + description: { + label: mw.msg( 'templatedata-modal-table-param-desc' ), + dom: $( '