1<?php 2/** 3 * Created by IntelliJ IDEA. 4 * User: andi 5 * Date: 2/10/17 6 * Time: 3:18 PM 7 */ 8 9namespace dokuwiki; 10 11use dokuwiki\Action\AbstractAction; 12use dokuwiki\Action\Exception\ActionDisabledException; 13use dokuwiki\Action\Exception\ActionException; 14use dokuwiki\Action\Exception\FatalException; 15use dokuwiki\Action\Exception\NoActionException; 16 17class ActionRouter { 18 19 /** @var AbstractAction */ 20 protected $action; 21 22 /** @var ActionRouter */ 23 protected $instance; 24 25 /** 26 * ActionRouter constructor. Singleton, thus protected! 27 */ 28 protected function __construct() { 29 } 30 31 /** 32 * Get the singleton instance 33 * 34 * @param bool $reinit 35 * @return ActionRouter 36 */ 37 public function getInstance($reinit = false) { 38 if(($this->instance === null) || $reinit) { 39 $this->instance = new ActionRouter(); 40 } 41 return $this->instance; 42 } 43 44 /** 45 * Setup the given action 46 * 47 * Instantiates the right class, runs permission checks and pre-processing and 48 * seta $action 49 * 50 * @param string $actionname 51 * @fixme implement redirect on action change with post 52 * @fixme add event handling 53 * @fixme add the action name back to $ACT for plugins relying on it 54 */ 55 protected function setupAction($actionname) { 56 try { 57 $this->action = $this->loadAction($actionname); 58 $this->action->checkPermissions(); 59 $this->ensureMinimumPermission($this->action->minimumPermission()); 60 $this->action->preProcess(); 61 62 } catch(ActionException $e) { 63 // we should have gotten a new action 64 $newaction = $e->getNewAction(); 65 66 // no infinite recursion 67 if($newaction === $actionname) { 68 // FIXME this doesn't catch larger circles 69 $this->handleFatalException(new FatalException('Infinite loop in actions', 500, $e)); 70 } 71 72 // this one should trigger a user message 73 if(is_a($e, ActionDisabledException::class)) { 74 msg('Action disabled: ' . hsc($actionname), -1); 75 } 76 77 // do setup for new action 78 $this->setupAction($newaction); 79 80 } catch(NoActionException $e) { 81 // FIXME here the unknown event needs to be run 82 $this->action = $this->loadAction('show'); 83 84 } catch(\Exception $e) { 85 $this->handleFatalException($e); 86 } 87 } 88 89 /** 90 * Check that the given minimum permissions are reached 91 * 92 * @param int $permneed 93 * @throws ActionException 94 */ 95 protected function ensureMinimumPermission($permneed) { 96 global $INFO; 97 if($INFO['perm'] < $permneed) { 98 throw new ActionException('denied'); 99 } 100 } 101 102 /** 103 * Aborts all processing with a message 104 * 105 * When a FataException instanc is passed, the code is treated as Status code 106 * 107 * @param \Exception|FatalException $e 108 */ 109 protected function handleFatalException(\Exception $e) { 110 if(is_a($e, FatalException::class)) { 111 http_status($e->getCode()); 112 } else { 113 http_status(500); 114 } 115 $msg = 'Something unforseen has happened: ' . $e->getMessage(); 116 nice_die(hsc($msg)); 117 } 118 119 /** 120 * Load the given action 121 * 122 * @param $actionname 123 * @return AbstractAction 124 * @throws NoActionException 125 */ 126 protected function loadAction($actionname) { 127 $class = 'dokuwiki\\Action\\' . ucfirst(strtolower($actionname)); 128 if(class_exists($class)) { 129 return new $class; 130 } 131 throw new NoActionException(); 132 } 133 134 /** 135 * Returns the action handling the current request 136 * 137 * @return AbstractAction 138 */ 139 public function getAction() { 140 return $this->action; 141 } 142} 143