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 / statistics / factory.php
vikappointments / site / helpers / libraries / statistics Last commit date
helpers 2 days ago widgets 2 days ago factory.php 2 days ago index.html 2 days ago widget.php 2 days ago
factory.php
447 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.statistics.widget');
15
16 /**
17 * Factory class used to obtain and instantiate the
18 * supported statistics widgets.
19 *
20 * @since 1.7
21 */
22 abstract class VAPStatisticsFactory
23 {
24 /**
25 * Returns an associative array represeting the complete dashboard of widgets.
26 *
27 * @param string $location The location in which the widgets have been published,
28 * such as 'dashboard' or 'finance'.
29 * @param mixed $user The user that configured the widgets. If not specified,
30 * the current one will be used.
31 *
32 * @return array An associative array of widgets at the assigned positions.
33 *
34 * @uses getActiveWidgets()
35 */
36 public static function getDashboard($location, $user = null)
37 {
38 // get list of active widgets
39 $widgets = static::getActiveWidgets($location, $user);
40
41 $dashboard = array();
42
43 // iterate widgets
44 foreach ($widgets as $w)
45 {
46 // check if the position already exists
47 if (!isset($dashboard[$w->getPosition()]))
48 {
49 // create position
50 $dashboard[$w->getPosition()] = array();
51 }
52
53 // Register widget within the position.
54 // Do not use an associative array because the same
55 // widget could be used more than once.
56 $dashboard[$w->getPosition()][] = $w;
57 }
58
59 return $dashboard;
60 }
61
62 /**
63 * Returns a list containing all the active widgets.
64 *
65 * @param string $location The location in which the widgets have been published,
66 * such as 'dashboard' or 'finance'.
67 * @param mixed $user The user that configured the widgets. If not specified,
68 * the current one will be used.
69 *
70 * @return array An array of widgets.
71 *
72 * @uses getSupportedWidgets()
73 */
74 public static function getActiveWidgets($location, $user = null)
75 {
76 $dbo = JFactory::getDbo();
77
78 if (!$user instanceof JUser)
79 {
80 // load specified/current user
81 $user = JFactory::getUser($user);
82 }
83
84 // get list of active widgets
85 $q = $dbo->getQuery(true)
86 ->select('*')
87 ->from($dbo->qn('#__vikappointments_stats_widget'))
88 ->where($dbo->qn('location') . ' = ' . $dbo->q($location))
89 ->where($dbo->qn('id_user') . ' IN (0, ' . (int) $user->id . ')')
90 ->order($dbo->qn('id_user') . ' DESC')
91 ->order($dbo->qn('ordering') . ' ASC');
92
93 $dbo->setQuery($q);
94
95 // load active widgets
96 $widgets = $dbo->loadObjectList();
97
98 if ($widgets)
99 {
100 // in case the first widget is not global, take only
101 // the widgets that are assigned to the specified user
102 if ($widgets[0]->id_user > 0)
103 {
104 // filter the array and exclude widgets with `id_user` equals to "0"
105 $widgets = array_values(array_filter($widgets, function($widget)
106 {
107 return $widget->id_user > 0;
108 }));
109 }
110 }
111 else
112 {
113 // no widgets loaded
114 $widgets = array();
115 }
116
117 // get all supported widgets
118 $supported = static::getSupportedWidgets($location, $user);
119
120 $list = array();
121
122 // iterate active widgets
123 foreach ($widgets as $data)
124 {
125 // make sure the widget is supported
126 if (isset($supported[$data->widget]))
127 {
128 // assign object to a temporary variable
129 // to avoid altering the list reference
130 $widget = clone $supported[$data->widget];
131
132 // set widget ID
133 $widget->setID($data->id);
134
135 // set widget user ID
136 $widget->setUserID($data->id_user);
137
138 // set widget title
139 $widget->setTitle($data->name);
140
141 // assign the widget to its position
142 $widget->setPosition($data->position);
143
144 // set widget size
145 $widget->setSize($data->size);
146
147 // Register using the default creation ordering.
148 // Do not use an associative array because the same
149 // widget could be used more than once.
150 $list[] = $widget;
151 }
152 }
153
154 return $list;
155 }
156
157 /**
158 * Returns a list containing all the supported widgets.
159 *
160 * @param string $location The location in which the widgets have been published,
161 * such as 'dashboard' or 'finance'.
162 * @param mixed $user The user that configured the widgets. If not
163 * specified, the current one will be used.
164 *
165 * @return array An associative array of widgets.
166 *
167 * @uses getInstance()
168 */
169 public static function getSupportedWidgets($location, $user = null)
170 {
171 if (!$user instanceof JUser)
172 {
173 // load specified/current user
174 $user = JFactory::getUser($user);
175 }
176
177 $widgets = array();
178 $files = array();
179
180 // prepare base path in which the widgets are stored
181 $basePath = VAPLIB . DIRECTORY_SEPARATOR . 'statistics' . DIRECTORY_SEPARATOR . 'widgets';
182 // recursively load all files, since they are grouped in categories
183 $files = JFolder::files($basePath, $filter = '\.php$', $fullPath = true, $recursive = true);
184
185 // map files to route path to file name
186 $files = array_map(function($file) use ($basePath)
187 {
188 // get rid of base path
189 $file = str_replace($basePath, '', $file);
190 return ltrim(preg_replace("/[\/\\\\]+/", '_', $file), '_');
191 }, $files);
192
193 /**
194 * Trigger event to let the plugins register external widgets
195 * to extend the statistics dashboard without editing any core files.
196 *
197 * @return array A list of supported widgets.
198 *
199 * @since 1.7
200 */
201 $list = VAPFactory::getEventDispatcher()->trigger('onFetchSupportedStatisticsWidgets');
202
203 foreach ($list as $chunk)
204 {
205 // merge default files with specified ones
206 $files = array_merge($files, (array) $chunk);
207 }
208
209 // iterate files
210 foreach ($files as $file)
211 {
212 try
213 {
214 // try to instantiate the widget
215 $widget = static::getInstance($file);
216
217 // instantiation went fine, make sure the widget supports the requested group
218 // and the permissions of the given user
219 if ($widget->isSupported($location) && $widget->checkPermissions($user))
220 {
221 // group supported, register widget within the list
222 $widgets[$widget->getName()] = $widget;
223 }
224 }
225 catch (Exception $e)
226 {
227 // widget not supported
228 }
229 }
230
231 // sort widgets alphabetically
232 uasort($widgets, function($a, $b)
233 {
234 // compare as normal strings
235 return strcasecmp($a->getTitle(), $b->getTitle());
236 });
237
238 return $widgets;
239 }
240
241 /**
242 * Returns the statistics widget instance, ready for the usage.
243 * Searches for any arguments set in the request required by the widget.
244 *
245 * @param string $widget The widget name/filename.
246 * @param mixed $options Either an array or an object of options to be passed
247 * to the order instance.
248 *
249 * @return VAPStatisticsWidget
250 *
251 * @throws Exception
252 *
253 * @uses getInstance()
254 */
255 public static function getWidget($widget, $options = array())
256 {
257 // get widget first
258 $widget = static::getInstance($widget, $options);
259
260 $input = JFactory::getApplication()->input;
261
262 // iterate form arguments
263 foreach ($widget->getForm() as $k => $field)
264 {
265 // use registry for ease of use
266 $field = new JRegistry($field);
267
268 // validate field filter
269 if ($field->get('multiple') == 1)
270 {
271 // retrieve an array
272 $filter = 'array';
273 }
274 else
275 {
276 // retrieve a string otherwise
277 $filter = 'string';
278 }
279
280 // get value from request
281 $value = $input->get($k, null, $filter);
282
283 // cast to integer in case of checkbox,
284 // so that we can use "0" instead of NULL
285 if ($field->get('type') == 'checkbox')
286 {
287 $value = (int) $value;
288 }
289
290 // make sure we have a value in the request
291 if ($value !== null)
292 {
293 // Push value within the options.
294 // Any value previously set will be overwritten.
295 $widget->setOption($k, $value);
296 }
297
298 // check if the field was mandatory
299 if ($field->get('required') == 1)
300 {
301 // get value from widget configuration
302 $value = $widget->getOption($k, null);
303
304 // make sure the value exists
305 if ($value === null || $value === array())
306 {
307 // missing field, throw exception
308 throw new Exception(sprintf('Statistics widget missing required [%s] field', $k), 400);
309 }
310 }
311 }
312
313 return $widget;
314 }
315
316 /**
317 * Returns the statistics widget instance.
318 *
319 * @param string $widget The widget name/filename.
320 * @param mixed $options Either an array or an object of options to be passed
321 * to the order instance.
322 *
323 * @return VAPStatisticsWidget
324 *
325 * @throws Exception
326 */
327 public static function getInstance($widget, $options = array())
328 {
329 // remove file extension if provided
330 $widget = preg_replace("/\.php$/", '', $widget);
331
332 /**
333 * Trigger event to let the plugins include external widgets.
334 * The plugins MUST include the resources needed, otherwise
335 * it wouldn't be possible to instantiate the returned classes.
336 *
337 * @param string $widget The name of the widget to include.
338 *
339 * @return string The classname of the widget.
340 *
341 * @since 1.7
342 */
343 $classname = VAPFactory::getEventDispatcher()->triggerOnce('onFetchStatisticsWidgetClassname', array($widget));
344
345 if (!$classname)
346 {
347 // replace underscores with dots to use folder notation
348 $pathId = preg_replace("/_+/", '.', $widget);
349
350 // attempt to load file by using the group notation
351 if (!VAPLoader::import('libraries.statistics.widgets.' . $pathId))
352 {
353 // fallback to default folder
354 if (!VAPLoader::import('libraries.statistics.widgets.' . $widget))
355 {
356 throw new Exception(sprintf('Statistics widget [%s] not found', $widget), 404);
357 }
358 }
359
360 // replace every non-alnum character with a blank space
361 $widget = preg_replace("/[^a-zA-Z0-9]+/", ' ', $widget);
362 // make widget name camel case and get rid of blank spaces
363 $widget = preg_replace("/\s+/", '', ucwords($widget));
364
365 // create class name
366 $classname = 'VAPStatisticsWidget' . $widget;
367 }
368
369 // make sure the class handler exists
370 if (!class_exists($classname))
371 {
372 throw new Exception(sprintf('Statistics widget class [%s] does not exist', $classname), 404);
373 }
374
375 // instantiate handler
376 $widget = new $classname($options);
377
378 // make sure the widget is a valid instance
379 if (!$widget instanceof VAPStatisticsWidget)
380 {
381 throw new Exception(sprintf('Statistics widget class [%s] is not a valid instance', $classname), 500);
382 }
383
384 return $widget;
385 }
386
387 /**
388 * Returns a list containing all the supported positions in which the widget can be placed.
389 *
390 * @param string $location The location in which the widgets have been published,
391 * such as 'dashboard' or 'finance'.
392 * @param mixed $user The user that configured the widgets. If not specified,
393 * the current one will be used.
394 *
395 * @return array A list of positions.
396 */
397 public static function getSupportedPositions($location = null, $user = null)
398 {
399 $dbo = JFactory::getDbo();
400
401 if (!$user instanceof JUser)
402 {
403 // load specified/current user
404 $user = JFactory::getUser($user);
405 }
406
407 $q = $dbo->getQuery(true)
408 ->select($dbo->qn(array('position', 'id_user')))
409 ->from($dbo->qn('#__vikappointments_stats_widget'))
410 ->where($dbo->qn('id_user') . ' IN (0, ' . (int) $user->id . ')')
411 ->group($dbo->qn('position'))
412 ->group($dbo->qn('id_user'))
413 ->order($dbo->qn('id_user') . ' DESC')
414 ->order($dbo->qn('ordering') . ' ASC');
415
416 if ($location)
417 {
418 $q->where($dbo->qn('location') . ' = ' . $dbo->q($location));
419 }
420
421 $dbo->setQuery($q);
422 $widgets = $dbo->loadObjectList();
423
424 if ($widgets)
425 {
426 // in case the first widget is not global, take only
427 // the widgets that are assigned to the specified user
428 if ($widgets[0]->id_user > 0)
429 {
430 // filter the array and exclude widgets with `id_user` equals to "0"
431 $widgets = array_values(array_filter($widgets, function($widget)
432 {
433 return $widget->id_user > 0;
434 }));
435 }
436
437 // return only the position
438 return array_map(function($widget)
439 {
440 return $widget->position;
441 }, $widgets);
442 }
443
444 return array();
445 }
446 }
447