Changes for page LiveTable View Sheet
Last modified by Mark Kohlmann on 2025/01/19 06:47
From version 2.1
edited by Mark Kohlmann
on 2020/04/03 03:53
on 2020/04/03 03:53
Change comment:
Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui/12.2]
To version 3.1
edited by Mark Kohlmann
on 2024/07/26 08:06
on 2024/07/26 08:06
Change comment:
Install extension [org.xwiki.platform:xwiki-platform-appwithinminutes-ui/16.5.0]
Summary
-
Page properties (2 modified, 0 added, 0 removed)
-
Attachments (0 modified, 0 added, 1 removed)
-
Objects (2 modified, 0 added, 0 removed)
Details
- Page properties
-
- Author
-
... ... @@ -1,1 +1,1 @@ 1 -XWiki. MarkKohlmann1 +XWiki.mark\.kohlmann@chiefintegrations\.com - Content
-
... ... @@ -11,16 +11,8 @@ 11 11 ## Display the live table only if it was generated. 12 12 #if ($doc.content.length() > 0) 13 13 = $services.localization.render('platform.appwithinminutes.appLiveTableHeading') = 14 - ## We don't use the Include macro (with empty reference) because we want the content to be executed with the rights 15 - ## of the current document rather than the rights of the sheet. This is important because the user can modify the 16 - ## content of the application home page which means we could execute untrusted content with the rights of the sheet. 17 - ## Ideally we should use the Display macro with a parameter to disable the sheet, but we don't have this parameter. 18 - ## We don't clean the HTML content because getRenderedContent() should produce clean HTML, unless the user has 19 - ## disabled the HTML cleaning, in which case he will get what he asked for. Note that one good reason to disable 20 - ## HTML cleaning is to preserve the whitespaces in the attribute values. 21 - ## Escape {{ in the rendered content to be sure that the HTML macro is not closed unintentionally. 22 - {{html clean="false"}}$doc.getRenderedContent($doc.content, 23 - $doc.syntax.toIdString()).replace('{{', '&#123;&#123;'){{/html}} 14 + 15 + {{include reference="" author="target"/}} 24 24 #end 25 25 #end 26 26 ... ... @@ -59,7 +59,7 @@ 59 59 ((( 60 60 = $services.localization.render('platform.appwithinminutes.appHomePageActionsHeading') = 61 61 #if ($hasCreateData) 62 - * [[$services.localization.render('platform.appwithinminutes.appHomePageAddEntryHint')>>||anchor="AddNewEntry" class="action add"]]## 54 + * [[{{displayIcon name="add"/}} $services.localization.render('platform.appwithinminutes.appHomePageAddEntryHint')>>||anchor="AddNewEntry" class="action add"]]## 63 63 #if ("$!templateProvider.getValue('terminal')" == '1') 64 64 #set ($entryReference = $services.model.createDocumentReference('__entryName__', $dataSpaceRef)) 65 65 #else ... ... @@ -68,6 +68,7 @@ 68 68 #end 69 69 ## We need to set the title if we want to be able to sort or filter the doc.title live table column. 70 70 #set ($params = { 63 + 'form_token': $services.csrf.token, 71 71 'template': "${className}Template", 72 72 'title': '__entryName__', 73 73 'parent': $services.model.serialize($doc.documentReference, 'local') ... ... @@ -91,10 +91,10 @@ 91 91 'appName': $doc.space, 92 92 'resolve': true 93 93 })) 94 - * [[$services.localization.render('platform.appwithinminutes.appHomePageEditAppLabel')>>AppWithinMinutes.CreateApplication||queryString="$queryString" class="action edit"]] 87 + * [[{{displayIcon name="edit"/}} $services.localization.render('platform.appwithinminutes.appHomePageEditAppLabel')>>AppWithinMinutes.CreateApplication||queryString="$queryString" class="action edit"]] 95 95 #end 96 96 #if ($hasEditTranslations) 97 - * [[$services.localization.render('platform.appwithinminutes.appHomePageTranslateAppLabel')>>path:${xwiki.getURL($translationsRef, 'edit', 'editor=wiki')}||class="action translate"]] 90 + * [[{{displayIcon name="translate"/}} $services.localization.render('platform.appwithinminutes.appHomePageTranslateAppLabel')>>path:${xwiki.getURL($translationsRef, 'edit', 'editor=wiki')}||class="action translate"]] 98 98 #end 99 99 #if ($hasDeleteData) 100 100 #set ($deleteDataURL = $xwiki.getURL('AppWithinMinutes.DeleteApplication', 'view', $escapetool.url({ ... ... @@ -103,7 +103,7 @@ 103 103 'scope': 'entries', 104 104 'xredirect': $doc.getURL() 105 105 }))) 106 - * [[$services.localization.render('platform.appwithinminutes.appHomePageDeleteEntriesLabel')>>path:${deleteDataURL}||class="action deleteData"]] 99 + * [[{{displayIcon name="cross"/}} $services.localization.render('platform.appwithinminutes.appHomePageDeleteEntriesLabel')>>path:${deleteDataURL}||class="action deleteData"]] 107 107 #end 108 108 #if ($hasDeleteApplication) 109 109 #set ($deleteAppURL = $xwiki.getURL('AppWithinMinutes.DeleteApplication', 'view', $escapetool.url({ ... ... @@ -111,7 +111,7 @@ 111 111 'resolve': true, 112 112 'xredirect': $doc.getURL() 113 113 }))) 114 - * [[$services.localization.render('platform.appwithinminutes.appHomePageDeleteAppLabel')>>path:${deleteAppURL}||class="action delete"]] 107 + * [[{{displayIcon name="trash"/}} $services.localization.render('platform.appwithinminutes.appHomePageDeleteAppLabel')>>path:${deleteAppURL}||class="action delete"]] 115 115 #end 116 116 ))) 117 117 #end ... ... @@ -122,23 +122,30 @@ 122 122 data-backdrop="static" data-keyboard="false"> 123 123 <div class="modal-dialog" role="document"> 124 124 <form class="modal-content xform"> 125 - <div class="modal-header"> 126 - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> 127 - <span aria-hidden="true">×</span> 128 - </button> 129 - <span class="modal-title" id="renameAppModal-label">Rename Application</span> 130 - </div> 131 - <div class="modal-body"> 132 - #renameAppModalBody 133 - </div> 134 - <div class="modal-footer"> 135 - <button type="button" class="btn btn-default" data-dismiss="modal"> 136 - $escapetool.xml($services.localization.render('cancel')) 137 - </button> 138 - <button type="submit" class="btn btn-primary" disabled="disabled"> 139 - $escapetool.xml($services.localization.render('core.rename.submit')) 140 - </button> 141 - </div> 118 + ## The fieldset allows us to disable and enable the entire form quickly and easy. 119 + <fieldset> 120 + <div class="modal-header"> 121 + <button type="button" class="close" data-dismiss="modal" 122 + title="$escapetool.xml($services.localization.render('appWithinMinutes.renameApp.close'))" 123 + aria-label="$escapetool.xml($services.localization.render('appWithinMinutes.renameApp.close'))"> 124 + <span aria-hidden="true">×</span> 125 + </button> 126 + <span class="modal-title" id="renameAppModal-label"> 127 + $escapetool.xml($services.localization.render('appWithinMinutes.renameApp.label')) 128 + </span> 129 + </div> 130 + <div class="modal-body"> 131 + #renameAppModalBody 132 + </div> 133 + <div class="modal-footer"> 134 + <button type="button" class="btn btn-default" data-dismiss="modal"> 135 + $escapetool.xml($services.localization.render('cancel')) 136 + </button> 137 + <button type="submit" class="btn btn-primary" disabled="disabled"> 138 + $escapetool.xml($services.localization.render('core.rename.submit')) 139 + </button> 140 + </div> 141 + </fieldset> 142 142 </form> 143 143 </div> 144 144 </div>
- locate.png
-
- Author
-
... ... @@ -1,1 +1,0 @@ 1 -XWiki.MarkKohlmann - Size
-
... ... @@ -1,1 +1,0 @@ 1 -746 bytes - Content
- XWiki.JavaScriptExtension[0]
-
- Code
-
... ... @@ -58,8 +58,9 @@ 58 58 /** 59 59 * Rename Application 60 60 */ 61 -require(['jquery', 'bootstrap'], function($) { 62 - #set ($currentDocReference = $xwiki.getDocument($request.currentApp).getDocumentReference()) 61 +require(['jquery', 'bootstrap', 'xwiki-form-validation-async'], function($) { 62 + ## Note: if not currentApp request param is not passed, we default to the wiki home page reference to avoid a NPE 63 + #set ($currentDocReference = $xwiki.getDocument("$!request.currentApp").getDocumentReference()) 63 63 // if we cannot find any extension related to this page app, it's not part of an extension. 64 64 var isNotAnExtension = $services.extension.xar.getInstalledExtensions($currentDocReference).isEmpty(); 65 65 ... ... @@ -69,7 +69,7 @@ 69 69 } 70 70 // Hijack the rename page action. 71 71 var renameAppModal = $('#renameAppModal'); 72 - $('#tmActionRename').click (function(event) {73 + $('#tmActionRename').on('click', function(event) { 73 73 event.preventDefault(); 74 74 renameAppModal.modal(); 75 75 }); ... ... @@ -77,7 +77,6 @@ 77 77 // Form validation. 78 78 var appNameInput = $('#renameAppTitle'); 79 79 var appParentInput = $('#renameAppParentReference'); 80 - var submitButton = renameAppModal.find('.btn-primary[type="submit"]'); 81 81 82 82 var appNameEmptyError = renameAppModal.find('.appNameEmptyError'); 83 83 var pageExistsError = renameAppModal.find('.pageExistsError'); ... ... @@ -100,52 +100,44 @@ 100 100 101 101 var startValidation = function() { 102 102 if (appNameInput.val() === '') { 103 - en dValidation(appNameEmptyError);103 + return Promise.reject(appNameEmptyError); 104 104 } else { 105 105 var newAppHomePage = getNewAppHomePage(); 106 106 if (newAppHomePage.documentReference.equals(XWiki.currentDocument.documentReference)) { 107 - en dValidation(pageExistsError);107 + return Promise.reject(pageExistsError); 108 108 } else { 109 - $.ajax({ 110 - type: 'HEAD', 111 - url: newAppHomePage.getURL() 112 - }).done(function() { 113 - endValidation(pageExistsError); 114 - }).fail(function(response) { 115 - if (response.status === 404) { 116 - $.ajax({ 117 - type: 'HEAD', 118 - url: newAppHomePage.getURL('edit') 119 - }).done(function() { 120 - endValidation(); 121 - }).fail(function() { 122 - endValidation(locationForbiddenError); 123 - }); 124 - } else if (response.status === 403) { 125 - endValidation(locationForbiddenError); 126 - } else { 127 - endValidation(); 128 - } 109 + return new Promise((resolve, reject) => { 110 + $.ajax({ 111 + type: 'HEAD', 112 + url: newAppHomePage.getURL() 113 + }).then(reject.bind(null, pageExistsError), response => { 114 + if (response.status === 404) { 115 + $.ajax({ 116 + type: 'HEAD', 117 + url: newAppHomePage.getURL('edit') 118 + }).then( 119 + () => resolve(), 120 + () => reject(locationForbiddenError) 121 + ); 122 + } else if (response.status === 403) { 123 + reject(locationForbiddenError); 124 + } else { 125 + resolve(); 126 + } 127 + }); 129 129 }); 130 130 } 131 131 } 132 132 }; 133 133 134 - var endValidation = function(error) { 135 - if (error) { 136 - error.show(); 137 - } 138 - appNameInput.removeClass('loading'); 139 - submitButton.prop('disabled', !!error); 140 - }; 141 - 142 - var validationTimeout; 143 143 var scheduleValidation = function() { 144 - clearTimeout(validationTimeout);134 + // Hide all error messages before starting the asynchronous validation. 145 145 renameAppModal.find('.xErrorMsg').hide(); 146 - appNameInput.addClass('loading'); 147 - submitButton.prop('disabled', true); 148 - validationTimeout = setTimeout(startValidation, 500); 136 + appNameInput.addClass('loading').validateAsync(startValidation, 500, 'awm').catch((error) => { 137 + error.show(); 138 + }).finally(() => { 139 + appNameInput.removeClass('loading'); 140 + }); 149 149 }; 150 150 151 151 appNameInput.add(appParentInput).on('input', scheduleValidation); ... ... @@ -167,7 +167,7 @@ 167 167 renameAppModal.modal('show'); 168 168 }); 169 169 170 - renameAppModal.find('form').submit (function(event) {162 + renameAppModal.find('form').on('submit', function(event) { 171 171 event.preventDefault(); 172 172 renameApp(getRenameData($(this))); 173 173 }); ... ... @@ -194,13 +194,13 @@ 194 194 195 195 var renameApp = function(data) { 196 196 // Disable the form to prevent it from being submitted twice. 197 - renameAppModal.find(' :input').prop('disabled', true);189 + renameAppModal.find('fieldset').prop('disabled', true); 198 198 var notification = new XWiki.widgets.Notification( 199 199 $jsontool.serialize($services.localization.render('appWithinMinutes.renameApp.inProgress')), 200 200 'inprogress' 201 201 ); 202 202 var renameAppURL = new XWiki.Document('RenameApplication', 'AppWithinMinutes').getURL('get'); 203 - $.post(renameAppURL, data).then(updateAppHomePage). done(function() {195 + Promise.resolve($.post(renameAppURL, data)).then(updateAppHomePage).then(function() { 204 204 renameAppModal.modal('hide'); 205 205 notification.replace(new XWiki.widgets.Notification( 206 206 $jsontool.serialize($services.localization.render('appWithinMinutes.renameApp.done')), ... ... @@ -210,33 +210,29 @@ 210 210 setTimeout(function() { 211 211 window.location.href = getNewAppHomePage().getURL(); 212 212 }, 0); 213 - }). fail(function() {205 + }).catch(function() { 214 214 notification.replace(new XWiki.widgets.Notification( 215 215 $jsontool.serialize($services.localization.render('appWithinMinutes.renameApp.failed')), 216 216 'error' 217 217 )); 218 - }).al ways(function() {210 + }).finally(function() { 219 219 // Re-enable the form. 220 - renameAppModal.find(' :input').prop('disabled', false);212 + renameAppModal.find('fieldset').prop('disabled', false); 221 221 }); 222 222 }; 223 223 224 224 var updateAppHomePage = function() { 225 - var deferred = $.Deferred(); 226 - var newAppHomePageEditURL = getNewAppHomePage().getURL('edit'); 227 - $('<div/>').load(newAppHomePageEditURL + ' #inline', function() { 228 - var formData = $(this).children('form#inline').serializeArray(); 229 - if (formData.length > 0) { 230 - formData.push({name: 'xaction_save', value: true}); 231 - $.post(newAppHomePageEditURL, formData).done(function() { 232 - deferred.resolve(); 233 - }).fail(function() { 234 - deferred.reject(); 235 - }); 236 - } else { 237 - deferred.reject(); 238 - } 217 + return new Promise((resolve, reject) => { 218 + var newAppHomePageEditURL = getNewAppHomePage().getURL('edit'); 219 + $('<div/>').load(newAppHomePageEditURL + ' #inline', function() { 220 + var formData = $(this).children('form#inline').serializeArray(); 221 + if (formData.length) { 222 + formData.push({name: 'xaction_save', value: true}); 223 + $.post(newAppHomePageEditURL, formData).then(resolve, reject); 224 + } else { 225 + reject(); 226 + } 227 + }); 239 239 }); 240 - return deferred.promise(); 241 241 } 242 242 });
- XWiki.StyleSheetExtension[0]
-
- Code
-
... ... @@ -16,30 +16,10 @@ 16 16 background: none no-repeat scroll 0 center transparent; 17 17 display: block; 18 18 font-size: .8em; 19 - padding: .3em .3em .3em 20px;19 + padding: .3em .3em .3em .3em; 20 20 text-transform: uppercase; 21 21 } 22 22 23 -#actionBox .action.edit { 24 - background-image: url("$xwiki.getSkinFile('icons/silk/application_edit.png')"); 25 -} 26 - 27 -#actionBox .action.translate { 28 - background-image: url("$doc.getAttachmentURL('locate.png')"); 29 -} 30 - 31 -#actionBox .action.delete { 32 - background-image: url("$xwiki.getSkinFile('icons/silk/application_delete.png')"); 33 -} 34 - 35 -#actionBox .action.deleteData { 36 - background-image: url("$xwiki.getSkinFile('icons/silk/application_form_delete.png')"); 37 -} 38 - 39 -#actionBox .action.add { 40 - background-image: url("$xwiki.getSkinFile('icons/silk/add.png')"); 41 -} 42 - 43 43 #entryNamePopup { 44 44 margin-right: 20px; 45 45 }