Changes for page Create Application
Last modified by Ludovic Dubost on 2024/07/22 15:51
From version 5.1
edited by Ludovic Dubost
on 2015/11/28 14:58
on 2015/11/28 14:58
Change comment:
Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui-7.3]
To 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]
Summary
-
Page properties (2 modified, 0 added, 0 removed)
-
Objects (1 modified, 0 added, 0 removed)
Details
- Page properties
-
- Syntax
-
... ... @@ -1,1 +1,1 @@ 1 -XWiki 2. 01 +XWiki 2.1 - Content
-
... ... @@ -1,44 +1,56 @@ 1 -{{include reference="AppWithinMinutes. WizardStep"/}}1 +{{include reference="AppWithinMinutes.VelocityMacros"/}} 2 2 3 +{{velocity}} 4 +#if ($request.wizard == 'true') 5 + {{include reference="AppWithinMinutes.WizardStep"/}} 6 +#end 7 +{{/velocity}} 8 + 3 3 {{template name="locationPicker_macros.vm" /}} 4 4 5 5 {{velocity output="false"}} 6 -#macro(showStep) 7 - {{html wiki="true"}} 8 - #appWizardHeader(1) 9 - (% class="wizard-help" %) 10 - ((( 11 - **$services.localization.render('platform.appwithinminutes.wizardStepHelpTitle')** $services.localization.render('platform.appwithinminutes.wizardStepHelpDescription') 12 - (% class="steps vertical" %) 13 - #foreach($index in [1, 2, 3]) 14 - * (% class="number" %)$index(%%) (% class="name" %)$services.localization.render("platform.appwithinminutes.wizardStep${index}Name")(%%) 15 - (% class="description" %)$services.localization.render("platform.appwithinminutes.wizardStep${index}Description") 16 - #end 17 - ))) 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> 18 18 <form action="$doc.getURL()" method="post" class="xform wizard-body"> 19 - #locationPicker({ 20 - 'id': 'app', 21 - 'title': { 22 - 'label': 'platform.appwithinminutes.appNameLabel', 23 - 'hint': 'platform.appwithinminutes.appNameHint', 24 - 'name': 'appName' 25 - }, 26 - 'preview': { 27 - 'label': 'appWithinMinutes.createApp.location.label', 28 - 'hint': 'appWithinMinutes.createApp.location.hint' 29 - }, 30 - 'parent': { 31 - 'label': 'appWithinMinutes.createApp.parent.label', 32 - 'hint': 'appWithinMinutes.createApp.parent.hint', 33 - 'name': 'appParentReference', 34 - 'reference': $doc.documentReference.wikiReference, 35 - 'placeholder': 'appWithinMinutes.createApp.parent.placeholder' 36 - } 37 - }) 38 - <div class="appName-preview"></div> 39 - #appWizardFooter(1) 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> 40 40 </form> 41 - {{/html}} 42 42 #end 43 43 44 44 #macro (processStep) ... ... @@ -46,10 +46,9 @@ 46 46 #getAppReference 47 47 #getAppDescriptor($appReference) 48 48 #if ($appDescriptor) 49 - ## Edit an existing application. Use the configured class name. 50 - #set ($appClassRef = $appDescriptor.getObject($appDescriptorClassName).getValue('class')) 51 - ## The class reference is relative to the document holding the application descriptor. 52 - #set ($appClassRef = $services.model.resolveDocument($appClassRef, 'explicit', $appDescriptor.documentReference)) 61 + ## Edit an existing application. 62 + #getAppClassReference($appDescriptor) 63 + #set ($appClassRef = $classReference) 53 53 #else 54 54 ## Create a new application. Use the default class name. 55 55 #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference)) ... ... @@ -59,7 +59,7 @@ 59 59 #if (!$xwiki.exists($appClassRef)) 60 60 #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference)) 61 61 #set ($discard = $queryString.putAll({ 62 - ' editor':'inline',73 + 'form_token': $services.csrf.getToken(), 63 63 'template': 'AppWithinMinutes.ClassTemplate', 64 64 'parent': $services.model.serialize($appHomeRef), 65 65 'title': "$appReference.name Class" ... ... @@ -71,37 +71,35 @@ 71 71 #macro (validateAppName) 72 72 #getAppReference 73 73 #if (!$appReference) 74 - (%class="xErrorMsg"%)$services.localization.render('platform.appwithinminutes.appNameEmptyError')85 + <span class="xErrorMsg">$services.localization.render('platform.appwithinminutes.appNameEmptyError')</span> 75 75 #else 76 76 #getAppDescriptor($appReference) 77 77 #if ($appDescriptor) 78 78 ## Edit an existing application. 79 - #set ($appDescriptorObj = $appDescriptor.getObject($appDescriptorClassName)) 80 - #set ($appClassRef = $appDescriptorObj.getValue('class')) 81 - ## The class reference is relative to the document holding the application descriptor. 82 - #set ($appClassRef = $services.model.resolveDocument($appClassRef, 'explicit', $appDescriptor.documentReference)) 83 - #set ($appDataRef = $appDescriptorObj.getValue('dataSpace')) 84 - ## The data space reference is relative to the document holding the application descriptor. 85 - #set ($appDataRef = $services.model.resolveSpace($appDataRef, 'explicit', $appDescriptor.documentReference)) 90 + #getAppClassReference($appDescriptor) 91 + #set ($appClassRef = $classReference) 86 86 #else 87 87 ## Create a new application. 88 - #set ($appDataRef = $services.model.createSpaceReference('Data', $appReference)) 89 89 #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference)) 90 90 #set ($appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef)) 91 91 #end 92 - ; $services.localization.render('platform.appwithinminutes.appNamePreviewHomePageUrlLabel') 93 - : {{{$!xwiki.getDocument($appReference).externalURL}}} 94 - ; $services.localization.render('platform.appwithinminutes.appNamePreviewDataSpaceLabel') 95 - : {{html clean="false"}}#hierarchy($appDataRef){{/html}} 96 - ; $services.localization.render('platform.appwithinminutes.appNamePreviewCodeSpaceLabel') 97 - : {{html clean="false"}}#hierarchy($appClassRef.parent){{/html}} 98 - ; $services.localization.render('platform.appwithinminutes.appNamePreviewClassReferenceLabel') 99 - : {{html clean="false"}}#hierarchy($appClassRef){{/html}} 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> 100 100 #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference)) 101 101 #if ($appDescriptor || $xwiki.exists($appHomeRef) || $xwiki.exists($appClassRef)) 102 - 103 - {{warning}}$services.localization.render('platform.appwithinminutes.appNameIsUsedWarning'){{/warning}} 105 + <div class="box warningmessage"> 106 + $services.localization.render('platform.appwithinminutes.appNameIsUsedWarning') 107 + </div> 104 104 #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 105 105 #end 106 106 #end 107 107 ... ... @@ -126,11 +126,14 @@ 126 126 #set ($appDescriptors = $services.query.xwql($appDescriptorStatement).bindValue('space', $localSpaceReference).execute()) 127 127 #if ($appDescriptors.size() > 0) 128 128 #set ($appDescriptor = $xwiki.getDocument($appDescriptors.get(0))) 138 + #else 139 + #set ($appDescriptor = $NULL) 129 129 #end 130 130 #end 131 131 {{/velocity}} 132 132 133 133 {{velocity}} 145 +{{html clean="false"}} 134 134 #if ("$!request.appName" != '') 135 135 #if ($xcontext.action == 'get') 136 136 #validateAppName ... ... @@ -138,8 +138,9 @@ 138 138 ## CSRF protection is not needed because this step only redirects to the next one passing data in the query string. 139 139 #processStep 140 140 #end 141 -#else 153 +#elseif ($request.wizard == 'true') 142 142 #showStep 155 + #set ($displayDocExtra = false) 143 143 #end 144 - #set($docextras = [])157 +{{/html}} 145 145 {{/velocity}}
- XWiki.JavaScriptExtension[0]
-
- Code
-
... ... @@ -1,18 +1,24 @@ 1 -require(['jquery'], function($) { 1 +require(['jquery', 'xwiki-form-validation-async'], function($) { 2 2 var appNameInput = $('input[name="appName"]'); 3 3 var appParentInput = $('input[name="appParentReference"]'); 4 4 var preview = $('.appName-preview'); 5 - var submitButton = $('#wizard-next').prop('disabled', true); 6 6 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'); 10 + } 11 + 7 7 var errorMessage = appNameInput.closest('dd').prev('dt').find('.xErrorMsg'); 8 - if (errorMessage. size()== 0){13 + if (!errorMessage.length) { 9 9 errorMessage = $(document.createElement('span')).addClass('xErrorMsg').hide().appendTo(errorMessage.addBack()); 10 10 } 11 11 12 12 var toggleValidationError = function(message) { 13 13 if (message) { 14 - appNameInput.addClass('xErrorField') .focus();19 + appNameInput.addClass('xErrorField'); 15 15 errorMessage.text(message).show(); 21 + return Promise.reject(); 16 16 } else { 17 17 appNameInput.removeClass('xErrorField'); 18 18 errorMessage.hide(); ... ... @@ -22,32 +22,24 @@ 22 22 var updatePreview = function(content) { 23 23 preview.removeClass('loading').html(content); 24 24 var error = preview.find('.xErrorMsg'); 25 - submitButton.prop('disabled', error.size() > 0); 26 - toggleValidationError(error.remove().text()); 31 + return toggleValidationError(error.remove().text()); 27 27 }; 28 28 29 29 var fetchPreviewUpdate = function() { 30 - if (appNameInput.val() == '') { 31 - updatePreview('<span class="xErrorMsg">$escapetool.javascript($services.localization.render("platform.appwithinminutes.appNameEmptyError"))</span>'); 35 + if (appNameInput.val() === '') { 36 + return updatePreview('<span class="xErrorMsg">$escapetool.javascript($services.localization.render("platform.appwithinminutes.appNameEmptyError"))</span>'); 32 32 } else { 33 33 preview.addClass('loading'); 34 - $.get(XWiki.currentDocument.getURL('get'), submitButton.closest('form').serialize(),updatePreview);39 + return $.get(XWiki.currentDocument.getURL('get'), appNameInput.closest('form').serialize()).then(updatePreview); 35 35 } 36 36 }; 37 37 38 - var previewTimeout; 39 - var schedulePreviewUpdate = function() { 40 - clearTimeout(previewTimeout); 41 - submitButton.prop('disabled', true); 42 - setTimeout(fetchPreviewUpdate, 500); 43 - }; 44 - 45 - appNameInput.add(appParentInput) 46 - .on('input', schedulePreviewUpdate) 47 - .keyup(function(event) { 48 - // Show the error message if the user presses Enter before typing anything. 49 - if (event.which == 13 && appNameInput.val() == '' && !appNameInput.hasClass('xErrorField')) { 50 - fetchPreviewUpdate(); 51 - } 52 - }); 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 + }); 53 53 });