Changes for page Create Application
Last modified by Ludovic Dubost on 2024/07/22 15:51
From version 6.1
edited by Ludovic Dubost
on 2017/01/07 20:21
on 2017/01/07 20:21
Change comment:
Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui-8.4.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"}} 12 +#macro (showStep) 8 8 #appWizardHeader('name') 9 - (% class="wizard-help" %) 10 - ((( 11 - **$services.localization.render('platform.appwithinminutes.wizardStepHelpTitle')** $services.localization.render('platform.appwithinminutes.wizardStepHelpDescription') 12 - (% class="steps vertical" %) 13 - #foreach($step in $awmSteps) 14 - * (% class="number" %)$mathtool.add($foreach.index, 1)(%%) (% class="name" %)$services.localization.render("appWithinMinutes.wizardStep.${step}.name")(%%) 15 - (% class="description" %)$services.localization.render("appWithinMinutes.wizardStep.${step}.description") 16 - #end 17 - ))) 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,6 +59,7 @@ 59 59 #if (!$xwiki.exists($appClassRef)) 60 60 #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference)) 61 61 #set ($discard = $queryString.putAll({ 73 + 'form_token': $services.csrf.getToken(), 62 62 'template': 'AppWithinMinutes.ClassTemplate', 63 63 'parent': $services.model.serialize($appHomeRef), 64 64 'title': "$appReference.name Class" ... ... @@ -70,29 +70,35 @@ 70 70 #macro (validateAppName) 71 71 #getAppReference 72 72 #if (!$appReference) 73 - (%class="xErrorMsg"%)$services.localization.render('platform.appwithinminutes.appNameEmptyError')85 + <span class="xErrorMsg">$services.localization.render('platform.appwithinminutes.appNameEmptyError')</span> 74 74 #else 75 75 #getAppDescriptor($appReference) 76 76 #if ($appDescriptor) 77 77 ## Edit an existing application. 78 - #set ($appDescriptorObj = $appDescriptor.getObject($appDescriptorClassName)) 79 - #set ($appClassRef = $appDescriptorObj.getValue('class')) 80 - ## The class reference is relative to the document holding the application descriptor. 81 - #set ($appClassRef = $services.model.resolveDocument($appClassRef, 'explicit', $appDescriptor.documentReference)) 90 + #getAppClassReference($appDescriptor) 91 + #set ($appClassRef = $classReference) 82 82 #else 83 83 ## Create a new application. 84 84 #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference)) 85 85 #set ($appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef)) 86 86 #end 87 - ; $services.localization.render('platform.appwithinminutes.appNamePreviewHomePageUrlLabel') 88 - : {{{$!xwiki.getDocument($appReference).externalURL}}} 89 - ; $services.localization.render('platform.appwithinminutes.appNamePreviewCodeSpaceLabel') 90 - : {{html clean="false"}}#hierarchy($appClassRef.parent){{/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> 91 91 #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference)) 92 92 #if ($appDescriptor || $xwiki.exists($appHomeRef) || $xwiki.exists($appClassRef)) 93 - 94 - {{warning}}$services.localization.render('platform.appwithinminutes.appNameIsUsedWarning'){{/warning}} 105 + <div class="box warningmessage"> 106 + $services.localization.render('platform.appwithinminutes.appNameIsUsedWarning') 107 + </div> 95 95 #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 96 96 #end 97 97 #end 98 98 ... ... @@ -117,11 +117,14 @@ 117 117 #set ($appDescriptors = $services.query.xwql($appDescriptorStatement).bindValue('space', $localSpaceReference).execute()) 118 118 #if ($appDescriptors.size() > 0) 119 119 #set ($appDescriptor = $xwiki.getDocument($appDescriptors.get(0))) 138 + #else 139 + #set ($appDescriptor = $NULL) 120 120 #end 121 121 #end 122 122 {{/velocity}} 123 123 124 124 {{velocity}} 145 +{{html clean="false"}} 125 125 #if ("$!request.appName" != '') 126 126 #if ($xcontext.action == 'get') 127 127 #validateAppName ... ... @@ -129,8 +129,9 @@ 129 129 ## CSRF protection is not needed because this step only redirects to the next one passing data in the query string. 130 130 #processStep 131 131 #end 132 -#else 153 +#elseif ($request.wizard == 'true') 133 133 #showStep 155 + #set ($displayDocExtra = false) 134 134 #end 135 - #set($displayDocExtra = false)157 +{{/html}} 136 136 {{/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 });