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