xref: /plugin/publish/syntax.php (revision 5882885d89ef50dd3d42bcc30441e73ba2a30d73)
1<?php
2/**
3 * DokuWiki Plugin publish (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Jarrod Lowe <dokuwiki@rrod.net>
7 * @author  Andreas Gohr <gohr@cosmocode.de>
8 */
9
10
11// must be run within DokuWiki
12if(!defined('DOKU_INC')) die();
13
14
15class syntax_plugin_publish extends DokuWiki_Syntax_Plugin {
16
17    /**
18     * @var helper_plugin_publish
19     */
20    private $hlp;
21
22    function syntax_plugin_publish(){
23        $this->hlp = plugin_load('helper','publish');
24    }
25
26    function pattern() {
27        return '\[APPROVALS.*?\]';
28    }
29
30    function getType() {
31        return 'substition';
32    }
33
34    function getSort() {
35        return 20;
36    }
37
38    function PType() {
39        return 'block';
40    }
41
42    function connectTo($mode) {
43        $this->Lexer->addSpecialPattern($this->pattern(),$mode,'plugin_publish');
44    }
45
46    function handle($match, $state, $pos, Doku_Handler $handler){
47        $namespace = substr($match, 11, -1);
48        return array($match, $state, $pos, $namespace);
49    }
50
51    function render($mode, Doku_Renderer $renderer, $data) {
52        global $conf;
53
54        if($mode != 'xhtml') {
55            return false;
56        }
57
58        list($match, $state, $pos, $namespace) = $data;
59
60        $namespace = cleanID(getNS($namespace . ":*"));
61
62        $pages = $this->getPagesFromNamespace($namespace);
63
64        if(count($pages) == 0) {
65            $renderer->doc .= '<p class="apr_none">' . $this->getLang('apr_p_none') . '</p>';
66            return true;
67        }
68
69        usort($pages, array($this,'_pagesorter'));
70
71        // Output Table
72        $renderer->doc .= '<table class="apr_table"><tr class="apr_head">';
73        $renderer->doc .= '<th class="apr_page">' . $this->getLang('apr_p_hdr_page') . '</th>';
74        $renderer->doc .= '<th class="apr_prev">' . $this->getLang('apr_p_hdr_previous') . '</th>';
75        $renderer->doc .= '<th class="apr_upd">' . $this->getLang('apr_p_hdr_updated') . '</th>';
76        $renderer->doc .= '</tr>';
77
78
79        $working_ns = null;
80        foreach($pages as $page) {
81            // $page: 0 -> pagename, 1 -> approval metadata, 2 -> last changed date
82            $this_ns = getNS($page[0]);
83
84            if($this_ns != $working_ns) {
85                $name_ns = $this_ns;
86                if($this_ns == '') { $name_ns = 'root'; }
87                $renderer->doc .= '<tr class="apr_ns"><td colspan="3"><a href="';
88                $renderer->doc .= wl($this_ns . ':' . $this->getConf('start'));
89                $renderer->doc .= '">';
90                $renderer->doc .= $name_ns;
91                $renderer->doc .= '</a></td></tr>';
92                $working_ns = $this_ns;
93            }
94
95            $updated = '<a href="' . wl($page[0]) . '">' . dformat($page[2]) . '</a>';
96            if($page[1] == null || count($page[1]) == 0) {
97                // Has never been approved
98                $approved = '';
99            }else{
100                $keys = array_keys($page[1]);
101                sort($keys);
102                $last = $keys[count($keys)-1];
103                $approved = sprintf($this->getLang('apr_p_approved'),
104                    $page[1][$last][1],
105                    wl($page[0], 'rev=' . $last),
106                    dformat($last));
107                if($last == $page[2]) { $updated = 'Unchanged'; } //shouldn't be possible:
108                //the search_helper should have
109                //excluded this
110            }
111
112            $renderer->doc .= '<tr class="apr_table';
113            if($approved == '') { $renderer->doc .= ' apr_never'; }
114            $renderer->doc .= '"><td class="apr_page"><a href="';
115            $renderer->doc .= wl($page[0]);
116            $renderer->doc .= '">';
117            $renderer->doc .= $page[0];
118            $renderer->doc .= '</a></td><td class="apr_prev">';
119            $renderer->doc .= $approved;
120            $renderer->doc .= '</td><td class="apr_upd">';
121            $renderer->doc .= $updated;
122            $renderer->doc .= '</td></tr>';
123
124            //$renderer->doc .= '<tr><td colspan="3">' . print_r($page, true) . '</td></tr>';
125        }
126        $renderer->doc .= '</table>';
127        return true;
128    }
129
130    function getPagesFromNamespace($namespace) {
131        global $conf;
132        $dir = $conf['datadir'] . '/' . str_replace(':', '/', $namespace);
133        $pages = array();
134        search($pages, $dir, array($this,'_search_helper'), array($namespace, $this->getConf('apr_namespaces'),
135                                                                  $this->getConf('no_apr_namespaces')));
136        return $pages;
137    }
138
139    /**
140     * search callback function
141     *
142     * filter out pages which can't be approved by the current user
143     * then check if they need approving
144     */
145    function _search_helper(&$data, $base, $file, $type, $lvl, $opts) {
146        $ns = $opts[0];
147        $valid_ns = $opts[1];
148        $invalid_ns = $opts[2];
149
150        if ($type == 'd') {
151            return $this->hlp->is_dir_valid($valid_ns, $ns . ':' . str_replace('/', ':', $file));
152        }
153
154        if (!preg_match('#\.txt$#', $file)) {
155            return false;
156        }
157
158        $id = pathID($ns . $file);
159        if (!empty($valid_ns) && !$this->hlp->in_namespace($valid_ns, $id)) {
160            return false;
161        }
162
163        if (!empty($invalid_ns) && $this->hlp->in_namespace($invalid_ns, $id)) {
164            return false;
165        }
166
167        if (auth_quickaclcheck($id) < AUTH_DELETE) {
168            return false;
169        }
170
171        $meta = $this->hlp->getMeta($id);
172        if ($this->hlp->isCurrentRevisionApproved($id)) {
173
174            // Already approved
175            return false;
176        }
177
178        $data[] = array($id, $meta['approval'], $meta['last_change']['date']);
179        return false;
180    }
181
182    /**
183     * Custom sort callback
184     */
185    function _pagesorter($a, $b){
186        $ac = explode(':',$a[0]);
187        $bc = explode(':',$b[0]);
188        $an = count($ac);
189        $bn = count($bc);
190
191        // Same number of elements, can just string sort
192        if($an == $bn) { return strcmp($a[0], $b[0]); }
193
194        // For each level:
195        // If this is not the last element in either list:
196        //   same -> continue
197        //   otherwise strcmp
198        // If this is the last element in either list, it wins
199        $n = 0;
200        while(true) {
201            if($n + 1 == $an) { return -1; }
202            if($n + 1 == $bn) { return 1; }
203            $s = strcmp($ac[$n], $bc[$n]);
204            if($s != 0) { return $s; }
205            $n += 1;
206        }
207    }
208
209}
210
211