PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / helpers / libraries / calendar / rect.php
vikappointments / site / helpers / libraries / calendar Last commit date
index.html 2 days ago rect.php 2 days ago wrapper.php 2 days ago
rect.php
477 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 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 * Class used to keep a list of events within a calendar rectangle.
16 *
17 * @since 1.6
18 */
19 class CalendarRect
20 {
21 /**
22 * The starting date delimiter.
23 *
24 * @var JDate
25 */
26 protected $start;
27
28 /**
29 * The ending date delimiter.
30 *
31 * @var JDate
32 */
33 protected $end;
34
35 /**
36 * The events list.
37 *
38 * @var array
39 */
40 protected $events;
41
42 /**
43 * Class constructor.
44 *
45 * @param string $start The starting delimiter.
46 * @param string $end The ending delimiter.
47 * @param mixed $event The event(s) to push.
48 *
49 * @uses addEvent()
50 */
51 public function __construct($start, $end, $event = null)
52 {
53 // ini bounds
54 $this->setBounds($start, $end);
55
56 $this->events = array();
57
58 if ($event)
59 {
60 $this->addEvent($event);
61 }
62 }
63
64 /**
65 * Pushes a new event within the internal list.
66 *
67 * @param object The event object.
68 *
69 * @return self This object to support chaining.
70 */
71 public function addEvent($event)
72 {
73 if (!is_array($event))
74 {
75 $event = array($event);
76 }
77
78 foreach ($event as $e)
79 {
80 $this->events[] = $e;
81 }
82
83 return $this;
84 }
85
86 /**
87 * Returns the starting delimiter.
88 *
89 * @param boolean True to return a string, false
90 * to obtain a date object.
91 *
92 * @return mixed Either a date object or a string.
93 */
94 public function start($string = true)
95 {
96 if ($string)
97 {
98 return $this->start->format('Y-m-d H:i:s', true);
99 }
100
101 return $this->start;
102 }
103
104 /**
105 * Returns the hours of the starting delimiter.
106 *
107 * @return integer
108 */
109 public function startH()
110 {
111 return (int) $this->start->format('H', true);
112 }
113
114 /**
115 * Returns the minutes of the starting delimiter.
116 *
117 * @return integer
118 */
119 public function startM()
120 {
121 return (int) $this->start->format('i', true);
122 }
123
124 /**
125 * Returns the hour-min amount of the starting delimiter.
126 *
127 * @return integer
128 *
129 * @uses startH()
130 * @uses startM()
131 */
132 public function startHM()
133 {
134 return $this->startH() * 60 + $this->startM();
135 }
136
137 /**
138 * Returns the ending delimiter.
139 *
140 * @param boolean True to return a string, false
141 * to obtain a date object.
142 *
143 * @return mixed Either a date object or a string.
144 */
145 public function end($string = true)
146 {
147 if ($string)
148 {
149 return $this->end->format('Y-m-d H:i:s', true);
150 }
151
152 return $this->end;
153 }
154
155 /**
156 * Returns the hours of the ending delimiter.
157 *
158 * @return integer
159 */
160 public function endH()
161 {
162 return (int) $this->end->format('H', true);
163 }
164
165 /**
166 * Returns the minutes of the ending delimiter.
167 *
168 * @return integer
169 */
170 public function endM()
171 {
172 return (int) $this->end->format('i', true);
173 }
174
175 /**
176 * Returns the hour-min amount of the ending delimiter.
177 *
178 * @return integer
179 *
180 * @uses endH()
181 * @uses endM()
182 */
183 public function endHM()
184 {
185 return $this->endH() * 60 + $this->endM();
186 }
187
188 /**
189 * Checks if the events are referring to the same day or not.
190 * This method return false in the following case:
191 * - start 2018-07-13 @ 23:00
192 * - end 2018-07-14 @ 02:00
193 *
194 * @return boolean True if the delimiters are within the same day, false otherwise.
195 */
196 public function isSameDay()
197 {
198 return $this->start->format('Ymd', true) == $this->end->format('Ymd', true);
199 }
200
201 /**
202 * Returns the events list.
203 *
204 * @return array
205 */
206 public function events()
207 {
208 return $this->events;
209 }
210
211 /**
212 * Returns the first event or the given property (if specified) of the first event.
213 *
214 * @param string $key The property to get. Null to return the whole object.
215 * @param mixed $def The default value in case the property doesn't exist.
216 *
217 * @return mixed The event object or a specific property of the object.
218 */
219 public function event($key = null, $def = null)
220 {
221 if (!$key)
222 {
223 return $this->events[0];
224 }
225
226 if (isset($this->events[0]->{$key}))
227 {
228 return $this->events[0]->{$key};
229 }
230
231 return $def;
232 }
233
234 /**
235 * Returns the number of events contained within the list
236 *
237 * @return integer
238 */
239 public function getEventsCount()
240 {
241 return count($this->events);
242 }
243
244 /**
245 * Extends the bounds of this box according to the delimiters of the given event.
246 *
247 * @param integer $start The starting delimiter.
248 * @param integer $end The ending delimiter.
249 * @param mixed $event The event(s) to push.
250 *
251 * @return self This object to support chaining.
252 *
253 * @uses addEvent()
254 */
255 public function extendBounds($start, $end, $event)
256 {
257 $min = min(array($this->start->format('Y-m-d H:i:s'), $start));
258 $max = max(array($this->end->format('Y-m-d H:i:s'), $end));
259
260 // update bounds
261 $this->setBounds($min, $max);
262
263 return $this->addEvent($event);
264 }
265
266 /**
267 * Manually updates the rect bounds.
268 *
269 * @param string $start The starting delimiter.
270 * @param string $end The ending delimiter.
271 *
272 * @return self This object to support chaining.
273 */
274 public function setBounds($start, $end)
275 {
276 $this->start = JFactory::getDate($start);
277 $this->end = JFactory::getDate($end);
278
279 $tz = JFactory::getUser()->getTimezone();
280
281 // adjust dates to current used timezone
282 $this->start->setTimezone($tz);
283 $this->end->setTimezone($tz);
284
285 return $this;
286 }
287
288 /**
289 * Checks if there is an intersection between the rect and the given delimiters.
290 *
291 * @param integer $start The starting delimiter.
292 * @param integer $end The ending delimiter.
293 *
294 * @return boolean True if it is, false otherwise.
295 */
296 public function intersects($start_b, $end_b)
297 {
298 $start_a = $this->start->format('Y-m-d H:i:s');
299 $end_a = $this->end->format('Y-m-d H:i:s');
300
301 // IN_A <= IN_B AND IN_B < OUT_A
302 // IN_A < OUT_B AND OUT_B <= OUT_A
303 // IN_B < IN_A AND OUT_A < OUT_B
304 return ($start_a <= $start_b && $start_b < $end_a)
305 || ($start_a < $end_b && $end_b <= $end_a)
306 || ($start_b < $start_a && $end_a < $end_b);
307 }
308
309 /**
310 * Checks if the appointment starts at the specified hour.
311 * This method doesn't consider the minutes (e.g. 9:30 starts @ 9:00).
312 *
313 * @param integer $hour The hour to check.
314 *
315 * @return boolean True if starts, false otherwise.
316 */
317 public function startsAt($hour)
318 {
319 if ($this->startH() == $hour)
320 {
321 return true;
322 }
323
324 return false;
325 }
326
327 /**
328 * Checks if the appointment ends at the specified hour.
329 * This method doesn't consider the minutes (e.g. 9:30 starts @ 9:00).
330 *
331 * @param integer $hour The hour to check.
332 *
333 * @return boolean True if ends, false otherwise.
334 */
335 public function endsAt($hour)
336 {
337 if ($this->endHM() > $hour * 60 && $this->endHM() <= ($hour + 1) * 60)
338 {
339 return true;
340 }
341
342 return false;
343 }
344
345 /**
346 * Checks if the appointment is between the given hour and the next one.
347 * This method doesn't consider the minutes.
348 *
349 * @param integer $hour The hour to check.
350 *
351 * @return boolean True if contained, false otherwise.
352 */
353 public function containsAt($hour)
354 {
355 if ($this->startH() < $hour && $hour < $this->endH())
356 {
357 return true;
358 }
359
360 return false;
361 }
362
363 /**
364 * Returns the texts that is going to be displayed within
365 * the calendar block.
366 *
367 * @return string
368 *
369 * @since 1.7
370 */
371 public function getDisplayData($use)
372 {
373 $data = array();
374
375 // build rect data
376 $data['id'] = array();
377 $data['service'] = array();
378 $data['employee'] = array();
379
380 foreach ($this->events() as $e)
381 {
382 $data['id'][] = $e->id;
383
384 if (!in_array($e->id_service, $data['service']))
385 {
386 $data['service'][] = $e->id_service;
387 }
388
389 if (!in_array($e->id_employee, $data['employee']))
390 {
391 $data['employee'][] = $e->id_employee;
392 }
393 }
394
395 if (count($data['service']) > 1)
396 {
397 // use custom color for shared blocks
398 $data['color'] = '00d498';
399 }
400 else
401 {
402 $data['color'] = $this->event('service_color');
403
404 // The color is missing... Probably the service was
405 // created before the colors were supported.
406 if (!$data['color'])
407 {
408 // use a random color from the preset
409 $data['color'] = ltrim(JHtml::fetch('vaphtml.color.preset'), '#');
410 }
411 }
412
413 if ($use)
414 {
415 // first block, define event label
416 $data['label'] = '';
417
418 $count = $this->getEventsCount();
419 $customer = $count ? $this->event()->purchaser_nominative : null;
420
421 if ($count == 1 && $customer)
422 {
423 // display the customer name
424 $data['label'] .= '<div class="app-rect-customer">' . $customer . '</div>';
425 }
426
427 if (count($data['service']) == 1)
428 {
429 // Only one service or multiple appointments for the same service.
430 // Use service name as label.
431 $data['label'] .= '<div class="app-rect-service">' . $this->event('service_name') . '</div>';
432 }
433
434 if ($count > 1)
435 {
436 $data['label'] .= '<div class="app-rect-count">' . JText::sprintf('VAPCALNUMAPP', $count) . '</div>';
437 }
438
439 $dispatcher = VAPFactory::getEventDispatcher();
440
441 /**
442 * Trigger event to let external plugins manipulate the text to display,
443 * the background color and additional data to introduce within the HTML rect.
444 *
445 * @param array &$data An associative array of display data.
446 * @param self $rect The object handling all the events.
447 *
448 * @return void
449 *
450 * @since 1.7
451 */
452 $dispatcher->trigger('onDisplayCalendarRect', array(&$data, $this));
453 }
454 else
455 {
456 $data['label'] = '';
457 }
458
459 // fetch background style
460 $data['background'] = 'background-color: #' . $data['color']. ';';
461
462 /**
463 * Check if we should use a darker color for text depending
464 * on the brightness of the background.
465 *
466 * @since 1.7
467 */
468 if (JHtml::fetch('vaphtml.color.dark', $data['color']))
469 {
470 // we have a dark background, use a light color
471 $data['background'] .= ' color: #fff;';
472 }
473
474 return $data;
475 }
476 }
477