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 / availability / timeline / factory.php
vikappointments / site / helpers / libraries / availability / timeline Last commit date
parser 3 days ago renderer 3 days ago block.php 3 days ago factory.php 3 days ago index.html 3 days ago parser.php 3 days ago renderer.php 3 days ago
factory.php
295 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 VAPLoader::import('libraries.availability.timeline.parser');
15 VAPLoader::import('libraries.availability.timeline.renderer');
16
17 /**
18 * Class responsible to dispatch the requested timeline parser.
19 *
20 * @since 1.7
21 */
22 final class VAPAvailabilityTimelineFactory
23 {
24 /**
25 * Creates a new instance of the timeline parser.
26 *
27 * @param VAPAvailabilitySearch $search The search handler.
28 * @param string $parser The parser name.
29 *
30 * @return VAPAvailabilityTimelineParser The parser handler.
31 */
32 public static function getParser(VAPAvailabilitySearch $search, $parser = null)
33 {
34 if (!$parser)
35 {
36 // no specified parser, retrieve the best one according to
37 // the details wrapped by the searcher
38 $parser = static::findParser($search);
39 }
40
41 /**
42 * This hook can be used to safely change the class instance
43 * responsible of generating the availability timeline.
44 * In addition to returning the new class name, plugins must
45 * include all the needed resources. The returned object must
46 * also inherit VAPAvailabilityTimelineParser class.
47 *
48 * @param VAPAvailabilitySearch $search The search handler.
49 * @param string $parser The parser name.
50 *
51 * @return string The parser class name.
52 *
53 * @since 1.7
54 */
55 $classname = VAPFactory::getEventDispatcher()->triggerOnce('onCreateTimelineParser', array($search, $parser));
56
57 if (!$classname)
58 {
59 // load default one
60 VAPLoader::import('libraries.availability.timeline.parser.' . $parser);
61 // create class name
62 $classname = 'VAPAvailabilityTimelineParser' . ucfirst($parser);
63 }
64
65 // make sure the object is loaded and exists
66 if (!class_exists($classname))
67 {
68 // class not found, throw an exception
69 throw new Exception(sprintf('Timeline parser [%s] not found', $classname), 404);
70 }
71
72 // create new instance
73 $handler = new $classname($search);
74
75 // make sure the object is a valid instance
76 if (!$handler instanceof VAPAvailabilityTimelineParser)
77 {
78 // Not a valid instance, unexpected behavior might occur...
79 // Prevent them by throwing an exception.
80 throw new Exception(sprintf('The class [%s] is not a valid instance', $classname), 500);
81 }
82
83 return $handler;
84 }
85
86 /**
87 * Creates a new instance of the timeline renderer.
88 *
89 * @param mixed $timeline Either a timeline instance or an array of timelines.
90 * @param string $renderer The renderer name.
91 *
92 * @return VAPAvailabilityTimelineRenderer The renderer handler.
93 */
94 public static function getRenderer($timeline, $renderer = null)
95 {
96 if (!$renderer)
97 {
98 // no specified renderer, retrieve the best one according to
99 // the details wrapped by the searcher
100 $renderer = static::findRenderer($timeline);
101 }
102
103 /**
104 * This hook can be used to safely change the class instance
105 * responsible of rendering the availability timeline.
106 * In addition to returning the new class name, plugins must
107 * include all the needed resources. The returned object must
108 * also inherit VAPAvailabilityTimelineRenderer class.
109 *
110 * @param VAPAvailabilityTimeline $timeline The search handler.
111 * @param string $renderer The renderer name.
112 *
113 * @return string The renderer class name.
114 *
115 * @since 1.7
116 */
117 $classname = VAPFactory::getEventDispatcher()->triggerOnce('onCreateTimelineRenderer', array($timeline, $renderer));
118
119 if (!$classname)
120 {
121 // load default one
122 VAPLoader::import('libraries.availability.timeline.renderer.' . $renderer);
123 // create class name
124 $classname = 'VAPAvailabilityTimelineRenderer' . ucfirst($renderer);
125 }
126
127 // make sure the object is loaded and exists
128 if (!class_exists($classname))
129 {
130 // class not found, throw an exception
131 throw new Exception(sprintf('Timeline renderer [%s] not found', $classname), 404);
132 }
133
134 // create new instance
135 $handler = new $classname($timeline);
136
137 // make sure the object is a valid instance
138 if (!$handler instanceof VAPAvailabilityTimelineRenderer)
139 {
140 // Not a valid instance, unexpected behavior might occur...
141 // Prevent them by throwing an exception.
142 throw new Exception(sprintf('The class [%s] is not a valid instance', $classname), 500);
143 }
144
145 return $handler;
146 }
147
148 /**
149 * Automatically finds the parser that fits at best the given search.
150 *
151 * @param VAPAvailabilitySearch $search The search handler.
152 *
153 * @return string The parser name.
154 */
155 protected static function findParser(VAPAvailabilitySearch $search)
156 {
157 $id_service = (int) $search->get('id_service');
158 $id_employee = (int) $search->get('id_employee');
159
160 if ($id_employee <= 0)
161 {
162 // get service model
163 $model = JModelVAP::getInstance('service');
164 // load assigned employees
165 $employees = $model->getEmployees($id_service);
166
167 if (count($employees) == 1)
168 {
169 // only one employee assigned to this service, use it
170 $id_employee = (int) $employees[0]->id;
171 // update search instance
172 $search->set('id_employee', $id_employee);
173 }
174 }
175
176 if ($id_employee <= 0)
177 {
178 // use service parser
179 return 'service';
180 }
181
182 // get service-employee association model
183 $model = JModelVAP::getInstance('serempassoc');
184 // get service details
185 $service = $model->getOverrides($id_service, $id_employee);
186
187 if ($service && $service->max_capacity > 1 && $service->app_per_slot)
188 {
189 /**
190 * Use an extended parser to calculate the availabilty of the time slots by looking
191 * also at the near times.
192 *
193 * @since 1.7.8
194 */
195 if ($service->interval == 2 && $service->duration != VAPFactory::getConfig()->getUint('minuteintervals'))
196 {
197 // use extended group parser
198 return 'groupext';
199 }
200
201 // use group parser
202 return 'group';
203 }
204
205 // fallback to employee parser
206 return 'employee';
207 }
208
209 /**
210 * Automatically finds the renderer that fits at best the given timeline.
211 *
212 * @param mixed $timeline Either a timeline instance or an array of timelines.
213 *
214 * @return string The renderer name.
215 */
216 protected static function findRenderer($timeline)
217 {
218 if (is_array($timeline))
219 {
220 // an array of timelines was returned
221 return 'multiple';
222 }
223
224 $id_service = (int) $timeline->getSearch()->get('id_service');
225 $id_employee = (int) $timeline->getSearch()->get('id_employee');
226
227 // get service-employee association model
228 $model = JModelVAP::getInstance('serempassoc');
229 // get service details
230 $service = $model->getOverrides($id_service, $id_employee);
231
232 // switch timeline layout depending on the configuration of the service
233
234 if ($service->checkout_selection && JFactory::getApplication()->isClient('site'))
235 {
236 // check-out selection available only from the front-end
237 $count = 1;
238
239 if ($id_employee <= 0)
240 {
241 // count number of employees assigned to the given service
242 $count = count(JModelVAP::getInstance('service')->getEmployees($id_service));
243 }
244
245 // in order to use this layout, the seach must specify the employee or
246 // the service must be assigned at most to a single employee
247 if ($id_employee > 0 || $count == 1)
248 {
249 // we are here as the service allows the selection of the checkout
250 return 'dropdown';
251 }
252 }
253
254 if ($service->display_seats)
255 {
256 // we are here as the service needs to display the remaining seats
257 return 'seats';
258 }
259
260 // get currently logged-in customer
261 $customer = VikAppointments::getCustomer();
262
263 // iterate all timeline levels
264 foreach ($timeline as $times)
265 {
266 // iterate all times
267 foreach ($times as $time)
268 {
269 // check whether the customer owns an active subscription for the booked service and
270 // check-in date time (adjusted to UTC)
271 $is_subscribed = ($customer && $customer->isSubscribed($id_service, $time->checkin('Y-m-d H:i:s', 'UTC')));
272
273 // check whether the time owns a trace and the customer doesn't own an active subscription
274 if ($time->ratesTrace && !$is_subscribed)
275 {
276 // display a timeline that includes the price per slot
277 return 'ratesgrid';
278 }
279 }
280 }
281
282 // fallback to default timeline
283 return 'default';
284 }
285
286 /**
287 * Class constructor.
288 * Declared with protected visibility to avoid its instantiation.
289 */
290 protected function __construct()
291 {
292 // cannot be publicly instantiated
293 }
294 }
295