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