xref: /plugin/publish/helper.php (revision 269d75574bbb6938447db274e2f5e5326a4e4af6)
1<?php
2/**
3 * DokuWiki Plugin publish (Helper 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// must be run within Dokuwiki
11if (!defined('DOKU_INC')) die();
12
13class helper_plugin_publish extends DokuWiki_Plugin {
14
15    private $sortedApprovedRevisions = null;
16
17    // FIXME find out what this is supposed to do and how it can be done better
18    function in_namespace($valid, $check) {
19        // PHP apparantly does not have closures -
20        // so we will parse $valid ourselves. Wasteful.
21        $valid = preg_split('/\s+/', $valid);
22        //if(count($valid) == 0) { return true; }//whole wiki matches
23        if((count($valid)==1) and ($valid[0]=="")) { return true; }//whole wiki matches
24        $check = trim($check, ':');
25        $check = explode(':', $check);
26
27        // Check against all possible namespaces
28        foreach($valid as $v) {
29            $v = explode(':', $v);
30            $n = 0;
31            $c = count($v);
32            $matching = 1;
33
34            // Check each element, untill all elements of $v satisfied
35            while($n < $c) {
36                if($v[$n] != $check[$n]) {
37                    // not a match
38                    $matching = 0;
39                    break;
40                }
41                $n += 1;
42            }
43            if($matching == 1) { return true; } // a match
44        }
45        return false;
46    }
47
48    // FIXME find out what this is supposed to do and how it can be done better
49    function in_sub_namespace($valid, $check) {
50        // is check a dir which contains any valid?
51        // PHP apparantly does not have closures -
52        // so we will parse $valid ourselves. Wasteful.
53        $valid = preg_split('/\s+/', $valid);
54        //if(count($valid) == 0) { return true; }//whole wiki matches
55        if((count($valid)==1) and ($valid[0]=="")) { return true; }//whole wiki matches
56        $check = trim($check, ':');
57        $check = explode(':', $check);
58
59        // Check against all possible namespaces
60        foreach($valid as $v) {
61            $v = explode(':', $v);
62            $n = 0;
63            $c = count($check); //this is what is different from above!
64            $matching = 1;
65
66            // Check each element, untill all elements of $v satisfied
67            while($n < $c) {
68                if($v[$n] != $check[$n]) {
69                    // not a match
70                    $matching = 0;
71                    break;
72                }
73                $n += 1;
74            }
75            if($matching == 1) { return true; } // a match
76        }
77        return false;
78    }
79
80    function canApprove() {
81        global $INFO;
82        global $ID;
83
84        if (!$this->in_namespace($this->getConf('apr_namespaces'), $ID)) {
85            return false;
86        }
87
88        return ($INFO['perm'] >= AUTH_DELETE);
89    }
90
91    function getRevision($id = null) {
92        global $REV;
93        if (isset($REV) && !empty($REV)) {
94            return $REV;
95        }
96        $meta = $this->getMeta($id);
97        if (isset($meta['last_change']['date'])) {
98            return $meta['last_change']['date'];
99        }
100        return $meta['date']['modified'];
101    }
102
103    function getApprovals($id = null) {
104        $meta = $this->getMeta($id);
105        if (!isset($meta['approval'])) {
106            return array();
107        }
108        $approvals = $meta['approval'];
109        if (!is_array($approvals)) {
110            return array();
111        }
112        return $approvals;
113    }
114
115    function getMeta($id = null) {
116        global $ID;
117        global $INFO;
118
119        if ($id === null) $id = $ID;
120
121        if($ID === $id && $INFO['meta']) {
122            $meta = $INFO['meta'];
123        } else {
124            $meta = p_get_metadata($id);
125        }
126
127        $this->checkApprovalFormat($meta, $id);
128
129        return $meta;
130    }
131
132    function checkApprovalFormat($meta, $id) {
133        if (isset($meta['approval_version']) && $meta['approval_version'] >= 2) {
134            return;
135        }
136
137        if (!$this->hasApprovals($meta)) {
138            return;
139        }
140
141        $approvals = $meta['approval'];
142        foreach (array_keys($approvals) as $approvedId) {
143            $keys = array_keys($approvals[$approvedId]);
144
145            if (is_array($approvals[$approvedId][$keys[0]])) {
146                continue; // current format
147            }
148
149            $newEntry = $approvals[$approvedId];
150            if (count($newEntry) !== 3) {
151                //continue; // some messed up format...
152            }
153            $newEntry[] = intval($approvedId); // revision is the time of page edit
154
155            $approvals[$approvedId] = array();
156            $approvals[$approvedId][$newEntry[0]] = $newEntry;
157        }
158        p_set_metadata($id, array('approval' => $approvals), true, true);
159        p_set_metadata($id, array('approval_version' => 2), true, true);
160    }
161
162    function hasApprovals($meta) {
163        return isset($meta['approval']) && !empty($meta['approval']);
164    }
165
166    function getApprovalsOnRevision($revision) {
167        $approvals = $this->getApprovals();
168
169        if (isset($approvals[$revision])) {
170            return $approvals[$revision];
171        }
172        return array();
173    }
174
175    function getSortedApprovedRevisions($id = null) {
176        if ($id === null) {
177            global $ID;
178            $id = $ID;
179        }
180
181        static $sortedApprovedRevisions = array();
182        if (!isset($sortedApprovedRevisions[$id])) {
183            $approvals = $this->getApprovals($id);
184            krsort($approvals);
185            $sortedApprovedRevisions[$id] = $approvals;
186        }
187
188        return $sortedApprovedRevisions[$id];
189    }
190
191    function isRevisionApproved($revision, $id = null) {
192        $approvals = $this->getApprovals($id);
193        if (!isset($approvals[$revision])) {
194            return false;
195        }
196        return (count($approvals[$revision]) >= $this->getConf('number_of_approved'));
197    }
198
199    function isCurrentRevisionApproved($id = null) {
200        return $this->isRevisionApproved($this->getRevision($id), $id);
201    }
202
203    function getLatestApprovedRevision($id = null) {
204        $approvals = $this->getSortedApprovedRevisions($id);
205        foreach ($approvals as $revision => $ignored) {
206            if ($this->isRevisionApproved($revision, $id)) {
207                return $revision;
208            }
209        }
210        return 0;
211    }
212
213    function getLastestRevision() {
214        global $INFO;
215        return $INFO['meta']['date']['modified'];
216    }
217
218    function getApprovalDate() {
219        if (!$this->isCurrentRevisionApproved()) {
220            return -1;
221        }
222
223        $approvals = $this->getApprovalsOnRevision($this->getRevision());
224        uasort($approvals, array(&$this, 'cmpApprovals'));
225        $keys = array_keys($approvals);
226        return $approvals[$keys[$this->getConf('number_of_approved') -1]][3];
227
228    }
229
230    function cmpApprovals($left, $right) {
231        if ($left[3] == $right[3]) {
232            return 0;
233        }
234        return ($left[3] < $right[3]) ? -1 : 1;
235    }
236
237    function getApprovers() {
238        $approvers = $this->getApprovalsOnRevision($this->getRevision());
239        if (count($approvers) === 0) {
240            return;
241        }
242
243        $result = array();
244        foreach ($approvers as $approver) {
245            $result[] = editorinfo($this->getApproverName($approver));
246        }
247        return $result;
248    }
249
250    function getApproverName($approver) {
251        if ($approver[1]) {
252            return $approver[1];
253        }
254        if ($approver[2]) {
255            return $approver[2];
256        }
257        return $approver[0];
258    }
259
260    function getPreviousApprovedRevision() {
261        $currentRevision = $this->getRevision();
262        $approvals = $this->getSortedApprovedRevisions();
263        foreach ($approvals as $revision => $ignored) {
264            if ($revision >= $currentRevision) {
265                continue;
266            }
267            if ($this->isRevisionApproved($revision)) {
268                return $revision;
269            }
270        }
271        return 0;
272    }
273
274    function isHidden($id = null) {
275        if (!$this->getConf('hide drafts')) {
276            return false;
277        }
278
279        // needs to check if the actual namespace belongs to the apr_namespaces
280        if ($id == null) {
281            global $ID;
282            $id = $ID;
283        }
284        if (!$this->isActive($id)) {
285            return false;
286        }
287
288        if ($this->getLatestApprovedRevision($id)) {
289            return false;
290        }
291        return true;
292    }
293
294    function isHiddenForUser($id = null) {
295        if (!$this->isHidden($id)) {
296            return false;
297        }
298
299        if ($id == null) {
300            global $ID;
301            $id = $ID;
302        }
303
304        $allowedGroups = array_filter(explode(' ', trim($this->getConf('author groups'))));
305        if (empty($allowedGroups)) {
306            return auth_quickaclcheck($id) < AUTH_EDIT;
307        }
308
309        if (!$_SERVER['REMOTE_USER']) {
310            return true;
311        }
312
313        global $USERINFO;
314        foreach ($allowedGroups as $allowedGroup) {
315            $allowedGroup = trim($allowedGroup);
316            if (in_array($allowedGroup, $USERINFO['grps'])) {
317                return false;
318            }
319        }
320        return true;
321    }
322
323    function isActive($id = null) {
324        if ($id == null) {
325            global $ID;
326            $id = $ID;
327        }
328        if (!$this->in_namespace($this->getConf('apr_namespaces'), $id)) {
329            return false;
330        }
331
332        $no_apr_namespaces = $this->getConf('no_apr_namespaces');
333        if (!empty($no_apr_namespaces)) {
334            if ($this->in_namespace($no_apr_namespaces, $id)) {
335                return false;
336            }
337        }
338        return true;
339    }
340}
341