1<?php
2/**
3 * DokuWiki Plugin struct (Helper Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Szymon Olewniczak <dokuwiki@cosmocode.de>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) {
11    die();
12}
13
14class helper_plugin_watchcycle extends DokuWiki_Plugin
15{
16    const MAINTAINERS_RAW = 0;
17    const MAINTAINERS_FLAT = 1;
18    const MAINTAINERS_EXPANDED = 2;
19
20    /**
21     * Create HTML for an icon showing the maintenance status of the provided pageid
22     *
23     * @param string $pageid the full pageid
24     *
25     * @return string span with inline svg icon and classes
26     */
27    public function getSearchResultIconHTML($pageid)
28    {
29        /* @var \DokuWiki_Auth_Plugin $auth */
30        global $auth;
31        if ($auth === null) return '';
32
33        /* @var \helper_plugin_watchcycle $helper */
34        $helper = plugin_load('helper', 'watchcycle');
35        $watchcycle = p_get_metadata($pageid, 'plugin watchcycle');
36        if (!$watchcycle) {
37            return '';
38        }
39
40        $days_ago = $helper->daysAgo($watchcycle['last_maintainer_rev']);
41
42        $check_needed = false;
43        if ($days_ago > $watchcycle['cycle']) {
44            $check_needed = true;
45        }
46
47        $all = $this->getMaintainers($watchcycle['maintainer']);
48        $title = $this->getLang('maintained by') . implode(', ', array_keys($all)) . ' ';
49
50        if ($watchcycle['changes'] === -1) {
51            $title .= $this->getLang('never checked');
52        } else {
53            $title .= sprintf($this->getLang('last check'), $days_ago);
54        }
55
56        $class = ['plugin__watchcycle_searchresult_icon'];
57        if ($check_needed) {
58            $class[] = 'check_needed';
59            $title .= ' (' . $this->getLang('check needed') . ')';
60        }
61        $icon = '<span class="' . implode(' ', $class) . '" title="' . $title . '">';
62        $icon .= inlineSVG(DOKU_PLUGIN . 'watchcycle/admin.svg');
63        $icon .= '</span>';
64        return $icon;
65    }
66
67    /**
68     * @param $time
69     * @param $now
70     *
71     * @return int
72     */
73    public function daysAgo($time, $now = false)
74    {
75        if (!$now) {
76            $now = time();
77        }
78
79        $diff = ($now - $time) / (60 * 60 * 24);
80        return (int)$diff;
81    }
82
83
84    /**
85     * Returns true if the maintainer definition matches existing users and groups
86     *
87     * @param string $def
88     * @return bool
89     */
90    public function validateMaintainerString($def)
91    {
92        /* @var DokuWiki_Auth_Plugin $auth */
93        global $auth;
94        if ($auth === null) return false; // no valid auth setup
95
96        $all = explode(',', $def);
97        foreach ($all as $item) {
98            $item = trim($item);
99            if (strpos($item, '@') !== false) {
100                // check if group exists
101                if (empty($auth->retrieveUsers(0, 1, array('grps' => ltrim($item, '@'))))) {
102                    return false;
103                }
104            } else {
105                if ($auth->getUserData($item) === false) {
106                    return false;
107                }
108            }
109        }
110        return true;
111    }
112
113    /**
114     * Returns a parsed representation of the maintainer string
115     *
116     * keys are the user and group names, value is either:
117     *
118     *  - the user data array
119     *  - null for groups
120     *  - false for unknown users
121     *
122     * @param string $def maintainer definition as given in the syntax
123     * @return array
124     */
125    public function getMaintainers($def)
126    {
127        /* @var DokuWiki_Auth_Plugin $auth */
128        global $auth;
129
130        $found = [];
131        if ($auth === null) return $found;
132
133        $all = explode(',', $def);
134        foreach ($all as $item) {
135            $item = trim($item);
136            if ($item[0] === '@') {
137                $found[$item] = null; // no detail info on groups
138            } else {
139                $found[$item] = $auth->getUserData($item);
140            }
141        }
142
143        return $found;
144    }
145
146    /**
147     * @param string $def maintainer definition as given in the syntax
148     * @return string[] list of email addresses to inform
149     */
150    public function getMaintainerMails($def)
151    {
152        /* @var DokuWiki_Auth_Plugin $auth */
153        global $auth;
154        if (!$auth) return [];
155
156        $data = $this->getMaintainers($def);
157        $mails = [];
158        foreach ($data as $name => $info) {
159            if (is_array($info)) {
160                $mails[] = $info['mail'];
161            } elseif ($name[0] === '@' && $auth->canDo('getUsers')) {
162                $members = $auth->retrieveUsers(0, 0, array('grps' => ltrim($name, '@')));
163                foreach ($members as $user) {
164                    $mails[] = $user['mail'];
165                }
166            }
167        }
168
169        return array_values(array_unique($mails));
170    }
171
172    /**
173     * @param string $user
174     * @param string $def
175     * @return bool
176     */
177    public function isMaintainer($user, $def)
178    {
179        /* @var DokuWiki_Auth_Plugin $auth */
180        global $auth;
181        if ($auth === null) return false;
182        if ($user === '') return false;
183        $userData = $auth->getUserData($user);
184
185        $all = explode(',', $def);
186        foreach ($all as $item) {
187            $item = trim($item);
188            if (strpos($item, '@') !== false && in_array(ltrim($item, '@'), $userData['grps'])) {
189                return true;
190            } elseif ($item === $user) {
191                return true;
192            }
193        }
194
195        return false;
196    }
197
198    /**
199     * Puts users and groups into a flat array; useful for simple string output
200     * @param array $all
201     * @return array
202     */
203    protected function flattenMaintainers($all)
204    {
205        if (empty($all['users'])) {
206            return $all;
207        }
208
209        $users = array_map(function ($user) {
210            return $user['name'];
211        }, $all['users']);
212
213        return array_merge($users, $all['groups']);
214    }
215
216    /**
217     * Expands groups into users; useful for email notification
218     *
219     * @param array $all
220     * @return array
221     */
222    protected function expandMaintainers($all)
223    {
224        if (empty($all['groups'])) {
225            return $all;
226        }
227
228        /* @var DokuWiki_Auth_Plugin $auth */
229        global $auth;
230        if ($auth === null) return [];
231
232        $members = array();
233        foreach ($all['groups'] as $group) {
234            $members = array_merge($members, $auth->retrieveUsers(0, 0, array('grps' => ltrim($group, '@'))));
235        }
236
237        // merge eliminates any duplicates since we use string keys
238        return array_merge($all['users'], $members);
239    }
240}
241