Änderungen von Dokument Calendar Macro

Zuletzt geändert von xwikiadmin am 2025/12/11 06:30

Von Version 5.1
bearbeitet von xwikiadmin
am 2025/01/07 11:32
Änderungskommentar: Install extension [com.xwiki.mocca-calendar:application-mocca-calendar-ui/2.15]
Auf Version 8.1
bearbeitet von xwikiadmin
am 2025/12/03 10:04
Änderungskommentar: Install extension [com.xwiki.mocca-calendar:application-mocca-calendar-ui/2.18.0]

Zusammenfassung

Details

XWiki.JavaScriptExtension[0]
Code
... ... @@ -16,8 +16,6 @@
16 16   XWiki.MoccaCalendar = {};
17 17   }
18 18  
19 -#template('colorThemeInit.vm')
20 -
21 21   XWiki.MoccaCalendar.Helper = Class.create({
22 22   initialize: function(calendar, dateFormat, jsonServiceUrl, createEventBaseUrl, updateEventUrl, newPageNameUrl, dateCheckUrl, deleteEventInstanceUrl, newPageParams, formToken) {
23 23   this.calendar = calendar;
... ... @@ -158,7 +158,6 @@
158 158   },
159 159   {
160 160   verticalPosition: "top",
161 - backgroundColor: "$theme.pageHeaderBackgroundColor",
162 162   title : this.interactionParameters.editMode ? "$escapetool.javascript($services.localization.render('MoccaCalendar.calendarevent.create'))" : "$escapetool.javascript($services.localization.render('MoccaCalendar.calendarevent.view'))",
163 163   removeOnClose : true,
164 164   onClose : function() {
... ... @@ -301,6 +301,24 @@
301 301   }
302 302   this.content.title.value = purify.sanitize(this.content.title.value)
303 303   this.content.writeAttribute('action', saveUrl + '&xpage=plain&ajax=true');
301 + let formData = new FormData(this.content);
302 + if (formData.get('MoccaCalendar.MoccaCalendarEventClass_0_allDay') == 1) {
303 + this.content.querySelectorAll('input.datetime').forEach(dateInput => {
304 + // Convert the date fields from the displayed format to the one accepted by the backend.
305 + const dateObj = moment(dateInput.value, moment().toMomentFormatString(dateInput.getAttribute('data-format')));
306 + if (dateInput.id == "MoccaCalendar.MoccaCalendarEventClass_0_endDate") {
307 + // For the end date, set the hour to 23:59 instead of 00:00.
308 + dateObj.hours(23);
309 + dateObj.minutes(59);
310 + }
311 + const calendarEventObjectDateFormat = moment().toMomentFormatString(this.helper.dateFormat);
312 + // Add a hidden element to the DOM to avoid flickering.
313 + const tempElement = dateInput.clone();
314 + tempElement.addClassName('hidden');
315 + tempElement.value = dateObj.isValid() ? dateObj.format(calendarEventObjectDateFormat) : "";
316 + dateInput.parentElement.insertBefore(tempElement, dateInput);
317 + });
318 + }
304 304   this.content.request({
305 305   onSuccess: function() {
306 306   this.saving = false;
... ... @@ -746,13 +746,14 @@
746 746  
747 747  });
748 748  
749 -define('mocca-calendar-import-notification', {
750 - prefix: 'MoccaCalendar.import.notification.',
764 +define('mocca-calendar-notification', {
765 + prefix: 'MoccaCalendar.notification.',
751 751   keys: [
752 - 'inprogress',
753 - 'done',
754 - 'error',
755 - 'filetoolarge'
767 + 'import.inprogress',
768 + 'import.done',
769 + 'import.error',
770 + 'import.filetoolarge',
771 + 'addObject.error'
756 756   ]
757 757  });
758 758  
... ... @@ -759,7 +759,7 @@
759 759  /**
760 760   * Delete event and calendar import actions.
761 761   */
762 -require(['jquery', 'xwiki-meta', 'xwiki-job-runner', 'xwiki-l10n!mocca-calendar-import-notification'],
778 +require(['jquery', 'xwiki-meta', 'xwiki-job-runner', 'xwiki-l10n!mocca-calendar-notification'],
763 763   function($, xwikiMeta, JobRunner, l10n) {
764 764   /**
765 765   * Events triggered before deleteEvents modal is shown: save the button that triggers
... ... @@ -796,33 +796,64 @@
796 796   new XWiki.MoccaCalendar.MoccaCalendarPopup(calendarPopup.interactionParameters, calendarPopup.helper);
797 797   });
798 798  
815 + $(document).on('click', '.add-calendar-object-container a', function(event) {
816 + event.preventDefault();
817 + const addObjectButton = $('#add-calendar-object');
818 + const target = addObjectButton.data('target');
819 + // To be adapted to the standard XWiki rest endpoint for object creation after
820 + // XWIKI-20704: NullPointerException (NPE) when accessing objects with ComputedField properties from REST is fixed.
821 + var documentReference = XWiki.Model.resolve('MoccaCalendar.Code.MoccaCalendarObjectCreator',
822 + XWiki.EntityType.DOCUMENT);
823 + var targetUrl = new XWiki.Document(documentReference).getURL();
824 + var params = {
825 + 'documentRef': target
826 + };
827 + $.ajax({
828 + url: targetUrl,
829 + type: 'POST',
830 + data: params,
831 + success: function (response) {
832 + window.location.reload();
833 + },
834 + error: function (xhr, status, error) {
835 + console.error('Failed to add the MoccaCalendarClass object', error);
836 + var notification = new XWiki.widgets.Notification(l10n.get('addObject.error'), 'error');
837 + }
838 + });
839 + });
840 +
799 799   // Trigger the upload manually when the button is clicked.
800 - $(document).on('click', '#import-calendar-file-button', function(event) {
801 - var form = $('#import-calendar-file form').get(0);
802 - if (event) {
803 - event.preventDefault();
804 - }
805 - startUploading(form);
842 + $(document).on('click', '.import-calendar-file-button', function(event) {
843 + event.preventDefault();
844 + var calId = $(event.target).data('calid');
845 + var form = $(`#import-calendar-file-${calId} form`).get(0);
846 + startUploading(form, calId);
806 806   });
807 807  
808 808   // Start uploading this file by creating a new XHR object with the file data.
809 - var startUploading = function (form) {
810 - var formData = new FormData(form);
811 - var select = form.down('#import-calendar-parent');
812 - var input = form.down('#import-ical-file-input');
813 - if (input.files[0].size < input.dataset.maxFileSize) {
850 + var startUploading = function (form, calId) {
851 + var input = form.down(`#import-ical-file-input-${calId}`);
852 + var inputFile = input.files[0];
853 + if (inputFile && inputFile.size < input.dataset.maxFileSize) {
854 + var select = form.down(`#import-calendar-parent-${calId}`);
814 814   var params = {};
815 815   params[select.name] = select.value;
816 - formData.action = form.action + "?" + $.param(params);
857 + let action = form.action + "?" + $.param(params);
817 817   // Create XMLHttpRequest object and POST the data
818 - var request = this.request = new XMLHttpRequest();
819 - request.open('POST', formData.action);
820 - request.send(formData);
821 - checkImportJob(select.value);
859 + var request = new XMLHttpRequest();
860 + request.open('POST', action);
861 + request.onload = function () {
862 + if (request.status === 202 || request.status === 302) {
863 + checkImportJob(select.value);
864 + } else {
865 + new XWiki.widgets.Notification(l10n.get('import.error'),'error');
866 + }
867 + };
868 + request.send(inputFile);
822 822   } else {
823 - var notification = new XWiki.widgets.Notification(l10n.get('filetoolarge'), 'error');
870 + var notification = new XWiki.widgets.Notification(l10n.get('import.filetoolarge'), 'error');
824 824   }
825 - }
872 + };
826 826  
827 827   const checkImportJob = function(selectedCalendar) {
828 828   let documentReference = XWiki.Model.resolve('MoccaCalendar.Code.ImportJobResource', XWiki.EntityType.DOCUMENT);
... ... @@ -837,8 +837,8 @@
837 837   {name: 'data', value: 'jobStatus'},
838 838   {name: 'form_token', value: xwikiMeta.form_token}
839 839   );
840 - var notification = new XWiki.widgets.Notification(l10n.get('inprogress'), 'inprogress');
841 - $('#import-calendar-file-button').prop('disabled', true);
887 + var notification = new XWiki.widgets.Notification(l10n.get('import.inprogress'), 'inprogress');
888 + $('.import-calendar-file-button').prop('disabled', true);
842 842   return Promise.resolve(new JobRunner({
843 843   createStatusRequest: function(jobId) {
844 844   return {
... ... @@ -856,13 +856,13 @@
856 856   throw new Error(response.error.message);
857 857   } else {
858 858   document.dispatchEvent(new Event('calendarImportCompleted'));
859 - notification.replace(new XWiki.widgets.Notification(l10n.get('done'),'done'));
906 + notification.replace(new XWiki.widgets.Notification(l10n.get('import.done'),'done'));
860 860   }
861 861   }).catch((reason) => {
862 - notification.replace(new XWiki.widgets.Notification(l10n.get('error'),'error'));
909 + notification.replace(new XWiki.widgets.Notification(l10n.get('import.error'),'error'));
863 863   return Promise.reject(reason);
864 864   }).finally(() => {
865 - $('#import-calendar-file-button').prop('disabled', false);
912 + $('.import-calendar-file-button').prop('disabled', false);
866 866   });
867 867   };
868 868  });
XWiki.StyleSheetExtension[0]
Code
... ... @@ -26,7 +26,12 @@
26 26   margin-top: 0.6em;
27 27  }
28 28  .xdialog-box-moccacal-modal-popup {
29 + background-color: @modal-content-bg;
30 + border-color: @modal-content-border-color;
29 29   width: 600px;
32 + top: 3vh !important;
33 + bottom: 3vh !important;
34 + max-height: 94vh;
30 30  }
31 31  
32 32  /* Make sure the date time picker is shown on top of the event modal. */
... ... @@ -90,13 +90,45 @@
90 90  }
91 91  
92 92  .import-form select,
93 -.import-form #import-ical-file-input {
98 +.import-form input {
94 94   width: 100%;
95 95   margin-bottom: 1.6em;
96 96   border-radius: 0.4em;
97 97  }
98 98  
99 -.import-form #import-ical-file-input {
104 +.import-form input {
100 100   padding: 0.6em 1em;
101 101   border: 1px solid #ccc;
102 102  }
108 +
109 +#recurrentDays dd {
110 + display: flex;
111 + gap: 0.3rem;
112 + margin-top: 1rem;
113 + justify-content: space-between;
114 +}
115 +
116 +#recurrentDays .xwiki-form-listclass {
117 + display: inline-flex;
118 + align-items: center;
119 + justify-content: center;
120 + padding: 0.8rem 1.1rem;
121 + background-color: @btn-default-bg;
122 + color: @btn-default-color;
123 + border-radius: 6px;
124 + cursor: pointer;
125 + user-select: none;
126 + transition: all 0.2s ease;
127 + position: relative;
128 +}
129 +
130 +#recurrentDays .xwiki-form-listclass input[type="checkbox"] {
131 + position: absolute;
132 + opacity: 0;
133 + pointer-events: none;
134 +}
135 +
136 +#recurrentDays .xwiki-form-listclass:has(input[type="checkbox"]:checked) {
137 + background-color: @btn-primary-bg;
138 + color: @btn-primary-color;
139 +}
Content Type
... ... @@ -1,1 +1,1 @@
1 -CSS
1 +LESS
Inhalt parsen
... ... @@ -1,0 +1,1 @@
1 +Nein
XWiki.WikiMacroClass[0]
Makro-Code
... ... @@ -1,4 +1,11 @@
1 1  {{velocity output="false"}}
2 +#set ($parameterMap = {})
3 +#if ($themeDoc)
4 + ## If a theme doc is found, we add the theme to the ssx requests to make sure that the cache is properly updated.
5 + ## This code can be removed when the following platform issue is fixed:
6 + ## XWIKI-18668: The changes on the color theme are not taken into account for various ss*x resources
7 + #set ($discard = $parameterMap.put('colorTheme', $themeDocFullName))
8 +#end
2 2  $xwiki.jsx.use("Calendar.FullCalendar", {'defer': false, 'minify': false})
3 3  $xwiki.jsx.use("MoccaCalendar.Code.Macro", {'defer': false, 'v' : '2.7'})
4 4  $xwiki.jsx.use("MoccaCalendar.Code.DatePickerExtension", {'defer': false})
... ... @@ -5,8 +5,8 @@
5 5  $xwiki.jsx.use("MoccaCalendar.MoccaCalendarEventSheet")
6 6  $xwiki.jsx.use("MoccaCalendar.Code.MoccaCalendarEventModificationClass")
7 7  #dateTimePicker_import()
8 -$xwiki.ssx.use("Calendar.FullCalendar")
9 -$xwiki.ssx.use("MoccaCalendar.Code.Macro")
15 +$xwiki.ssx.use("Calendar.FullCalendar", $parameterMap)
16 +$xwiki.ssx.use("MoccaCalendar.Code.Macro", $parameterMap)
10 10  #set($calcounter = $request.getAttribute('MoccaCalendar.Code.Macro:counter'))
11 11  #if(!$calcounter) #set($calcounter = 0) #else #set($calcounter = $calcounter + 1) #end
12 12  #set($discard = $request.setAttribute('MoccaCalendar.Code.Macro:counter', $calcounter))
... ... @@ -153,7 +153,7 @@
153 153  #end
154 154  
155 155  #macro(importCalendarFileModal)
156 - <div class="modal fade" id="import-calendar-file" tabindex="-1" role="dialog">
163 + <div class="modal fade" id="import-calendar-file-$calcounter" tabindex="-1" role="dialog">
157 157   <div class="modal-dialog">
158 158   <div class="modal-content">
159 159   <div class="modal-header">
... ... @@ -166,32 +166,46 @@
166 166   #set ($escapedCalendarSpace = $escapetool.xml($doc.getSpace()))
167 167   #set ($escapedCalendarName = $escapetool.xml($doc.getDocumentReference().getName()))
168 168   #set ($actionURL = "$request.getContextPath()/rest/moccacalendar/import")
169 - <form class="xform" action="$actionURL" method="post">
176 + #set ($xwikiCalendarDoc = $xwiki.getDocument($calendarDoc))
177 + #set ($calendarObject = $xwikiCalendarDoc.getObject('MoccaCalendar.MoccaCalendarClass'))
178 + <form class="xform" action="${actionURL}" method="post">
170 170   <div class="import-form">
171 - <label for="import-calendar-parent">
172 - $escapetool.xml($services.localization.render('MoccaCalendar.calendar'))</label>
173 - <select id="import-calendar-parent" name="parentCalendar">
174 - #foreach ($item in $services.moccacalendar.getAllCalendars()) ## TODO: add filter here, see MOCCACAL-76
175 - #set ($itemdoc = $xwiki.getDocument($item))
176 - #if ($!{itemdoc} && ${itemdoc.hasAccessLevel("edit")})
177 - #set ($selected="")
178 - #if ($itemdoc.getId() == $doc.getId())
179 - #set ($selected=" selected='selected'")
180 + #set ($calendarReferences = [])
181 + #if ($filter == 'wiki')
182 + #set ($calendarReferences = $services.moccacalendar.getAllCalendars())
183 + #elseif ($filter == 'space')
184 + #set ($calendarReferences = $services.moccacalendar.getAllCalendarsInDocumentSpace($services.model.resolveDocument($calendarDoc)))
185 + #end
186 + #if ($calendarReferences.size() > 0)
187 + <label for="import-calendar-parent-${calcounter}">
188 + $escapetool.xml($services.localization.render('MoccaCalendar.calendar'))</label>
189 + <select id="import-calendar-parent-${calcounter}" name="parentCalendar">
190 + #foreach ($item in $calendarReferences)
191 + #set ($itemdoc = $xwiki.getDocument($item))
192 + #if ($!{itemdoc} && ${itemdoc.hasAccessLevel("edit")})
193 + #set ($selected="")
194 + #if ($itemdoc.getId() == $doc.getId())
195 + #set ($selected=" selected='selected'")
196 + #end
197 + <option value="$escapetool.xml($services.model.serialize($itemdoc.getDocumentReference(),'default'))"$selected>
198 + $itemdoc.getDisplayTitle()</option>
180 180   #end
181 - <option value="$escapetool.html($itemdoc.getFullName())"$selected>
182 - $itemdoc.getDisplayTitle()</option>
183 183   #end
184 - #end
185 - </select>
186 - <label for="import-ical-file-input">$escapetool.xml($services.localization.render(
201 + </select>
202 + #else
203 + <select id="import-calendar-parent-${calcounter}" name="parentCalendar" hidden>
204 + <option value="$escapetool.xml($services.model.serialize($services.model.resolveDocument($calendarDoc),'default'))" selected></option>
205 + </select>
206 + #end
207 + <label for="import-ical-file-input-${calcounter}">$escapetool.xml($services.localization.render(
187 187   'MoccaCalendar.import.modal.file.label'))</label>
188 - <input type="file" id="import-ical-file-input" accept=".ics" name="importedfile"
209 + <input type="file" id="import-ical-file-input-${calcounter}" accept=".ics" name="importedfile"
189 189   data-max-file-size="$!escapetool.xml($xwiki.getSpacePreference('upload_maxsize'))">
190 190   </div>
191 191   </form>
192 192   </div>
193 193   <div class="modal-footer">
194 - <input type="button" class="btn btn-primary" id="import-calendar-file-button"
215 + <input type="button" class="btn btn-primary import-calendar-file-button" data-calid="${calcounter}"
195 195   value="$escapetool.xml($services.localization.render('MoccaCalendar.import.modal.button.start'))">
196 196   <input type="button" class="btn btn-default"
197 197   value="$escapetool.xml($services.localization.render('cancel'))" data-dismiss="modal">
... ... @@ -200,6 +200,16 @@
200 200   </div>
201 201   </div>
202 202  #end
224 +#macro (addCalendarObject $docRef)
225 + {{html clean=false wiki=true}}
226 + <input type="hidden" id="add-calendar-object" data-target="$docRef">
227 +
228 + {{info cssClass="add-calendar-object-container"}}
229 + $escapetool.xml($services.localization.render('rendering.macro.moccacalendar.addObject.description'))
230 + [[**$escapetool.xml($services.localization.render('rendering.macro.moccacalendar.addObject.button'))**>>$docRef]]
231 + {{/info}}
232 + {{/html}}
233 +#end
203 203  {{/velocity}}
204 204  
205 205  {{velocity}}
... ... @@ -262,12 +262,18 @@
262 262  #if (!$services.licensing.licensor.hasLicensureForEntity($mainReference))
263 263   {{missingLicenseMessage extensionName="moccacalendar.extension.name"/}}
264 264  #else
265 -#if($xcontext.action=='view'){{html clean="false" wiki="false"}}
296 +#if($xcontext.action=='view')
297 +#set ($xwikiCalendarDoc = $xwiki.getDocument($calendarDoc))
298 +#set ($calendarObject = $xwikiCalendarDoc.getObject('MoccaCalendar.MoccaCalendarClass'))
299 +#if ($filter == 'page' && !$calendarObject)
300 + #addCalendarObject($calendarDoc)
301 +#end
302 +{{html clean="false" wiki="false"}}
266 266  #if($canCreateEvents)
267 267  ## create event link
268 268  <div class="calendar-buttons">
269 269  <span class="buttonwrapper">
270 -<button data-toggle="modal" data-target="#import-calendar-file"
307 +<button data-toggle="modal" data-target="#import-calendar-file-$calcounter"
271 271   title="$escapetool.xml($services.localization.render('MoccaCalendar.calendarevent.import.title'))">
272 272   $escapetool.html($services.localization.render('MoccaCalendar.calendarevent.import'))</button>
273 273  <button class="btn btn-success" id="calendar${calcounter}-btn"><span class="glyphicon glyphicon-plus"></span> $escapetool.html($services.localization.render('MoccaCalendar.calendarevent.create'))</button>
... ... @@ -448,7 +448,7 @@
448 448   });
449 449   jQuery('#calendar${calcounter}-btn').click( function(e) { calendarHelper.showCreateEvent(); e.preventDefault(); });
450 450   // helper to be used in callback above
451 -#set($newPageParams = "template=MoccaCalendar.MoccaCalendarEventTemplate&parentFROM=${escapetool.url($calendarDoc)}&form_token=${services.csrf.getToken()}&ocalcaction=create&disabled=$disableCreationEvent")
488 +#set($newPageParams = "template=MoccaCalendar.MoccaCalendarEventTemplate&parentFROM=${escapetool.url($calendarDoc)}&calendarParentFilter=${escapetool.url($filter)}&form_token=${services.csrf.getToken()}&ocalcaction=create&disabled=$disableCreationEvent")
452 452  #set($randomDocUrl = $xwiki.getURL("randomPage${util.generateRandomString(10)}",'edit',$newPageParams))
453 453  #set($updateUrlParams="?xpage=plain&outputSyntax=plain&calendarDoc=${escapetool.url(${calendarDoc})}&")
454 454   var calendarHelper = new XWiki.MoccaCalendar.Helper(calendar,
... ... @@ -465,9 +465,9 @@
465 465   });
466 466  });
467 467  </script>
505 +#importCalendarFileModal
468 468  #if($calcounter == 0)
469 469   #showDeleteEventsModal
470 - #importCalendarFileModal
471 471  #end
472 472  {{/html}}
473 473  #else ## of #if($xcontext.action=='view')