1<?php
2/**
3 *
4 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
5 * @author     Andreas Gohr <andi@splitbrain.org>
6 */
7// must be run within Dokuwiki
8if(!defined('DOKU_INC')) die();
9require_once(DOKU_PLUGIN.'syntax.php');
10
11class syntax_plugin_semanticdata_entry extends DokuWiki_Syntax_Plugin {
12
13	/**
14	 * will hold the data helper plugin
15	 */
16	var $dthlp = null;
17
18	/**
19	 * Constructor. Load helper plugin
20	 */
21	function syntax_plugin_semanticdata_entry(){
22		$this->dthlp =& plugin_load('helper', 'semanticdata');
23		if(!$this->dthlp) msg('Loading the data helper failed. Make sure the semanticdata plugin is installed.',-1);
24	}
25
26	/**
27	 * What kind of syntax are we?
28	 */
29	function getType(){
30		return 'substition';
31	}
32
33	/**
34	 * What about paragraphs?
35	 */
36	function getPType(){
37		return 'block';
38	}
39
40	/**
41	 * Where to sort in?
42	 */
43	function getSort(){
44		return 155;
45	}
46
47	/**
48	 * Connect pattern to lexer
49	 */
50	function connectTo($mode) {
51		$this->Lexer->addSpecialPattern('----+ *dataentry(?: [ a-zA-Z0-9_]*)?-+\n.*?\n----+',$mode,'plugin_semanticdata_entry');
52	}
53
54	/**
55	 * Handle the match - parse the data
56	 */
57	function handle($match, $state, $pos, &$handler){
58		// get lines
59		$lines = explode("\n",$match);
60		array_pop($lines);
61		$class = array_shift($lines);
62		$class = str_replace('dataentry','',$class);
63		$class = trim($class,'- ');
64
65		// parse info
66		$data = array();
67		$meta = array();
68		foreach ( $lines as $line ) {
69			// ignore comments
70			preg_match('/^(.*?(?<![&\\\\]))(?:#(.*))?$/',$line, $matches);
71			$line = $matches[1];
72			$line = str_replace('\\#','#',$line);
73			$line = trim($line);
74			if(empty($line)) continue;
75			$line = preg_split('/\s*:\s*/',$line,2);
76
77			$column = $this->dthlp->_column($line[0]);
78			if (isset($matches[2])) $column['comment'] = $matches[2];
79			if($column['multi']){
80				if(!isset($data[$column['key']])) {
81					// init with empty array
82					// Note that multiple occurrences of the field are
83					// practically merged
84					$data[$column['key']] = array();
85				}
86				$vals = explode(',',$line[1]);
87				foreach($vals as $val){
88					$val = trim($this->dthlp->_cleanData($val,$column['type']));
89					if($val == '') continue;
90					if(!in_array($val,$data[$column['key']])) $data[$column['key']][] = $val;
91				}
92			}else{
93				$data[$column['key']] = $this->dthlp->_cleanData($line[1],$column['type']);
94			}
95			$columns[$column['key']]  = $column;
96		}
97		return array('data'=>$data, 'cols'=>$columns, 'classes'=>$class,
98                     'pos' => $pos, 'len' => strlen($match)); // not utf8_strlen
99	}
100
101	/**
102	 * Create output or save the data
103	 */
104	function render($format, &$renderer, $data) {
105		global $ID;
106		switch ($format){
107			case 'xhtml':
108				$this->_showData($data,$renderer);
109				return true;
110			case 'metadata':
111				$this->_saveData($data,$ID,$renderer->meta['title']);
112				return true;
113			case 'plugin_semanticdata_edit':
114				$this->_editData($data, $renderer);
115				return true;
116			default:
117				return false;
118		}
119	}
120
121	/**
122	 * Output the data in a table
123	 */
124	function _showData($data,&$R){
125		global $ID;
126		$ret = '';
127
128		if (method_exists($R, 'startSectionEdit')) {
129			$data['classes'] .= ' ' . $R->startSectionEdit($data['pos'], 'plugin_semanticdata');
130		}
131		$ret .= '<div class="inline dataplugin_entry '.$data['classes'].'"><dl>';
132		foreach($data['data'] as $key => $val){
133			if($val == '' || !count($val)) continue;
134			$type = $data['cols'][$key]['type'];
135			if (is_array($type)) $type = $type['type'];
136			switch ($type) {
137				case 'pageid':
138					$type = 'title';
139				case 'wiki':
140					$val = $ID . '|' . $val;
141					break;
142			}
143
144			$ret .= '<dt class="' . hsc($key) . '">'.hsc($data['cols'][$key]['title']).'<span class="sep">: </span></dt>';
145			if(is_array($val)){
146				$cnt = count($val);
147				for ($i=0; $i<$cnt; $i++){
148					$ret .= '<dd class="' . hsc($key) . '">';
149					$ret .= $this->dthlp->_formatData($data['cols'][$key], $val[$i],$R);
150					if($i < $cnt - 1) $ret .= '<span class="sep">, </span>';
151					$ret .= '</dd>';
152				}
153			}else{
154				$ret .= '<dd class="' . hsc($key) . '">'.
155				$this->dthlp->_formatData($data['cols'][$key], $val, $R).'</dd>';
156			}
157		}
158		$ret .= '</dl></div>';
159		$R->doc .= $ret;
160		if (method_exists($R, 'finishSectionEdit')) {
161			$R->finishSectionEdit($data['len'] + $data['pos']);
162		}
163	}
164
165	/**
166	 * Save date to the triple store
167	 */
168	function _saveData($data,$id,$title){
169		$store = $this->dthlp->_getTripleStore();
170		$resultFormat = phpSesame::SPARQL_XML;
171		$lang = "sparql";
172		$infer = true;
173
174		if(!$store) return false;
175
176		$error = '';
177		if(!$title) $title = $id;
178
179		$class = $data['classes'];
180
181		// delete all data from this page
182		$sparql =
183			sprintf('DELETE DATA { <%s%s> ?s ?o }',$this->getConf('base_url'),urlencode($id));
184		$result = $store->update($sparql, $resultFormat, $lang, $infer);
185
186
187		// add page information
188		$sparql =
189			"PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>" .
190			sprintf("PREFIX spd:<%s> ",$this->getConf('base_url')) .
191			"INSERT DATA " .
192			sprintf("{ <%s%s> rdfs:label \"%s\" ; spd:hastitle \"%s\" ; spd:class \"%s\" . }",$this->getConf('base_url'),urlencode($id),$id,$title,$class);
193		$result = $store->update($sparql, $resultFormat, $lang, $infer);
194
195		// note, it should be possible to delete and update in a single query <<TODO>>
196
197		// insert new data
198		foreach ($data['data'] as $key => $val){
199			if(is_array($val)) foreach($val as $v){
200				$sparql =
201					sprintf("PREFIX spd:<%s> ",$this->getConf('base_url')) .
202					'INSERT DATA ' .
203					sprintf('{ <%s%s> <%s%s> "%s" . }',$this->getConf('base_url'),urlencode($id),$this->getConf('base_url'),urlencode($key),$v);
204				$result = $store->update($sparql, $resultFormat, $lang, $infer);
205			}else {
206				$sparql =
207					sprintf("PREFIX spd:<%s> ",$this->getConf('base_url')) .
208					'INSERT DATA ' .
209					sprintf('{ <%s%s> <%s%s> "%s" . }',$this->getConf('base_url'),urlencode($id),$this->getConf('base_url'),urlencode($key),$val);
210				$result = $store->update($sparql, $resultFormat, $lang, $infer);
211			}
212		}
213
214		return true;
215	}
216
217	function _editData($data, &$renderer) {
218		$renderer->form->startFieldset($this->getLang('dataentry'));
219		$renderer->form->_content[count($renderer->form->_content) - 1]['class'] = 'plugin__data';
220
221		if ($this->getConf('edit_content_only')) {
222			$renderer->form->addHidden('data_edit[classes]', $data['classes']);
223			$renderer->form->addElement('<table>');
224		} else {
225			$renderer->form->addElement(form_makeField('text', 'data_edit[classes]', $data['classes'], $this->getLang('class'), 'data__classes'));
226			$renderer->form->addElement('<table>');
227
228			$text = '<tr>';
229			foreach(array('title', 'type', 'multi', 'value', 'comment') as $val) {
230				$text .= '<th>' . $this->getLang($val) . '</th>';
231			}
232			$renderer->form->addElement($text . '</tr>');
233
234			// New line
235			$data['data'][''] = '';
236			$data['cols'][''] = array('type' => '', 'multi' => false);
237		}
238
239		$n = 0;
240		foreach($data['cols'] as $key => $vals) {
241			$fieldid = 'data_edit[data][' . $n++ . ']';
242			$content = $vals['multi'] ? implode(', ', $data['data'][$key]) : $data['data'][$key];
243			if (is_array($vals['type'])) {
244				$vals['basetype'] = $vals['type']['type'];
245				if (isset($vals['type']['enum'])) {
246					$vals['enum'] = $vals['type']['enum'];
247				}
248				$vals['type'] = $vals['origtype'];
249			} else {
250				$vals['basetype'] = $vals['type'];
251			}
252			$renderer->form->addElement('<tr>');
253			if ($this->getConf('edit_content_only')) {
254				if (isset($vals['enum'])) {
255					$values = preg_split('/\s*,\s*/', $vals['enum']);
256					if (!$vals['multi']) array_unshift($values, '');
257					$content = form_makeListboxField($fieldid . '[value][]', $values,
258					$data['data'][$key], $vals['title'], '', '', ($vals['multi'] ? array('multiple' => 'multiple'): array()));
259				} else {
260					$classes = 'data_type_' . $vals['type'] . ($vals['multi'] ? 's' : '') .  ' ' .
261                               'data_type_' . $vals['basetype'] . ($vals['multi'] ? 's' : '');
262					$content = form_makeField('text', $fieldid . '[value]', $content, $vals['title'], '', $classes);
263
264				}
265				$cells = array($vals['title'] . ':',
266				$content,
267				$vals['comment']);
268				foreach(array('title', 'multi', 'comment', 'type') as $field) {
269					$renderer->form->addHidden($fieldid . "[$field]", $vals[$field]);
270				}
271			} else {
272				$check_data = $vals['multi'] ? array('checked' => 'checked') : array();
273				$cells = array(form_makeField('text', $fieldid . '[title]', $vals['title'], $this->getLang('title')),
274				form_makeMenuField($fieldid . '[type]',
275				array_merge(array('', 'page', 'nspage', 'title',
276                                                                    'mail', 'url', 'tag', 'wiki', 'dt'),
277				array_keys($this->dthlp->_aliases())),
278				$vals['type'],
279				$this->getLang('type')),
280				form_makeCheckboxField($fieldid . '[multi]', array('1', ''), $this->getLang('multi'), '', '', $check_data),
281				form_makeField('text', $fieldid . '[value]', $content, $this->getLang('value')),
282				form_makeField('text', $fieldid . '[comment]', $vals['comment'], $this->getLang('comment'), '', 'data_comment', array('readonly' => 1)));
283			}
284			foreach($cells as $cell) {
285				$renderer->form->addElement('<td>');
286				$renderer->form->addElement($cell);
287				$renderer->form->addElement('</td>');
288			}
289			$renderer->form->addElement('</tr>');
290		}
291
292		$renderer->form->addElement('</table>');
293		$renderer->form->endFieldset();
294	}
295
296	function _normalize($txt) {
297		return str_replace('#', '\#', trim($txt));
298	}
299
300	public static function editToWiki($data) {
301		$nudata = array();
302		$len = 0;
303		foreach ($data['data'] as $field) {
304			if ($field['title'] === '') continue;
305			$s = syntax_plugin_semanticdata_entry::_normalize($field['title']);
306			if (trim($field['type']) !== '' ||
307			(substr($s, -1, 1) === 's' && $field['multi'] === '')) {
308				$s .= '_' . syntax_plugin_semanticdata_entry::_normalize($field['type']);
309			}
310			if ($field['multi'] === '1') {
311				$s .= 's';
312			}
313			if (is_array($field['value'])) {
314				$field['value'] = join(', ', $field['value']);
315			}
316
317			$nudata[] = array($s, syntax_plugin_semanticdata_entry::_normalize($field['value']),
318			isset($field['comment']) ? trim($field['comment']) : '');
319			$len = max($len, utf8_strlen($nudata[count($nudata) - 1][0]));
320		}
321
322		$ret = '---- dataentry ' . trim($data['classes']) . ' ----' . DOKU_LF;
323		foreach ($nudata as $field) {
324			$ret .= $field[0] . str_repeat(' ', $len + 1 - utf8_strlen($field[0])) . ': ' .
325			$field[1];
326			if ($field[2] !== '') {
327				$ret .= ' #' . $field[2];
328			}
329			$ret .= DOKU_LF;
330		}
331		$ret .= '----';
332		return $ret;
333	}
334}
335