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
Change comment: Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui/16.5.0]
To version 6.1
edited by Ludovic Dubost
on 2017/01/07 20:21
Change comment: Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui-8.4.3]

Summary

Details

Page properties
Syntax
... ... @@ -1,1 +1,1 @@
1 -XWiki 2.1
1 +XWiki 2.0
Content
... ... @@ -1,56 +1,44 @@
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 9  {{template name="locationPicker_macros.vm" /}}
10 10  
11 11  {{velocity output="false"}}
12 -#macro (showStep)
6 +#macro(showStep)
7 + {{html wiki="true"}}
13 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>
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 + )))
29 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>
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)
53 53   </form>
41 + {{/html}}
54 54  #end
55 55  
56 56  #macro (processStep)
... ... @@ -58,9 +58,10 @@
58 58   #getAppReference
59 59   #getAppDescriptor($appReference)
60 60   #if ($appDescriptor)
61 - ## Edit an existing application.
62 - #getAppClassReference($appDescriptor)
63 - #set ($appClassRef = $classReference)
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))
64 64   #else
65 65   ## Create a new application. Use the default class name.
66 66   #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference))
... ... @@ -70,7 +70,6 @@
70 70   #if (!$xwiki.exists($appClassRef))
71 71   #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference))
72 72   #set ($discard = $queryString.putAll({
73 - 'form_token': $services.csrf.getToken(),
74 74   'template': 'AppWithinMinutes.ClassTemplate',
75 75   'parent': $services.model.serialize($appHomeRef),
76 76   'title': "$appReference.name Class"
... ... @@ -82,35 +82,29 @@
82 82  #macro (validateAppName)
83 83   #getAppReference
84 84   #if (!$appReference)
85 - <span class="xErrorMsg">$services.localization.render('platform.appwithinminutes.appNameEmptyError')</span>
73 + (% class="xErrorMsg" %)$services.localization.render('platform.appwithinminutes.appNameEmptyError')
86 86   #else
87 87   #getAppDescriptor($appReference)
88 88   #if ($appDescriptor)
89 89   ## Edit an existing application.
90 - #getAppClassReference($appDescriptor)
91 - #set ($appClassRef = $classReference)
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))
92 92   #else
93 93   ## Create a new application.
94 94   #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference))
95 95   #set ($appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef))
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>
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}}
103 103   #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference))
104 104   #if ($appDescriptor || $xwiki.exists($appHomeRef) || $xwiki.exists($appClassRef))
105 - <div class="box warningmessage">
106 - $services.localization.render('platform.appwithinminutes.appNameIsUsedWarning')
107 - </div>
93 +
94 + {{warning}}$services.localization.render('platform.appwithinminutes.appNameIsUsedWarning'){{/warning}}
108 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
114 114   #end
115 115  #end
116 116  
... ... @@ -135,14 +135,11 @@
135 135   #set ($appDescriptors = $services.query.xwql($appDescriptorStatement).bindValue('space', $localSpaceReference).execute())
136 136   #if ($appDescriptors.size() > 0)
137 137   #set ($appDescriptor = $xwiki.getDocument($appDescriptors.get(0)))
138 - #else
139 - #set ($appDescriptor = $NULL)
140 140   #end
141 141  #end
142 142  {{/velocity}}
143 143  
144 144  {{velocity}}
145 -{{html clean="false"}}
146 146  #if ("$!request.appName" != '')
147 147   #if ($xcontext.action == 'get')
148 148   #validateAppName
... ... @@ -150,9 +150,8 @@
150 150   ## CSRF protection is not needed because this step only redirects to the next one passing data in the query string.
151 151   #processStep
152 152   #end
153 -#elseif ($request.wizard == 'true')
132 +#else
154 154   #showStep
155 - #set ($displayDocExtra = false)
156 156  #end
157 -{{/html}}
135 +#set ($displayDocExtra = false)
158 158  {{/velocity}}
XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,24 +1,18 @@
1 -require(['jquery', 'xwiki-form-validation-async'], function($) {
1 +require(['jquery'], 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);
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');
10 - }
11 -
12 12   var errorMessage = appNameInput.closest('dd').prev('dt').find('.xErrorMsg');
13 - if (!errorMessage.length) {
8 + if (errorMessage.size() == 0) {
14 14   errorMessage = $(document.createElement('span')).addClass('xErrorMsg').hide().appendTo(errorMessage.addBack());
15 15   }
16 16  
17 17   var toggleValidationError = function(message) {
18 18   if (message) {
19 - appNameInput.addClass('xErrorField');
14 + appNameInput.addClass('xErrorField').focus();
20 20   errorMessage.text(message).show();
21 - return Promise.reject();
22 22   } else {
23 23   appNameInput.removeClass('xErrorField');
24 24   errorMessage.hide();
... ... @@ -28,24 +28,32 @@
28 28   var updatePreview = function(content) {
29 29   preview.removeClass('loading').html(content);
30 30   var error = preview.find('.xErrorMsg');
31 - return toggleValidationError(error.remove().text());
25 + submitButton.prop('disabled', error.size() > 0);
26 + toggleValidationError(error.remove().text());
32 32   };
33 33  
34 34   var fetchPreviewUpdate = function() {
35 - if (appNameInput.val() === '') {
36 - return updatePreview('<span class="xErrorMsg">$escapetool.javascript($services.localization.render("platform.appwithinminutes.appNameEmptyError"))</span>');
30 + if (appNameInput.val() == '') {
31 + updatePreview('<span class="xErrorMsg">$escapetool.javascript($services.localization.render("platform.appwithinminutes.appNameEmptyError"))</span>');
37 37   } else {
38 38   preview.addClass('loading');
39 - return $.get(XWiki.currentDocument.getURL('get'), appNameInput.closest('form').serialize()).then(updatePreview);
34 + $.get(XWiki.currentDocument.getURL('get'), submitButton.closest('form').serialize(), updatePreview);
40 40   }
41 41   };
42 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 - });
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 + });
51 51  });