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 2.1
edited by Thomas Mortagne
on 2014/09/29 12:41
Change comment: Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui-6.2]

Summary

Details

Page properties
Author
... ... @@ -1,1 +1,1 @@
1 -xwiki:XWiki.ludovic
1 +xwiki:XWiki.ThomasMortagne
Content
... ... @@ -1,7 +1,5 @@
1 1  {{include reference="AppWithinMinutes.WizardStep"/}}
2 2  
3 -{{template name="locationPicker_macros.vm" /}}
4 -
5 5  {{velocity output="false"}}
6 6  #macro(showStep)
7 7   {{html wiki="true"}}
... ... @@ -15,131 +15,86 @@
15 15   (% class="description" %)$services.localization.render("platform.appwithinminutes.wizardStep${index}Description")
16 16   #end
17 17   )))
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>
16 + <form action="" 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" />
39 39   #appWizardFooter(1)
40 40   </form>
41 41   {{/html}}
42 42  #end
43 43  
44 -#macro (processStep)
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)
45 45   ## Check if the application already exists.
46 - #getAppReference
47 - #getAppDescriptor($appReference)
48 - #if ($appDescriptor)
36 + #getAppDescriptor($request.appName)
37 + #if($appDescriptor)
49 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))
39 + #set($classStringRef = $appDescriptor.getObject($appDescriptorClassName).getProperty('class').value)
40 + ## The class string reference is relative to the document holding the application descriptor.
41 + #set($classRef = $services.model.resolveDocument($classStringRef, 'explicit', $appDescriptor.documentReference))
53 53   #else
54 54   ## Create a new application. Use the default class name.
55 - #set ($appCodeRef = $services.model.createSpaceReference('Code', $appReference))
56 - #set ($appClassRef = $services.model.createDocumentReference("$!{appReference.name}Class", $appCodeRef))
44 + #set($classRef = $services.model.createDocumentReference($doc.wiki, "${request.appName}Code", "#toXMLName($request.appName)Class"))
57 57   #end
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 - }))
46 + #set($queryString = 'wizard=true')
47 + #if(!$xwiki.exists($classRef))
48 + #set($classTitle = "$request.appName Class")
49 + #set($queryString = "$queryString&editor=inline&template=AppWithinMinutes.ClassTemplate&parent=${request.appName}.WebHome&title=$escapetool.url($classTitle)")
67 67   #end
68 - $response.sendRedirect($xwiki.getURL($appClassRef, 'edit', $escapetool.url($queryString)))
51 + $response.sendRedirect($xwiki.getURL($classRef, 'edit', $queryString))
69 69  #end
70 70  
71 -#macro (validateAppName)
72 - #getAppReference
73 - #if (!$appReference)
74 - (% class="xErrorMsg" %)$services.localization.render('platform.appwithinminutes.appNameEmptyError')
54 +#macro(validateAppName $appName)
55 + #getAppDescriptor("$!appName")
56 + #if($appDescriptor)
57 + ## Edit an existing application.
58 + #set($appHomeRef = $appDescriptor.documentReference)
59 + #set($classStringRef = $appDescriptor.getObject($appDescriptorClassName).getProperty('class').value)
60 + ## The class string reference is relative to the document holding the application descriptor.
61 + #set($appClassRef = $services.model.resolveDocument($classStringRef, 'explicit', $appDescriptor.documentReference))
75 75   #else
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))
63 + ## Create a new application.
64 + #set($className = "#toXMLName($appName)")
65 + #if($className == '')
66 + (% class="xErrorMsg" %)$services.localization.render('platform.appwithinminutes.appNameInvalidClassNameError')
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}}
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
68 + #set($appHomeRef = $services.model.createDocumentReference($doc.wiki, $appName, 'WebHome'))
69 + #set($appClassRef = $services.model.createDocumentReference($doc.wiki, "$!{appName}Code", "$!{className}Class"))
105 105   #end
106 -#end
71 + #set($appHomeURL = $stringtool.removeEnd($xwiki.getDocument($appHomeRef).getExternalURL(), 'WebHome'))
72 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewHomePageUrlLabel')
73 + : {{{$!appHomeURL}}}
74 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewDataSpaceLabel')
75 + : {{{$doc.wiki}}} » {{{$appName}}}
76 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewCodeSpaceLabel')
77 + : {{{$doc.wiki}}} » {{{${appName}Code}}}
78 + ; $services.localization.render('platform.appwithinminutes.appNamePreviewClassReferenceLabel')
79 + : {{{$appClassRef.wikiReference.name}}} » {{{$appClassRef.lastSpaceReference.name}}} » {{{$appClassRef.name}}}
80 + #if($appDescriptor || $xwiki.exists($appHomeRef) || $xwiki.exists($appClassRef))
107 107  
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)
82 + {{warning}}$services.localization.render('platform.appwithinminutes.appNameIsUsedWarning'){{/warning}}
119 119   #end
120 120  #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
131 131  {{/velocity}}
132 132  
133 133  {{velocity}}
134 -#if ("$!request.appName" != '')
135 - #if ($xcontext.action == 'get')
136 - #validateAppName
88 +#if("$!request.appName" != '')
89 + #if($xcontext.action == 'get')
90 + #validateAppName($request.appName)
137 137   #else
138 138   ## CSRF protection is not needed because this step only redirects to the next one passing data in the query string.
139 - #processStep
93 + #processStep()
140 140   #end
141 141  #else
142 - #showStep
96 + #showStep()
143 143  #end
144 -#set ($docextras = [])
145 145  {{/velocity}}
XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,53 +1,89 @@
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);
1 +var XWiki = (function (XWiki) {
6 6  
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());
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);
10 10   }
14 +});
11 11  
12 - var toggleValidationError = function(message) {
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) {
13 13   if (message) {
14 - appNameInput.addClass('xErrorField').focus();
15 - errorMessage.text(message).show();
58 + this.input.addClassName('xErrorField').focus();
59 + this.errorMessage.update(message.escapeHTML()).show();
16 16   } else {
17 - appNameInput.removeClass('xErrorField');
18 - errorMessage.hide();
61 + this.input.removeClassName('xErrorField');
62 + this.errorMessage.hide();
19 19   }
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>');
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 + });
32 32   } else {
33 - preview.addClass('loading');
34 - $.get(XWiki.currentDocument.getURL('get'), submitButton.closest('form').serialize(), updatePreview);
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 + });
35 35   }
36 - };
78 + }
79 +});
37 37  
38 - var previewTimeout;
39 - var schedulePreviewUpdate = function() {
40 - clearTimeout(previewTimeout);
41 - submitButton.prop('disabled', true);
42 - setTimeout(fetchPreviewUpdate, 500);
43 - };
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);
44 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 - });
53 -});
88 +return XWiki;
89 +}(XWiki || {}));
XWiki.StyleSheetExtension[0]
Code
... ... @@ -24,6 +24,10 @@
24 24   margin-bottom: 1em;
25 25  }
26 26  
27 +.appName-preview dl tt {
28 + color: $theme.textColor;
29 +}
30 +
27 27  .appName-preview dt {
28 28   font-weight: normal;
29 29   margin-top: 1em;
... ... @@ -32,8 +32,3 @@
32 32  .appName-preview dt:after {
33 33   content: ":"
34 34  }
35 -
36 -.appName-preview .breadcrumb {
37 - background-color: transparent;
38 - padding: 0;
39 -}
Content Type
... ... @@ -1,1 +1,0 @@
1 -CSS