1<?
2
3require_once dirname(__FILE__) . '/component_manager.php';
4require_once dirname(__FILE__) . '/action.php';
5
6define(DOKU_ACTION_ROOT, '/commands');
7
8class ConflictActionException extends Exception {
9	private $first = '';
10	private $second = '';
11	public function first() { return $this->first; }
12	public function second() { return $this->second; }
13	public function __construct($first, $second) {
14		$this->first = $first;
15		$this->second = $second;
16	}
17}
18
19class Doku_Action_Manager extends Doku_Component_Manager {
20    // this holds the renderer of the specific action
21    private $renderer = NULL;
22    // this holds the handler of the specific action
23    private $handler = NULL;
24    // this holds all the loaded renderers
25    private $renderers = array();
26    // this holds all the loaded handlers
27    private $handlers = array();
28    // this is the default manager
29    private static $manager = NULL;
30
31    public static function manager() {
32    	if (!self::$manager)
33    		self::$manager = new Doku_Action_Manager;
34    	return self::$manager;
35    }
36
37    // create an object and check if it responds to the correct action
38    private static function create($class, $action) {
39        $handler = new $class;
40        return ($handler->action() != $action) ? NULL : $handler;
41    }
42
43    /**
44     * handles a new class that is loaded in
45     * @param string $class the name of the new class.
46     */
47	protected function handle($class) {
48        if (is_subclass_of($class, 'Doku_Action'))
49            $this->handlers[] = $class;
50        else if (is_subclass_of($class, 'Doku_Action_Renderer'))
51        	$this->renderers[] = $class;
52	}
53
54	// filter the classes and find the handler that
55	// can handle the action, and is not extended
56	private function unique($classes, $action) {
57		$handler = NULL;
58		foreach ($classes as $class) {
59			$new = $this->create($class, $action);
60			if (!$handler) {
61				$handler = $new;
62				continue;
63			}
64			if ($new && $new->action() == $action) {
65				if (is_subclass_of($new, get_class($handler)))
66					$handler = $new;
67				else if (!is_subclass_of($handler, get_class($new)))
68					throw new ConflictActionException($handler, $new);
69			}
70		}
71		return $handler;
72	}
73
74	/**
75	 * perform the action
76	 * the action name must be plugin_name.action_name.
77	 * @global string $ID the page ID
78	 * @global array $INFO the page information array
79	 * @param string $action the action to be peformed;
80	 * @return bool whether the action has been performed (regardless of being successful).
81	 */
82	public function act(&$action) {
83		// some times the action is an array
84		if (is_array($action))
85			list($action) = array_keys($action);
86
87		$this->handler = NULL;
88		$this->renderer = NULL;
89		$this->handlers = array();
90		$this->renderers = array();
91
92		$components = explode('.', $action);
93		if (count($components) <= 1) return FALSE;
94		$plugin = array_shift($components);
95		$action = implode('.', $components);
96		$path = DOKU_PLUGIN  . $plugin . DOKU_ACTION_ROOT;
97		$this->load($path, $action);
98
99		$this->handler = $this->unique($this->handlers, $action);
100		$this->renderer = $this->unique($this->renderers, $action);
101
102        // check if the action is disabled
103        if (!actionOK($action)) {
104            msg('action disabled: ' . htmlspecialchars($action), -1);
105            $action = 'show';
106            return self::act($action);
107        }
108
109        // check if we can handle the action
110        if (!$this->handler) return FALSE;
111
112        global $INFO;
113        // check permission
114        if ($this->handler->permission_required() > $INFO['perm']) {
115            $action = 'denied';
116            return $this->act($action);
117        }
118
119        // handle the action
120        $new_action = $this->handler->handle();
121        // handle the next action
122        if ($new_action && $new_action !== $action) {
123        	$action = $new_action;
124            return $this->act($action);
125        }
126
127        return TRUE;
128	}
129
130    /**
131     * Doku_Action public interface to render the result of an action
132     *
133     * @param type $action the action to display
134     * @return boolean whether the results has been successfully displayed
135     */
136    public function render($action) {
137        if (!$this->renderer) return FALSE;
138		if (is_array($action)) {
139			$result = '';
140			foreach ($action as $act => $x)
141				$result .= $this->render($act);
142			return $result;
143		}
144
145        ob_start();
146        $this->renderer->xhtml();
147        $html_output = ob_get_clean();
148
149        trigger_event('TPL_CONTENT_DISPLAY', $html_output, 'ptln');
150        return !empty($html_output);
151    }
152}