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 / widget.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
widget.php
569 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 * Abstract class used to represent a widget for analytics.
16 *
17 * @since 1.7
18 */
19 abstract class VAPStatisticsWidget
20 {
21 /**
22 * Static counter used to generate unique IDs based on
23 * an auto-incremental integer.
24 *
25 * @var integer
26 */
27 protected static $AUTO_INCREMENT = 0;
28
29 /**
30 * The widget ID.
31 *
32 * @var integer
33 */
34 protected $id;
35
36 /**
37 * The widget user ID.
38 *
39 * @var integer
40 */
41 protected $id_user;
42
43 /**
44 * An optional title for the widget.
45 *
46 * @var string
47 */
48 protected $title;
49
50 /**
51 * The widget position identifier.
52 *
53 * @var string
54 */
55 protected $position;
56
57 /**
58 * The widget size identifier.
59 *
60 * @var string
61 */
62 protected $size;
63
64 /**
65 * A registry of options.
66 *
67 * @var JObject
68 */
69 protected $options;
70
71 /**
72 * Children classes can fill this property to specify a custom
73 * layout path, which will be used in place of the default one.
74 *
75 * By default, the layout path must be within "statistics/widgets".
76 *
77 * @var null|string
78 */
79 protected $layoutId = null;
80
81 /**
82 * Class constructor.
83 *
84 * @param mixed $options Either an array or an object of options to be passed
85 * to the order instance.
86 */
87 public function __construct($options = array())
88 {
89 $this->options = new JObject($options);
90 }
91
92 /**
93 * Returns the widget name/identifier.
94 *
95 * @return string
96 */
97 public function getName()
98 {
99 // get current class name
100 $class = get_class($this);
101
102 // extract widget name from class
103 if (preg_match("/^VAPStatisticsWidget([a-z0-9_]+)$/i", $class, $match))
104 {
105 // place an underscore between each camelCase
106 return strtolower(preg_replace("/([a-z])([A-Z])/", '$1_$2', end($match)));
107 }
108
109 // the widget doesn't follow the standard notation, return full class name
110 return $class;
111 }
112
113 /**
114 * Returns the ID of the widget.
115 *
116 * @return integer The widget ID.
117 */
118 final public function getID()
119 {
120 if (!$this->id)
121 {
122 // use a random identifier when not specified
123 $this->setID(++static::$AUTO_INCREMENT);
124 }
125
126 return (int) $this->id;
127 }
128
129 /**
130 * Sets the ID of the widget.
131 *
132 * @param integer $id The widget ID.
133 *
134 * @return self This object to support chaining.
135 */
136 public function setID($id)
137 {
138 $this->id = (int) $id;
139
140 return $this;
141 }
142
143 /**
144 * Returns the user ID of the widget.
145 *
146 * @return integer The widget user ID.
147 */
148 public function getUserID()
149 {
150 return (int) $this->id_user;
151 }
152
153 /**
154 * Sets the user ID of the widget.
155 *
156 * @param integer $id The widget user ID.
157 *
158 * @return self This object to support chaining.
159 */
160 public function setUserID($id)
161 {
162 $this->id_user = (int) $id;
163
164 return $this;
165 }
166
167 /**
168 * Checks whether the specified user is capable to access this widget.
169 * Children classes can override this method to restrict the visibility
170 * of this widget to certain users only.
171 *
172 * @param JUser $user The user instance.
173 *
174 * @return boolean Always true by default.
175 */
176 public function checkPermissions($user)
177 {
178 return true;
179 }
180
181 /**
182 * Returns the widget title.
183 * By default, the title is a translatable string built
184 * in the following format: VAP_STATS_WIDGET_[NAME]_TITLE.
185 *
186 * @return string
187 */
188 public function getTitle($default = true)
189 {
190 // use custom title if set
191 if (!empty($this->title))
192 {
193 return $this->title;
194 }
195
196 if (!$default)
197 {
198 // do not return default title
199 return '';
200 }
201
202 // get widget name (UPPERCASE)
203 $widget = strtoupper($this->getName());
204
205 // build language key
206 $key = 'VAP_STATS_WIDGET_' . $widget . '_TITLE';
207
208 // try to translate the title
209 $title = JText::translate($key);
210
211 // check if the title is equals to language key
212 if ($title === $key)
213 {
214 // missing translation, return widget name
215 return $widget;
216 }
217
218 // return translated title instead
219 return $title;
220 }
221
222 /**
223 * Sets a custom title for the widget.
224 *
225 * @param string $title The widget title.
226 *
227 * @return self This object to support chaining.
228 */
229 public function setTitle($title)
230 {
231 $this->title = $title;
232
233 return $this;
234 }
235
236 /**
237 * Returns the widget description.
238 * By default, the description is a translatable string built
239 * in the following format: VAP_STATS_WIDGET_[NAME]_DESC.
240 *
241 * @return string
242 */
243 public function getDescription()
244 {
245 // build language key
246 $key = 'VAP_STATS_WIDGET_' . strtoupper($this->getName()) . '_DESC';
247
248 // try to translate the description
249 $desc = JText::translate($key);
250
251 // check if the description is equals to language key
252 if ($desc === $key)
253 {
254 // missing translation, return empty description
255 return '';
256 }
257
258 // return translated description instead
259 return $desc;
260 }
261
262 /**
263 * Returns the position of the widget.
264 *
265 * @return string The position identifier.
266 */
267 public function getPosition()
268 {
269 return $this->position ? $this->position : 'inherit';
270 }
271
272 /**
273 * Sets the position of the widget.
274 *
275 * @param string $position The position identifier.
276 *
277 * @return self This object to support chaining.
278 */
279 public function setPosition($position)
280 {
281 $this->position = $position;
282
283 return $this;
284 }
285
286 /**
287 * Returns the size of the widget.
288 *
289 * @return string The size identifier.
290 */
291 public function getSize()
292 {
293 return (string) $this->size;
294 }
295
296 /**
297 * Sets the size of the widget.
298 *
299 * @param string $size The size identifier.
300 *
301 * @return self This object to support chaining.
302 */
303 public function setSize($size)
304 {
305 $this->size = $size;
306
307 return $this;
308 }
309
310 /**
311 * Returns the configuration options
312 *
313 * @return array
314 */
315 public function getOptions()
316 {
317 static $form = null;
318
319 if (is_null($form))
320 {
321 // get the configuration array only once
322 $form = $this->getForm();
323 }
324
325 // get all properties
326 $options = $this->options->getProperties();
327
328 // scan all the supported form parameters
329 foreach ($form as $k => $param)
330 {
331 // if not specified, use the default value of the current paremeter
332 if (!isset($options[$k]) && isset($param['default']))
333 {
334 $options[$k] = $param['default'];
335 }
336 }
337
338 return $options;
339 }
340
341 /**
342 * Returns a configuration option.
343 *
344 * @param string $key The option key.
345 * @param mixed $def The default value.
346 *
347 * @return mixed The option value if exists, the default value otherwise.
348 */
349 public function getOption($key, $def = null)
350 {
351 $value = $this->options->get($key, null);
352
353 // return default value in case the option is
354 // null or contains an empty string
355 if ($value === null || $value === '')
356 {
357 return $def;
358 }
359
360 return $value;
361 }
362
363 /**
364 * Updates the options of the registry.
365 *
366 * @param mixed $options Either an array or an object of options to be passed
367 * to the order instance.
368 *
369 * @return self This object to support chaining.
370 */
371 public function setOptions($options = array())
372 {
373 $this->options->setProperties($options);
374
375 return $this;
376 }
377
378 /**
379 * Updates or insert a value within the configuration.
380 *
381 * @param string $key The option key.
382 * @param mixed $val The option value.
383 *
384 * @return self This object to support chaining.
385 */
386 public function setOption($key, $val)
387 {
388 $this->options->set($key, $val);
389
390 return $this;
391 }
392
393 /**
394 * Override this method to return a configuration form of the widget.
395 *
396 * @return array
397 */
398 public function getForm()
399 {
400 return array();
401 }
402
403 /**
404 * Returns the value of the parameters used the last time this widget was invoked.
405 *
406 * @return array
407 */
408 public function getParams()
409 {
410 // get widget identifier
411 $widget = $this->getName();
412
413 // get configuration stored in the user state to retrieve those arguments
414 // that don't have to be stored permanently
415 $state = JFactory::getApplication()->getUserState('vap.statistics.' . $widget . '.' . $this->getID(), array());
416
417 // get widget model
418 $model = JModelVAP::getInstance('statswidget');
419
420 // load widget parameters
421 $params = $model->getParams($this->getID());
422
423 // merge user state with configuration
424 return array_merge($params, $state);
425 }
426
427 /**
428 * Saves the last used parameters of the widget within the configuration/user state.
429 *
430 * @return void
431 */
432 public function saveParams()
433 {
434 $app = JFactory::getApplication();
435
436 // get widget identifier
437 $widget = $this->getName();
438
439 // get configuration stored in the user state to
440 // retrieve those arguments that don't have to
441 // be stored permanently
442 $state = $app->getUserState('vap.statistics.' . $widget . '.' . $this->getID(), array());
443
444 // get widget model
445 $model = JModelVAP::getInstance('statswidget');
446
447 // load widget parameters
448 $params = $model->getParams($this->getID());
449
450 // iterate the widget form to save only the internal parameters
451 foreach ($this->getForm() as $k => $field)
452 {
453 // check if the field is volatile or permanent
454 if (empty($field['volatile']))
455 {
456 // save widget param in configuration
457 $params[$k] = $this->getOption($k);
458 }
459 else
460 {
461 // save widget param in user state
462 $state[$k] = $this->getOption($k);
463 }
464 }
465
466 // prepare record data to be saved
467 $data = array(
468 'id' => $this->getID(),
469 'params' => $params,
470 );
471
472 // save widget parameters
473 $model->save($data);
474
475 // update user state
476 $app->setUserState('vap.statistics.' . $widget . '.' . $this->getID(), $state);
477 }
478
479 /**
480 * Megic method to return the widget name when the object is casted to string.
481 *
482 * @return string
483 */
484 public function __toString()
485 {
486 return $this->getName();
487 }
488
489 /**
490 * Returns the HTML used to display the widget.
491 *
492 * By default, the class tries to use a layout file under this path:
493 * /layouts/statistics/widgets/[WIDGET].php
494 *
495 * In case the widget doesn't support a layout file, it is possible to override
496 * this method to return a HTML string.
497 *
498 * @param array An associative array of display data.
499 *
500 * @return string
501 */
502 public function display(array $data = array())
503 {
504 // check whether we should use the default layout path or the one specified by the child class
505 $layoutId = !empty($this->layoutId) ? $this->layoutId : preg_replace("/_+/", '.', $this->getName());
506
507 // get layout file (prefer back-end folder)
508 $layout = new JLayoutFile('statistics.widgets.' . $layoutId, null, [
509 'component' => 'com_vikappointments',
510 'client' => 'admin',
511 ]);
512
513 // inject widget instance within display data
514 $data['widget'] = $this;
515
516 // return layout HTML string
517 return $layout->render($data);
518 }
519
520 /**
521 * Checks whether the widget is able to export the fetched data.
522 * It is possible to return a boolean or an array of supported rules.
523 * In example, by returning an array containing 'print' and 'export'
524 * rules, the system will display 2 different links.
525 *
526 * @return mixed Always false by default.
527 */
528 public function isExportable()
529 {
530 return false;
531 }
532
533 /**
534 * Create adapter for export method.
535 * Children classes can override this method to implement their
536 * own dowload/export feature.
537 *
538 * @param mixed $rule The requested export type.
539 *
540 * @return void
541 */
542 public function export($rule = null)
543 {
544 // do nothing here
545 }
546
547 /**
548 * Loads the dataset(s) that will be recovered asynchronously
549 * for being displayed within the widget.
550 *
551 * It is possible to return an array of records to be passed
552 * to a chart or directly the HTML to replace.
553 *
554 * @return mixed
555 */
556 abstract public function getData();
557
558 /**
559 * Checks whether the specified group is supported
560 * by the widget. Children classes can override this
561 * method to drop the support for a specific group.
562 *
563 * @param string $group The group to check.
564 *
565 * @return boolean True if supported, false otherwise.
566 */
567 abstract public function isSupported($group);
568 }
569