Changes for page Create Application

Last modified by Thomas Mortagne on 2023/11/30 14:34

From version 7.1
edited by Guillaume Delhumeau
on 2015/06/16 18:54
Change comment: Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui-7.1]
To version 8.1
edited by Thomas Mortagne
on 2015/11/23 12:25
Change comment: Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui-7.3]

Summary

Details

Page properties
Author
... ... @@ -1,1 +1,1 @@
1 -xwiki:XWiki.gdelhumeau
1 +xwiki:XWiki.ThomasMortagne
Content
... ... @@ -1,5 +1,7 @@
1 1  {{include reference="AppWithinMinutes.WizardStep"/}}
2 2  
3 +{{template name="locationPicker_macros.vm" /}}
4 +
3 3  {{velocity output="false"}}
4 4  #macro(showStep)
5 5   {{html wiki="true"}}
... ... @@ -14,90 +14,130 @@
14 14   #end
15 15   )))
16 16   <form action="$doc.getURL()" 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" />
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>
20 20   #appWizardFooter(1)
21 21   </form>
22 22   {{/html}}
23 23  #end
24 24  
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)
44 +#macro (processStep)
35 35   ## Check if the application already exists.
36 - #set($appName = $request.appName)
37 - #getAppDescriptor($appName)
38 - #if($appDescriptor)
46 + #getAppReference
47 + #getAppDescriptor($appReference)
48 + #if ($appDescriptor)
39 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))
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))
43 43   #else
44 44   ## Create a new application. Use the default class name.
45 - #set($className = "#toXMLName($appName)")
46 - #set($classRef = $services.model.createDocumentReference($doc.wiki, "${className}Code", "${className}Class"))
55 + #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference))
56 + #set ($appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef))
47 47   #end
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)")
58 + #set ($queryString = {'wizard': true})
59 + #if (!$xwiki.exists($appClassRef))
60 + #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference))
61 + #set ($discard = $queryString.putAll({
62 + 'editor': 'inline',
63 + 'template': 'AppWithinMinutes.ClassTemplate',
64 + 'parent': $services.model.serialize($appHomeRef),
65 + 'title': "$appReference.name Class"
66 + }))
54 54   #end
55 - $response.sendRedirect($xwiki.getURL($classRef, 'edit', $queryString))
68 + $response.sendRedirect($xwiki.getURL($appClassRef, 'edit', $escapetool.url($queryString)))
56 56  #end
57 57  
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))
71 +#macro (validateAppName)
72 + #getAppReference
73 + #if (!$appReference)
74 + (% class="xErrorMsg" %)$services.localization.render('platform.appwithinminutes.appNameEmptyError')
66 66   #else
67 - ## Create a new application.
68 - #set($className = "#toXMLName($appName)")
69 - #if($className == '')
70 - (% class="xErrorMsg" %)$services.localization.render('platform.appwithinminutes.appNameInvalidClassNameError')
76 + #getAppDescriptor($appReference)
77 + #if ($appDescriptor)
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))
86 + #else
87 + ## Create a new application.
88 + #set ($appDataRef = $services.model.createSpaceReference('Data', $appReference))
89 + #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference))
90 + #set ($appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef))
71 71   #end
72 - #set($appHomeRef = $services.model.createDocumentReference($doc.wiki, $appName, 'WebHome'))
73 - #set($appClassRef = $services.model.createDocumentReference($doc.wiki, "$!{className}Code", "$!{className}Class"))
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}}
100 + #set ($appHomeRef = $services.model.resolveDocument('', 'default', $appReference))
101 + #if ($appDescriptor || $xwiki.exists($appHomeRef) || $xwiki.exists($appClassRef))
102 +
103 + {{warning}}$services.localization.render('platform.appwithinminutes.appNameIsUsedWarning'){{/warning}}
104 + #end
74 74   #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))
106 +#end
85 85  
86 - {{warning}}$services.localization.render('platform.appwithinminutes.appNameIsUsedWarning'){{/warning}}
108 +#macro (getAppReference)
109 + #if ($request.resolve == 'true')
110 + #set ($appReference = $services.model.resolveSpace($request.appName))
111 + #elseif ("$!request.appName" != '')
112 + #set ($parentReference = $doc.documentReference.wikiReference)
113 + #if ("$!request.appParentReference" != '')
114 + #set ($parentReference = $services.model.resolveSpace($request.appParentReference))
115 + #end
116 + #set ($appReference = $services.model.createSpaceReference($request.appName, $parentReference))
117 + #else
118 + #set ($appReference = $NULL)
87 87   #end
88 88  #end
121 +
122 +#macro (getAppDescriptor $appReference)
123 + #set ($appDescriptorClassName = 'AppWithinMinutes.LiveTableClass')
124 + #set ($appDescriptorStatement = "from doc.object($appDescriptorClassName) as obj where doc.space = :space")
125 + #set ($localSpaceReference = $services.model.serialize($appReference, 'local'))
126 + #set ($appDescriptors = $services.query.xwql($appDescriptorStatement).bindValue('space', $localSpaceReference).execute())
127 + #if ($appDescriptors.size() > 0)
128 + #set ($appDescriptor = $xwiki.getDocument($appDescriptors.get(0)))
129 + #end
130 +#end
89 89  {{/velocity}}
90 90  
91 91  {{velocity}}
92 -#if("$!request.appName" != '')
93 - #if($xcontext.action == 'get')
94 - #validateAppName($request.appName)
134 +#if ("$!request.appName" != '')
135 + #if ($xcontext.action == 'get')
136 + #validateAppName
95 95   #else
96 96   ## CSRF protection is not needed because this step only redirects to the next one passing data in the query string.
97 - #processStep()
139 + #processStep
98 98   #end
99 99  #else
100 - #showStep()
142 + #showStep
101 101  #end
102 -#set($docextras=[])
144 +#set ($docextras = [])
103 103  {{/velocity}}
XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,89 +1,53 @@
1 -var XWiki = (function (XWiki) {
1 +require(['jquery'], function($) {
2 + var appNameInput = $('input[name="appName"]');
3 + var appParentInput = $('input[name="appParentReference"]');
4 + var preview = $('.appName-preview');
5 + var submitButton = $('#wizard-next').prop('disabled', true);
2 2  
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);
7 + var errorMessage = appNameInput.closest('dd').prev('dt').find('.xErrorMsg');
8 + if (errorMessage.size() == 0) {
9 + errorMessage = $(document.createElement('span')).addClass('xErrorMsg').hide().appendTo(errorMessage.addBack());
13 13   }
14 -});
15 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));
24 -
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) {
12 + var toggleValidationError = function(message) {
57 57   if (message) {
58 - this.input.addClassName('xErrorField').focus();
59 - this.errorMessage.update(message.escapeHTML()).show();
14 + appNameInput.addClass('xErrorField').focus();
15 + errorMessage.text(message).show();
60 60   } else {
61 - this.input.removeClassName('xErrorField');
62 - this.errorMessage.hide();
17 + appNameInput.removeClass('xErrorField');
18 + errorMessage.hide();
63 63   }
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 - });
20 + };
21 +
22 + var updatePreview = function(content) {
23 + preview.removeClass('loading').html(content);
24 + var error = preview.find('.xErrorMsg');
25 + submitButton.prop('disabled', error.size() > 0);
26 + toggleValidationError(error.remove().text());
27 + };
28 +
29 + var fetchPreviewUpdate = function() {
30 + if (appNameInput.val() == '') {
31 + updatePreview('<span class="xErrorMsg">$escapetool.javascript($services.localization.render("platform.appwithinminutes.appNameEmptyError"))</span>');
70 70   } else {
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 - });
33 + preview.addClass('loading');
34 + $.get(XWiki.currentDocument.getURL('get'), submitButton.closest('form').serialize(), updatePreview);
77 77   }
78 - }
79 -});
36 + };
80 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);
38 + var previewTimeout;
39 + var schedulePreviewUpdate = function() {
40 + clearTimeout(previewTimeout);
41 + submitButton.prop('disabled', true);
42 + setTimeout(fetchPreviewUpdate, 500);
43 + };
87 87  
88 -return XWiki;
89 -}(XWiki || {}));
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 + });
53 +});
XWiki.StyleSheetExtension[0]
Code
... ... @@ -24,10 +24,6 @@
24 24   margin-bottom: 1em;
25 25  }
26 26  
27 -.appName-preview dl tt {
28 - color: $theme.textColor;
29 -}
30 -
31 31  .appName-preview dt {
32 32   font-weight: normal;
33 33   margin-top: 1em;
... ... @@ -36,3 +36,8 @@
36 36  .appName-preview dt:after {
37 37   content: ":"
38 38  }
35 +
36 +.appName-preview .breadcrumb {
37 + background-color: transparent;
38 + padding: 0;
39 +}
Content Type
... ... @@ -1,0 +1,1 @@
1 +CSS