1<?php
2/**
3 * Wiki farm manager admin plugin
4 *
5 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author   Etienne MELEARD <etienne.meleard@cru.fr>
7 * @desc     Allow farm administration through the DokuWiki admin interface,
8 *           handle a farm command from $_REQUEST['farm_cmd'], loads a handler class
9 *           form it then process request / display output html
10 */
11
12// must be run within Dokuwiki
13if(!defined('DOKU_INC')) die();
14
15if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
16if(!defined('DOKU_FARM_PLUGIN')) define('DOKU_FARM_PLUGIN', DOKU_PLUGIN.'farm/');
17
18if(!defined('DOKU_FARM_WWW')) define('DOKU_FARM_WWW', 'lib/plugins/farm/');
19
20if(!defined('DOKU_FARMPLUGINLOADED')) define('DOKU_FARMPLUGINLOADED', true);
21
22require_once(DOKU_PLUGIN.'admin.php');
23
24/**
25 * Version string, will be used to check whether we need to upgrade the farm or not,
26 * it can be in the case of a new installation or if we updated the plugin from
27 * plugin manager / fs and the downloaded version is higher than the installed one,
28 * in this case we update structure / config if needed.
29 */
30define('DOKU_FARM_VERSION', '1');
31
32/**
33 * All DokuWiki plugins to extend the admin function
34 * need to inherit from this class
35 */
36class admin_plugin_farm extends DokuWiki_Admin_Plugin {
37	var $disabled = 0;
38
39	var $cmd = '';
40	var $opt = array();
41
42	var $conf = array();
43
44	var $handler = null;
45	var $lang = array();
46	var $localised = false;
47
48	var $errors = array();
49	var $success = array();
50
51	var $version = null;
52
53	function __construct() {
54		global $conf;
55
56		if(defined('DOKU_IS_FARMER')) $this->getConf();
57
58		$this->disabled = (isset($conf['pluginfarm']) && ($conf['pluginfarm'] == 0));
59	}
60
61	/**
62	 * Return some info
63	 */
64	function getInfo() {
65		return array(
66			'author' => 'Etienne Meleard',
67			'email'  => 'etienne.meleard@cru.fr',
68			'date'   => '2009-12-16',
69			'name'   => 'Farm Manager',
70			'desc'   => 'Manage Wiki farm'.($this->disabled ? ' (disabled)' : ''),
71			'url'    => 'http://wiki.splitbrain.org/plugin:farm',
72		);
73	}
74
75	/**
76	 * Return prompt for admin menu
77	 * @param $l language identifier
78	 * @return nothing if not in farmer context, menu title otherwise
79	 */
80	function getMenuText($l) {
81		if(!defined('DOKU_IS_FARMER')) {
82			@touch(DOKU_CONF.'local.php');
83			return $this->getLang('menu'); // not installed
84		}
85		if(!DOKU_IS_FARMER) return; // don't display farm management item in the animals' admin page
86		if(!$this->disabled) return $this->getLang('menu');
87		return '';
88	}
89
90	/**
91	 * Return sort order for position in admin menu
92	 * @return integer
93	 */
94	function getMenuSort() {
95		return 20;
96	}
97
98	/**
99	 * Get localized string
100	 * @param $code lang string identifier
101	 * @param $data string or array used to replace %s occurences in the localized string
102	 * @param $opt option to postprocess localized string
103	 * @return localized string
104	 */
105	function getLang($code, $data = null, $opt='') {
106		if(!$this->localised) $this->setupLocale();
107		if(!isset($this->lang[$code])) return '{'.strtoupper($code).'}';
108		$str = $this->lang[$code];
109		if(!is_null($data)) if(!is_array($data)) $data = array($data);
110		if(is_array($data)) foreach($data as $d) $str = preg_replace('`%s`', $d, $str, 1);
111		switch($opt) {
112			case 'js': $str = htmlentities(str_replace('\'', '\\\'', $str)); break;
113		}
114		return $str;
115	}
116
117	/**
118	 * Get farm config
119	 * @return array of farm config parameters
120	 */
121	function getConf() {
122		$farmconf = array();
123		include DOKU_FARM_PLUGIN.'config.php';
124		$this->conf = $farmconf;
125		return $this->conf;
126	}
127
128	/**
129	 * Add error to error list
130	 * @param $family error familly identifier (lang string identifier)
131	 * @param $e error code (lang string identifier) or array containing 'code' => string and 'data => array entries
132	 */
133	function error($family, $e) {
134		if($family == '') $family = 'not_classified_errors';
135		if(!isset($this->errors[$family])) $this->errors[$family] = array();
136		$this->errors[$family][] = $e;
137	}
138
139	/**
140	 * Print errors
141	 */
142	function putErrors() {
143		if(!count($this->errors)) return;
144		foreach($this->errors as $c => $list) {
145			ptln('<div class="error">');
146			ptln('	'.$this->getLang($c));
147			ptln('	<ul>');
148			foreach($list as $e) ptln('		<li>'.$this->getLang(is_array($e) ? $e['code'] : $e, is_array($e) ? $e['data'] : null).'</li>');
149			ptln('	</ul>');
150			ptln('</div>');
151		}
152	}
153
154	/**
155	 * Add success to success list
156	 * @param $s success code (lang string identifier)
157	 */
158	function success($s) {
159		$this->success[] = $s;
160	}
161
162	/**
163	 * Print successes
164	 */
165	function putSuccess() {
166		if(!count($this->success)) return;
167		ptln('<div class="success">');
168		if(count($this->success) > 1) {
169			ptln('	<ul>');
170			foreach($this->success as $s) ptln('		<li>'.$this->getLang(is_array($s) ? $s['code'] : $s, is_array($s) ? $s['data'] : null).'</li>');
171			ptln('	</ul>');
172		}else{
173			$s = reset($this->success);
174			ptln('	'.$this->getLang(is_array($s) ? $s['code'] : $s, is_array($s) ? $s['data'] : null));
175		}
176		ptln('</div>');
177	}
178
179	/**
180	 * Returns human readable size
181	 * @param $s size as integer
182	 * @return human readabe localized size as a string
183	 */
184	function nicesize($s) {
185		$e = '';
186		$m = array('k', 'M', 'G', 'T');
187		foreach($m as $p) {
188			if($s < 1024) break;
189			$s /= 1024;
190			$e = $p;
191		}
192		return number_format($s, ($s >= 100 || $e == '') ? 0 : 1).$e.$this->getLang('size_unit');
193	}
194
195	/**
196	 * Print form head
197	 * @param $data associative array of hidden fields names and values to be printed in the form
198	 */
199	function formHead($data = array()) {
200		global $ID;
201		echo '<form method="post" action="'.wl($ID).'">'."\n";
202		echo '	<fieldset class="hidden">'."\n";
203		echo '		<input type="hidden" name="do" value="admin" />'."\n";
204		echo '		<input type="hidden" name="page" value="farm" />'."\n";
205		foreach($data as $k => $v) echo '		<input type="hidden" name="'.$k.'" value="'.$v.'" />'."\n";
206		formSecurityToken();
207		echo '	</fieldset>'."\n";
208	}
209
210	/**
211	 * Builds a link inside farm manager
212	 * @param $cmd farm command
213	 * @param $opt associative array of farm options
214	 * @return url string
215	 */
216	function wl($cmd, $opt = array()) {
217		global $ID;
218		$p = array('do' => 'admin', 'page' => 'farm', 'farm_cmd' => $cmd);
219		foreach($opt as $k => $v) $p['farm_opt__'.$k] = $v;
220		return wl($ID, $p);
221	}
222
223	/**
224	 * Handle requests
225	 */
226	function handle() {
227		// Do nothing if disabled / not installed
228		if($this->disabled) return;
229		if(defined('DOKU_IS_FARMER')) if(!DOKU_IS_FARMER) return;
230
231		// Enable direct access to language strings
232		$this->setupLocale();
233
234		// get installed version
235		$this->version = @file_exists(DOKU_FARM_PLUGIN.'installed') ? trim(@file_get_contents(DOKU_FARM_PLUGIN.'installed'), "\n") : null;
236		if($this->version != DOKU_FARM_VERSION) {
237			// set installer class as handler if the farm is not installed
238			if(is_null($this->version)) {
239				$_REQUEST['farm_cmd'] = 'farminstall';
240			}elseif($this->version == '0.99') {
241				if($fp = fopen(DOKU_FARM_PLUGIN.'installed', 'w')) {
242					fwrite($fp, '1');
243					fclose($fp);
244					$this->success('install_success');
245					$_REQUEST['farm_plugin_cmd'] = 'farmconfig';
246				}else $this->error('install_errors', 'install_stepupdate_failure');
247			}
248			// Add code here to upgrade to other (future) version
249		}
250
251		// Fetch command from request
252		$this->cmd = 'overview';
253		$this->opt = array();
254		if(isset($_REQUEST['farm_cmd'])) {
255			if(is_array($_REQUEST['farm_cmd'])) {
256				$this->cmd = key($_REQUEST['farm_cmd']);
257				$t = $_REQUEST['farm_cmd'][$this->cmd];
258				while(is_array($t)) {
259					$k = key($t);
260					if(is_array($t[$k])) {
261						$v = key($t[$k]);
262						$t = $t[$k][$v];
263						if(preg_match('`^(true|false)$`i', $v, $m)) {
264							$v = (strtolower($m[1]) == 'true');
265						}elseif(preg_match('`^([0-9]+)$`', $v, $m)) {
266							$v = (int)$m[1];
267						}elseif(preg_match('`^([0-9]+\.[0-9]+)$`', $v, $m)) {
268							$v = (float)$m[1];
269						}
270						$this->opt[$k] = $v;
271					}else{
272						$this->opt[$k] = $t[$k];
273						$t = null;
274					}
275				}
276			}else $this->cmd = $_REQUEST['farm_cmd'];
277		}
278
279		// Fetch arguments from request
280		if(isset($_REQUEST['farm_opt']) && !empty($_REQUEST['farm_opt'])) {
281			if(is_array($_REQUEST['farm_opt'])) {
282				foreach($_REQUEST['farm_opt'] as $k => $v) {
283					if(preg_match('`^(true|false)$`i', $v, $m)) {
284						$v = (strtolower($m[1]) == 'true');
285					}elseif(preg_match('`^([0-9]+)$`', $v, $m)) {
286						$v = (int)$m[1];
287					}elseif(preg_match('`^([0-9]+\.[0-9]+)$`', $v, $m)) {
288						$v = (float)$m[1];
289					}
290					if(!isset($this->opt[$k])) $this->opt[$k] = $v;
291				}
292			}else $this->opt[$_REQUEST['farm_opt']] = true;
293		}
294
295		// Fetch arguments from non array get var
296		foreach($_GET as $k => $v) {
297			if(preg_match('`^farm_opt__([a-zA-Z0-9_]+)$`', $k, $m)) {
298				$this->opt[$m[1]] = $v;
299			}
300		}
301
302		//echo 'REQUEST : '; print_r($_REQUEST);
303		//echo '<br />CMD : '; print_r($this->cmd);
304		//echo '<br />OPT : '; print_r($this->opt);
305
306		// List of allowed commands and their handlers
307		$commands = array(
308			'farminstall' => 'install',
309			'overview' => 'overview',
310			'farmconfig' => 'config',
311			'soapconfig' => 'soapconfig',
312			'virtualhostconfig' => 'virtualhostconfig',
313			'animal' => 'animalmanager'
314		);
315
316		// verify command vars
317		if(!isset($commands[$this->cmd])) $this->cmd = 'overview';
318
319		// create an object to handle the command, then process request with it
320		$file = DOKU_FARM_PLUGIN.$commands[$this->cmd].'.class.php';
321		if(@file_exists($file)) {
322			include_once $file;
323			$class = 'dokuwiki_farm_'.$commands[$this->cmd];
324			if(!class_exists($class)) {
325				include_once DOKU_FARM_PLUGIN.'overview.class.php';
326				$class = 'dokuwiki_farm_overview';
327			}
328			$this->handler = & new $class($this);
329			if($this->handler) $this->handler->process();
330		}
331	}
332
333	/**
334	 * Output appropriate html
335	 */
336	function html() {
337		global $ID;
338
339		// Do nothing if disabled / not installed
340		if($this->disabled) return;
341		if(defined('DOKU_IS_FARMER')) if(!DOKU_IS_FARMER) return;
342
343		// Enable direct access to language strings
344		$this->setupLocale();
345
346		ptln('<div id="farm__manager">');
347		ptln('	<div class="farm_title">'.$this->getLang('menu').'</div>');
348
349		// Farm menu
350		if($this->cmd != 'farminstall') {
351			ptln('	<div class="farm_commands">');
352			ptln('		<a '.($this->cmd == 'overview' ? 'class="current_cmd"' : '').' href="'.$this->wl('overview').'"><img src="'.DOKU_FARM_WWW.'images/home.png" alt="overview" /> '.$this->getLang('overview_title').'</a>');
353			if($this->cmd == 'animal' && isset($this->opt['aid'])) ptln('		<a class="current_cmd" href="'.$this->wl('animal', array('aid' => $this->opt['aid'])).'"><img src="'.DOKU_FARM_WWW.'images/aconf.png" alt="animal settings" /> '.$this->getLang('animal_title', array($this->opt['aid'])).'</a>');
354			ptln('		<a '.($this->cmd == 'animal' && isset($this->opt['new']) ? 'class="current_cmd"' : '').' href="'.$this->wl('animal', array('new' => 1)).'"><img src="'.DOKU_FARM_WWW.'images/add.png" alt="add animal" /> '.$this->getLang('animal_new_title').'</a>');
355			ptln('		<a '.($this->cmd == 'farmconfig' ? 'class="current_cmd"' : '').' href="'.$this->wl('farmconfig').'"><img src="'.DOKU_FARM_WWW.'images/configure.png" alt="config" /> '.$this->getLang('config_title').'</a>');
356			if($this->conf['enablesoap']) ptln('		<a '.($this->cmd == 'soapconfig' ? 'class="current_cmd"' : '').' href="'.$this->wl('soapconfig').'"><img src="'.DOKU_FARM_WWW.'images/soap.png" alt="soap config" /> '.$this->getLang('soapconfig_title').'</a>');
357			if($this->conf['virtual']) ptln('		<a '.($this->cmd == 'virtualhostconfig' ? 'class="current_cmd"' : '').' href="'.$this->wl('virtualhostconfig').'"><img src="'.DOKU_FARM_WWW.'images/virtualhost.png" alt="virtualhost config" /> '.$this->getLang('virtualhostconfig_title').'</a>');
358			ptln('	</div>');
359		}
360
361		// Call handler renderer, prints errors / successes if needed
362		ptln('	<div class="farm_cmd farm_cmd_'.$this->cmd.'_class">');
363		if(!$this->handler) $this->error('system_errors', array('code' => 'system_nohandler_failure', 'data' => array($this->cmd)));
364		if($this->handler) if(method_exists($this->handler, 'htmlheader')) $this->handler->htmlheader();
365		$this->putErrors();
366		$this->putSuccess();
367		if($this->handler) $this->handler->html();
368		ptln('	</div>');
369		ptln('</div><!-- #farm__manager -->');
370	}
371}
372
373?>
374