15153720fSfkaag71<?php 25153720fSfkaag71/** 35153720fSfkaag71 * Strata, data entry plugin 45153720fSfkaag71 * 55153720fSfkaag71 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 65153720fSfkaag71 * @author Brend Wanders <b.wanders@utwente.nl> 75153720fSfkaag71 */ 85153720fSfkaag71 95153720fSfkaag71if(!defined('DOKU_INC')) die('Meh.'); 105153720fSfkaag71 115153720fSfkaag71/** 125153720fSfkaag71 * Select syntax for basic query handling. 135153720fSfkaag71 */ 145153720fSfkaag71class syntax_plugin_strata_select extends DokuWiki_Syntax_Plugin { 155153720fSfkaag71 function __construct() { 165153720fSfkaag71 $this->helper =& plugin_load('helper', 'strata_syntax'); 175153720fSfkaag71 $this->util =& plugin_load('helper', 'strata_util'); 185153720fSfkaag71 $this->triples =& plugin_load('helper', 'strata_triples'); 195153720fSfkaag71 } 205153720fSfkaag71 215153720fSfkaag71 function getType() { 225153720fSfkaag71 return 'substition'; 235153720fSfkaag71 } 245153720fSfkaag71 255153720fSfkaag71 function getPType() { 265153720fSfkaag71 return 'block'; 275153720fSfkaag71 } 285153720fSfkaag71 295153720fSfkaag71 function getSort() { 305153720fSfkaag71 return 450; 315153720fSfkaag71 } 325153720fSfkaag71 335153720fSfkaag71 function connectTo($mode) { 345153720fSfkaag71 } 355153720fSfkaag71 365153720fSfkaag71 function getUISettings($numFields, $hasUIBlock) { 375153720fSfkaag71 $sort_choices = array( 385153720fSfkaag71 'y' => array('default', 'yes', 'y'), 395153720fSfkaag71 'l' => array('left to right', 'ltr', 'l'), 405153720fSfkaag71 'r' => array('right to left', 'rtl', 'r'), 415153720fSfkaag71 'n' => array('none', 'no', 'n') 425153720fSfkaag71 ); 435153720fSfkaag71 $filter_choices = array( 445153720fSfkaag71 't' => array('text', 't'), 455153720fSfkaag71 's' => array('select', 's'), 465153720fSfkaag71 'p' => array('prefix select', 'ps'), 475153720fSfkaag71 'e' => array('suffix select', 'ss'), 485153720fSfkaag71 'n' => array('none', 'no', 'n') 495153720fSfkaag71 ); 505153720fSfkaag71 $globalProperties = array( 515153720fSfkaag71 'ui' => $this->getUISettingUI($hasUIBlock), 525153720fSfkaag71 'sort' => array('choices' => $sort_choices, 'minOccur' => $numFields, 'maxOccur' => $numFields, 'default' => 'yes'), 535153720fSfkaag71 'filter' => array('choices' => $filter_choices, 'minOccur' => $numFields, 'maxOccur' => $numFields, 'default' => 'none') 545153720fSfkaag71 ); 555153720fSfkaag71 $groupProperties = array( 565153720fSfkaag71 'sort' => array('choices' => $sort_choices), 575153720fSfkaag71 'filter' => array('choices' => $filter_choices), 585153720fSfkaag71 ); 595153720fSfkaag71 return array($globalProperties, $groupProperties); 605153720fSfkaag71 } 615153720fSfkaag71 625153720fSfkaag71 function getUISettingUI($hasUIBlock) { 635153720fSfkaag71 return array('choices' => array('none' => array('none', 'no', 'n'), 'generic' => array('generic', 'g')), 'default' => ($hasUIBlock ? 'generic' : 'none')); 645153720fSfkaag71 } 655153720fSfkaag71 665153720fSfkaag71 function handle($match, $state, $pos, Doku_Handler $handler) { 675153720fSfkaag71 try { 685153720fSfkaag71 $result = array(); 695153720fSfkaag71 $typemap = array(); 705153720fSfkaag71 715153720fSfkaag71 // allow subclass handling of the whole match 725153720fSfkaag71 $match = $this->preprocess($match, $state, $pos, $handler, $result, $typemap); 735153720fSfkaag71 745153720fSfkaag71 // split into lines and remove header and footer 755153720fSfkaag71 $lines = explode("\n",$match); 765153720fSfkaag71 $header = trim(array_shift($lines)); 775153720fSfkaag71 $footer = trim(array_pop($lines)); 785153720fSfkaag71 795153720fSfkaag71 // allow subclass header handling 805153720fSfkaag71 $header = $this->handleHeader($header, $result, $typemap); 815153720fSfkaag71 825153720fSfkaag71 // parse projection information in 'short syntax' if available 835153720fSfkaag71 if(trim($header) != '') { 845153720fSfkaag71 $result['fields'] = $this->helper->parseFieldsShort($header, $typemap); 85*ef8a6397SFKaag } else { 86*ef8a6397SFKaag $result['fields'] = []; 875153720fSfkaag71 } 885153720fSfkaag71 895153720fSfkaag71 $tree = $this->helper->constructTree($lines,'query'); 905153720fSfkaag71 915153720fSfkaag71 // parse long fields, if available 925153720fSfkaag71 $longFields = $this->helper->getFields($tree, $typemap); 935153720fSfkaag71 945153720fSfkaag71 // check double data 955153720fSfkaag71 if(count($result['fields']) && count($longFields)) { 965153720fSfkaag71 $this->helper->_fail($this->getLang('error_query_bothfields')); 975153720fSfkaag71 } 985153720fSfkaag71 995153720fSfkaag71 // assign longfields if necessary 1005153720fSfkaag71 if(count($result['fields']) == 0) { 1015153720fSfkaag71 $result['fields'] = $longFields; 1025153720fSfkaag71 } 1035153720fSfkaag71 1045153720fSfkaag71 // check no data 1055153720fSfkaag71 if(count($result['fields']) == 0) { 1065153720fSfkaag71 $this->helper->_fail($this->helper->getLang('error_query_noselect')); 1075153720fSfkaag71 } 1085153720fSfkaag71 1095153720fSfkaag71 // determine the variables to project 1105153720fSfkaag71 $projection = array(); 1115153720fSfkaag71 foreach($result['fields'] as $f) $projection[] = $f['variable']; 1125153720fSfkaag71 $projection = array_unique($projection); 1135153720fSfkaag71 1145153720fSfkaag71 // allow subclass body handling 1155153720fSfkaag71 $this->handleBody($tree, $result, $typemap); 1165153720fSfkaag71 1175153720fSfkaag71 // parse UI group 1185153720fSfkaag71 $this->handleUI($tree, $result, $typemap); 1195153720fSfkaag71 1205153720fSfkaag71 // parse the query itself 1215153720fSfkaag71 list($result['query'], $variables) = $this->helper->constructQuery($tree, $typemap, $projection); 1225153720fSfkaag71 1235153720fSfkaag71 // allow subclass footer handling 1245153720fSfkaag71 $footer = $this->handleFooter($footer, $result, $typemap, $variable); 1255153720fSfkaag71 1265153720fSfkaag71 // check projected variables and load types 1275153720fSfkaag71 foreach($result['fields'] as $i=>$f) { 1285153720fSfkaag71 $var = $f['variable']; 1295153720fSfkaag71 if(!in_array($var, $variables)) { 1305153720fSfkaag71 $this->helper->_fail(sprintf($this->helper->getLang('error_query_unknownselect'),utf8_tohtml(hsc($var)))); 1315153720fSfkaag71 } 1325153720fSfkaag71 1335153720fSfkaag71 if(empty($f['type'])) { 1345153720fSfkaag71 if(!empty($typemap[$var])) { 1355153720fSfkaag71 $result['fields'][$i] = array_merge($result['fields'][$i],$typemap[$var]); 1365153720fSfkaag71 } else { 1375153720fSfkaag71 list($type, $hint) = $this->util->getDefaultType(); 1385153720fSfkaag71 $result['fields'][$i]['type'] = $type; 1395153720fSfkaag71 $result['fields'][$i]['hint'] = $hint; 1405153720fSfkaag71 } 1415153720fSfkaag71 } 1425153720fSfkaag71 } 1435153720fSfkaag71 1445153720fSfkaag71 return $result; 1455153720fSfkaag71 } catch(strata_exception $e) { 1465153720fSfkaag71 return array('error'=>array( 1475153720fSfkaag71 'message'=>$e->getMessage(), 1485153720fSfkaag71 'regions'=>$e->getData(), 1495153720fSfkaag71 'lines'=>$lines 1505153720fSfkaag71 )); 1515153720fSfkaag71 } 1525153720fSfkaag71 } 1535153720fSfkaag71 1545153720fSfkaag71 function handleUI(&$tree, &$result, &$typemap) { 1555153720fSfkaag71 $trees = $this->helper->extractGroups($tree, 'ui'); 1565153720fSfkaag71 1575153720fSfkaag71 list($globalProperties, $groupProperties) = $this->getUISettings(count($result['fields']), count($trees)); 1585153720fSfkaag71 1595153720fSfkaag71 // Extract column settings which are set as a group 1605153720fSfkaag71 1615153720fSfkaag71 // Extract named column settings 1625153720fSfkaag71 $namedGroupSettings = array(); 1635153720fSfkaag71 foreach ($result['fields'] as $i => $f) { 1645153720fSfkaag71 if(isset($namedGroupSettings[$f['caption']])) continue; 1655153720fSfkaag71 $groups = array(); 1665153720fSfkaag71 foreach($trees as &$t) { 1675153720fSfkaag71 $groups = array_merge($groups, $this->helper->extractGroups($t, $f['caption'])); 1685153720fSfkaag71 } 1695153720fSfkaag71 $namedGroupSettings[$f['caption']] = $this->helper->setProperties($groupProperties, $groups); 1705153720fSfkaag71 } 1715153720fSfkaag71 1725153720fSfkaag71 // Extract numbered column settings 1735153720fSfkaag71 $groupsettings = array(); 1745153720fSfkaag71 foreach ($result['fields'] as $i => $f) { 1755153720fSfkaag71 $groups = array(); 1765153720fSfkaag71 foreach ($trees as &$t) { 1775153720fSfkaag71 $groups = array_merge($groups, $this->helper->extractGroups($t, '#' . ($i+1))); 1785153720fSfkaag71 } 1795153720fSfkaag71 1805153720fSfkaag71 // process settings for this column 1815153720fSfkaag71 $groupsettings[$i] = $this->helper->setProperties($groupProperties, $groups); 1825153720fSfkaag71 1835153720fSfkaag71 // fill in unset properties from named settings 1845153720fSfkaag71 foreach($namedGroupSettings[$f['caption']] as $k=>$v) { 1855153720fSfkaag71 if(!isset($groupsettings[$i][$k])) { 1865153720fSfkaag71 $groupsettings[$i][$k] = $v; 1875153720fSfkaag71 } 1885153720fSfkaag71 } 1895153720fSfkaag71 } 1905153720fSfkaag71 1915153720fSfkaag71 // Extract global settings 1925153720fSfkaag71 $result['strata-ui'] = $this->helper->setProperties($globalProperties, $trees); 1935153720fSfkaag71 1945153720fSfkaag71 // Merge column settings into global ones 1955153720fSfkaag71 foreach ($groupsettings as $i => $s) { 1965153720fSfkaag71 foreach ($s as $p => $v) { 1975153720fSfkaag71 $result['strata-ui'][$p][$i] = $v[0]; 1985153720fSfkaag71 } 1995153720fSfkaag71 } 2005153720fSfkaag71 } 2015153720fSfkaag71 2025153720fSfkaag71 /** 2035153720fSfkaag71 * Handles the whole match. This method is called before any processing 2045153720fSfkaag71 * is done by the actual class. 2055153720fSfkaag71 * 2065153720fSfkaag71 * @param match string the complete match 2075153720fSfkaag71 * @param state the parser state 2085153720fSfkaag71 * @param pos the position in the source 2095153720fSfkaag71 * @param handler object the parser handler 2105153720fSfkaag71 * @param result array the result array passed to the render method 2115153720fSfkaag71 * @param typemap array the type map 2125153720fSfkaag71 * @return a preprocessed string 2135153720fSfkaag71 */ 2145153720fSfkaag71 function preprocess($match, $state, $pos, &$handler, &$result, &$typemap) { 2155153720fSfkaag71 return $match; 2165153720fSfkaag71 } 2175153720fSfkaag71 2185153720fSfkaag71 2195153720fSfkaag71 /** 2205153720fSfkaag71 * Handles the header of the syntax. This method is called before 2215153720fSfkaag71 * the header is handled. 2225153720fSfkaag71 * 2235153720fSfkaag71 * @param header string the complete header 2245153720fSfkaag71 * @param result array the result array passed to the render method 2255153720fSfkaag71 * @param typemap array the type map 2265153720fSfkaag71 * @return a string containing the unhandled parts of the header 2275153720fSfkaag71 */ 2285153720fSfkaag71 function handleHeader($header, &$result, &$typemap) { 2295153720fSfkaag71 return $header; 2305153720fSfkaag71 } 2315153720fSfkaag71 2325153720fSfkaag71 /** 2335153720fSfkaag71 * Handles the body of the syntax. This method is called before any 2345153720fSfkaag71 * of the body is handled, but after the 'fields' groups have been processed. 2355153720fSfkaag71 * 2365153720fSfkaag71 * @param tree array the parsed tree 2375153720fSfkaag71 * @param result array the result array passed to the render method 2385153720fSfkaag71 * @param typemap array the type map 2395153720fSfkaag71 */ 2405153720fSfkaag71 function handleBody(&$tree, &$result, &$typemap) { 2415153720fSfkaag71 } 2425153720fSfkaag71 2435153720fSfkaag71 /** 2445153720fSfkaag71 * Handles the footer of the syntax. This method is called after the 2455153720fSfkaag71 * query has been parsed, but before the typemap is applied to determine 2465153720fSfkaag71 * all field types. 2475153720fSfkaag71 * 2485153720fSfkaag71 * @param footer string the footer string 2495153720fSfkaag71 * @param result array the result array passed to the render method 2505153720fSfkaag71 * @param typemape array the type map 2515153720fSfkaag71 * @param variables array of variables used in query 2525153720fSfkaag71 * @return a string containing the unhandled parts of the footer 2535153720fSfkaag71 */ 2545153720fSfkaag71 function handleFooter($footer, &$result, &$typemap, &$variables) { 2555153720fSfkaag71 return $footer; 2565153720fSfkaag71 } 2575153720fSfkaag71 2585153720fSfkaag71 /** 2595153720fSfkaag71 * This method performs just-in-time modification to prepare 2605153720fSfkaag71 * the query for use. 2615153720fSfkaag71 * 2625153720fSfkaag71 * @param query array the query tree 2635153720fSfkaag71 * @return the query tree to use 2645153720fSfkaag71 */ 2655153720fSfkaag71 function prepareQuery($query) { 2665153720fSfkaag71 // fire event 2675153720fSfkaag71 trigger_event('STRATA_PREPARE_QUERY', $query); 2685153720fSfkaag71 2695153720fSfkaag71 // return the (possibly modified) query 2705153720fSfkaag71 return $query; 2715153720fSfkaag71 } 2725153720fSfkaag71 2735153720fSfkaag71 /** 2745153720fSfkaag71 * This method renders the data view. 2755153720fSfkaag71 * 2765153720fSfkaag71 * @param mode the rendering mode 2775153720fSfkaag71 * @param R the renderer 2785153720fSfkaag71 * @param data the custom data from the handle phase 2795153720fSfkaag71 */ 2805153720fSfkaag71 function render($mode, Doku_Renderer $R, $data) { 2815153720fSfkaag71 return false; 2825153720fSfkaag71 } 2835153720fSfkaag71 2845153720fSfkaag71 /** 2855153720fSfkaag71 * This method renders the container for any strata select. 2865153720fSfkaag71 * 2875153720fSfkaag71 * The open tag will contain all give classes plus additional metadata, e.g., generated by the ui group 2885153720fSfkaag71 * 2895153720fSfkaag71 * @param mode the render mode 2905153720fSfkaag71 * @param R the renderer 2915153720fSfkaag71 * @param data the custom data from the handle phase 2925153720fSfkaag71 * @param additionalClasses array containing classes to be set on the generated container 2935153720fSfkaag71 */ 2945153720fSfkaag71 function ui_container_open($mode, &$R, $data, $additionalClasses=array()) { 2955153720fSfkaag71 // only xhtml mode needs the UI container 2965153720fSfkaag71 if($mode != 'xhtml') return; 2975153720fSfkaag71 2985153720fSfkaag71 $p = $data['strata-ui']; 2995153720fSfkaag71 $c = array(); 3005153720fSfkaag71 3015153720fSfkaag71 // Default sort: rtl for suffix and ltr otherwise 3025153720fSfkaag71 for ($i = 0; $i < count($p['sort']); $i++) { 3035153720fSfkaag71 if ($p['sort'][$i] == 'y') { 3045153720fSfkaag71 $p['sort'][$i] = ($p['filter'][$i] == 'e' ? 'r' : 'l'); 3055153720fSfkaag71 } 3065153720fSfkaag71 } 3075153720fSfkaag71 3085153720fSfkaag71 if (trim(implode($p['sort']), 'n') != '') { 3095153720fSfkaag71 $c[] = 'strata-ui-sort'; 3105153720fSfkaag71 } 3115153720fSfkaag71 if (trim(implode($p['filter']), 'n') != '') { 3125153720fSfkaag71 $c[] = 'strata-ui-filter'; 3135153720fSfkaag71 } 3145153720fSfkaag71 3155153720fSfkaag71 $classes = implode(' ', array_merge($c, $additionalClasses)); 3165153720fSfkaag71 $properties = implode(' ', array_map( 3175153720fSfkaag71 function($k, $v) { 3185153720fSfkaag71 if (empty($v)) { 3195153720fSfkaag71 return ''; 3205153720fSfkaag71 } else { 3215153720fSfkaag71 return 'data-strata-ui-' . $k . '="' . implode($v) . '"'; 3225153720fSfkaag71 } 3235153720fSfkaag71 }, array_keys($p), $p) 3245153720fSfkaag71 ); 3255153720fSfkaag71 3265153720fSfkaag71 $R->doc .= '<div class="' . $classes . '" ' . $properties . '>' . DOKU_LF; 3275153720fSfkaag71 } 3285153720fSfkaag71 3295153720fSfkaag71 function ui_container_close($mode, &$R) { 3305153720fSfkaag71 // only xhtml mode needs the UI container 3315153720fSfkaag71 if($mode != 'xhtml') return; 3325153720fSfkaag71 $R->doc .= '</div>' . DOKU_LF; 3335153720fSfkaag71 } 3345153720fSfkaag71 3355153720fSfkaag71 protected function displayError($mode, &$R, $data) { 3365153720fSfkaag71 if($mode == 'xhtml') { 3375153720fSfkaag71 $style = ''; 3385153720fSfkaag71 if(isset($data['error']['regions'])) $style = ' strata-debug-continued'; 3395153720fSfkaag71 $R->doc .= '<div class="strata-debug-message '.$style.'">'; 3405153720fSfkaag71 $R->cdata($this->helper->getLang('content_error_explanation')); 3415153720fSfkaag71 $R->doc .= ': '.$data['error']['message']; 3425153720fSfkaag71 $R->doc .= '</div>'; 3435153720fSfkaag71 if(isset($data['error']['regions'])) $R->doc .= $this->helper->debugTree($data['error']['lines'], $data['error']['regions']); 3445153720fSfkaag71 } elseif($mode == 'odt') { 3455153720fSfkaag71 $R->cdata($this->helper->getLang('content_error_explanation__non_xhtml')); 3465153720fSfkaag71 } 3475153720fSfkaag71 } 3485153720fSfkaag71} 349