1<?php
2
3use dokuwiki\File\PageResolver;
4
5/**
6 * DokuWiki Plugin tplinc (Helper Component)
7 *
8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
9 * @author  Andreas Gohr <dokuwiki@cosmocode.de>
10 */
11class helper_plugin_tplinc extends DokuWiki_Plugin
12{
13
14    protected $file = DOKU_CONF . 'tplinc.conf';
15
16    /**
17     * Load the current assignments into an array
18     *
19     * @return array
20     */
21    public function loadAssignments()
22    {
23        $assignments = array();
24        if (!file_exists($this->file)) return $assignments;
25
26        $data = file($this->file);
27        foreach ($data as $line) {
28            //ignore comments (except escaped ones)
29            $line = preg_replace('/(?<![&\\\\])#.*$/', '', $line);
30            $line = str_replace('\\#', '#', $line);
31            $line = trim($line);
32            if (empty($line)) continue;
33            $assignments[] = array_map('trim', explode("\t", $line, 4));
34        }
35
36        return $assignments;
37    }
38
39    /**
40     * The same as loadAssignments but uses caching
41     *
42     * @return array
43     */
44    public function getAssignments()
45    {
46        static $assignements = null;
47        if ($assignements === null) $assignements = $this->loadAssignments();
48        return $assignements;
49    }
50
51    /**
52     * Save new assignment data
53     *
54     * @param array $data as returned by loadAssignment
55     * @return bool
56     */
57    public function saveAssignments($data)
58    {
59        $content = '';
60
61        foreach ($data as $row) {
62            $row = array_map('trim', $row);
63            if ($row[0] === '' || $row[1] === '') continue;
64            if (count($row) < 4) $row[3] = 0;
65
66            $content .= join("\t", $row) . "\n";
67        }
68
69        return io_saveFile($this->file, $content);
70    }
71
72    /**
73     * Get a list of pages that should be included at the given $location
74     *
75     * @param string $location
76     * @param null|string $id the ID to check against, null for global $ID
77     * @return array list of pages to include
78     */
79    public function getIncludes($location, $id = null)
80    {
81        global $ID;
82        if ($id === null) $id = $ID;
83        $id = cleanID($id);
84        $ns = getNS($id);
85        $pns = ":$ns:";
86
87        $assignments = $this->getAssignments();
88        $pages = array();
89
90        foreach ($assignments as $row) {
91            list($pattern, $page, $loc, $skipacl) = $row;
92            if ($loc != $location) continue;
93            $page = $this->matchPagePattern($pattern, $id, $page, $pns);
94            if ($page === false) continue;
95            $page = (new PageResolver($ns))->resolveId($page);
96            if (!page_exists($page)) continue;
97            if (!$skipacl && auth_quickaclcheck($page) < AUTH_READ) continue;
98            $pages[] = $page;
99        }
100
101        return array_unique($pages);
102    }
103
104    /**
105     * Render the include pagesfor the given $location
106     *
107     * @param $location
108     * @param null|string $id the ID to check against, null for global $ID
109     * @return string the rendered XHTML
110     */
111    public function renderIncludes($location, $id = null)
112    {
113        $pages = $this->getIncludes($location, $id);
114        $content = '';
115        foreach ($pages as $page) {
116            $content .= p_wiki_xhtml($page, '', false);
117        }
118        return $content;
119    }
120
121    /**
122     * Get the locations supported by the template
123     *
124     * The template needs to implement the apropriate event hook
125     *
126     * @return array
127     */
128    public function getLocations()
129    {
130        $data = array('' => $this->getLang('unknown'));
131        $event = new Doku_Event('PLUGIN_TPLINC_LOCATIONS_SET', $data);
132        $event->advise_before(false);
133        asort($data);
134        $event->advise_after();
135
136        return $data;
137    }
138
139    /**
140     * Check if the given pattern matches the given page id
141     *
142     * @param string $pattern the pattern to check against
143     * @param string $id the cleaned pageid to check
144     * @param string $page the page to include on success - may contain regexp placeholders
145     * @param string|null $pns optimization, the colon wrapped namespace of the page, set null for automatic
146     * @return string|bool the page as matched (with placeholders replaced) or false if the pattern does not match
147     */
148    protected function matchPagePattern($pattern, $id, $page, $pns = null)
149    {
150        if (trim($pattern, ':') == '**') return $page; // match all
151
152        // regex patterns
153        if ($pattern[0] == '/') {
154            if (preg_match($pattern, ":$id", $matches)) {
155                $len = count($matches);
156                for ($i = $len - 1; $i >= 0; $i--) {
157                    $page = str_replace('$' . $i, $matches[$i], $page);
158                }
159                return $page;
160            }
161            return false;
162        }
163
164        if (is_null($pns)) {
165            $pns = ':' . getNS($id) . ':';
166        }
167
168        $ans = ':' . cleanID($pattern) . ':';
169        if (substr($pattern, -2) == '**') {
170            // upper namespaces match
171            if (strpos($pns, $ans) === 0) {
172                return $page;
173            }
174        } else {
175            if (substr($pattern, -1) == '*') {
176                // namespaces match exact
177                if ($ans == $pns) {
178                    return $page;
179                }
180            } else {
181                // exact match
182                if (cleanID($pattern) == $id) {
183                    return $page;
184                }
185            }
186        }
187
188        return false;
189    }
190
191}
192