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