Changes for page Create Application
Last modified by Ludovic Dubost on 2024/07/22 15:51
From version 8.1
edited by Ludovic Dubost
on 2024/07/22 15:51
on 2024/07/22 15:51
Change comment:
Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui/16.5.0]
To version 3.1
edited by Ludovic Dubost
on 2014/11/18 12:31
on 2014/11/18 12:31
Change comment:
Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui-6.3]
Summary
-
Page properties (2 modified, 0 added, 0 removed)
-
Objects (2 modified, 0 added, 0 removed)
Details
- Page properties
-
- Syntax
-
... ... @@ -1,1 +1,1 @@ 1 -XWiki 2. 11 +XWiki 2.0 - Content
-
... ... @@ -1,158 +1,102 @@ 1 -{{include reference="AppWithinMinutes. VelocityMacros"/}}1 +{{include reference="AppWithinMinutes.WizardStep"/}} 2 2 3 -{{velocity}} 4 -#if ($request.wizard == 'true') 5 - {{include reference="AppWithinMinutes.WizardStep"/}} 6 -#end 7 -{{/velocity}} 8 - 9 -{{template name="locationPicker_macros.vm" /}} 10 - 11 11 {{velocity output="false"}} 12 -#macro (showStep) 13 - #appWizardHeader('name') 14 - <div class="wizard-help"> 15 - <p> 16 - <strong>$services.localization.render('platform.appwithinminutes.wizardStepHelpTitle')</strong> 17 - $services.localization.render('platform.appwithinminutes.wizardStepHelpDescription') 18 - </p> 19 - <ul class="steps vertical"> 20 - #foreach($step in $awmSteps) 21 - <li> 22 - <span class="btn btn-xs number">$mathtool.add($foreach.index, 1)</span> 23 - <span class="name">$services.localization.render("appWithinMinutes.wizardStep.${step}.name")</span> 24 - <span class="description">$services.localization.render("appWithinMinutes.wizardStep.${step}.description")</span> 25 - </li> 26 - #end 27 - </ul> 28 - </div> 29 - <form action="$doc.getURL()" method="post" class="xform wizard-body"> 30 - <fieldset> 31 - #locationPicker({ 32 - 'id': 'app', 33 - 'title': { 34 - 'label': 'platform.appwithinminutes.appNameLabel', 35 - 'hint': 'platform.appwithinminutes.appNameHint', 36 - 'name': 'appName' 37 - }, 38 - 'preview': { 39 - 'label': 'appWithinMinutes.createApp.location.label', 40 - 'hint': 'appWithinMinutes.createApp.location.hint' 41 - }, 42 - 'parent': { 43 - 'label': 'appWithinMinutes.createApp.parent.label', 44 - 'hint': 'appWithinMinutes.createApp.parent.hint', 45 - 'name': 'appParentReference', 46 - 'reference': $doc.documentReference.wikiReference, 47 - 'placeholder': 'appWithinMinutes.createApp.parent.placeholder' 48 - } 49 - }) 50 - <div class="appName-preview"></div> 51 - #appWizardFooter(1) 52 - </fieldset> 4 +#macro(showStep) 5 + {{html wiki="true"}} 6 + #appWizardHeader(1) 7 + (% class="wizard-help" %) 8 + ((( 9 + **$services.localization.render('platform.appwithinminutes.wizardStepHelpTitle')** $services.localization.render('platform.appwithinminutes.wizardStepHelpDescription') 10 + (% class="steps vertical" %) 11 + #foreach($index in [1, 2, 3]) 12 + * (% class="number" %)$index(%%) (% class="name" %)$services.localization.render("platform.appwithinminutes.wizardStep${index}Name")(%%) 13 + (% class="description" %)$services.localization.render("platform.appwithinminutes.wizardStep${index}Description") 14 + #end 15 + ))) 16 + <form action="" method="post" class="xform wizard-body"> 17 + ; <label for="appName">$services.localization.render('platform.appwithinminutes.appNameLabel')</label> 18 + (% class="xHint" %)$services.localization.render('platform.appwithinminutes.appNameHint') 19 + : <input type="text" id="appName" name="appName" /> 20 + #appWizardFooter(1) 53 53 </form> 22 + {{/html}} 54 54 #end 55 55 56 -#macro (processStep) 25 +#macro(getAppDescriptor $appName) 26 + #set($appDescriptorClassName = 'AppWithinMinutes.LiveTableClass') 27 + #set($appDescriptorStatement = "from doc.object($appDescriptorClassName) as obj where doc.space = :space") 28 + #set($appDescriptors = $services.query.xwql($appDescriptorStatement).bindValue('space', $appName).execute()) 29 + #if($appDescriptors.size() > 0) 30 + #set($appDescriptor = $xwiki.getDocument($appDescriptors.get(0))) 31 + #end 32 +#end 33 + 34 +#macro(processStep) 57 57 ## Check if the application already exists. 58 - #getAppReference 59 - #getAppDescriptor($appReference) 60 - #if ($appDescriptor) 61 - ## Edit an existing application. 62 - #getAppClassReference($appDescriptor) 63 - #set ($appClassRef = $classReference) 36 + #set($appName = $request.appName) 37 + #getAppDescriptor($appName) 38 + #if($appDescriptor) 39 + ## Edit an existing application. Use the configured class name. 40 + #set($classStringRef = $appDescriptor.getObject($appDescriptorClassName).getProperty('class').value) 41 + ## The class string reference is relative to the document holding the application descriptor. 42 + #set($classRef = $services.model.resolveDocument($classStringRef, 'explicit', $appDescriptor.documentReference)) 64 64 #else 65 65 ## Create a new application. Use the default class name. 66 - #set ppCodeRef=$services.model.createSpaceReference('Code',$appReference))67 - #set appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef))45 + #set($className = "#toXMLName($appName)") 46 + #set($classRef = $services.model.createDocumentReference($doc.wiki, "${className}Code", "${className}Class")) 68 68 #end 69 - #set ($queryString = {'wizard': true}) 70 - #if (!$xwiki.exists($appClassRef)) 71 - #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference)) 72 - #set ($discard = $queryString.putAll({ 73 - 'form_token': $services.csrf.getToken(), 74 - 'template': 'AppWithinMinutes.ClassTemplate', 75 - 'parent': $services.model.serialize($appHomeRef), 76 - 'title': "$appReference.name Class" 77 - })) 48 + #set($queryString = 'wizard=true') 49 + #if(!$xwiki.exists($classRef)) 50 + #set($classTitle = "$appName Class") 51 + #set($appHomeRef = $services.model.createDocumentReference($doc.wiki, $appName, 'WebHome')) 52 + #set($classParent = $services.model.serialize($appHomeRef)) 53 + #set($queryString = "$queryString&editor=inline&template=AppWithinMinutes.ClassTemplate&parent=$escapetool.url($classParent)&title=$escapetool.url($classTitle)&AppWithinMinutes.MetadataClass_0_dataSpaceName=$escapetool.url($appName)") 78 78 #end 79 - $response.sendRedirect($xwiki.getURL($ appClassRef, 'edit', $escapetool.url($queryString)))55 + $response.sendRedirect($xwiki.getURL($classRef, 'edit', $queryString)) 80 80 #end 81 81 82 -#macro (validateAppName) 83 - #getAppReference 84 - #if (!$appReference) 85 - <span class="xErrorMsg">$services.localization.render('platform.appwithinminutes.appNameEmptyError')</span> 58 +#macro(validateAppName $appName) 59 + #getAppDescriptor("$!appName") 60 + #if($appDescriptor) 61 + ## Edit an existing application. 62 + #set($appHomeRef = $appDescriptor.documentReference) 63 + #set($classStringRef = $appDescriptor.getObject($appDescriptorClassName).getProperty('class').value) 64 + ## The class string reference is relative to the document holding the application descriptor. 65 + #set($appClassRef = $services.model.resolveDocument($classStringRef, 'explicit', $appDescriptor.documentReference)) 86 86 #else 87 - #getAppDescriptor($appReference) 88 - #if ($appDescriptor) 89 - ## Edit an existing application. 90 - #getAppClassReference($appDescriptor) 91 - #set ($appClassRef = $classReference) 92 - #else 93 - ## Create a new application. 94 - #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference)) 95 - #set ($appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef)) 67 + ## Create a new application. 68 + #set($className = "#toXMLName($appName)") 69 + #if($className == '') 70 + (% class="xErrorMsg" %)$services.localization.render('platform.appwithinminutes.appNameInvalidClassNameError') 96 96 #end 97 - <dl> 98 - <dt>$services.localization.render('platform.appwithinminutes.appNamePreviewHomePageUrlLabel')</dt> 99 - <dd><pre>$!escapetool.xml($xwiki.getDocument($appReference).externalURL)</pre></dd> 100 - <dt>$services.localization.render('platform.appwithinminutes.appNamePreviewCodeSpaceLabel')</dt> 101 - <dd>#hierarchy($appClassRef.parent)</dd> 102 - </dl> 103 - #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference)) 104 - #if ($appDescriptor || $xwiki.exists($appHomeRef) || $xwiki.exists($appClassRef)) 105 - <div class="box warningmessage"> 106 - $services.localization.render('platform.appwithinminutes.appNameIsUsedWarning') 107 - </div> 108 - #end 109 - #if (!$services.security.authorization.hasAccess('script', $xcontext.userReference, $appHomeRef)) 110 - <div class="box errormessage"> 111 - $escapetool.xml($services.localization.render('platform.appwithinminutes.appHomePageNoScriptRight')) 112 - </div> 113 - #end 72 + #set($appHomeRef = $services.model.createDocumentReference($doc.wiki, $appName, 'WebHome')) 73 + #set($appClassRef = $services.model.createDocumentReference($doc.wiki, "$!{className}Code", "$!{className}Class")) 114 114 #end 115 -#end 75 + #set($appHomeURL = $stringtool.removeEnd($xwiki.getDocument($appHomeRef).getExternalURL(), 'WebHome')) 76 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewHomePageUrlLabel') 77 + : {{{$!appHomeURL}}} 78 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewDataSpaceLabel') 79 + : {{{$appHomeRef.wikiReference.name}}} » {{{$appHomeRef.lastSpaceReference.name}}} 80 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewCodeSpaceLabel') 81 + : {{{$appClassRef.wikiReference.name}}} » {{{$appClassRef.lastSpaceReference.name}}} 82 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewClassReferenceLabel') 83 + : {{{$appClassRef.wikiReference.name}}} » {{{$appClassRef.lastSpaceReference.name}}} » {{{$appClassRef.name}}} 84 + #if($appDescriptor || $xwiki.exists($appHomeRef) || $xwiki.exists($appClassRef)) 116 116 117 -#macro (getAppReference) 118 - #if ($request.resolve == 'true') 119 - #set ($appReference = $services.model.resolveSpace($request.appName)) 120 - #elseif ("$!request.appName" != '') 121 - #set ($parentReference = $doc.documentReference.wikiReference) 122 - #if ("$!request.appParentReference" != '') 123 - #set ($parentReference = $services.model.resolveSpace($request.appParentReference)) 124 - #end 125 - #set ($appReference = $services.model.createSpaceReference($request.appName, $parentReference)) 126 - #else 127 - #set ($appReference = $NULL) 86 + {{warning}}$services.localization.render('platform.appwithinminutes.appNameIsUsedWarning'){{/warning}} 128 128 #end 129 129 #end 130 - 131 -#macro (getAppDescriptor $appReference) 132 - #set ($appDescriptorClassName = 'AppWithinMinutes.LiveTableClass') 133 - #set ($appDescriptorStatement = "from doc.object($appDescriptorClassName) as obj where doc.space = :space") 134 - #set ($localSpaceReference = $services.model.serialize($appReference, 'local')) 135 - #set ($appDescriptors = $services.query.xwql($appDescriptorStatement).bindValue('space', $localSpaceReference).execute()) 136 - #if ($appDescriptors.size() > 0) 137 - #set ($appDescriptor = $xwiki.getDocument($appDescriptors.get(0))) 138 - #else 139 - #set ($appDescriptor = $NULL) 140 - #end 141 -#end 142 142 {{/velocity}} 143 143 144 144 {{velocity}} 145 -{{html clean="false"}} 146 -#if ("$!request.appName" != '') 147 - #if ($xcontext.action == 'get') 148 - #validateAppName 92 +#if("$!request.appName" != '') 93 + #if($xcontext.action == 'get') 94 + #validateAppName($request.appName) 149 149 #else 150 150 ## CSRF protection is not needed because this step only redirects to the next one passing data in the query string. 151 - #processStep 97 + #processStep() 152 152 #end 153 -#elseif ($request.wizard == 'true') 154 - #showStep 155 - #set ($displayDocExtra = false) 99 +#else 100 + #showStep() 156 156 #end 157 -{{/html}} 158 158 {{/velocity}}
- XWiki.JavaScriptExtension[0]
-
- Code
-
... ... @@ -1,51 +1,89 @@ 1 -require(['jquery', 'xwiki-form-validation-async'], function($) { 2 - var appNameInput = $('input[name="appName"]'); 3 - var appParentInput = $('input[name="appParentReference"]'); 4 - var preview = $('.appName-preview'); 1 +var XWiki = (function (XWiki) { 5 5 6 - if (appNameInput.val() === '') { 7 - // We use a function instead of passing directly the promise because we want to avoid the "Uncaught (in promise)" 8 - // error. Basically, we want the rejected promise to be caught. 9 - appNameInput.validateAsync(() => Promise.reject(), 'awm'); 3 +XWiki.DeferredUpdater = Class.create({ 4 + initialize : function(updatable) { 5 + this.elapsedHandler = updatable.onUpdate.bind(updatable); 6 + this.updatable = updatable; 7 + }, 8 + deferUpdate : function() { 9 + if (this.timer) { 10 + clearTimeout(this.timer); 11 + } 12 + this.timer = setTimeout(this.elapsedHandler, 500); 10 10 } 14 +}); 11 11 12 - var errorMessage = appNameInput.closest('dd').prev('dt').find('.xErrorMsg'); 13 - if (!errorMessage.length) { 14 - errorMessage = $(document.createElement('span')).addClass('xErrorMsg').hide().appendTo(errorMessage.addBack()); 15 - } 16 +XWiki.AppNameValidator = Class.create({ 17 + initialize : function(input, submitButton) { 18 + this.input = input; 19 + this.deferredUpdater = new XWiki.DeferredUpdater(this); 20 + var updateScheduler = this._scheduleUpdate.bindAsEventListener(this); 21 + ['keypress', 'paste', 'cut'].each(function(eventType) { 22 + input.observe(eventType, updateScheduler); 23 + }.bind(this)); 16 16 17 - var toggleValidationError = function(message) { 25 + this.submitButton = submitButton; 26 + this.submitButton.observe('click', this._onSubmit.bindAsEventListener(this)); 27 + 28 + this.preview = new Element('div', {'class': 'appName-preview'}); 29 + this.input.insert({after: this.preview}); 30 + 31 + var previousDT = input.up('dd').previous(); 32 + this.errorMessage = previousDT.down('xErrorMsg'); 33 + if (!this.errorMessage) { 34 + this.errorMessage = new Element('span', {'class': 'xErrorMsg'}); 35 + previousDT.insert(this.errorMessage.hide()); 36 + } 37 + }, 38 + _onSubmit : function(event) { 39 + if (!this.input._validated) { 40 + event.stop(); 41 + this._scheduleUpdate(); 42 + } 43 + }, 44 + _scheduleUpdate : function(event) { 45 + if(!this.input._validated || [9, 13, 35, 36, 37, 38, 39, 40].indexOf(event.keyCode) < 0) { 46 + this.input._validated = false; 47 + this.deferredUpdater.deferUpdate(); 48 + } 49 + }, 50 + _onValidate : function(response) { 51 + this.preview.removeClassName('loading').update(response.responseText); 52 + var error = this.preview.down('.xErrorMsg'); 53 + this.input._validated = !error; 54 + this._showError(error ? error.remove().firstChild.nodeValue : ''); 55 + }, 56 + _showError : function(message) { 18 18 if (message) { 19 - appNameInput.addClass('xErrorField'); 20 - errorMessage.text(message).show(); 21 - return Promise.reject(); 58 + this.input.addClassName('xErrorField').focus(); 59 + this.errorMessage.update(message.escapeHTML()).show(); 22 22 } else { 23 - appNameInput.removeClass('xErrorField');24 - errorMessage.hide(); 61 + this.input.removeClassName('xErrorField'); 62 + this.errorMessage.hide(); 25 25 } 26 - }; 27 - 28 - var updatePreview = function(content) { 29 - preview.removeClass('loading').html(content); 30 - var error = preview.find('.xErrorMsg'); 31 - return toggleValidationError(error.remove().text()); 32 - }; 33 - 34 - var fetchPreviewUpdate = function() { 35 - if (appNameInput.val() === '') { 36 - return updatePreview('<span class="xErrorMsg">$escapetool.javascript($services.localization.render("platform.appwithinminutes.appNameEmptyError"))</span>'); 64 + }, 65 + onUpdate : function() { 66 + if (this.input.value == '') { 67 + this._onValidate({ 68 + responseText: '<span class="xErrorMsg">$escapetool.javascript($services.localization.render('platform.appwithinminutes.appNameEmptyError'))</span>' 69 + }); 37 37 } else { 38 - preview.addClass('loading'); 39 - return $.get(XWiki.currentDocument.getURL('get'), appNameInput.closest('form').serialize()).then(updatePreview); 71 + this.preview.addClassName('loading'); 72 + new Ajax.Request('$doc.getURL('get')', { 73 + method: 'get', 74 + parameters: {'appName': this.input.value}, 75 + onSuccess: this._onValidate.bind(this) 76 + }); 40 40 } 41 - }; 42 - 43 - appNameInput.add(appParentInput).on('input', () => { 44 - appNameInput.validateAsync(fetchPreviewUpdate, 500, 'awm'); 45 - }).on('keyup', function(event) { 46 - // Show the error message if the user presses Enter before typing anything. 47 - if (event.which === 13 && appNameInput.val() === '' && !appNameInput.hasClass('xErrorField')) { 48 - appNameInput.validateAsync(fetchPreviewUpdate, 'awm').catch(() => appNameInput.focus()); 49 - } 50 - }); 78 + } 51 51 }); 80 + 81 +function init() { 82 + var appNameInput = $('appName'); 83 + appNameInput && new XWiki.AppNameValidator(appNameInput, $('wizard-next')); 84 + return !!appNameInput; 85 +} 86 +(XWiki.domIsLoaded && init()) || document.observe('xwiki:dom:loaded', init); 87 + 88 +return XWiki; 89 +}(XWiki || {}));
- XWiki.StyleSheetExtension[0]
-
- Code
-
... ... @@ -24,6 +24,10 @@ 24 24 margin-bottom: 1em; 25 25 } 26 26 27 +.appName-preview dl tt { 28 + color: $theme.textColor; 29 +} 30 + 27 27 .appName-preview dt { 28 28 font-weight: normal; 29 29 margin-top: 1em; ... ... @@ -32,8 +32,3 @@ 32 32 .appName-preview dt:after { 33 33 content: ":" 34 34 } 35 - 36 -.appName-preview .breadcrumb { 37 - background-color: transparent; 38 - padding: 0; 39 -} - Content Type
-
... ... @@ -1,1 +1,0 @@ 1 -CSS