1 <?php
2 /**
3  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4  * @author     Andreas Gohr <andi@splitbrain.org>
5  */
6 // must be run within Dokuwiki
7 if(!defined('DOKU_INC')) die();
8 
9 if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
10 require_once(DOKU_PLUGIN.'syntax.php');
11 require_once(DOKU_INC.'inc/infoutils.php');
12 require_once(DOKU_PLUGIN.'semanticdata/phpSesame/phpSesame.php');
13 
14 
15 /**
16  * This is the base class for all syntax classes, providing some general stuff
17  */
18 class helper_plugin_semanticdata extends DokuWiki_Plugin {
19 
20 
21 	/**
22 	 * Connect to the triple store
23 	 */
24 	function _getTripleStore(){
25 		static $ts = null;
26 		if ($ts === null) {
27 			$sesame = array('url' => $this->getConf('connect_url'), 'repository' => $this->getConf('connect_repository'));
28 			$ts = new phpSesame($sesame['url'], $sesame['repository']);
29 		}
30 		return $ts;
31 	}
32 
33 
34 	/**
35 	 * Makes sure the given data fits with the given type
36 	 */
37 	function _cleanData($value, $type){
38 		$value = trim($value);
39 		if(!$value) return '';
40 		if (is_array($type)) {
41 			if (isset($type['enum']) &&
42 			!preg_match('/(^|,\s*)' . preg_quote_cb($value) . '($|\s*,)/', $type['enum'])) {
43 				return '';
44 			}
45 			$type = $type['type'];
46 		}
47 		switch($type){
48 			case 'dt':
49 				if(preg_match('/^(\d\d\d\d)-(\d\d?)-(\d\d?)$/',$value,$m)){
50 					return sprintf('%d-%02d-%02d',$m[1],$m[2],$m[3]);
51 				}
52 				return '';
53 			case 'url':
54 				if(!preg_match('!^[a-z]+://!i',$value)) $value='http://'.$value;
55 				return $value;
56 			case 'mail':
57 				$email = '';
58 				$name = '';
59 				$part = '';
60 				$parts = preg_split('/\s+/',$value);
61 				do{
62 					$part = array_shift($parts);
63 					if(!$email && mail_isvalid($part)){
64 						$email = strtolower($part);
65 						continue;
66 					}
67 					$name .= $part.' ';
68 				}while($part);
69 				return trim($email.' '.$name);
70 			case 'page': case 'nspage':
71 				return cleanID($value);
72 			default:
73 				return $value;
74 		}
75 	}
76 
77 	function _addPrePostFixes($type, $val, $pre='', $post='') {
78 		if (is_array($type)) {
79 			if (isset($type['prefix'])) $pre = $type['prefix'];
80 			if (isset($type['postfix'])) $post = $type['postfix'];
81 		}
82 		return $pre.$val.$post;
83 	}
84 
85 	/**
86 	 * Return XHTML formated data, depending on column type
87 	 */
88 	function _formatData($column, $value, &$R){
89 		global $conf;
90 		$vals = explode("\n",$value);
91 		$outs = array();
92 		foreach($vals as $val){
93 			$val = trim($val);
94 			if($val=='') continue;
95 			$type = $column['type'];
96 			if (is_array($type)) $type = $type['type'];
97 			switch($type){
98 				case 'page':
99 					$val = $this->_addPrePostFixes($column['type'], $val, ':');
100 					$outs[] = $R->internallink($val,null,null,true);
101 					break;
102 				case 'title':
103 					list($id,$title) = explode('|',$val,2);
104 					$id = $this->_addPrePostFixes($column['type'], $id, ':');
105 					$outs[] = $R->internallink($id,$title,null,true);
106 					break;
107 				case 'nspage':
108 					// no prefix/postfix here
109 					$val = ':'.$column['key'].":$val";
110 
111 					$outs[] = $R->internallink($val,null,null,true);
112 					break;
113 				case 'mail':
114 					list($id,$title) = explode(' ',$val,2);
115 					$id = $this->_addPrePostFixes($column['type'], $id);
116 					$id = obfuscate(hsc($id));
117 					if(!$title){
118 						$title = $id;
119 					}else{
120 						$title = hsc($title);
121 					}
122 					if($conf['mailguard'] == 'visible') $id = rawurlencode($id);
123 					$outs[] = '<a href="mailto:'.$id.'" class="mail" title="'.$id.'">'.$title.'</a>';
124 					break;
125 				case 'url':
126 					$val = $this->_addPrePostFixes($column['type'], $val);
127 					$outs[] = '<a href="'.hsc($val).'" class="urlextern" title="'.hsc($val).'">'.hsc($val).'</a>';
128 					break;
129 				case 'tag':
130 					#FIXME handle pre/postfix
131 					$outs[] = '<a href="'.wl(str_replace('/',':',cleanID($column['key'])),array('dataflt'=>$column['key'].':'.$val )).
132                               '" title="'.sprintf($this->getLang('tagfilter'),hsc($val)).
133                               '" class="wikilink1">'.hsc($val).'</a>';
134 					break;
135 				case 'wiki':
136 					global $ID;
137 					$oldid = $ID;
138 					list($ID,$data) = explode('|',$val,2);
139 					$data = $this->_addPrePostFixes($column['type'], $data);
140 					// Trim document_{start,end}, p_{open,close}
141 					$ins = array_slice(p_get_instructions($data), 2, -2);
142 					$outs[] = p_render('xhtml', $ins, $byref_ignore);
143 					$ID = $oldid;
144 					break;
145 				default:
146 					$val = $this->_addPrePostFixes($column['type'], $val);
147 					if(substr($type,0,3) == 'img'){
148 						$sz = (int) substr($type,3);
149 						if(!$sz) $sz = 40;
150 						$title = $column['key'].': '.basename(str_replace(':','/',$val));
151 						$outs[] = '<a href="'.ml($val).'" class="media" rel="lightbox"><img src="'.ml($val,"w=$sz").'" alt="'.hsc($title).'" title="'.hsc($title).'" width="'.$sz.'" /></a>';
152 					}else{
153 						$outs[] = hsc($val);
154 					}
155 			}
156 		}
157 		return join(', ',$outs);
158 	}
159 
160 	/**
161 	 * Split a column name into its parts
162 	 *
163 	 * @returns array with key, type, ismulti, title, opt
164 	 */
165 	function _column($col){
166 		preg_match('/^([^_]*)(?:_(.*))?((?<!s)|s)$/', $col, $matches);
167 		$column = array('multi' => ($matches[3] === 's'),
168                         'key'   => utf8_strtolower($matches[1]),
169                         'title' => $matches[1],
170                         'type'  => utf8_strtolower($matches[2]));
171 
172 		// fix title for special columns
173 		static $specials = array('%title%'  => array('page', 'title'),
174                                  '%pageid%' => array('title', 'page'),
175                                  '%class%'  => array('class'));
176 		if (isset($specials[$column['title']])) {
177 			$s = $specials[$column['title']];
178 			$column['title'] = $this->getLang($s[0]);
179 			if($column['type'] === '' && isset($s[1])) {
180 				$column['type'] = $s[1];
181 			}
182 		}
183 
184 		// check if the type is some alias
185 		$aliases = $this->_aliases();
186 		if(isset($aliases[$column['type']])){
187 			$column['origtype'] = $column['type'];
188 			$column['type']     = $aliases[$column['type']];
189 		}
190 		return $column;
191 	}
192 
193 	/**
194 	 * Load defined type aliases
195 	 */
196 	function _aliases(){
197 		static $aliases = null;
198 		if(!is_null($aliases)) return $aliases;
199 
200 		//$sqlite = $this->_getDB();
201 		$sqlite = $this->_getTripleStore();
202 		if(!$sqlite) return array();
203 
204 		// aliases come later
205 		$aliases = array();
206 
207 		/*
208 		 * Aliases are currently not supported
209 		 */
210 		return $aliases;
211 	}
212 
213 	/**
214 	 * Parse a filter line into an array
215 	 *
216 	 * @return mixed - array on success, false on error
217 	 */
218 	function _parse_filter($filterline){
219 		if(preg_match('/^(.*?)([=<>!~]{1,2})(.*)$/',$filterline,$matches)){
220 			$column = $this->_column(trim($matches[1]));
221 
222 			$com = $matches[2];
223 			$aliasses = array('<>' => '!=', '=!' => '!=', '~!' => '!~',
224                               '==' => '=',  '~=' => '~',  '=~' => '~');
225 
226 			if (isset($aliasses[$com])) {
227 				$com = $aliasses[$com];
228 			} elseif (!preg_match('/(!?[=~])|([<>]=?)/', $com)) {
229 				msg('Failed to parse comparison "'.hsc($com).'"',-1);
230 				return false;
231 			}
232 
233 			$val = trim($matches[3]);
234 			// allow current user name in filter:
235 			$val = str_replace('%user%',$_SERVER['REMOTE_USER'],$val);
236 			// allow current date in filter:
237 			$val = str_replace('%now%', dformat(null, '%Y-%m-%d'),$val);
238 
239 			if(strpos($com, '~') !== false) {
240 				$val = str_replace('*','%',$val);
241 				if ($com == '!~'){
242 					$com = 'NOT LIKE';
243 				} else {
244 					$com = 'LIKE';
245 				}
246 			} else {
247 				// Clean if there are no asterisks I could kill
248 				$val = $this->_cleanData($val, $column['type']);
249 			}
250 
251 			$val = addslashes($val);
252 
253 			return array('key'     => $column['key'],
254                          'value'   => $val,
255                          'compare' => $com,
256 			);
257 		}
258 		msg('Failed to parse filter "'.hsc($filterline).'"',-1);
259 		return false;
260 	}
261 
262 	/**
263 	 * Get filters given in the request via GET or POST
264 	 */
265 	function _get_filters(){
266 		$flt = array();
267 		$filters = array();
268 
269 		if(!isset($_REQUEST['dataflt'])){
270 			$flt = array();
271 		}elseif(!is_array($_REQUEST['dataflt'])){
272 			$flt = (array) $_REQUEST['dataflt'];
273 		}else{
274 			$flt = $_REQUEST['dataflt'];
275 		}
276 
277 		foreach($flt as $key => $line){
278 			// we also take the column and filtertype in the key:
279 			if(!is_numeric($key)) $line = $key.$line;
280 			$f = $this->_parse_filter($line);
281 			if(is_array($f)){
282 				$f['logic'] = 'AND';
283 				$filters[] = $f;
284 			}
285 		}
286 
287 		return $filters;
288 	}
289 
290 	/**
291 	 * prepare an array to be passed through buildURLparams()
292 	 */
293 	function _a2ua($name,$array){
294 		$urlarray = array();
295 		foreach((array) $array as $key => $val){
296 			$urlarray[rawurlencode($name).'['.rawurlencode($key).']'] = $val;
297         }
298         return $urlarray;
299     }
300 
301 }
302