1<?php
2/**
3 * Include Plugin: displays a wiki page within another
4 * Usage:
5 * {{page>page}} for "page" in same namespace
6 * {{page>:page}} for "page" in top namespace
7 * {{page>namespace:page}} for "page" in namespace "namespace"
8 * {{page>.namespace:page}} for "page" in subnamespace "namespace"
9 * {{page>page#section}} for a section of "page"
10 *
11 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
12 * @author     Esther Brunner <wikidesign@gmail.com>
13 * @author     Christopher Smith <chris@jalakai.co.uk>
14 * @author     Gina Häußge, Michael Klier <dokuwiki@chimeric.de>
15 */
16
17/**
18 * All DokuWiki plugins to extend the parser/rendering mechanism
19 * need to inherit from this class
20 */
21class syntax_plugin_include_include extends DokuWiki_Syntax_Plugin {
22
23    /** @var $helper helper_plugin_include */
24    var $helper = null;
25
26    /**
27     * Get syntax plugin type.
28     *
29     * @return string The plugin type.
30     */
31    function getType() { return 'substition'; }
32
33    /**
34     * Get sort order of syntax plugin.
35     *
36     * @return int The sort order.
37     */
38    function getSort() { return 303; }
39
40    /**
41     * Get paragraph type.
42     *
43     * @return string The paragraph type.
44     */
45    function getPType() { return 'block'; }
46
47    /**
48     * Connect patterns/modes
49     *
50     * @param $mode mixed The current mode
51     */
52    function connectTo($mode) {
53        $this->Lexer->addSpecialPattern("{{page>.+?}}", $mode, 'plugin_include_include');
54        $this->Lexer->addSpecialPattern("{{section>.+?}}", $mode, 'plugin_include_include');
55        $this->Lexer->addSpecialPattern("{{namespace>.+?}}", $mode, 'plugin_include_include');
56        $this->Lexer->addSpecialPattern("{{tagtopic>.+?}}", $mode, 'plugin_include_include');
57    }
58
59    /**
60     * Handle syntax matches
61     *
62     * @param string       $match   The current match
63     * @param int          $state   The match state
64     * @param int          $pos     The position of the match
65     * @param Doku_Handler $handler The hanlder object
66     * @return array The instructions of the plugin
67     */
68    function handle($match, $state, $pos, Doku_Handler $handler) {
69
70        $match = substr($match, 2, -2); // strip markup
71        list($match, $flags) = array_pad(explode('&', $match, 2), 2, '');
72
73        // break the pattern up into its parts
74        list($mode, $page, $sect) = array_pad(preg_split('/>|#/u', $match, 3), 3, null);
75        $check = false;
76        if (isset($sect)) $sect = sectionID($sect, $check);
77        $level = NULL;
78        return array($mode, $page, $sect, explode('&', $flags), $level, $pos);
79    }
80
81    /**
82     * Renders the included page(s)
83     *
84     * @author Michael Hamann <michael@content-space.de>
85     */
86    function render($format, Doku_Renderer $renderer, $data) {
87        global $ID;
88
89        // static stack that records all ancestors of the child pages
90        static $page_stack = array();
91
92        // when there is no id just assume the global $ID is the current id
93        if (empty($page_stack)) $page_stack[] = $ID;
94
95        $parent_id = $page_stack[count($page_stack)-1];
96        $root_id = $page_stack[0];
97
98        list($mode, $page, $sect, $flags, $level, $pos) = $data;
99
100        if (!$this->helper)
101            $this->helper = plugin_load('helper', 'include');
102        $flags = $this->helper->get_flags($flags);
103
104        $pages = $this->helper->_get_included_pages($mode, $page, $sect, $parent_id, $flags);
105
106        if ($format == 'metadata') {
107            /** @var Doku_Renderer_metadata $renderer */
108
109            // remove old persistent metadata of previous versions of the include plugin
110            if (isset($renderer->persistent['plugin_include'])) {
111                unset($renderer->persistent['plugin_include']);
112                unset($renderer->meta['plugin_include']);
113            }
114
115            $renderer->meta['plugin_include']['instructions'][] = compact('mode', 'page', 'sect', 'parent_id', 'flags');
116            if (!isset($renderer->meta['plugin_include']['pages']))
117               $renderer->meta['plugin_include']['pages'] = array(); // add an array for array_merge
118            $renderer->meta['plugin_include']['pages'] = array_merge($renderer->meta['plugin_include']['pages'], $pages);
119            $renderer->meta['plugin_include']['include_content'] = isset($_REQUEST['include_content']);
120        }
121
122        $secids = array();
123        if ($format == 'xhtml' || $format == 'odt') {
124            $secids = p_get_metadata($ID, 'plugin_include secids');
125        }
126
127        foreach ($pages as $page) {
128            extract($page);
129            $id = $page['id'];
130            $exists = $page['exists'];
131
132            if (in_array($id, $page_stack)) continue;
133            array_push($page_stack, $id);
134
135            // add references for backlink
136            if ($format == 'metadata') {
137                $renderer->meta['relation']['references'][$id] = $exists;
138                $renderer->meta['relation']['haspart'][$id]    = $exists;
139                if (!$sect && !$flags['firstsec'] && !$flags['linkonly'] && !isset($renderer->meta['plugin_include']['secids'][$id])) {
140                    $renderer->meta['plugin_include']['secids'][$id] = array('hid' => 'plugin_include__'.str_replace(':', '__', $id), 'pos' => $pos);
141                }
142            }
143
144            if (isset($secids[$id]) && $pos === $secids[$id]['pos']) {
145                $flags['include_secid'] = $secids[$id]['hid'];
146            } else {
147                unset($flags['include_secid']);
148            }
149
150            $instructions = $this->helper->_get_instructions($id, $sect, $mode, $level, $flags, $root_id, $secids);
151
152            if (!$flags['editbtn']) {
153                global $conf;
154                $maxseclevel_org = $conf['maxseclevel'];
155                $conf['maxseclevel'] = 0;
156            }
157            $renderer->nest($instructions);
158            if (isset($maxseclevel_org)) {
159                $conf['maxseclevel'] = $maxseclevel_org;
160                unset($maxseclevel_org);
161            }
162
163            array_pop($page_stack);
164        }
165
166        // When all includes have been handled remove the current id
167        // in order to allow the rendering of other pages
168        if (count($page_stack) == 1) array_pop($page_stack);
169
170        return true;
171    }
172}
173// vim:ts=4:sw=4:et:
174