xref: /plugin/structnotification/action/notification.php (revision a3d2920411ed6fdc34fba3a33b94a3ee70450482)
1a1f4c7baSSzymon Olewniczak<?php
222ceb3ecSAnna Dabrowska
3a1f4c7baSSzymon Olewniczak/**
4a1f4c7baSSzymon Olewniczak * DokuWiki Plugin structnotification (Action Component)
5a1f4c7baSSzymon Olewniczak *
6a1f4c7baSSzymon Olewniczak * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
7a1f4c7baSSzymon Olewniczak * @author  Szymon Olewniczak <it@rid.pl>
8a1f4c7baSSzymon Olewniczak */
9a1f4c7baSSzymon Olewniczak
1022ceb3ecSAnna Dabrowskause dokuwiki\Extension\ActionPlugin;
1122ceb3ecSAnna Dabrowskause dokuwiki\Extension\EventHandler;
1222ceb3ecSAnna Dabrowskause dokuwiki\Extension\Event;
1322ceb3ecSAnna Dabrowskause dokuwiki\plugin\structgroup\types\Group;
14922aade7SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\Search;
15922aade7SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\Value;
16922aade7SSzymon Olewniczak
1722ceb3ecSAnna Dabrowskaclass action_plugin_structnotification_notification extends ActionPlugin
18a1f4c7baSSzymon Olewniczak{
19a1f4c7baSSzymon Olewniczak    /**
20a1f4c7baSSzymon Olewniczak     * Registers a callback function for a given event
21a1f4c7baSSzymon Olewniczak     *
2222ceb3ecSAnna Dabrowska     * @param EventHandler $controller DokuWiki's event controller object
23a1f4c7baSSzymon Olewniczak     *
24a1f4c7baSSzymon Olewniczak     * @return void
25a1f4c7baSSzymon Olewniczak     */
2622ceb3ecSAnna Dabrowska    public function register(EventHandler $controller)
27a1f4c7baSSzymon Olewniczak    {
2822ceb3ecSAnna Dabrowska        $controller->register_hook('PLUGIN_NOTIFICATION_REGISTER_SOURCE', 'AFTER', $this, 'addNotificationsSource');
2922ceb3ecSAnna Dabrowska        $controller->register_hook('PLUGIN_NOTIFICATION_GATHER', 'AFTER', $this, 'addNotifications');
3022ceb3ecSAnna Dabrowska        $controller->register_hook(
3122ceb3ecSAnna Dabrowska            'PLUGIN_NOTIFICATION_CACHE_DEPENDENCIES',
3222ceb3ecSAnna Dabrowska            'AFTER',
3322ceb3ecSAnna Dabrowska            $this,
3422ceb3ecSAnna Dabrowska            'addNotificationCacheDependencies'
3522ceb3ecSAnna Dabrowska        );
36922aade7SSzymon Olewniczak    }
37922aade7SSzymon Olewniczak
3822ceb3ecSAnna Dabrowska    public function addNotificationsSource(Event $event)
39922aade7SSzymon Olewniczak    {
40922aade7SSzymon Olewniczak        $event->data[] = 'structnotification';
41922aade7SSzymon Olewniczak    }
42922aade7SSzymon Olewniczak
4322ceb3ecSAnna Dabrowska    public function addNotificationCacheDependencies(Event $event)
44922aade7SSzymon Olewniczak    {
45922aade7SSzymon Olewniczak        if (!in_array('structnotification', $event->data['plugins'])) return;
46922aade7SSzymon Olewniczak
47922aade7SSzymon Olewniczak        try {
48922aade7SSzymon Olewniczak            /** @var \helper_plugin_structnotification_db $db_helper */
49922aade7SSzymon Olewniczak            $db_helper = plugin_load('helper', 'structnotification_db');
50922aade7SSzymon Olewniczak            $sqlite = $db_helper->getDB();
51922aade7SSzymon Olewniczak            $event->data['dependencies'][] = $sqlite->getAdapter()->getDbFile();
52922aade7SSzymon Olewniczak        } catch (Exception $e) {
53922aade7SSzymon Olewniczak            msg($e->getMessage(), -1);
54922aade7SSzymon Olewniczak            return;
55922aade7SSzymon Olewniczak        }
56922aade7SSzymon Olewniczak    }
57922aade7SSzymon Olewniczak
58922aade7SSzymon Olewniczak    protected function getValueByLabel($values, $label)
59922aade7SSzymon Olewniczak    {
60922aade7SSzymon Olewniczak        /* @var Value $value */
61922aade7SSzymon Olewniczak        foreach ($values as $value) {
62922aade7SSzymon Olewniczak            $colLabel = $value->getColumn()->getLabel();
63922aade7SSzymon Olewniczak            if ($colLabel == $label) {
64922aade7SSzymon Olewniczak                return $value->getRawValue();
65922aade7SSzymon Olewniczak            }
66922aade7SSzymon Olewniczak        }
67922aade7SSzymon Olewniczak        //nothing found
68922aade7SSzymon Olewniczak        throw new Exception("column: $label not found in values");
69922aade7SSzymon Olewniczak    }
70922aade7SSzymon Olewniczak
7122ceb3ecSAnna Dabrowska    public function addNotifications(Event $event)
72922aade7SSzymon Olewniczak    {
73922aade7SSzymon Olewniczak        if (!in_array('structnotification', $event->data['plugins'])) return;
74922aade7SSzymon Olewniczak
75922aade7SSzymon Olewniczak        try {
76922aade7SSzymon Olewniczak            /** @var \helper_plugin_structnotification_db$db_helper */
77922aade7SSzymon Olewniczak            $db_helper = plugin_load('helper', 'structnotification_db');
78922aade7SSzymon Olewniczak            $sqlite = $db_helper->getDB();
79922aade7SSzymon Olewniczak        } catch (Exception $e) {
80922aade7SSzymon Olewniczak            msg($e->getMessage(), -1);
81922aade7SSzymon Olewniczak            return;
82922aade7SSzymon Olewniczak        }
83922aade7SSzymon Olewniczak
84922aade7SSzymon Olewniczak        $user = $event->data['user'];
85922aade7SSzymon Olewniczak
86922aade7SSzymon Olewniczak        $q = 'SELECT * FROM predicate';
87922aade7SSzymon Olewniczak        $res = $sqlite->query($q);
88922aade7SSzymon Olewniczak
89922aade7SSzymon Olewniczak        $predicates = $sqlite->res2arr($res);
90922aade7SSzymon Olewniczak
91922aade7SSzymon Olewniczak        foreach ($predicates as $predicate) {
92922aade7SSzymon Olewniczak            $schema = $predicate['schema'];
93922aade7SSzymon Olewniczak            $field = $predicate['field'];
94922aade7SSzymon Olewniczak            $operator = $predicate['operator'];
954677fc3cSSzymon Olewniczak            $value = $predicate['value'];
96e96b00ceSAnna Dabrowska            $filters = $predicate['filters'];
97922aade7SSzymon Olewniczak            $users_and_groups = $predicate['users_and_groups'];
98922aade7SSzymon Olewniczak            $message = $predicate['message'];
99922aade7SSzymon Olewniczak
100922aade7SSzymon Olewniczak            try {
101922aade7SSzymon Olewniczak                $search = new Search();
1024677fc3cSSzymon Olewniczak                foreach (explode(',', $schema) as $table) {
1034677fc3cSSzymon Olewniczak                    $search->addSchema($table);
1044677fc3cSSzymon Olewniczak                    $search->addColumn($table . '.*');
1054677fc3cSSzymon Olewniczak                }
106115044e5SSzymon Olewniczak                // add special columns
107115044e5SSzymon Olewniczak                $special_columns = ['%pageid%', '%title%', '%lastupdate%', '%lasteditor%', '%lastsummary%', '%rowid%'];
108115044e5SSzymon Olewniczak                foreach ($special_columns as $special_column) {
109115044e5SSzymon Olewniczak                    $search->addColumn($special_column);
110115044e5SSzymon Olewniczak                }
111e96b00ceSAnna Dabrowska                $this->addFiltersToSearch($search, $filters);
112*a3d29204SAnna Dabrowska
113*a3d29204SAnna Dabrowska                // temporary workaround for GETACCESSLEVEL check in struct queries in closed wikis:
114*a3d29204SAnna Dabrowska                // cli user has undefined permissions, so we disable ACLs to get any results at all
115*a3d29204SAnna Dabrowska                if (PHP_SAPI === 'cli') {
116*a3d29204SAnna Dabrowska                    global $conf;
117*a3d29204SAnna Dabrowska                    $origACL = $conf['useacl'];
118*a3d29204SAnna Dabrowska                    $conf['useacl'] = false;
119*a3d29204SAnna Dabrowska                }
120*a3d29204SAnna Dabrowska
1214742b234SAnna Dabrowska                $result = $search->getRows();
122922aade7SSzymon Olewniczak                $result_pids = $search->getPids();
123*a3d29204SAnna Dabrowska
124*a3d29204SAnna Dabrowska                // restore ACL setting
125*a3d29204SAnna Dabrowska                if (PHP_SAPI === 'cli') {
126*a3d29204SAnna Dabrowska                    $conf['useacl'] = $origACL;
127*a3d29204SAnna Dabrowska                }
128*a3d29204SAnna Dabrowska
12922ceb3ecSAnna Dabrowska                /* @var Value[] $row */
13022ceb3ecSAnna Dabrowska                $counter = count($result);
131922aade7SSzymon Olewniczak
132922aade7SSzymon Olewniczak                /* @var Value[] $row */
13322ceb3ecSAnna Dabrowska                for ($i = 0; $i < $counter; $i++) {
134922aade7SSzymon Olewniczak                    $values = $result[$i];
135922aade7SSzymon Olewniczak                    $pid = $result_pids[$i];
1362c65b673SSzymon Olewniczak
13722ceb3ecSAnna Dabrowska                    $users_set = $this->usersSet($users_and_groups, $values);
1382c65b673SSzymon Olewniczak                    if (!isset($users_set[$user])) continue;
1392c65b673SSzymon Olewniczak
140922aade7SSzymon Olewniczak                    $rawDate = $this->getValueByLabel($values, $field);
1414677fc3cSSzymon Olewniczak                    if ($this->predicateTrue($rawDate, $operator, $value)) {
142c0d6a71fSSzymon Olewniczak                        $message_with_replacements = $this->replacePlaceholders($message, $values);
14322ceb3ecSAnna Dabrowska                        $message_with_replacements_html = p_render(
14422ceb3ecSAnna Dabrowska                            'xhtml',
14522ceb3ecSAnna Dabrowska                            p_get_instructions($message_with_replacements),
14622ceb3ecSAnna Dabrowska                            $info
14722ceb3ecSAnna Dabrowska                        );
148922aade7SSzymon Olewniczak                        $event->data['notifications'][] = [
149922aade7SSzymon Olewniczak                            'plugin' => 'structnotification',
150922aade7SSzymon Olewniczak                            'id' => $predicate['id'] . ':' . $schema . ':' . $pid . ':'  . $rawDate,
151c0d6a71fSSzymon Olewniczak                            'full' => $message_with_replacements_html,
152c0d6a71fSSzymon Olewniczak                            'brief' => $message_with_replacements_html,
153922aade7SSzymon Olewniczak                            'timestamp' => (int) strtotime($rawDate)
154922aade7SSzymon Olewniczak                        ];
155922aade7SSzymon Olewniczak                    }
156922aade7SSzymon Olewniczak                }
157922aade7SSzymon Olewniczak            } catch (Exception $e) {
158922aade7SSzymon Olewniczak                msg($e->getMessage(), -1);
159922aade7SSzymon Olewniczak                return;
160922aade7SSzymon Olewniczak            }
161922aade7SSzymon Olewniczak        }
162a1f4c7baSSzymon Olewniczak    }
163a1f4c7baSSzymon Olewniczak
164a1f4c7baSSzymon Olewniczak    /**
165922aade7SSzymon Olewniczak     * @return array
166a1f4c7baSSzymon Olewniczak     */
16722ceb3ecSAnna Dabrowska    protected function usersSet($user_and_groups, $values)
16822ceb3ecSAnna Dabrowska    {
169922aade7SSzymon Olewniczak        /** @var DokuWiki_Auth_Plugin $auth */
170922aade7SSzymon Olewniczak        global $auth;
171922aade7SSzymon Olewniczak
172cfa4f911SAnna Dabrowska        // $auth is missing in CLI context
173cfa4f911SAnna Dabrowska        if (is_null($auth)) {
174cfa4f911SAnna Dabrowska            auth_setup();
175cfa4f911SAnna Dabrowska        }
176cfa4f911SAnna Dabrowska
1772c65b673SSzymon Olewniczak        //make substitutions
1782c65b673SSzymon Olewniczak        $user_and_groups = preg_replace_callback(
1792c65b673SSzymon Olewniczak            '/@@(.*?)@@/',
1802c65b673SSzymon Olewniczak            function ($matches) use ($values) {
18122ceb3ecSAnna Dabrowska                [$schema, $field] = explode('.', trim($matches[1]));
1822c65b673SSzymon Olewniczak                if (!$field) return '';
1832c65b673SSzymon Olewniczak                /* @var Value $value */
1842c65b673SSzymon Olewniczak                foreach ($values as $value) {
18586ba6958SSzymon Olewniczak                    $column = $value->getColumn();
18686ba6958SSzymon Olewniczak                    $colLabel = $column->getLabel();
18786ba6958SSzymon Olewniczak                    $type = $column->getType();
1882c65b673SSzymon Olewniczak                    if ($colLabel == $field) {
18922ceb3ecSAnna Dabrowska                        if (
19022ceb3ecSAnna Dabrowska                            class_exists('\dokuwiki\plugin\structgroup\types\Group') &&
19122ceb3ecSAnna Dabrowska                            $type instanceof Group
19222ceb3ecSAnna Dabrowska                        ) {
19386ba6958SSzymon Olewniczak                            if ($column->isMulti()) {
19422ceb3ecSAnna Dabrowska                                return implode(
19522ceb3ecSAnna Dabrowska                                    ',',
19622ceb3ecSAnna Dabrowska                                    array_map(static fn($rawValue) => '@' . $rawValue, $value->getRawValue())
19722ceb3ecSAnna Dabrowska                                );
19886ba6958SSzymon Olewniczak                            } else {
1992c65b673SSzymon Olewniczak                                return '@' . $value->getRawValue();
2002c65b673SSzymon Olewniczak                            }
20186ba6958SSzymon Olewniczak                        }
20286ba6958SSzymon Olewniczak                        if ($column->isMulti()) {
20386ba6958SSzymon Olewniczak                            return implode(',', $value->getRawValue());
20486ba6958SSzymon Olewniczak                        } else {
2052c65b673SSzymon Olewniczak                            return $value->getRawValue();
2062c65b673SSzymon Olewniczak                        }
2072c65b673SSzymon Olewniczak                    }
20886ba6958SSzymon Olewniczak                }
2092c65b673SSzymon Olewniczak                return '';
2102c65b673SSzymon Olewniczak            },
2112c65b673SSzymon Olewniczak            $user_and_groups
2122c65b673SSzymon Olewniczak        );
2132c65b673SSzymon Olewniczak
214922aade7SSzymon Olewniczak        $user_and_groups_set = array_map('trim', explode(',', $user_and_groups));
215922aade7SSzymon Olewniczak        $users = [];
216922aade7SSzymon Olewniczak        $groups = [];
217922aade7SSzymon Olewniczak        foreach ($user_and_groups_set as $user_or_group) {
218922aade7SSzymon Olewniczak            if ($user_or_group[0] == '@') {
219922aade7SSzymon Olewniczak                $groups[] = substr($user_or_group, 1);
220922aade7SSzymon Olewniczak            } else {
221922aade7SSzymon Olewniczak                $users[] = $user_or_group;
222a1f4c7baSSzymon Olewniczak            }
223a1f4c7baSSzymon Olewniczak        }
224922aade7SSzymon Olewniczak        $set = [];
225922aade7SSzymon Olewniczak
226922aade7SSzymon Olewniczak        $all_users = $auth->retrieveUsers();
227922aade7SSzymon Olewniczak        foreach ($all_users as $user => $info) {
228922aade7SSzymon Olewniczak            if (in_array($user, $users)) {
229922aade7SSzymon Olewniczak                $set[$user] = $info;
230922aade7SSzymon Olewniczak            } elseif (array_intersect($groups, $info['grps'])) {
231922aade7SSzymon Olewniczak                $set[$user] = $info;
232922aade7SSzymon Olewniczak            }
233922aade7SSzymon Olewniczak        }
234922aade7SSzymon Olewniczak
235922aade7SSzymon Olewniczak        return $set;
236922aade7SSzymon Olewniczak    }
237922aade7SSzymon Olewniczak
23822ceb3ecSAnna Dabrowska    protected function predicateTrue($date, $operator, $value)
23922ceb3ecSAnna Dabrowska    {
240922aade7SSzymon Olewniczak        $date = date('Y-m-d', strtotime($date));
241922aade7SSzymon Olewniczak
242922aade7SSzymon Olewniczak        switch ($operator) {
243922aade7SSzymon Olewniczak            case 'before':
2444677fc3cSSzymon Olewniczak                $days = date('Y-m-d', strtotime("+$value days"));
245922aade7SSzymon Olewniczak                return $days >= $date;
246922aade7SSzymon Olewniczak            case 'after':
2474677fc3cSSzymon Olewniczak                $days = date('Y-m-d', strtotime("-$value days"));
248922aade7SSzymon Olewniczak                return $date <= $days;
2494677fc3cSSzymon Olewniczak            case 'at':
2504677fc3cSSzymon Olewniczak                $now = new DateTime();
2514677fc3cSSzymon Olewniczak                $at = new DateTime(date($value, strtotime($date)));
2524677fc3cSSzymon Olewniczak                return $now >= $at;
253922aade7SSzymon Olewniczak            default:
254922aade7SSzymon Olewniczak                return false;
255922aade7SSzymon Olewniczak        }
256a1f4c7baSSzymon Olewniczak    }
257a1f4c7baSSzymon Olewniczak
25822ceb3ecSAnna Dabrowska    protected function replacePlaceholders($message, $values)
25922ceb3ecSAnna Dabrowska    {
260ac63f65fSSzymon Olewniczak        $patterns = [];
261ac63f65fSSzymon Olewniczak        $replacements = [];
262ac63f65fSSzymon Olewniczak        /* @var Value $value */
263ac63f65fSSzymon Olewniczak        foreach ($values as $value) {
264ac63f65fSSzymon Olewniczak            $schema = $value->getColumn()->getTable();
265ac63f65fSSzymon Olewniczak            $label = $value->getColumn()->getLabel();
266ac63f65fSSzymon Olewniczak            $patterns[] = "/@@$schema.$label@@/";
267ac63f65fSSzymon Olewniczak            $replacements[] = $value->getDisplayValue();
268ac63f65fSSzymon Olewniczak        }
269ac63f65fSSzymon Olewniczak
270ac63f65fSSzymon Olewniczak        return preg_replace($patterns, $replacements, $message);
271ac63f65fSSzymon Olewniczak    }
272ac63f65fSSzymon Olewniczak
273e96b00ceSAnna Dabrowska    /**
274e96b00ceSAnna Dabrowska     * @param Search $search
275e96b00ceSAnna Dabrowska     * @param string $filters
276e96b00ceSAnna Dabrowska     */
277e96b00ceSAnna Dabrowska    protected function addFiltersToSearch(&$search, $filters)
278e96b00ceSAnna Dabrowska    {
279e96b00ceSAnna Dabrowska        if (!$filters) return;
280a1f4c7baSSzymon Olewniczak
281e96b00ceSAnna Dabrowska        /** @var \helper_plugin_struct_config $confHelper */
282e96b00ceSAnna Dabrowska        $confHelper = plugin_load('helper', 'struct_config');
283e96b00ceSAnna Dabrowska
284e96b00ceSAnna Dabrowska        $filterConfigs = explode("\r\n", $filters);
285e96b00ceSAnna Dabrowska
286e96b00ceSAnna Dabrowska        foreach ($filterConfigs as $config) {
28722ceb3ecSAnna Dabrowska            [$colname, $comp, $value, ] = $confHelper->parseFilterLine('AND', $config);
288e96b00ceSAnna Dabrowska            $search->addFilter($colname, $value, $comp, 'AND');
289e96b00ceSAnna Dabrowska        }
290e96b00ceSAnna Dabrowska    }
291e96b00ceSAnna Dabrowska}
292