xref: /plugin/watchcycle/helper.php (revision 92c202adbea42a9ca4b9ef1a602628210f038ade) !
14048d1eeSSzymon Olewniczak<?php
29de4e228SAnna Dabrowska
3*92c202adSanndause dokuwiki\Extension\Plugin;
4*92c202adSannda
54048d1eeSSzymon Olewniczak/**
64048d1eeSSzymon Olewniczak * DokuWiki Plugin struct (Helper Component)
74048d1eeSSzymon Olewniczak *
84048d1eeSSzymon Olewniczak * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
94048d1eeSSzymon Olewniczak * @author  Szymon Olewniczak <dokuwiki@cosmocode.de>
104048d1eeSSzymon Olewniczak */
11*92c202adSanndaclass helper_plugin_watchcycle extends Plugin
121cd7657bSMichael Große{
139de4e228SAnna Dabrowska    public const MAINTAINERS_RAW = 0;
149de4e228SAnna Dabrowska    public const MAINTAINERS_FLAT = 1;
159de4e228SAnna Dabrowska    public const MAINTAINERS_EXPANDED = 2;
167ec9f11bSAnna Dabrowska
1738cc216eSMichael Große    /**
1838cc216eSMichael Große     * Create HTML for an icon showing the maintenance status of the provided pageid
1938cc216eSMichael Große     *
2038cc216eSMichael Große     * @param string $pageid the full pageid
2138cc216eSMichael Große     *
2238cc216eSMichael Große     * @return string span with inline svg icon and classes
2338cc216eSMichael Große     */
241cd7657bSMichael Große    public function getSearchResultIconHTML($pageid)
251cd7657bSMichael Große    {
2638cc216eSMichael Große        /* @var \DokuWiki_Auth_Plugin $auth */
2738cc216eSMichael Große        global $auth;
28208265aeSAndreas Gohr        if ($auth === null) return '';
2938cc216eSMichael Große
3038cc216eSMichael Große        /* @var \helper_plugin_watchcycle $helper */
3138cc216eSMichael Große        $helper = plugin_load('helper', 'watchcycle');
3238cc216eSMichael Große        $watchcycle = p_get_metadata($pageid, 'plugin watchcycle');
3338cc216eSMichael Große        if (!$watchcycle) {
3438cc216eSMichael Große            return '';
3538cc216eSMichael Große        }
3638cc216eSMichael Große
3738cc216eSMichael Große        $days_ago = $helper->daysAgo($watchcycle['last_maintainer_rev']);
3838cc216eSMichael Große
3938cc216eSMichael Große        $check_needed = false;
4038cc216eSMichael Große        if ($days_ago > $watchcycle['cycle']) {
4138cc216eSMichael Große            $check_needed = true;
4238cc216eSMichael Große        }
4338cc216eSMichael Große
447e31aba7SAndreas Gohr        $all = $this->getMaintainers($watchcycle['maintainer']);
457e31aba7SAndreas Gohr        $title = $this->getLang('maintained by') . implode(', ', array_keys($all)) . ' ';
4638cc216eSMichael Große
4738cc216eSMichael Große        if ($watchcycle['changes'] === -1) {
4838cc216eSMichael Große            $title .= $this->getLang('never checked');
4938cc216eSMichael Große        } else {
5038cc216eSMichael Große            $title .= sprintf($this->getLang('last check'), $days_ago);
5138cc216eSMichael Große        }
5238cc216eSMichael Große
5338cc216eSMichael Große        $class = ['plugin__watchcycle_searchresult_icon'];
5438cc216eSMichael Große        if ($check_needed) {
5538cc216eSMichael Große            $class[] = 'check_needed';
5638cc216eSMichael Große            $title .= ' (' . $this->getLang('check needed') . ')';
5738cc216eSMichael Große        }
5838cc216eSMichael Große        $icon = '<span class="' . implode(' ', $class) . '" title="' . $title . '">';
5938cc216eSMichael Große        $icon .= inlineSVG(DOKU_PLUGIN . 'watchcycle/admin.svg');
6038cc216eSMichael Große        $icon .= '</span>';
6138cc216eSMichael Große        return $icon;
6238cc216eSMichael Große    }
631cd7657bSMichael Große
641cd7657bSMichael Große    /**
651cd7657bSMichael Große     * @param $time
661cd7657bSMichael Große     * @param $now
671cd7657bSMichael Große     *
681cd7657bSMichael Große     * @return int
691cd7657bSMichael Große     */
701cd7657bSMichael Große    public function daysAgo($time, $now = false)
711cd7657bSMichael Große    {
721cd7657bSMichael Große        if (!$now) {
731cd7657bSMichael Große            $now = time();
741cd7657bSMichael Große        }
751cd7657bSMichael Große
761cd7657bSMichael Große        $diff = ($now - $time) / (60 * 60 * 24);
771cd7657bSMichael Große        return (int)$diff;
781cd7657bSMichael Große    }
797ec9f11bSAnna Dabrowska
807ec9f11bSAnna Dabrowska
817ec9f11bSAnna Dabrowska    /**
827ec9f11bSAnna Dabrowska     * Returns true if the maintainer definition matches existing users and groups
837ec9f11bSAnna Dabrowska     *
847ec9f11bSAnna Dabrowska     * @param string $def
857ec9f11bSAnna Dabrowska     * @return bool
867ec9f11bSAnna Dabrowska     */
877ec9f11bSAnna Dabrowska    public function validateMaintainerString($def)
887ec9f11bSAnna Dabrowska    {
897ec9f11bSAnna Dabrowska        /* @var DokuWiki_Auth_Plugin $auth */
907ec9f11bSAnna Dabrowska        global $auth;
91208265aeSAndreas Gohr        if ($auth === null) return false; // no valid auth setup
927ec9f11bSAnna Dabrowska
937ec9f11bSAnna Dabrowska        $all = explode(',', $def);
947ec9f11bSAnna Dabrowska        foreach ($all as $item) {
957ec9f11bSAnna Dabrowska            $item = trim($item);
967ec9f11bSAnna Dabrowska            if (strpos($item, '@') !== false) {
977ec9f11bSAnna Dabrowska                // check if group exists
98*92c202adSannda                if (empty($auth->retrieveUsers(0, 1, ['grps' => ltrim($item, '@')]))) {
997ec9f11bSAnna Dabrowska                    return false;
1007ec9f11bSAnna Dabrowska                }
101*92c202adSannda            } elseif ($auth->getUserData($item) === false) {
1027ec9f11bSAnna Dabrowska                return false;
1037ec9f11bSAnna Dabrowska            }
1047ec9f11bSAnna Dabrowska        }
1057ec9f11bSAnna Dabrowska        return true;
1067ec9f11bSAnna Dabrowska    }
1077ec9f11bSAnna Dabrowska
1087ec9f11bSAnna Dabrowska    /**
1097e31aba7SAndreas Gohr     * Returns a parsed representation of the maintainer string
1107ec9f11bSAnna Dabrowska     *
1117e31aba7SAndreas Gohr     * keys are the user and group names, value is either:
1127e31aba7SAndreas Gohr     *
1137e31aba7SAndreas Gohr     *  - the user data array
1147e31aba7SAndreas Gohr     *  - null for groups
1157e31aba7SAndreas Gohr     *  - false for unknown users
1167e31aba7SAndreas Gohr     *
1177e31aba7SAndreas Gohr     * @param string $def maintainer definition as given in the syntax
1187ec9f11bSAnna Dabrowska     * @return array
1197ec9f11bSAnna Dabrowska     */
1207e31aba7SAndreas Gohr    public function getMaintainers($def)
1217ec9f11bSAnna Dabrowska    {
1227ec9f11bSAnna Dabrowska        /* @var DokuWiki_Auth_Plugin $auth */
1237ec9f11bSAnna Dabrowska        global $auth;
1247ec9f11bSAnna Dabrowska
1257e31aba7SAndreas Gohr        $found = [];
126208265aeSAndreas Gohr        if ($auth === null) return $found;
1277ec9f11bSAnna Dabrowska
1287ec9f11bSAnna Dabrowska        $all = explode(',', $def);
1297ec9f11bSAnna Dabrowska        foreach ($all as $item) {
1307ec9f11bSAnna Dabrowska            $item = trim($item);
1317e31aba7SAndreas Gohr            if ($item[0] === '@') {
1327e31aba7SAndreas Gohr                $found[$item] = null; // no detail info on groups
1337ec9f11bSAnna Dabrowska            } else {
1347e31aba7SAndreas Gohr                $found[$item] = $auth->getUserData($item);
1357ec9f11bSAnna Dabrowska            }
1367ec9f11bSAnna Dabrowska        }
1377ec9f11bSAnna Dabrowska
1387ec9f11bSAnna Dabrowska        return $found;
1397ec9f11bSAnna Dabrowska    }
1407ec9f11bSAnna Dabrowska
1417ec9f11bSAnna Dabrowska    /**
1427e31aba7SAndreas Gohr     * @param string $def maintainer definition as given in the syntax
1437e31aba7SAndreas Gohr     * @return string[] list of email addresses to inform
1447e31aba7SAndreas Gohr     */
1457e31aba7SAndreas Gohr    public function getMaintainerMails($def)
1467e31aba7SAndreas Gohr    {
147ba165cbdSAnna Dabrowska        /* @var \dokuwiki\Extension\AuthPlugin $auth */
1487e31aba7SAndreas Gohr        global $auth;
149*92c202adSannda        $auth ??= auth_setup();
1507e31aba7SAndreas Gohr        if (!$auth) return [];
1517e31aba7SAndreas Gohr
1527e31aba7SAndreas Gohr        $data = $this->getMaintainers($def);
1537e31aba7SAndreas Gohr        $mails = [];
1547e31aba7SAndreas Gohr        foreach ($data as $name => $info) {
1557e31aba7SAndreas Gohr            if (is_array($info)) {
1567e31aba7SAndreas Gohr                $mails[] = $info['mail'];
1577e31aba7SAndreas Gohr            } elseif ($name[0] === '@' && $auth->canDo('getUsers')) {
158*92c202adSannda                $members = $auth->retrieveUsers(0, 0, ['grps' => ltrim($name, '@')]);
1597e31aba7SAndreas Gohr                foreach ($members as $user) {
1607e31aba7SAndreas Gohr                    $mails[] = $user['mail'];
1617e31aba7SAndreas Gohr                }
1627e31aba7SAndreas Gohr            }
1637e31aba7SAndreas Gohr        }
1647e31aba7SAndreas Gohr
1657e31aba7SAndreas Gohr        return array_values(array_unique($mails));
1667e31aba7SAndreas Gohr    }
1677e31aba7SAndreas Gohr
1687e31aba7SAndreas Gohr    /**
1697ec9f11bSAnna Dabrowska     * @param string $user
1707ec9f11bSAnna Dabrowska     * @param string $def
1717ec9f11bSAnna Dabrowska     * @return bool
1727ec9f11bSAnna Dabrowska     */
1737ec9f11bSAnna Dabrowska    public function isMaintainer($user, $def)
1747ec9f11bSAnna Dabrowska    {
1757ec9f11bSAnna Dabrowska        /* @var DokuWiki_Auth_Plugin $auth */
1767ec9f11bSAnna Dabrowska        global $auth;
177208265aeSAndreas Gohr        if ($auth === null) return false;
17830e745d9SAndreas Gohr        if ($user === '') return false;
1797ec9f11bSAnna Dabrowska        $userData = $auth->getUserData($user);
1807ec9f11bSAnna Dabrowska
1817ec9f11bSAnna Dabrowska        $all = explode(',', $def);
1827ec9f11bSAnna Dabrowska        foreach ($all as $item) {
1837ec9f11bSAnna Dabrowska            $item = trim($item);
1847ec9f11bSAnna Dabrowska            if (strpos($item, '@') !== false && in_array(ltrim($item, '@'), $userData['grps'])) {
1857ec9f11bSAnna Dabrowska                return true;
1867ec9f11bSAnna Dabrowska            } elseif ($item === $user) {
1877ec9f11bSAnna Dabrowska                return true;
1887ec9f11bSAnna Dabrowska            }
1897ec9f11bSAnna Dabrowska        }
1907ec9f11bSAnna Dabrowska
1917ec9f11bSAnna Dabrowska        return false;
1927ec9f11bSAnna Dabrowska    }
1937ec9f11bSAnna Dabrowska
1947ec9f11bSAnna Dabrowska    /**
195ba165cbdSAnna Dabrowska     * Inform all maintainers that the page needs checking
196ba165cbdSAnna Dabrowska     *
197ba165cbdSAnna Dabrowska     * @param string $def defined maintainers
198ba165cbdSAnna Dabrowska     * @param string $page that needs checking
199ba165cbdSAnna Dabrowska     */
200ba165cbdSAnna Dabrowska    public function informMaintainer($def, $page)
201ba165cbdSAnna Dabrowska    {
202ba165cbdSAnna Dabrowska        $mails = $this->getMaintainerMails($def);
203ba165cbdSAnna Dabrowska        foreach ($mails as $mail) {
204ba165cbdSAnna Dabrowska            $this->sendMail($mail, $page);
205ba165cbdSAnna Dabrowska        }
206ba165cbdSAnna Dabrowska    }
207ba165cbdSAnna Dabrowska
208ba165cbdSAnna Dabrowska    /**
209ba165cbdSAnna Dabrowska     * Sends an email
210ba165cbdSAnna Dabrowska     *
211ba165cbdSAnna Dabrowska     * @param array $mail
212ba165cbdSAnna Dabrowska     * @param string $page
213ba165cbdSAnna Dabrowska     */
214ba165cbdSAnna Dabrowska    protected function sendMail($mail, $page)
215ba165cbdSAnna Dabrowska    {
216ba165cbdSAnna Dabrowska        $mailer = new Mailer();
217ba165cbdSAnna Dabrowska        $mailer->to($mail);
218ba165cbdSAnna Dabrowska        $mailer->subject($this->getLang('mail subject'));
219*92c202adSannda
220ba165cbdSAnna Dabrowska        $text = sprintf($this->getLang('mail body'), $page);
221ba165cbdSAnna Dabrowska        $link = '<a href="' . wl($page, '', true) . '">' . $page . '</a>';
222ba165cbdSAnna Dabrowska        $html = sprintf($this->getLang('mail body'), $link);
223ba165cbdSAnna Dabrowska        $mailer->setBody($text, null, null, $html);
224ba165cbdSAnna Dabrowska
225ba165cbdSAnna Dabrowska        if (!$mailer->send()) {
226ba165cbdSAnna Dabrowska            msg($this->getLang('error mail'), -1);
227ba165cbdSAnna Dabrowska        }
228ba165cbdSAnna Dabrowska    }
229ba165cbdSAnna Dabrowska
230ba165cbdSAnna Dabrowska    /**
2317ec9f11bSAnna Dabrowska     * Puts users and groups into a flat array; useful for simple string output
2327ec9f11bSAnna Dabrowska     * @param array $all
2337ec9f11bSAnna Dabrowska     * @return array
2347ec9f11bSAnna Dabrowska     */
2357ec9f11bSAnna Dabrowska    protected function flattenMaintainers($all)
2367ec9f11bSAnna Dabrowska    {
2377ec9f11bSAnna Dabrowska        if (empty($all['users'])) {
2387ec9f11bSAnna Dabrowska            return $all;
2397ec9f11bSAnna Dabrowska        }
2407ec9f11bSAnna Dabrowska
241*92c202adSannda        $users = array_map(static fn($user) => $user['name'], $all['users']);
2427ec9f11bSAnna Dabrowska
2437ec9f11bSAnna Dabrowska        return array_merge($users, $all['groups']);
2447ec9f11bSAnna Dabrowska    }
2452ca9f4fbSAnna Dabrowska
2462ca9f4fbSAnna Dabrowska    /**
2472ca9f4fbSAnna Dabrowska     * Expands groups into users; useful for email notification
2482ca9f4fbSAnna Dabrowska     *
2492ca9f4fbSAnna Dabrowska     * @param array $all
2502ca9f4fbSAnna Dabrowska     * @return array
2512ca9f4fbSAnna Dabrowska     */
2522ca9f4fbSAnna Dabrowska    protected function expandMaintainers($all)
2532ca9f4fbSAnna Dabrowska    {
2542ca9f4fbSAnna Dabrowska        if (empty($all['groups'])) {
2552ca9f4fbSAnna Dabrowska            return $all;
2562ca9f4fbSAnna Dabrowska        }
2572ca9f4fbSAnna Dabrowska
2582ca9f4fbSAnna Dabrowska        /* @var DokuWiki_Auth_Plugin $auth */
2592ca9f4fbSAnna Dabrowska        global $auth;
260208265aeSAndreas Gohr        if ($auth === null) return [];
2612ca9f4fbSAnna Dabrowska
262*92c202adSannda        $members = [];
2632ca9f4fbSAnna Dabrowska        foreach ($all['groups'] as $group) {
264*92c202adSannda            $members = array_merge($members, $auth->retrieveUsers(0, 0, ['grps' => ltrim($group, '@')]));
2652ca9f4fbSAnna Dabrowska        }
2662ca9f4fbSAnna Dabrowska
2672ca9f4fbSAnna Dabrowska        // merge eliminates any duplicates since we use string keys
2682ca9f4fbSAnna Dabrowska        return array_merge($all['users'], $members);
2692ca9f4fbSAnna Dabrowska    }
2704048d1eeSSzymon Olewniczak}
271