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
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
Change comment: Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui/16.5.0]

Summary

Details

Page properties
Syntax
... ... @@ -1,1 +1,1 @@
1 -XWiki 2.0
1 +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  });