1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8/** 9 * TransitionLib 10 * 11 */ 12class TransitionLib 13{ 14 private $transitionType; 15 16 /** 17 * @param $transitionType 18 */ 19 function __construct($transitionType) 20 { 21 $this->transitionType = $transitionType; 22 } 23 24 /** 25 * @param $object 26 * @param null $type 27 * @return array 28 */ 29 function getAvailableTransitions($object, $type = null) 30 { 31 $states = $this->getCurrentStates($object, $type); 32 33 $transitions = $this->getTransitionsFromStates($states); 34 $transitions = Perms::filter( 35 ['type' => 'transition'], 36 'object', 37 $transitions, 38 ['object' => 'transitionId'], 39 'trigger_transition' 40 ); 41 42 foreach ($transitions as & $tr) { 43 $object = new Tiki_Transition($tr['from'], $tr['to']); 44 $object->setStates($states); 45 foreach ($tr['guards'] as $guard) { 46 call_user_func_array([$object, 'addGuard' ], $guard); 47 } 48 49 $tr['enabled'] = $object->isReady(); 50 $tr['explain'] = $object->explain(); 51 } 52 53 return $transitions; 54 } 55 56 /** 57 * @param $state 58 * @param $object 59 * @param null $type 60 * @return array 61 */ 62 function getAvailableTransitionsFromState($state, $object, $type = null) 63 { 64 $transitions = $this->getAvailableTransitions($object, $type); 65 66 $out = []; 67 foreach ($transitions as $tr) { 68 if ($tr['from'] == $state) { 69 $out[$tr['transitionId']] = $tr['name']; 70 } 71 } 72 73 return $out; 74 } 75 76 /** 77 * @param $transitionId 78 * @param $object 79 * @param null $type 80 * @return bool 81 */ 82 function triggerTransition($transitionId, $object, $type = null) 83 { 84 // Make sure the transition exists 85 if (! $transition = $this->getTransition($transitionId)) { 86 return false; 87 } 88 89 // Make sure the user can use it 90 $perms = Perms::get(['type' => 'transition', 'object' => $transitionId]); 91 if (! $perms->trigger_transition) { 92 return false; 93 } 94 95 // Verify that the states are consistent 96 $states = $this->getCurrentStates($object, $type); 97 98 $tr = new Tiki_Transition($transition['from'], $transition['to']); 99 $tr->setStates($states); 100 101 foreach ($transition['guards'] as $guard) { 102 call_user_func_array([$tr, 'addGuard'], $guard); 103 } 104 105 if (! $tr->isReady()) { 106 return false; 107 } 108 109 $this->addState($transition['to'], $object, $type); 110 if (! $transition['preserve']) { 111 $this->removeState($transition['from'], $object, $type); 112 } 113 114 return true; 115 } 116 117 /** 118 * @param $states 119 * @return array 120 */ 121 function listTransitions($states) 122 { 123 $db = TikiDb::get(); 124 125 if (empty($states)) { 126 return []; 127 } 128 129 $bindvars = [$this->transitionType]; 130 $query = "SELECT `transitionId`, `preserve`, `name`, `from`, `to`, `guards` FROM `tiki_transitions` WHERE `type` = ? AND ( " . 131 $db->in('from', $states, $bindvars) . 132 ' OR ' . $db->in('to', $states, $bindvars) . ')'; 133 134 $result = $db->fetchAll($query, $bindvars); 135 136 return array_map([$this, 'expandGuards'], $result); 137 } 138 139 // Database interaction 140 141 /** 142 * @param $from 143 * @param $to 144 * @param $name 145 * @param bool $preserve 146 * @param array $guards 147 * @return mixed 148 */ 149 function addTransition($from, $to, $name, $preserve = false, array $guards = []) 150 { 151 $db = TikiDb::get(); 152 153 $db->query( 154 "INSERT INTO `tiki_transitions` ( `type`, `from`, `to`, `name`, `preserve`, `guards`) VALUES( ?, ?, ?, ?, ?, ? )", 155 [$this->transitionType, $from, $to, $name, (int) $preserve, json_encode($guards)] 156 ); 157 158 return $db->getOne('SELECT MAX(`transitionId`) FROM `tiki_transitions`'); 159 } 160 161 /** 162 * @param $transitionId 163 * @param $from 164 * @param $to 165 * @param $label 166 * @param $preserve 167 */ 168 function updateTransition($transitionId, $from, $to, $label, $preserve) 169 { 170 $db = TikiDb::get(); 171 $db->query( 172 'UPDATE `tiki_transitions` SET `name` = ?, `from` = ?, `to` = ?, `preserve` = ? WHERE `transitionId` = ?', 173 [$label, $from, $to, (int) $preserve, (int) $transitionId] 174 ); 175 } 176 177 /** 178 * @param $transitionId 179 * @param array $guards 180 */ 181 function updateGuards($transitionId, array $guards) 182 { 183 $db = TikiDb::get(); 184 $db->query( 185 'UPDATE `tiki_transitions` SET `guards` = ? WHERE `transitionId` = ?', 186 [json_encode($guards), (int) $transitionId] 187 ); 188 } 189 190 /** 191 * @param $transitionId 192 */ 193 function removeTransition($transitionId) 194 { 195 $db = TikiDb::get(); 196 197 $db->query('DELETE FROM `tiki_transitions` WHERE `transitionId` = ?', [$transitionId]); 198 } 199 200 /** 201 * @param $states 202 * @return array 203 */ 204 private function getTransitionsFromStates($states) 205 { 206 $db = TikiDb::get(); 207 208 if (empty($states)) { 209 return []; 210 } 211 212 $bindvars = [$this->transitionType]; 213 $query = "SELECT `transitionId`, `preserve`, `name`, `from`, `to`, `guards` FROM `tiki_transitions` WHERE `type` = ? AND " . 214 $db->in('from', $states, $bindvars) . ' AND NOT (' . 215 $db->in('to', $states, $bindvars) . ')'; 216 217 $result = $db->fetchAll($query, $bindvars); 218 219 return array_map([$this, 'expandGuards'], $result); 220 } 221 222 /** 223 * @param $transitionId 224 * @return mixed 225 */ 226 function getTransition($transitionId) 227 { 228 $db = TikiDb::get(); 229 230 $bindvars = [$this->transitionType, $transitionId]; 231 $query = "SELECT `transitionId`, `preserve`, `name`, `from`, `to`, `guards` FROM" . 232 " `tiki_transitions` WHERE `type` = ? AND `transitionId` = ?"; 233 $result = $db->fetchAll($query, $bindvars); 234 235 return $this->expandGuards(reset($result)); 236 } 237 238 /** 239 * @param $transition 240 * @return mixed 241 */ 242 private function expandGuards($transition) 243 { 244 $transition['guards'] = json_decode($transition['guards'], true); 245 if (! $transition['guards']) { 246 $transition['guards'] = []; 247 } 248 249 return $transition; 250 } 251 252 // The following functions vary depending on the transition type 253 254 /** 255 * @param $object 256 * @param $type 257 * @return array 258 */ 259 private function getCurrentStates($object, $type) 260 { 261 switch ($this->transitionType) { 262 case 'group': 263 $userlib = TikiLib::lib('user'); 264 return $userlib->get_user_groups($object); 265 case 'category': 266 $categlib = TikiLib::lib('categ'); 267 return $categlib->get_object_categories($type, $object); 268 } 269 } 270 271 /** 272 * @param $state 273 * @param $object 274 * @param $type 275 */ 276 private function addState($state, $object, $type) 277 { 278 global $prefs; 279 280 switch ($this->transitionType) { 281 case 'group': 282 $userlib = TikiLib::lib('user'); 283 $userlib->assign_user_to_group($object, $state); 284 if ($prefs['default_group_transitions'] === 'y') { 285 $userlib->set_default_group($object, $state); 286 } 287 return; 288 case 'category': 289 $categlib = TikiLib::lib('categ'); 290 $categlib->categorize_any($type, $object, $state); 291 return; 292 } 293 } 294 295 /** 296 * @param $state 297 * @param $object 298 * @param $type 299 */ 300 private function removeState($state, $object, $type) 301 { 302 switch ($this->transitionType) { 303 case 'group': 304 $userlib = TikiLib::lib('user'); 305 $userlib->remove_user_from_group($object, $state); 306 return; 307 case 'category': 308 $categlib = TikiLib::lib('categ'); 309 if ($catobj = $categlib->is_categorized($type, $object)) { 310 $categlib->uncategorize($catobj, $state); 311 } 312 return; 313 } 314 } 315} 316