xref: /plugin/publish/syntax.php (revision 7fcadbfe49e52208fe09000352d49f623d9804de)
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        return $pages;
136    }
137
138    /**
139     * search callback function
140     *
141     * filter out pages which can't be approved by the current user
142     * then check if they need approving
143     */
144    function _search_helper(&$data, $base, $file, $type, $lvl, $opts) {
145        $ns = $opts[0];
146        $valid_ns = $opts[1];
147
148        if ($type == 'd') {
149            return $this->hlp->in_sub_namespace($valid_ns, $ns . ':' . str_replace('/', ':', $file));
150        }
151
152        if (!preg_match('#\.txt$#', $file)) {
153            return false;
154        }
155
156        $id = pathID($ns . $file);
157        if (!$this->hlp->in_namespace($valid_ns, $id)) {
158            return false;
159        }
160
161        if (auth_quickaclcheck($id) < AUTH_DELETE) {
162            return false;
163        }
164
165        $meta = $this->hlp->getMeta($id);
166        if ($this->hlp->isCurrentRevisionApproved($id)) {
167
168            // Already approved
169            return false;
170        }
171
172        $data[] = array($id, $meta['approval'], $meta['last_change']['date']);
173        return false;
174    }
175
176    /**
177     * Custom sort callback
178     */
179    function _pagesorter($a, $b){
180        $ac = explode(':',$a[0]);
181        $bc = explode(':',$b[0]);
182        $an = count($ac);
183        $bn = count($bc);
184
185        // Same number of elements, can just string sort
186        if($an == $bn) { return strcmp($a[0], $b[0]); }
187
188        // For each level:
189        // If this is not the last element in either list:
190        //   same -> continue
191        //   otherwise strcmp
192        // If this is the last element in either list, it wins
193        $n = 0;
194        while(true) {
195            if($n + 1 == $an) { return -1; }
196            if($n + 1 == $bn) { return 1; }
197            $s = strcmp($ac[$n], $bc[$n]);
198            if($s != 0) { return $s; }
199            $n += 1;
200        }
201    }
202
203}
204
205