xref: /plugin/structnotification/action/notification.php (revision cfa4f911d6ee042544ed1fbd20489de13d42df19)
1<?php
2/**
3 * DokuWiki Plugin structnotification (Action Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Szymon Olewniczak <it@rid.pl>
7 */
8
9use dokuwiki\plugin\struct\meta\Search;
10use dokuwiki\plugin\struct\meta\Value;
11
12class action_plugin_structnotification_notification extends DokuWiki_Action_Plugin
13{
14
15    /**
16     * Registers a callback function for a given event
17     *
18     * @param Doku_Event_Handler $controller DokuWiki's event controller object
19     *
20     * @return void
21     */
22    public function register(Doku_Event_Handler $controller)
23    {
24        $controller->register_hook('PLUGIN_NOTIFICATION_REGISTER_SOURCE', 'AFTER', $this, 'add_notifications_source');
25        $controller->register_hook('PLUGIN_NOTIFICATION_GATHER', 'AFTER', $this, 'add_notifications');
26        $controller->register_hook('PLUGIN_NOTIFICATION_CACHE_DEPENDENCIES', 'AFTER', $this, 'add_notification_cache_dependencies');
27    }
28
29    public function add_notifications_source(Doku_Event $event)
30    {
31        $event->data[] = 'structnotification';
32    }
33
34    public function add_notification_cache_dependencies(Doku_Event $event)
35    {
36        if (!in_array('structnotification', $event->data['plugins'])) return;
37
38        try {
39            /** @var \helper_plugin_structnotification_db $db_helper */
40            $db_helper = plugin_load('helper', 'structnotification_db');
41            $sqlite = $db_helper->getDB();
42            $event->data['dependencies'][] = $sqlite->getAdapter()->getDbFile();
43        } catch (Exception $e) {
44            msg($e->getMessage(), -1);
45            return;
46        }
47    }
48
49    protected function getValueByLabel($values, $label)
50    {
51        /* @var Value $value */
52        foreach ($values as $value) {
53            $colLabel = $value->getColumn()->getLabel();
54            if ($colLabel == $label) {
55                return $value->getRawValue();
56            }
57        }
58        //nothing found
59        throw new Exception("column: $label not found in values");
60    }
61
62    public function add_notifications(Doku_Event $event)
63    {
64        if (!in_array('structnotification', $event->data['plugins'])) return;
65
66        try {
67            /** @var \helper_plugin_structnotification_db$db_helper */
68            $db_helper = plugin_load('helper', 'structnotification_db');
69            $sqlite = $db_helper->getDB();
70        } catch (Exception $e) {
71            msg($e->getMessage(), -1);
72            return;
73        }
74
75        $user = $event->data['user'];
76
77        $q = 'SELECT * FROM predicate';
78        $res = $sqlite->query($q);
79
80        $predicates = $sqlite->res2arr($res);
81
82        foreach ($predicates as $predicate) {
83            $schema = $predicate['schema'];
84            $field = $predicate['field'];
85            $operator = $predicate['operator'];
86            $value = $predicate['value'];
87            $filters = $predicate['filters'];
88            $users_and_groups = $predicate['users_and_groups'];
89            $message = $predicate['message'];
90
91             try {
92                $search = new Search();
93                foreach (explode(',', $schema) as $table) {
94                    $search->addSchema($table);
95                    $search->addColumn($table . '.*');
96                }
97                // add special columns
98                $special_columns = ['%pageid%', '%title%', '%lastupdate%', '%lasteditor%', '%lastsummary%', '%rowid%'];
99                foreach ($special_columns as $special_column) {
100                    $search->addColumn($special_column);
101                }
102                $this->addFiltersToSearch($search, $filters);
103                $result = $search->execute();
104                $result_pids = $search->getPids();
105
106                 /* @var Value[] $row */
107                for ($i = 0; $i < count($result); $i++) {
108                    $values = $result[$i];
109                    $pid = $result_pids[$i];
110
111                    $users_set = $this->users_set($users_and_groups, $values);
112                    if (!isset($users_set[$user])) continue;
113
114                    $rawDate = $this->getValueByLabel($values, $field);
115                    if ($this->predicateTrue($rawDate, $operator, $value)) {
116                        $message_with_replacements = $this->replacePlaceholders($message, $values);
117                        $message_with_replacements_html = p_render('xhtml',
118                            p_get_instructions($message_with_replacements), $info);
119                        $event->data['notifications'][] = [
120                            'plugin' => 'structnotification',
121                            'id' => $predicate['id'] . ':'. $schema . ':' . $pid . ':'  . $rawDate,
122                            'full' => $message_with_replacements_html,
123                            'brief' => $message_with_replacements_html,
124                            'timestamp' => (int) strtotime($rawDate)
125                        ];
126                    }
127                }
128            } catch (Exception $e) {
129                msg($e->getMessage(), -1);
130                return;
131            }
132        }
133    }
134
135    /**
136     * @return array
137     */
138    protected function users_set($user_and_groups, $values) {
139        /** @var DokuWiki_Auth_Plugin $auth */
140        global $auth;
141
142        // $auth is missing in CLI context
143        if (is_null($auth)) {
144            auth_setup();
145        }
146
147        //make substitutions
148        $user_and_groups = preg_replace_callback(
149            '/@@(.*?)@@/',
150            function ($matches) use ($values) {
151                list($schema, $field) = explode('.', trim($matches[1]));
152                if (!$field) return '';
153                /* @var Value $value */
154                foreach ($values as $value) {
155                    $column = $value->getColumn();
156                    $colLabel = $column->getLabel();
157                    $type = $column->getType();
158                    if ($colLabel == $field) {
159                        if (class_exists('\dokuwiki\plugin\structgroup\types\Group') &&
160                            $type instanceof \dokuwiki\plugin\structgroup\types\Group) {
161                            if ($column->isMulti()) {
162                                return implode(',', array_map(function ($rawValue) {
163                                    return '@' . $rawValue;
164                                }, $value->getRawValue()));
165                            } else {
166                                return '@' . $value->getRawValue();
167                            }
168                        }
169                        if ($column->isMulti()) {
170                            return implode(',', $value->getRawValue());
171                        } else {
172                            return $value->getRawValue();
173                        }
174                    }
175                }
176                return '';
177            },
178            $user_and_groups
179        );
180
181        $user_and_groups_set = array_map('trim', explode(',', $user_and_groups));
182        $users = [];
183        $groups = [];
184        foreach ($user_and_groups_set as $user_or_group) {
185            if ($user_or_group[0] == '@') {
186                $groups[] = substr($user_or_group, 1);
187            } else {
188                $users[] = $user_or_group;
189            }
190        }
191        $set = [];
192
193        $all_users = $auth->retrieveUsers();
194        foreach ($all_users as $user => $info) {
195            if (in_array($user, $users)) {
196                $set[$user] = $info;
197            } elseif (array_intersect($groups, $info['grps'])) {
198                $set[$user] = $info;
199            }
200        }
201
202        return $set;
203    }
204
205    protected function predicateTrue($date, $operator, $value) {
206        $date = date('Y-m-d', strtotime($date));
207
208        switch ($operator) {
209            case 'before':
210                $days = date('Y-m-d', strtotime("+$value days"));
211                return $days >= $date;
212            case 'after':
213                $days = date('Y-m-d', strtotime("-$value days"));
214                return $date <= $days;
215            case 'at':
216                $now = new DateTime();
217                $at = new DateTime(date($value, strtotime($date)));
218                return $now >= $at;
219            default:
220                return false;
221        }
222    }
223
224    protected function replacePlaceholders($message, $values) {
225        $patterns = [];
226        $replacements = [];
227        /* @var Value $value */
228        foreach ($values as $value) {
229            $schema = $value->getColumn()->getTable();
230            $label = $value->getColumn()->getLabel();
231            $patterns[] = "/@@$schema.$label@@/";
232            $replacements[] = $value->getDisplayValue();
233        }
234
235        return preg_replace($patterns, $replacements, $message);
236    }
237
238    /**
239     * @param Search $search
240     * @param string $filters
241     */
242    protected function addFiltersToSearch(&$search, $filters)
243    {
244        if (!$filters) return;
245
246        /** @var \helper_plugin_struct_config $confHelper */
247        $confHelper = plugin_load('helper', 'struct_config');
248
249        $filterConfigs = explode("\r\n", $filters);
250
251        foreach ($filterConfigs as $config) {
252            list($colname, $comp, $value,) = $confHelper->parseFilterLine('AND', $config);
253            $search->addFilter($colname, $value, $comp, 'AND');
254        }
255    }
256}
257