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 / wizard / step.php
vikappointments / site / helpers / libraries / wizard Last commit date
classes 3 days ago index.html 3 days ago step.php 3 days ago wizard.php 3 days ago
step.php
475 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 representation of a wizard step.
16 *
17 * @since 1.7.1
18 */
19 abstract class VAPWizardStep
20 {
21 /**
22 * An options registry.
23 *
24 * @var JObject
25 */
26 protected $options;
27
28 /**
29 * A list of dependencies.
30 *
31 * @var VAPWizardStep[]
32 */
33 protected $dependencies = array();
34
35 /**
36 * Class constructor.
37 *
38 * @param mixed $options Either an array or an object of options.
39 */
40 public function __construct($options = array())
41 {
42 if (is_array($options) || $options instanceof stdClass)
43 {
44 // wrap data in a registry
45 $this->options = new JObject($options);
46 }
47 else if (!$options instanceof JObject)
48 {
49 // use empty registry
50 $this->options = new JObject();
51 }
52 }
53
54 /**
55 * Returns the step unique identifier.
56 * By default, it is always based on the classname.
57 *
58 * @return string The step ID.
59 */
60 public function getID()
61 {
62 // get class name
63 $id = get_class($this);
64
65 // strip initial base class
66 $id = preg_replace("/^VAPWizardStep/i", '', $id);
67
68 // add an underscore before every camel case
69 $id = preg_replace("/([a-z])([A-Z])/", '$1_$2', $id);
70
71 // always use lower-case letters
72 return strtolower($id);
73 }
74
75 /**
76 * Returns the step title.
77 * Used as a very-short description.
78 *
79 * @return string The step title.
80 */
81 abstract public function getTitle();
82
83 /**
84 * Returns the step description.
85 *
86 * @return string The step description.
87 */
88 public function getDescription()
89 {
90 return '';
91 }
92
93 /**
94 * Returns an optional step icon.
95 *
96 * @return string The step icon.
97 */
98 public function getIcon()
99 {
100 return '';
101 }
102
103 /**
104 * Return the group to which the step belongs.
105 *
106 * @return string The group name.
107 */
108 abstract public function getGroup();
109
110 /**
111 * Returns the step options.
112 *
113 * @return array The options.
114 */
115 final public function getOptions()
116 {
117 return $this->options->getProperties();
118 }
119
120 /**
121 * Sets the step options.
122 *
123 * @param mixed $options Either an array or an object.
124 *
125 * @return self This object to support chaining.
126 */
127 public function setOptions($options)
128 {
129 $this->options->setProperties($options);
130
131 return $this;
132 }
133
134 /**
135 * Checks whether the step is supported by the
136 * current configuration progress.
137 *
138 * @return boolean True if supported, false otherwise.
139 */
140 public function isSupported()
141 {
142 return true;
143 }
144
145 /**
146 * Returns the completion progress in percentage.
147 *
148 * @return integer The percentage progress (always rounded).
149 */
150 public function getProgress()
151 {
152 // return by default 100% progress if completed, 0 otherwise
153 return $this->isCompleted() ? 100 : 0;
154 }
155
156 /**
157 * Returns a list of steps to which this class depends.
158 * The step will be completed only once all the
159 * dependencies are completed.
160 *
161 * @return array
162 */
163 final public function getDependencies()
164 {
165 return $this->dependencies;
166 }
167
168 /**
169 * Returns the searched dependency.
170 *
171 * @param string $id The step identifier.
172 *
173 * @return mixed The step instance if exists, null otherwise.
174 */
175 final public function getDependency($id)
176 {
177 // iterate dependencies
178 foreach ($this->dependencies as $dep)
179 {
180 // compare ID
181 if ($dep->getID() == $id)
182 {
183 // return dependency
184 return $dep;
185 }
186 }
187
188 return null;
189 }
190
191 /**
192 * Set up a list of dependencies.
193 *
194 * @param array $steps A list of steps.
195 *
196 * @return self This object to support chaining.
197 */
198 final public function setDependencies(array $steps)
199 {
200 // add steps one by one
201 foreach ($steps as $step)
202 {
203 // make sure we are handling a valid class
204 if ($step instanceof VAPWizardStep)
205 {
206 // register dependency
207 $this->addDependency($step);
208 }
209 }
210
211 return $this;
212 }
213
214 /**
215 * Adds a new dependency to this step.
216 *
217 * @param mixed ...$args An undefined number of steps to link.
218 *
219 * @return self This object to support chaining.
220 */
221 final public function addDependency()
222 {
223 // iterate list of arguments
224 foreach (func_get_args() as $step)
225 {
226 // add only if not already in list
227 if ($step instanceof VAPWizardStep && !$this->hasDependency($step))
228 {
229 $this->dependencies[] = $step;
230 }
231 }
232
233 return $this;
234 }
235
236 /**
237 * Removes the specified dependency.
238 *
239 * @param mixed $step Either an array or a step to add as dependency.
240 *
241 * @return boolean True on success, false otherwise.
242 */
243 final public function removeDependency($step)
244 {
245 if ($step instanceof VAPWizardStep)
246 {
247 // extract ID from step
248 $step = $step->getID();
249 }
250
251 // iterate dependencies
252 foreach ($this->dependencies as $i => $dep)
253 {
254 // compare ID
255 if ($dep->getID() == $step)
256 {
257 // splice array at the index found
258 array_splice($this->dependencies, $i, 1);
259
260 return true;
261 }
262 }
263
264 // nothing to remove
265 return false;
266 }
267
268 /**
269 * Checks whether this step owns a dependency with the given one.
270 *
271 * @param VAPWizardStep $step The step to check.
272 *
273 * @return boolean True in case of dependency, false otherwise
274 */
275 final public function hasDependency(VAPWizardStep $step)
276 {
277 return in_array($step, $this->dependencies, true);
278 }
279
280 /**
281 * Returns the HTML to display description and actions
282 * needed to complete the step.
283 *
284 * @return string The HTML of the step.
285 */
286 public function display()
287 {
288 // always try to search for a layout related to this step
289 return JLayoutHelper::render('wizard.steps.' . $this->getID(), array('step' => $this));
290 }
291
292 /**
293 * Checks whether the step can be processed by checking
294 * all the registered dependencies.
295 *
296 * @return boolean True if executable, false otherwise.
297 */
298 public function canExecute()
299 {
300 // iterate steps
301 foreach ($this->dependencies as $step)
302 {
303 // make sure the step has been already completed
304 if (!$step->isCompleted())
305 {
306 // step not yet completed, then cannot execute this one yet
307 return false;
308 }
309 }
310
311 // step can be executed
312 return true;
313 }
314
315 /**
316 * Checks whether the step has been completed.
317 *
318 * @return boolean True if completed, false otherwise.
319 */
320 public function isCompleted()
321 {
322 // check whether the step has been completed
323 return (bool) $this->options->get('completed', false);
324 }
325
326 /**
327 * Processes the step according to the given data.
328 *
329 * @param mixed $data The request data.
330 *
331 * @return void
332 */
333 final public function execute($data = array())
334 {
335 if (is_array($data) || $data instanceof stdClass)
336 {
337 // wrap data in a registry
338 $data = new JRegistry($data);
339 }
340 else if (!$data instanceof JRegistry)
341 {
342 // use empty registry
343 $data = new JRegistry();
344 }
345
346 // make sure the step can be executed
347 if ($this->canExecute())
348 {
349 // execute step
350 $status = $this->doExecute($data);
351
352 if ($status)
353 {
354 // register completed state
355 $this->options->set('completed', true);
356 }
357 }
358 }
359
360 /**
361 * Implements the step execution.
362 *
363 * @param JRegistry $data The request data.
364 *
365 * @return boolean True to mark the step as completed.
366 */
367 protected function doExecute($data)
368 {
369 // do nothing here
370
371 return true;
372 }
373
374 /**
375 * Returns the button used to process the step.
376 *
377 * @return string The HTML of the button.
378 */
379 public function getExecuteButton()
380 {
381 // use by default the standard save button
382 return '<button type="button" class="btn btn-success" data-role="process">' . JText::translate('VAPSAVE') . '</button>';
383 }
384
385 /**
386 * Checks whether the specified step can be skipped.
387 * By default, all the steps are mandatory.
388 *
389 * @return boolean True if skippable, false otherwise.
390 */
391 public function canIgnore()
392 {
393 return false;
394 }
395
396 /**
397 * Checks whether the step has been ignored.
398 *
399 * @return boolean True if ignored, false otherwise.
400 */
401 public function isIgnored()
402 {
403 // iterate dependencies
404 foreach ($this->dependencies as $step)
405 {
406 // check whether the dependency was ignored
407 if ($step->isIgnored())
408 {
409 // then ignore also this step
410 return true;
411 }
412 }
413
414 // check whether the step has been ignored
415 return (bool) $this->options->get('ignored', false);
416 }
417
418 /**
419 * Ignores the step.
420 *
421 * @return self This object to support chaining.
422 */
423 final public function ignore()
424 {
425 // make sure the step can be ignored
426 if ($this->canIgnore())
427 {
428 // register ignored state
429 $this->options->set('ignored', true);
430 }
431
432 return $this;
433 }
434
435 /**
436 * Checks whether the step has been dismissed.
437 *
438 * @return boolean True if dismissed, false otherwise.
439 */
440 public function isDismissed()
441 {
442 // check whether the step has been dismissed
443 return (bool) $this->options->get('dismissed', false);
444 }
445
446 /**
447 * Dismisses the step.
448 *
449 * @return self This object to support chaining.
450 */
451 final public function dismiss()
452 {
453 // make sure the step can be dismissed (only if completed)
454 if ($this->isCompleted())
455 {
456 // register dismissed state
457 $this->options->set('dismissed', true);
458 }
459
460 return $this;
461 }
462
463 /**
464 * Checks whether the step should is visible.
465 * A step is visible only in case it has never been
466 * ignored or dismissed.
467 *
468 * @return boolean True if visible, false otherwise.
469 */
470 final public function isVisible()
471 {
472 return !$this->isIgnored() && !$this->isDismissed();
473 }
474 }
475