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
7if(!defined('DOKU_INC')) die();
8
9if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
10require_once(DOKU_PLUGIN.'syntax.php');
11require_once(DOKU_INC.'inc/infoutils.php');
12require_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 */
18class 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