3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2015
37 * All state machines subclass for the core one for functionality specific to their needs.
39 * A state machine keeps track of different states and forms for a
40 * html quickform controller.
42 class CRM_Core_StateMachine
{
45 * The controller of this state machine.
48 protected $_controller;
51 * The list of states that belong to this state machine.
57 * The list of pages that belong to this state machine. Note
58 * that a state and a form have a 1 <-> 1 relationship. so u
59 * can always derive one from the other
65 * The names of the pages.
69 protected $_pageNames;
72 * The mode that the state machine is operating in.
75 protected $_action = NULL;
78 * The display name for this machine.
81 protected $_name = NULL;
86 * @param object $controller
87 * The controller for this state machine.
89 * @param \const|int $action
91 * @return \CRM_Core_StateMachine
93 public function __construct(&$controller, $action = CRM_Core_Action
::NONE
) {
94 $this->_controller
= &$controller;
95 $this->_action
= $action;
97 $this->_states
= array();
105 public function getName() {
112 * @param string $name
114 public function setName($name) {
115 $this->_name
= $name;
119 * Do a state transition jump.
121 * Currently only supported types are
122 * Next and Back. The other actions (Cancel, Done, Submit etc) do
123 * not need the state machine to figure out where to go
125 * @param CRM_Core_Form $page
126 * The current form-page.
127 * @param string $actionName
128 * Current action name, as one Action object can serve multiple actions.
129 * @param string $type
130 * The type of transition being requested (Next or Back).
134 public function perform(&$page, $actionName, $type = 'Next') {
135 // save the form values and validation status to the session
136 $page->isFormBuilt() or $page->buildForm();
138 $pageName = $page->getAttribute('name');
139 $data = &$page->controller
->container();
141 $data['values'][$pageName] = $page->exportValues();
142 $data['valid'][$pageName] = $page->validate();
144 // if we are going to the next state
145 // Modal form and page is invalid: don't go further
146 if ($type == 'Next' && !$data['valid'][$pageName]) {
147 return $page->handle('display');
150 $state = &$this->_states
[$pageName];
152 // dont know how or why we landed here so abort and display
155 return $page->handle('display');
158 // the page is valid, process it if we are jumping to the next state
159 if ($type == 'Next') {
160 $page->mainProcess();
161 // we get the state again, since postProcess might have changed it
162 // this bug took me forever to find :) Lobo
163 $state = &$this->_states
[$pageName];
164 $state->handleNextState($page);
167 $state->handleBackState($page);
172 * Helper function to add a State to the state machine.
174 * @param string $name
177 * The type of state (START|FINISH|SIMPLE).
178 * @param object $prev
179 * The previous page if any.
180 * @param object $next
181 * The next page if any.
183 public function addState($name, $type, $prev, $next) {
184 $this->_states
[$name] = new CRM_Core_State($name, $type, $prev, $next, $this);
188 * Given a name find the corresponding state.
190 * @param string $name
196 public function find($name) {
197 if (array_key_exists($name, $this->_states
)) {
198 return $this->_states
[$name];
206 * Return the list of state objects.
209 * array of states in the state machine
211 public function getStates() {
212 return $this->_states
;
216 * Return the state object corresponding to the name.
218 * @param string $name
221 * @return CRM_Core_State
222 * state object matching the name
224 public function &getState($name) {
225 if (isset($this->_states
[$name])) {
226 return $this->_states
[$name];
230 * This is a gross hack for ajax driven requests where
231 * we change the form name to allow multiple edits to happen
232 * We need a cleaner way of doing this going forward
234 foreach ($this->_states
as $n => $s) {
235 if (substr($name, 0, strlen($n)) == $n) {
244 * Return the list of form objects.
247 * array of pages in the state machine
249 public function getPages() {
250 return $this->_pages
;
254 * Add sequential pages.
256 * Meta level function to create a simple wizard for a state machine that is completely sequential.
258 * @param array $pages
259 * (reference ) the array of page objects.
261 public function addSequentialPages(&$pages) {
262 $this->_pages
= &$pages;
263 $numPages = count($pages);
265 $this->_pageNames
= array();
266 foreach ($pages as $tempName => $value) {
267 if (!empty($value['className'])) {
268 $this->_pageNames
[] = $tempName;
271 $this->_pageNames
[] = CRM_Utils_String
::getClassName($tempName);
276 foreach ($pages as $tempName => $value) {
277 $name = $this->_pageNames
[$i];
279 $className = CRM_Utils_Array
::value('className',
283 $classPath = str_replace('_', '/', $className) . '.php';
284 if ($numPages == 1) {
285 $prev = $next = NULL;
286 $type = CRM_Core_State
::START | CRM_Core_State
::FINISH
;
291 $next = $this->_pageNames
[$i +
1];
292 $type = CRM_Core_State
::START
;
294 elseif ($i == $numPages - 1) {
296 $prev = $this->_pageNames
[$i - 1];
298 $type = CRM_Core_State
::FINISH
;
301 // in between simple state
302 $prev = $this->_pageNames
[$i - 1];
303 $next = $this->_pageNames
[$i +
1];
304 $type = CRM_Core_State
::SIMPLE
;
307 $this->addState($name, $type, $prev, $next);
314 * Reset the state machine.
316 public function reset() {
317 $this->_controller
->reset();
325 public function getAction() {
326 return $this->_action
;
330 * Setter for content.
332 * @param string $content
333 * The content generated by this state machine.
335 public function setContent(&$content) {
336 $this->_controller
->setContent($content);
340 * Getter for content.
344 public function &getContent() {
345 return $this->_controller
->getContent();
351 public function getDestination() {
352 return $this->_controller
->getDestination();
358 public function getSkipRedirection() {
359 return $this->_controller
->getSkipRedirection();
365 public function fini() {
366 return $this->_controller
->fini();
372 public function cancelAction() {
373 return $this->_controller
->cancelAction();
377 * Should the controller reset the session
378 * In some cases, specifically search we want to remember
379 * state across various actions and want to go back to the
380 * beginning from the final state, but retain the same session
385 public function shouldReset() {