PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.8
VikBooking Hotel Booking Engine & PMS v1.8.8
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
vikbooking / admin / layouts / taskmanager / calendar / day.php

day.php in VikBooking Hotel Booking Engine & PMS 1.8.8, at admin/layouts/taskmanager/calendar/day.php

471 lines 19.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2025 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Obtain vars from arguments received in the layout file.
16 *
17 * @var array $data The data for rendering the tasks of a given day within a calendar.
18 */
19 extract($displayData);
20
21 // get the task area IDs
22 $areaIds = [];
23
24 if (is_array($data['area_ids'] ?? null)) {
25 $areaIds = $data['area_ids'];
26 }
27
28 // get the filters for rendering the calendar tasks
29 $filters = (array) ($data['filters'] ?? []);
30
31 // determine the day to load
32 $dayYmd = $filters['calendar_day'] ?? date('Y-m-d');
33 if (($filters['dates'] ?? null) && !($filters['calendar_day'] ?? null)) {
34 // when no calendar day filter set, use the current dates filter
35 list($fromDt, $toDt) = VBOTaskModelTask::getInstance()->getFilterDatesInterval((string) $filters['dates'], $local = true, $sql = false);
36 if ($fromDt) {
37 $dayYmd = date('Y-m-d', strtotime($fromDt));
38 }
39 }
40
41 // build the pool of day tasks
42 $dayTasks = [];
43
44 if ($areaIds) {
45 // save current filters in the user state
46 JFactory::getApplication()->setUserState('vbo.tm.filters', $filters);
47
48 $filterOptions = array_merge($filters, [
49 'id_areas' => $areaIds,
50 'dates' => $dayYmd . ':' . $dayYmd,
51 'calendar' => true,
52 ]);
53
54 // load tasks according to filters, by always forcing/injecting the area IDs and the dates
55 foreach (VBOTaskModelTask::getInstance()->filterItems($filterOptions, 0, 0) as $taskRecord) {
56 // push task registry
57 $dayTasks[] = VBOTaskTaskregistry::getInstance((array) $taskRecord);
58 }
59 }
60
61 // obtain the current day information
62 $dayDate = getdate(strtotime($dayYmd));
63
64 // get navigation dates
65 $todayYmd = date('Y-m-d');
66 $dayBack = date('Y-m-d', strtotime('-1 day', $dayDate[0]));
67 $dayNext = date('Y-m-d', strtotime('+1 day', $dayDate[0]));
68
69 // build the task objects, if any, for JS positioning
70 $taskObjects = [];
71 foreach ($dayTasks as $dayTask) {
72 $duration = $dayTask->getDuration();
73
74 $start = $dayTask->getDueDate(true, 'Y-m-d\TH:i:s');
75 $begin = $dayTask->getBeginDate(true, 'Y-m-d\TH:i:s');
76
77 if ($begin) {
78 $start = $begin;
79 }
80
81 $startDate = JFactory::getDate($start);
82
83 // get selected date at midnight
84 $dateMidnight = JFactory::getDate($dayYmd . ' 00:00:00');
85
86 // check whether the event starts on a previous date
87 if ($startDate < $dateMidnight) {
88 // decrease duration by the difference between today and the real start date
89 $diff = $startDate->diff($dateMidnight);
90 $duration -= $diff->days * 1440 + $diff->h * 60 + $diff->i;
91
92 // use today at midnight
93 $startDate = clone $dateMidnight;
94 }
95
96 if ($duration <= 0) {
97 // ignore the event in case of invalid duration
98 continue;
99 }
100
101 $endDate = clone $startDate;
102 $endDate->modify('+' . $duration . ' minutes');
103
104 // extract hours and minutes
105 list($hour, $min) = explode(':', $startDate->format('G:i'));
106
107 $taskObjects[] = [
108 'id' => $dayTask->getID(),
109 'start' => $startDate->format('Y-m-d\TH:i:s'),
110 'end' => $endDate->format('Y-m-d\TH:i:s'),
111 'hour' => (int) $hour,
112 'min' => (int) $min,
113 'duration' => (int) $duration,
114 'html' => JLayoutHelper::render('taskmanager.calendar.day.task', ['task' => $dayTask]),
115 ];
116 }
117
118 ?>
119
120 <div class="vbo-tm-calendar-wrap">
121 <div class="vbo-tm-calendar-head">
122 <div class="vbo-tm-calendar-info">
123 <a href="javascript:void(0)" class="vbo-tm-calendar-day-back" data-month="<?php echo date('Y-m-01', $dayDate[0]); ?>">
124 <?php echo VikBookingIcons::e('chevron-left'); ?>
125 </a>
126 <span class="vbo-tm-calendar-day-day"><?php echo VikBooking::sayWeekDay($dayDate['wday']) . ', ' . $dayDate['mday']; ?></span>
127 <span class="vbo-tm-calendar-day-month"><?php echo VikBooking::sayMonth($dayDate['mon']); ?></span>
128 <span class="vbo-tm-calendar-day-year"><?php echo $dayDate['year']; ?></span>
129 </div>
130
131 <div class="vbo-tm-calendar-nav">
132 <div class="vbo-tm-calendar-nav-btns">
133 <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-nav-back" data-day="<?php echo $dayBack; ?>"><?php VikBookingIcons::e('chevron-left'); ?></span>
134 <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-nav-today" data-day="<?php echo $todayYmd; ?>"><?php echo JText::translate('VBTODAY'); ?></span>
135 <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-nav-next" data-day="<?php echo $dayNext; ?>"><?php VikBookingIcons::e('chevron-right'); ?></span>
136 </div>
137 </div>
138 </div>
139
140 <div class="vbo-tm-calendar-day-timeline">
141 <div class="vbo-tm-calendar-day-timeline-week">
142
143 <?php
144 // display up to 3 week-days in the past
145 for ($d = 3; $d > 0; $d--) {
146 $timeline_date = getdate(strtotime(sprintf('-%d %s', $d, ($d == 1 ? 'day' : 'days')), $dayDate[0]));
147 ?>
148 <div class="vbo-tm-calendar-day-timeline-week-day">
149 <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-day-timeline-week-nav" data-day="<?php echo date('Y-m-d', $timeline_date[0]); ?>">
150 <span class="vbo-tm-calendar-day-timeline-week-nav-mday"><?php echo $timeline_date['mday']; ?></span>
151 <span class="vbo-tm-calendar-day-timeline-week-nav-wday"><?php echo VikBooking::sayWeekDay($timeline_date['wday'], true); ?></span>
152 </span>
153 </div>
154 <?php
155 }
156 ?>
157 <div class="vbo-tm-calendar-day-timeline-week-day">
158 <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-day-timeline-week-nav vbo-tm-calendar-day-timeline-week-today" data-day="<?php echo $todayYmd; ?>">
159 <span class="vbo-tm-calendar-day-timeline-week-nav-mday"><?php echo $dayDate['mday']; ?></span>
160 <span class="vbo-tm-calendar-day-timeline-week-nav-wday"><?php echo VikBooking::sayWeekDay($dayDate['wday'], true); ?></span>
161 </span>
162 </div>
163 <?php
164 // display up to 3 week-days ahead
165 for ($d = 1; $d < 4; $d++) {
166 $timeline_date = getdate(strtotime(sprintf('+%d %s', $d, ($d == 1 ? 'day' : 'days')), $dayDate[0]));
167 ?>
168 <div class="vbo-tm-calendar-day-timeline-week-day">
169 <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-day-timeline-week-nav" data-day="<?php echo date('Y-m-d', $timeline_date[0]); ?>">
170 <span class="vbo-tm-calendar-day-timeline-week-nav-mday"><?php echo $timeline_date['mday']; ?></span>
171 <span class="vbo-tm-calendar-day-timeline-week-nav-wday"><?php echo VikBooking::sayWeekDay($timeline_date['wday'], true); ?></span>
172 </span>
173 </div>
174 <?php
175 }
176 ?>
177 </div>
178 <div class="vbo-tm-calendar-day-timeline-rows">
179 <?php
180 for ($h = 0; $h < 24; $h++) {
181 ?>
182 <div class="vbo-tm-calendar-day-timeline-row" data-hour="<?php echo $h; ?>">
183 <div class="vbo-tm-calendar-day-hour">
184 <strong><?php echo str_pad($h, 2, '0', STR_PAD_LEFT) . ':' . '00'; ?></strong>
185 </div>
186 <div class="vbo-tm-calendar-day-tasks"></div>
187 </div>
188 <?php
189 }
190 ?>
191 </div>
192 </div>
193 </div>
194
195 <script type="text/javascript">
196 jQuery(function($) {
197 // setup the task (events) within the calendar daily layout
198 window.taskManagerSetupCalendar(<?php echo json_encode($taskObjects); ?>);
199
200 /**
201 * Register listeners for day navigation buttons.
202 */
203 document
204 .querySelectorAll('.vbo-tm-calendar-nav-btn')
205 .forEach((nav) => {
206 // get the navigation day
207 const day = nav.getAttribute('data-day');
208
209 nav.addEventListener('click', () => {
210 VBOCore.emitEvent('vbo-tm-apply-filters', {
211 filters: {
212 calendar_day: day,
213 },
214 });
215 });
216 });
217
218 /**
219 * Register listeners for month calendar type.
220 */
221 document
222 .querySelectorAll('.vbo-tm-calendar-day-back')
223 .forEach((mname) => {
224 // get the navigation month
225 const month = mname.getAttribute('data-month');
226
227 mname.addEventListener('click', () => {
228 VBOCore.emitEvent('vbo-tm-apply-filters', {
229 filters: {
230 calendar_type: 'month',
231 calendar_month: month,
232 calendar_day: '',
233 },
234 });
235 });
236 });
237
238 /**
239 * Register listeners for editing an existing task.
240 */
241 document
242 .querySelectorAll('.vbo-tm-calendar-day-task[data-task-id]')
243 .forEach((taskElement) => {
244 if (taskElement.clickListener) {
245 // listener added already
246 return;
247 }
248
249 // get the clicked task and area IDs
250 const taskId = taskElement.getAttribute('data-task-id');
251 const areaId = taskElement.getAttribute('data-area-id');
252
253 if (!taskId || !areaId) {
254 // missing task details
255 return;
256 }
257
258 taskElement.addEventListener('click', (event) => {
259 event.preventDefault();
260 event.stopPropagation();
261
262 // define the modal delete button
263 let delete_btn = $('<button></button>')
264 .attr('type', 'button')
265 .addClass('btn btn-danger')
266 .text(<?php echo json_encode(JText::translate('VBELIMINA')); ?>)
267 .on('click', function() {
268 if (!confirm(<?php echo json_encode(JText::translate('VBDELCONFIRM')); ?>)) {
269 return false;
270 }
271
272 // disable button to prevent double submissions
273 let submit_btn = $(this);
274 submit_btn.prop('disabled', true);
275
276 // start loading animation
277 VBOCore.emitEvent('vbo-tm-edittask-loading');
278
279 // make the request
280 VBOCore.doAjax(
281 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.deleteTask'); ?>",
282 {
283 data: {
284 id: taskId,
285 },
286 },
287 (resp) => {
288 // trigger filters-changed event on success
289 VBOCore.emitEvent('vbo-tm-filters-changed', {
290 filters: vboTmFilters,
291 });
292
293 // dismiss the modal
294 VBOCore.emitEvent('vbo-tm-edittask-dismiss');
295 },
296 (error) => {
297 // display error message
298 alert(error.responseText);
299
300 // re-enable submit button
301 submit_btn.prop('disabled', false);
302
303 // stop loading
304 VBOCore.emitEvent('vbo-tm-edittask-loading');
305 }
306 );
307 });
308
309 // define the modal save button
310 let save_btn = $('<button></button>')
311 .attr('type', 'button')
312 .addClass('btn btn-success')
313 .text(<?php echo json_encode(JText::translate('VBSAVE')); ?>)
314 .on('click', function() {
315 // disable button to prevent double submissions
316 let submit_btn = $(this);
317 submit_btn.prop('disabled', true);
318
319 // start loading animation
320 VBOCore.emitEvent('vbo-tm-edittask-loading');
321
322 // get form data
323 const taskForm = new FormData(document.querySelector('#vbo-tm-task-manage-form'));
324
325 // build query parameters for the request
326 let qpRequest = new URLSearchParams(taskForm);
327
328 // make sure the request always includes the assignees query parameter, even if the list is empty
329 if (!qpRequest.has('data[assignees][]')) {
330 qpRequest.append('data[assignees][]', []);
331 }
332
333 // make sure the request always includes the tags query parameter, even if the list is empty
334 if (!qpRequest.has('data[tags][]')) {
335 qpRequest.append('data[tags][]', []);
336 }
337
338 // make the request
339 VBOCore.doAjax(
340 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.updateTask'); ?>",
341 qpRequest.toString(),
342 (resp) => {
343 // trigger filters-changed event on success
344 VBOCore.emitEvent('vbo-tm-filters-changed', {
345 filters: vboTmFilters,
346 });
347
348 // dismiss the modal
349 VBOCore.emitEvent('vbo-tm-edittask-dismiss');
350 },
351 (error) => {
352 // display error message
353 alert(error.responseText);
354
355 // re-enable submit button
356 submit_btn.prop('disabled', false);
357
358 // stop loading
359 VBOCore.emitEvent('vbo-tm-edittask-loading');
360 }
361 );
362 });
363
364 // display modal
365 let modalBody = VBOCore.displayModal({
366 suffix: 'tm_edittask_modal',
367 title: <?php echo json_encode(JText::translate('VBO_TASK')); ?> + ' #' + taskId,
368 extra_class: 'vbo-modal-rounded vbo-modal-taller vbo-modal-large',
369 body_prepend: true,
370 lock_scroll: true,
371 escape_dismiss: false,
372 footer_left: delete_btn,
373 footer_right: save_btn,
374 loading_event: 'vbo-tm-edittask-loading',
375 dismiss_event: 'vbo-tm-edittask-dismiss',
376 });
377
378 // start loading animation
379 VBOCore.emitEvent('vbo-tm-edittask-loading');
380
381 // make the request
382 VBOCore.doAjax(
383 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.renderLayout'); ?>",
384 {
385 type: 'tasks.managetask',
386 data: {
387 task_id: taskId,
388 area_id: areaId,
389 form_id: 'vbo-tm-task-manage-form',
390 },
391 },
392 (resp) => {
393 // stop loading
394 VBOCore.emitEvent('vbo-tm-edittask-loading');
395
396 try {
397 // decode the response (if needed), and append the content to the modal body
398 let obj_res = typeof resp === 'string' ? JSON.parse(resp) : resp;
399 modalBody.append(obj_res['html']);
400 } catch (err) {
401 console.error('Error decoding the response', err, resp);
402 }
403 },
404 (error) => {
405 // display error message
406 alert(error.responseText);
407
408 // stop loading
409 VBOCore.emitEvent('vbo-tm-edittask-loading');
410 }
411 );
412 });
413
414 // turn flag on for listener set
415 taskElement.clickListener = true;
416 });
417
418 let newTaskButtons = [], selectedTime = null;
419
420 newTaskButtons.push({
421 text: <?php echo json_encode(JText::translate('VBO_NEW_TASK')); ?>,
422 class: 'btngroup',
423 disabled: true,
424 });
425
426 vboTmAllAreas.forEach((area) => {
427 // push area button
428 newTaskButtons.push({
429 areaId: area.id,
430 class: 'vbo-context-menu-entry-secondary',
431 icon: '<?php echo VikBookingIcons::i('plus-circle'); ?>',
432 text: area.name,
433 action: (root, event) => {
434 VBOCore.emitEvent('vbo-tm-newtask-trigger', {
435 areaId: area.id,
436 filters: Object.assign({}, vboTmFilters, {
437 calendar_time: selectedTime,
438 }),
439 });
440 },
441 });
442 });
443
444 /**
445 * Register context menu on each hour.
446 */
447 document
448 .querySelectorAll('.vbo-tm-calendar-day-tasks')
449 .forEach((tasks) => {
450 $(tasks).vboContextMenu({
451 buttons: newTaskButtons,
452 class: 'vbo-dropdown-cxmenu',
453 onShow: (root, popup, event) => {
454 // calculate the time close to the clicked point
455 let hour = parseInt($(root).closest('[data-hour]').attr('data-hour')) || 0;
456 let min = Math.round(Math.floor(event.originalEvent.offsetY / $(root).height() * 60) / 15) * 15;
457
458 if (min == 60) {
459 hour++;
460 min = 0;
461 }
462
463 // format time
464 selectedTime = (hour.toString().padStart(2, '0')) + ':' + (min.toString().padStart(2, '0'));
465 },
466 });
467 });
468
469 });
470 </script>
471