Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
bc77d7c0 TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 TO |
11 | |
12 | /** | |
13 | * | |
14 | * @package CRM | |
ca5cec67 | 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 TO |
16 | */ |
17 | ||
18 | /** | |
8eedd10a | 19 | * Core StateMachine. |
6a488035 | 20 | * |
8eedd10a | 21 | * All state machines subclass for the core one for functionality specific to their needs. |
6a488035 | 22 | * |
8eedd10a | 23 | * A state machine keeps track of different states and forms for a |
24 | * html quickform controller. | |
6a488035 TO |
25 | */ |
26 | class CRM_Core_StateMachine { | |
27 | ||
28 | /** | |
f9e31d7f | 29 | * The controller of this state machine. |
6a488035 TO |
30 | * @var object |
31 | */ | |
32 | protected $_controller; | |
33 | ||
34 | /** | |
f9e31d7f | 35 | * The list of states that belong to this state machine. |
6a488035 TO |
36 | * @var array |
37 | */ | |
38 | protected $_states; | |
39 | ||
40 | /** | |
100fef9d | 41 | * The list of pages that belong to this state machine. Note |
6a488035 TO |
42 | * that a state and a form have a 1 <-> 1 relationship. so u |
43 | * can always derive one from the other | |
44 | * @var array | |
45 | */ | |
46 | protected $_pages; | |
47 | ||
48 | /** | |
f9e31d7f | 49 | * The names of the pages. |
6a488035 TO |
50 | * |
51 | * @var array | |
52 | */ | |
53 | protected $_pageNames; | |
54 | ||
55 | /** | |
f9e31d7f | 56 | * The mode that the state machine is operating in. |
6a488035 TO |
57 | * @var int |
58 | */ | |
59 | protected $_action = NULL; | |
60 | ||
61 | /** | |
f9e31d7f | 62 | * The display name for this machine. |
6a488035 TO |
63 | * @var string |
64 | */ | |
65 | protected $_name = NULL; | |
66 | ||
67 | /** | |
f9e31d7f | 68 | * Class constructor. |
6a488035 | 69 | * |
6a0b768e TO |
70 | * @param object $controller |
71 | * The controller for this state machine. | |
6a488035 | 72 | * |
da3c7979 | 73 | * @param \const|int $action |
dd244018 EM |
74 | * |
75 | * @return \CRM_Core_StateMachine | |
6a488035 | 76 | */ |
00be9182 | 77 | public function __construct(&$controller, $action = CRM_Core_Action::NONE) { |
6a488035 TO |
78 | $this->_controller = &$controller; |
79 | $this->_action = $action; | |
80 | ||
be2fb01f | 81 | $this->_states = []; |
6a488035 TO |
82 | } |
83 | ||
84 | /** | |
2e2605fe | 85 | * Getter for name. |
6a488035 TO |
86 | * |
87 | * @return string | |
6a488035 TO |
88 | */ |
89 | public function getName() { | |
90 | return $this->_name; | |
91 | } | |
92 | ||
93 | /** | |
2e2605fe | 94 | * Setter for name. |
6a488035 | 95 | * |
389bcebf | 96 | * @param string $name |
6a488035 TO |
97 | */ |
98 | public function setName($name) { | |
99 | $this->_name = $name; | |
100 | } | |
101 | ||
102 | /** | |
2e2605fe EM |
103 | * Do a state transition jump. |
104 | * | |
105 | * Currently only supported types are | |
6a488035 TO |
106 | * Next and Back. The other actions (Cancel, Done, Submit etc) do |
107 | * not need the state machine to figure out where to go | |
108 | * | |
6a0b768e TO |
109 | * @param CRM_Core_Form $page |
110 | * The current form-page. | |
111 | * @param string $actionName | |
112 | * Current action name, as one Action object can serve multiple actions. | |
113 | * @param string $type | |
114 | * The type of transition being requested (Next or Back). | |
6a488035 | 115 | * |
8eedd10a | 116 | * @return object |
6a488035 | 117 | */ |
00be9182 | 118 | public function perform(&$page, $actionName, $type = 'Next') { |
6a488035 TO |
119 | // save the form values and validation status to the session |
120 | $page->isFormBuilt() or $page->buildForm(); | |
121 | ||
122 | $pageName = $page->getAttribute('name'); | |
123 | $data = &$page->controller->container(); | |
124 | ||
125 | $data['values'][$pageName] = $page->exportValues(); | |
126 | $data['valid'][$pageName] = $page->validate(); | |
127 | ||
128 | // if we are going to the next state | |
129 | // Modal form and page is invalid: don't go further | |
130 | if ($type == 'Next' && !$data['valid'][$pageName]) { | |
131 | return $page->handle('display'); | |
132 | } | |
133 | ||
134 | $state = &$this->_states[$pageName]; | |
135 | ||
136 | // dont know how or why we landed here so abort and display | |
137 | // current page | |
138 | if (empty($state)) { | |
139 | return $page->handle('display'); | |
140 | } | |
141 | ||
142 | // the page is valid, process it if we are jumping to the next state | |
143 | if ($type == 'Next') { | |
e6a0a301 | 144 | $page->mainProcess(); |
6a488035 TO |
145 | // we get the state again, since postProcess might have changed it |
146 | // this bug took me forever to find :) Lobo | |
147 | $state = &$this->_states[$pageName]; | |
148 | $state->handleNextState($page); | |
149 | } | |
150 | else { | |
151 | $state->handleBackState($page); | |
152 | } | |
153 | } | |
154 | ||
155 | /** | |
2e2605fe | 156 | * Helper function to add a State to the state machine. |
6a488035 | 157 | * |
6a0b768e TO |
158 | * @param string $name |
159 | * The internal name. | |
160 | * @param int $type | |
161 | * The type of state (START|FINISH|SIMPLE). | |
162 | * @param object $prev | |
163 | * The previous page if any. | |
164 | * @param object $next | |
165 | * The next page if any. | |
6a488035 | 166 | */ |
00be9182 | 167 | public function addState($name, $type, $prev, $next) { |
6a488035 TO |
168 | $this->_states[$name] = new CRM_Core_State($name, $type, $prev, $next, $this); |
169 | } | |
170 | ||
171 | /** | |
2e2605fe | 172 | * Given a name find the corresponding state. |
6a488035 | 173 | * |
6a0b768e TO |
174 | * @param string $name |
175 | * The state name. | |
6a488035 | 176 | * |
a6c01b45 CW |
177 | * @return object |
178 | * the state object | |
6a488035 | 179 | */ |
00be9182 | 180 | public function find($name) { |
6a488035 TO |
181 | if (array_key_exists($name, $this->_states)) { |
182 | return $this->_states[$name]; | |
183 | } | |
184 | else { | |
185 | return NULL; | |
186 | } | |
187 | } | |
188 | ||
189 | /** | |
2e2605fe | 190 | * Return the list of state objects. |
6a488035 | 191 | * |
a6c01b45 CW |
192 | * @return array |
193 | * array of states in the state machine | |
6a488035 | 194 | */ |
00be9182 | 195 | public function getStates() { |
6a488035 TO |
196 | return $this->_states; |
197 | } | |
198 | ||
199 | /** | |
f9e31d7f | 200 | * Return the state object corresponding to the name. |
6a488035 | 201 | * |
6a0b768e TO |
202 | * @param string $name |
203 | * Name of page. | |
6a488035 | 204 | * |
16b10e64 CW |
205 | * @return CRM_Core_State |
206 | * state object matching the name | |
6a488035 | 207 | */ |
00be9182 | 208 | public function &getState($name) { |
6a488035 | 209 | if (isset($this->_states[$name])) { |
2aa397bc TO |
210 | return $this->_states[$name]; |
211 | } | |
6a488035 TO |
212 | |
213 | /* | |
214 | * This is a gross hack for ajax driven requests where | |
215 | * we change the form name to allow multiple edits to happen | |
216 | * We need a cleaner way of doing this going forward | |
217 | */ | |
481a74f4 | 218 | foreach ($this->_states as $n => $s) { |
6a488035 TO |
219 | if (substr($name, 0, strlen($n)) == $n) { |
220 | return $s; | |
221 | } | |
222 | } | |
223 | ||
2aa397bc | 224 | return NULL; |
6a488035 TO |
225 | } |
226 | ||
227 | /** | |
f9e31d7f | 228 | * Return the list of form objects. |
6a488035 | 229 | * |
a6c01b45 CW |
230 | * @return array |
231 | * array of pages in the state machine | |
6a488035 | 232 | */ |
00be9182 | 233 | public function getPages() { |
6a488035 TO |
234 | return $this->_pages; |
235 | } | |
236 | ||
237 | /** | |
2e2605fe | 238 | * Add sequential pages. |
6a488035 | 239 | * |
2e2605fe | 240 | * Meta level function to create a simple wizard for a state machine that is completely sequential. |
6a488035 | 241 | * |
6a0b768e TO |
242 | * @param array $pages |
243 | * (reference ) the array of page objects. | |
6a488035 | 244 | */ |
00be9182 | 245 | public function addSequentialPages(&$pages) { |
6a488035 TO |
246 | $this->_pages = &$pages; |
247 | $numPages = count($pages); | |
248 | ||
be2fb01f | 249 | $this->_pageNames = []; |
6a488035 | 250 | foreach ($pages as $tempName => $value) { |
a7488080 | 251 | if (!empty($value['className'])) { |
6a488035 TO |
252 | $this->_pageNames[] = $tempName; |
253 | } | |
254 | else { | |
255 | $this->_pageNames[] = CRM_Utils_String::getClassName($tempName); | |
256 | } | |
257 | } | |
258 | ||
259 | $i = 0; | |
260 | foreach ($pages as $tempName => $value) { | |
261 | $name = $this->_pageNames[$i]; | |
262 | ||
263 | $className = CRM_Utils_Array::value('className', | |
264 | $value, | |
265 | $tempName | |
266 | ); | |
267 | $classPath = str_replace('_', '/', $className) . '.php'; | |
268 | if ($numPages == 1) { | |
269 | $prev = $next = NULL; | |
270 | $type = CRM_Core_State::START | CRM_Core_State::FINISH; | |
271 | } | |
272 | elseif ($i == 0) { | |
273 | // start state | |
274 | $prev = NULL; | |
275 | $next = $this->_pageNames[$i + 1]; | |
276 | $type = CRM_Core_State::START; | |
277 | } | |
278 | elseif ($i == $numPages - 1) { | |
279 | // finish state | |
280 | $prev = $this->_pageNames[$i - 1]; | |
281 | $next = NULL; | |
282 | $type = CRM_Core_State::FINISH; | |
283 | } | |
284 | else { | |
285 | // in between simple state | |
286 | $prev = $this->_pageNames[$i - 1]; | |
287 | $next = $this->_pageNames[$i + 1]; | |
288 | $type = CRM_Core_State::SIMPLE; | |
289 | } | |
290 | ||
291 | $this->addState($name, $type, $prev, $next); | |
292 | ||
293 | $i++; | |
294 | } | |
295 | } | |
296 | ||
297 | /** | |
f9e31d7f | 298 | * Reset the state machine. |
6a488035 | 299 | */ |
00be9182 | 300 | public function reset() { |
6a488035 TO |
301 | $this->_controller->reset(); |
302 | } | |
303 | ||
304 | /** | |
f9e31d7f | 305 | * Getter for action. |
6a488035 TO |
306 | * |
307 | * @return int | |
6a488035 | 308 | */ |
00be9182 | 309 | public function getAction() { |
6a488035 TO |
310 | return $this->_action; |
311 | } | |
312 | ||
313 | /** | |
f9e31d7f | 314 | * Setter for content. |
6a488035 | 315 | * |
6a0b768e TO |
316 | * @param string $content |
317 | * The content generated by this state machine. | |
6a488035 | 318 | */ |
00be9182 | 319 | public function setContent(&$content) { |
6a488035 TO |
320 | $this->_controller->setContent($content); |
321 | } | |
322 | ||
323 | /** | |
f9e31d7f | 324 | * Getter for content. |
6a488035 TO |
325 | * |
326 | * @return string | |
6a488035 | 327 | */ |
00be9182 | 328 | public function &getContent() { |
6a488035 TO |
329 | return $this->_controller->getContent(); |
330 | } | |
331 | ||
a0ee3941 EM |
332 | /** |
333 | * @return mixed | |
334 | */ | |
00be9182 | 335 | public function getDestination() { |
6a488035 TO |
336 | return $this->_controller->getDestination(); |
337 | } | |
338 | ||
a0ee3941 EM |
339 | /** |
340 | * @return mixed | |
341 | */ | |
00be9182 | 342 | public function getSkipRedirection() { |
6a488035 TO |
343 | return $this->_controller->getSkipRedirection(); |
344 | } | |
345 | ||
a0ee3941 EM |
346 | /** |
347 | * @return mixed | |
348 | */ | |
00be9182 | 349 | public function fini() { |
6a488035 TO |
350 | return $this->_controller->fini(); |
351 | } | |
352 | ||
a0ee3941 EM |
353 | /** |
354 | * @return mixed | |
355 | */ | |
00be9182 | 356 | public function cancelAction() { |
6a488035 TO |
357 | return $this->_controller->cancelAction(); |
358 | } | |
359 | ||
360 | /** | |
361 | * Should the controller reset the session | |
362 | * In some cases, specifically search we want to remember | |
363 | * state across various actions and want to go back to the | |
364 | * beginning from the final state, but retain the same session | |
365 | * values | |
366 | * | |
389bcebf | 367 | * @return bool |
6a488035 | 368 | */ |
00be9182 | 369 | public function shouldReset() { |
6a488035 | 370 | return TRUE; |
2aa397bc | 371 | } |
6a488035 TO |
372 | |
373 | } |