xref: /plugin/strata/syntax/select.php (revision ef8a6397a195eebdb23c47b7f21c76e3170409b4)
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