Comment fixes
[civicrm-core.git] / CRM / Core / StateMachine.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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
31 * @copyright CiviCRM LLC (c) 2004-2014
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 /**
47 * The controller of this state machine
48 * @var object
49 */
50 protected $_controller;
51
52 /**
53 * The list of states that belong to this state machine
54 * @var array
55 */
56 protected $_states;
57
58 /**
59 * The list of pages that belong to this state machine. Note
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 /**
74 * The mode that the state machine is operating in
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 /**
86 * Class constructor
87 *
88 * @param object $controller
89 * The controller for this state machine.
90 *
91 * @param \const|int $action
92 *
93 * @return \CRM_Core_StateMachine
94 */
95 public function __construct(&$controller, $action = CRM_Core_Action::NONE) {
96 $this->_controller = &$controller;
97 $this->_action = $action;
98
99 $this->_states = array();
100 }
101
102 /**
103 * Getter for name
104 *
105 * @return string
106 */
107 public function getName() {
108 return $this->_name;
109 }
110
111 /**
112 * Setter for name
113 *
114 * @param string $name
115 *
116 * @return void
117 */
118 public function setName($name) {
119 $this->_name = $name;
120 }
121
122 /**
123 * Do a state transition jump. Currently only supported types are
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 *
127 * @param CRM_Core_Form $page
128 * The current form-page.
129 * @param string $actionName
130 * Current action name, as one Action object can serve multiple actions.
131 * @param string $type
132 * The type of transition being requested (Next or Back).
133 *
134 * @return void
135 */
136 public function perform(&$page, $actionName, $type = 'Next') {
137 // save the form values and validation status to the session
138 $page->isFormBuilt() or $page->buildForm();
139
140 $pageName = $page->getAttribute('name');
141 $data = &$page->controller->container();
142
143 $data['values'][$pageName] = $page->exportValues();
144 $data['valid'][$pageName] = $page->validate();
145
146 // if we are going to the next state
147 // Modal form and page is invalid: don't go further
148 if ($type == 'Next' && !$data['valid'][$pageName]) {
149 return $page->handle('display');
150 }
151
152 $state = &$this->_states[$pageName];
153
154 // dont know how or why we landed here so abort and display
155 // current page
156 if (empty($state)) {
157 return $page->handle('display');
158 }
159
160 // the page is valid, process it if we are jumping to the next state
161 if ($type == 'Next') {
162 $page->mainProcess();
163 // we get the state again, since postProcess might have changed it
164 // this bug took me forever to find :) Lobo
165 $state = &$this->_states[$pageName];
166 $state->handleNextState($page);
167 }
168 else {
169 $state->handleBackState($page);
170 }
171 }
172
173 /**
174 * Helper function to add a State to the state machine
175 *
176 * @param string $name
177 * The internal name.
178 * @param int $type
179 * The type of state (START|FINISH|SIMPLE).
180 * @param object $prev
181 * The previous page if any.
182 * @param object $next
183 * The next page if any.
184 *
185 * @return void
186 */
187 public function addState($name, $type, $prev, $next) {
188 $this->_states[$name] = new CRM_Core_State($name, $type, $prev, $next, $this);
189 }
190
191 /**
192 * Given a name find the corresponding state
193 *
194 * @param string $name
195 * The state name.
196 *
197 * @return object
198 * the state object
199 */
200 public function find($name) {
201 if (array_key_exists($name, $this->_states)) {
202 return $this->_states[$name];
203 }
204 else {
205 return NULL;
206 }
207 }
208
209 /**
210 * Return the list of state objects
211 *
212 * @return array
213 * array of states in the state machine
214 */
215 public function getStates() {
216 return $this->_states;
217 }
218
219 /**
220 * Return the state object corresponding to the name
221 *
222 * @param string $name
223 * Name of page.
224 *
225 * @return CRM_Core_State
226 * state object matching the name
227 */
228 public function &getState($name) {
229 if (isset($this->_states[$name])) {
230 return $this->_states[$name];
231 }
232
233 /*
234 * This is a gross hack for ajax driven requests where
235 * we change the form name to allow multiple edits to happen
236 * We need a cleaner way of doing this going forward
237 */
238 foreach ($this->_states as $n => $s) {
239 if (substr($name, 0, strlen($n)) == $n) {
240 return $s;
241 }
242 }
243
244 return NULL;
245 }
246
247 /**
248 * Return the list of form objects
249 *
250 * @return array
251 * array of pages in the state machine
252 */
253 public function getPages() {
254 return $this->_pages;
255 }
256
257 /**
258 * AddSequentialStates: meta level function to create a simple
259 * wizard for a state machine that is completely sequential.
260 *
261 *
262 * @param array $pages
263 * (reference ) the array of page objects.
264 *
265 * @internal param array $states states is an array of arrays. Each element
266 * of the top level array describes a state. Each state description
267 * includes the name, the display name and the class name
268 *
269 * @return void
270 */
271 public function addSequentialPages(&$pages) {
272 $this->_pages = &$pages;
273 $numPages = count($pages);
274
275 $this->_pageNames = array();
276 foreach ($pages as $tempName => $value) {
277 if (!empty($value['className'])) {
278 $this->_pageNames[] = $tempName;
279 }
280 else {
281 $this->_pageNames[] = CRM_Utils_String::getClassName($tempName);
282 }
283 }
284
285 $i = 0;
286 foreach ($pages as $tempName => $value) {
287 $name = $this->_pageNames[$i];
288
289 $className = CRM_Utils_Array::value('className',
290 $value,
291 $tempName
292 );
293 $classPath = str_replace('_', '/', $className) . '.php';
294 if ($numPages == 1) {
295 $prev = $next = NULL;
296 $type = CRM_Core_State::START | CRM_Core_State::FINISH;
297 }
298 elseif ($i == 0) {
299 // start state
300 $prev = NULL;
301 $next = $this->_pageNames[$i + 1];
302 $type = CRM_Core_State::START;
303 }
304 elseif ($i == $numPages - 1) {
305 // finish state
306 $prev = $this->_pageNames[$i - 1];
307 $next = NULL;
308 $type = CRM_Core_State::FINISH;
309 }
310 else {
311 // in between simple state
312 $prev = $this->_pageNames[$i - 1];
313 $next = $this->_pageNames[$i + 1];
314 $type = CRM_Core_State::SIMPLE;
315 }
316
317 $this->addState($name, $type, $prev, $next);
318
319 $i++;
320 }
321 }
322
323 /**
324 * Reset the state machine
325 *
326 * @return void
327 */
328 public function reset() {
329 $this->_controller->reset();
330 }
331
332 /**
333 * Getter for action
334 *
335 * @return int
336 */
337 public function getAction() {
338 return $this->_action;
339 }
340
341 /**
342 * Setter for content
343 *
344 * @param string $content
345 * The content generated by this state machine.
346 *
347 * @return void
348 */
349 public function setContent(&$content) {
350 $this->_controller->setContent($content);
351 }
352
353 /**
354 * Getter for content
355 *
356 * @return string
357 */
358 public function &getContent() {
359 return $this->_controller->getContent();
360 }
361
362 /**
363 * @return mixed
364 */
365 public function getDestination() {
366 return $this->_controller->getDestination();
367 }
368
369 /**
370 * @return mixed
371 */
372 public function getSkipRedirection() {
373 return $this->_controller->getSkipRedirection();
374 }
375
376 /**
377 * @return mixed
378 */
379 public function fini() {
380 return $this->_controller->fini();
381 }
382
383 /**
384 * @return mixed
385 */
386 public function cancelAction() {
387 return $this->_controller->cancelAction();
388 }
389
390 /**
391 * Should the controller reset the session
392 * In some cases, specifically search we want to remember
393 * state across various actions and want to go back to the
394 * beginning from the final state, but retain the same session
395 * values
396 *
397 * @return bool
398 */
399 public function shouldReset() {
400 return TRUE;
401 }
402
403 }