1<?php
2
3if (!class_exists('settingshierarchy',false)){
4
5require_once('settingslevel.class.php');
6
7
8class settingshierarchy{
9	private $_root = null;					// root settingslevel of the hierarchy
10	private $_pluginname = null;			// the pluginname of the settings we set
11	private $_meta = array();				// the metadata for each setting (from plugin config) array by key=>meta
12	private $_defaults = array();			// the defaults for each settings (from plugin config) array by key=>default
13	private $_values = array();				// the values and protections set by admins in admin pages. array by path => [key => [ prot, value]];
14
15	private $_lang = null;					// to be used for getLang (lang of the _pluginname)
16	private static $_config_lang = null;		// to be used if needed (lang of the config plugin)
17	private static $conf_local = null;		// local configurations (only for plugins)
18	private static $conf_protected = null;	// protected configurations (only for plugins)
19	public static $cache = null;			// memcache
20	public static $helper = null;			// settingstree for local getLang
21
22
23	function __construct($pluginname,$meta,$defaults,$values){
24		$this->_pluginname = $pluginname;
25		$this->_meta = $meta;
26		$this->_defaults = $defaults;
27		$this->_values = $values;
28
29
30	}
31	function getLang($key){
32		return $this->_getLang($key);
33	}
34	function _getLang($key,$config_plugin = false){
35		$lang = $this->_getLangInited($config_plugin);
36		global $conf;
37		if (!($ret = @$lang[$conf['lang']][$key])){
38			if (!($ret = @$lang['en'][$key])){
39				// we need to check, if it's '{setting}_o_{key}' from a plugin setting, and return null if it is, but translation does not exists, to display only the 'key' part of it.
40				if (!preg_match('~^(?:'.implode('|',array_keys($this->_meta)).')_o_(.*)$~',$key,$match)){
41					if (!$config_plugin && ($ret = $this->_getLang($key,true)) === null){	// check if it a key for config plugin.
42						// Note: if lang keys needs to be html escaped then there is a conceptual problem about msgids...
43						$ret = "{msgid:{$key}}"; // else we need to return the something if we want to display, that the key is missing instead of simply ignore a message...
44					}
45				/** 	imagine a situation:
46				 *	function resultMessage($error){
47				 *		$message = '';
48				 *		if ($error)
49				 *			$message .= getLang('corruption_warning');			// key missing, should be 'Corruption WARNING!';
50				 *		$message .= ' '.sprintf(
51				 *			getLang('your_data_is_%s_saved'),					// key=>value: 'Your data is %s saved!'
52				 *			($error ? getLang('not') : ''),						// key missing, should be 'not'
53				 *			);
54				 *		if ($error)
55				 *			$message .= ' '.getLang('backup_your_data');		// key missing, should be 'Make sure you backup your data manually!'
56				 *		return trim($message);
57				 *	}
58				 *		on error:
59				 *		The user should see if keys where there:			 'Corruption WARNING! Your data is not saved! Make sure you backup your data manually!';
60				 *		User should at least see:							 '{msgid:corruption_warning} Your data is {msgid:not} saved! {msgid:backup_your_data}';
61				 *		By ignoring missing messages, the user will see: 	 'Your data is  saved!';
62				 */
63				}
64				else{
65					if (($ret = $this->_getLang($match[1],$config_plugin)) === "{msgid:{$key}}");	// try to get the 'key' part as localized from the '{setting}_o_{key}'
66						return null; // if there is not localisation for the key, then return null.
67				}
68			}
69		}
70		return $ret;
71	}
72
73	private function _getLangInited($config_plugin = false){
74		if($config_plugin){
75			$_lang = &$this->_config_lang;
76		}else{
77			$_lang = &$this->_lang;
78		}
79		if ($_lang === null){
80			// set the $_lang as a reference for the lang array we're updating
81			global $conf;
82			$_lang = array();
83			$type = $config_plugin ? 'lang' : 'settings';
84			$pluginname = ($config_plugin ? 'config' : $this->_pluginname);
85			$ls = array($conf['lang']);
86			if ($conf['lang'] !== 'en') $ls[] = 'en';	// English is always the fallback.
87
88			foreach ($ls as $l){	// for all language we need
89				$path = DOKU_INC."lib/plugins/{$pluginname}/lang/{$l}/{$type}.php";
90				if (static::$cache 	//if caching is enabled
91					&& @filemtime($path) <= static::$cache->get("plugin_{$pluginname}_lang_{$l}_{$type}_time") 	// and cached version is old enough (note: cache is not bound to settingstree: "plugin_{name}_lang_{lang}_type[_time]" is usable by all plugins
92					&& $ll = static::$cache->get("plugin_{$pluginname}_lang_{$l}_{$type}")						// and cache contains the language array
93					){
94					$_lang[$l] = $ll;
95					continue; // use that, no need to include the files.
96				}
97				if (file_exists($path)){
98					$lang = array();
99					@include($path);
100					$_lang[$l] = $lang;
101					if (static::$cache){	// update the cache so next we don't need to read filesystem
102						static::$cache->set("plugin_{$pluginname}_lang_{$l}_{$type}",$lang,0);
103						static::$cache->set("plugin_{$pluginname}_lang_{$l}_{$type}_time",filemtime($path),0);
104					}
105				}
106			}
107		}
108		return $_lang;
109	}
110	function getPluginName(){return $this->_pluginname;}
111	function getLevel($folder){
112		if (!$this->_root) $this->_loadTree();
113		$path = explode(':',ltrim(strtr($folder,'/',':'),':'));
114		if ($path[0] == '') return $this->_root;
115		return $this->_root->getLevel($path);
116	}
117
118
119	function getFieldConfig(){
120		$return = array();
121		foreach ($this->_meta as $key=>$meta){
122			if (@$meta['_ignore_for_settingstree']) continue;
123			$return[$key] = $meta;
124		}
125		return $return;
126	}
127
128
129	private function _loadTree(){
130		$this->_root = new settingslevel($this,null,':');
131		foreach ($this->_values as $path=>$values){
132			if ($path == ':'){
133				$this->_root->setValues($values);
134			}
135			else{
136				$this->_root->addLevel($path,$values);
137			}
138		}
139	}
140	function getValueTree(){
141		return $this->_root->getValuesRecursive();
142	}
143
144	function getDefault($key){ 			return @$this->_defaults[$key];		}
145	function getLocal($key){ 			return self::_getlocal($key,$this->_pluginname);		}
146	function getProtected($key){		return self::_getprotected($key,$this->_pluginname);	}
147	function isExtended($key){			return self::_isextended($key,$this->_pluginname);	}
148
149	static function _getlocal($key,$pluginname){
150		if (static::_isextended($key,$pluginname)){
151			return null;
152		}
153		if (!static::$conf_local){
154			$conf = array();
155			require (DOKU_INC."conf/local.php");
156			if (is_array(!$conf['plugin'])) $conf['plugin'] = array();	// no plugins sub-array
157			static::$conf_local = $conf['plugin'];
158		}
159		return @static::$conf_local[$pluginname][$key];
160	}
161	static function _getprotected($key,$pluginname){
162		if (static::_isextended($key,$pluginname)){
163			return null;
164		}
165		if (!static::$conf_protected){
166			$conf = array();
167			require (DOKU_INC."conf/local.protected.php");
168			if (is_array(!$conf['plugin'])) $conf['plugin'] = array();	// no plugins sub-array
169			static::$conf_protected = $conf['plugin'];
170		}
171		return @static::$conf_protected[$pluginname][$key];
172	}
173	static function _isextended($key,$pluginname){
174		global $conf;
175		return !array_key_exists($key,(array)@$conf['plugin'][$pluginname]);
176	}
177
178	function showHierarchyLevelRecursive($level,$key,&$empty){
179		$ch_empty = true;
180		$chn = $level->getChildren();
181		if (!empty($chn)){
182			$chhtml = '<ul>';
183			foreach ($chn as $ch){
184				$_chhtml = $this->showHierarchyLevelRecursive($ch,$key,$_empty);
185				if (!$_empty) $chhtml .= $_chhtml;
186				$ch_empty = $ch_empty && $_empty;
187			}
188			$chhtml .= '</ul>';
189		}
190		$p = $level->isLevelProtected($key);
191		$v = $level->isLevelValue($key);
192		$empty = !$p && !$v && $ch_empty;
193		$lev = "<li data-path='{$level->getPath()}' class='".($empty ? 'empty':'')."'>";
194		$lev .= "<b class='".($p ? 'protect':'').' '.($v ? 'value':'')."'>"
195			."{$level->getLevelNameRelative()}</b>";
196		$lev .= ($p ? "<span class='_p'>".settingshierarchy::$helper->getLang('became_protected').".</span>" : "");
197		$lev .= ($v ? "<span class='_v'>".settingshierarchy::$helper->getLang('value_set_to')." <code>{$this->format($key,$level->getLevelValue($key))}</code>".($level->isLevelValueIgnored($key) ? " <i class='_i'>".settingshierarchy::$helper->getLang('but_ignored')."</i>" : "").".</span>" : "");
198		return $lev . ($ch_empty ? "" : $chhtml) ."</li>";
199	}
200	function showHierarchy($key){
201		$ret .= '<ul class="settings_hierarchy_history">';
202		$ret .= "<li class='title'>".sprintf(settingshierarchy::$helper->getLang("settings_for_%s"),$key).'</li>';
203		if (!$this->isExtended($key)){
204			$v = $this->getLocal($key) !== null;
205			$p = $this->getProtected($key) !== null;
206			$ret .= "<li><b class='".($p ? 'protect':'').' '.($v ? 'value':'')."'>".settingshierarchy::$helper->getLang("in_config").":</b>";
207			$ret .=	"<span class='_d'>".settingshierarchy::$helper->getLang('default_is')." <code>{$this->format($key,$this->getDefault($key))}</code>.</span>";
208			$ret .= ($p ? "<span class='_p'>".settingshierarchy::$helper->getLang('became_protected').".</span>" : "");
209			$ret .= ($v ? "<span class='_v'>".settingshierarchy::$helper->getLang('local_is')." <code>{$this->format($key,$this->getLocal($key))}</code>.</span>" : "");
210		}
211		else{
212			$ret .= "<li><b>".settingshierarchy::$helper->getLang("this_is_extended")."</b>";
213			$ret .=	"<span class='_d'>".settingshierarchy::$helper->getLang('default_is')." <code>{$this->format($key,$this->getDefault($key))}</code>.</span>";
214		}
215		if (!$this->_root) $this->_loadTree();
216		$roothtml = "<ul>".$this->showHierarchyLevelRecursive($this->_root,$key,$empty)."</ul>";
217		return $ret. ($empty ? "" : $roothtml)."</li></ul>";
218	}
219	function format($key,$value){
220		if ($value === null) return "[".settingshierarchy::$helper->getLang('default_value')."]";
221		if ($this->_meta[$key][0] == 'onoff'){
222			return settingshierarchy::$helper->getLang($value ? "on" : "off");
223		}
224		if ($value === ''){
225			return "[".settingshierarchy::$helper->getLang('empty_string')."]";
226		}
227		return $value;
228	}
229
230}
231} // class_exists
232