xref: /plugin/struct/syntax/output.php (revision 61356325e2c5dbdcb8405fa2eb4c34732d79b65f)
1<?php
2
3/**
4 * DokuWiki Plugin struct (Syntax Component)
5 *
6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
7 * @author  Andreas Gohr, Michael Große <dokuwiki@cosmocode.de>
8 */
9
10// must be run within Dokuwiki
11use dokuwiki\plugin\struct\meta\AccessTable;
12use dokuwiki\plugin\struct\meta\Assignments;
13use dokuwiki\plugin\struct\meta\StructException;
14
15if (!defined('DOKU_INC')) die();
16
17class syntax_plugin_struct_output extends DokuWiki_Syntax_Plugin
18{
19
20    protected $hasBeenRendered = false;
21
22    const XHTML_OPEN = '<div id="plugin__struct_output">';
23    const XHTML_CLOSE = '</div>';
24
25    /**
26     * Class names of renderers which should NOT render struct data.
27     * All descendants are also blacklisted.
28     */
29    const BLACKLIST_RENDERER = array('Doku_Renderer_metadata');
30
31    /**
32     * Regexp to check on which actions the struct data may be rendered
33     */
34    const WHITELIST_ACTIONS = '/^(show|export_.*)$/';
35
36    /**
37     * @return string Syntax mode type
38     */
39    public function getType()
40    {
41        return 'substition';
42    }
43
44    /**
45     * @return string Paragraph type
46     */
47    public function getPType()
48    {
49        return 'block';
50    }
51
52    /**
53     * @return int Sort order - Low numbers go before high numbers
54     */
55    public function getSort()
56    {
57        return 155;
58    }
59
60    /**
61     * Connect lookup pattern to lexer.
62     *
63     * We do not connect any pattern here, because the call to this plugin is not
64     * triggered from syntax but our action component
65     *
66     * @asee action_plugin_struct_output
67     * @param string $mode Parser mode
68     */
69    public function connectTo($mode)
70    {
71    }
72
73    /**
74     * Handle matches of the struct syntax
75     *
76     * @param string $match The match of the syntax
77     * @param int $state The state of the handler
78     * @param int $pos The position in the document
79     * @param Doku_Handler $handler The handler
80     * @return array Data for the renderer
81     */
82    public function handle($match, $state, $pos, Doku_Handler $handler)
83    {
84        // this is never called
85        return array();
86    }
87
88    /**
89     * Render schema data
90     *
91     * Currently completely renderer agnostic
92     *
93     * @param string $mode Renderer mode
94     * @param Doku_Renderer $R The renderer
95     * @param array $data The data from the handler() function
96     * @return bool If rendering was successful.
97     */
98    public function render($mode, Doku_Renderer $R, $data)
99    {
100        global $ACT;
101        global $ID;
102        global $INFO;
103        global $REV;
104
105        foreach (self::BLACKLIST_RENDERER as $blacklisted) {
106            if ($R instanceof $blacklisted) {
107                return true;
108            }
109        }
110        if ($ID != $INFO['id']) return true;
111        if (!$INFO['exists']) return true;
112        if ($this->hasBeenRendered) return true;
113        if (!preg_match(self::WHITELIST_ACTIONS, act_clean($ACT))) return true;
114
115        // do not render the output twice on the same page, e.g. when another page has been included
116        $this->hasBeenRendered = true;
117        try {
118            $assignments = Assignments::getInstance();
119        } catch (StructException $e) {
120            return false;
121        }
122        $tables = $assignments->getPageAssignments($ID);
123        if (!$tables) return true;
124
125        if ($mode == 'xhtml') $R->doc .= self::XHTML_OPEN;
126
127        $hasdata = false;
128        foreach ($tables as $table) {
129            try {
130                // use the current time if no revision is specified, otherwise we can't access page data
131                $ts = $REV ?: time();
132                $schemadata = AccessTable::byTableName($table, $ID, $ts);
133            } catch (StructException $ignored) {
134                continue; // no such schema at this revision
135            }
136            $schemadata->optionSkipEmpty(true);
137            $data = $schemadata->getData();
138            if (!count($data)) continue;
139            $hasdata = true;
140
141            $R->table_open();
142
143            $R->tablethead_open();
144            $R->tablerow_open();
145            $R->tableheader_open(2);
146            $R->cdata($schemadata->getSchema()->getTranslatedLabel());
147            $R->tableheader_close();
148            $R->tablerow_close();
149            $R->tablethead_close();
150
151            $R->tabletbody_open();
152            foreach ($data as $field) {
153                $R->tablerow_open();
154                $R->tableheader_open();
155                $R->cdata($field->getColumn()->getTranslatedLabel());
156                $R->tableheader_close();
157                $R->tablecell_open();
158                if ($mode == 'xhtml') {
159                    $R->doc = substr($R->doc, 0, -1) . ' data-struct="' . hsc($field->getColumn()->getFullQualifiedLabel()) . '">';
160                }
161                $field->render($R, $mode);
162                $R->tablecell_close();
163                $R->tablerow_close();
164            }
165            $R->tabletbody_close();
166            $R->table_close();
167        }
168
169        if ($mode == 'xhtml') $R->doc .= self::XHTML_CLOSE;
170
171        // if no data has been output, remove empty wrapper again
172        if ($mode == 'xhtml' && !$hasdata) {
173            $R->doc = substr($R->doc, 0, -1 * strlen(self::XHTML_OPEN . self::XHTML_CLOSE));
174        }
175
176        return true;
177    }
178}
179
180// vim:ts=4:sw=4:et:
181