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