1a1f4c7baSSzymon Olewniczak<?php 2*22ceb3ecSAnna 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 10*22ceb3ecSAnna Dabrowskause dokuwiki\Extension\ActionPlugin; 11*22ceb3ecSAnna Dabrowskause dokuwiki\Extension\EventHandler; 12*22ceb3ecSAnna Dabrowskause dokuwiki\Extension\Event; 13*22ceb3ecSAnna Dabrowskause dokuwiki\plugin\structgroup\types\Group; 14922aade7SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\Search; 15922aade7SSzymon Olewniczakuse dokuwiki\plugin\struct\meta\Value; 16922aade7SSzymon Olewniczak 17*22ceb3ecSAnna Dabrowskaclass action_plugin_structnotification_notification extends ActionPlugin 18a1f4c7baSSzymon Olewniczak{ 19a1f4c7baSSzymon Olewniczak /** 20a1f4c7baSSzymon Olewniczak * Registers a callback function for a given event 21a1f4c7baSSzymon Olewniczak * 22*22ceb3ecSAnna Dabrowska * @param EventHandler $controller DokuWiki's event controller object 23a1f4c7baSSzymon Olewniczak * 24a1f4c7baSSzymon Olewniczak * @return void 25a1f4c7baSSzymon Olewniczak */ 26*22ceb3ecSAnna Dabrowska public function register(EventHandler $controller) 27a1f4c7baSSzymon Olewniczak { 28*22ceb3ecSAnna Dabrowska $controller->register_hook('PLUGIN_NOTIFICATION_REGISTER_SOURCE', 'AFTER', $this, 'addNotificationsSource'); 29*22ceb3ecSAnna Dabrowska $controller->register_hook('PLUGIN_NOTIFICATION_GATHER', 'AFTER', $this, 'addNotifications'); 30*22ceb3ecSAnna Dabrowska $controller->register_hook( 31*22ceb3ecSAnna Dabrowska 'PLUGIN_NOTIFICATION_CACHE_DEPENDENCIES', 32*22ceb3ecSAnna Dabrowska 'AFTER', 33*22ceb3ecSAnna Dabrowska $this, 34*22ceb3ecSAnna Dabrowska 'addNotificationCacheDependencies' 35*22ceb3ecSAnna Dabrowska ); 36922aade7SSzymon Olewniczak } 37922aade7SSzymon Olewniczak 38*22ceb3ecSAnna Dabrowska public function addNotificationsSource(Event $event) 39922aade7SSzymon Olewniczak { 40922aade7SSzymon Olewniczak $event->data[] = 'structnotification'; 41922aade7SSzymon Olewniczak } 42922aade7SSzymon Olewniczak 43*22ceb3ecSAnna 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 71*22ceb3ecSAnna 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); 112922aade7SSzymon Olewniczak $result = $search->execute(); 113922aade7SSzymon Olewniczak $result_pids = $search->getPids(); 114*22ceb3ecSAnna Dabrowska /* @var Value[] $row */ 115*22ceb3ecSAnna Dabrowska $counter = count($result); 116922aade7SSzymon Olewniczak 117922aade7SSzymon Olewniczak /* @var Value[] $row */ 118*22ceb3ecSAnna Dabrowska for ($i = 0; $i < $counter; $i++) { 119922aade7SSzymon Olewniczak $values = $result[$i]; 120922aade7SSzymon Olewniczak $pid = $result_pids[$i]; 1212c65b673SSzymon Olewniczak 122*22ceb3ecSAnna Dabrowska $users_set = $this->usersSet($users_and_groups, $values); 1232c65b673SSzymon Olewniczak if (!isset($users_set[$user])) continue; 1242c65b673SSzymon Olewniczak 125922aade7SSzymon Olewniczak $rawDate = $this->getValueByLabel($values, $field); 1264677fc3cSSzymon Olewniczak if ($this->predicateTrue($rawDate, $operator, $value)) { 127c0d6a71fSSzymon Olewniczak $message_with_replacements = $this->replacePlaceholders($message, $values); 128*22ceb3ecSAnna Dabrowska $message_with_replacements_html = p_render( 129*22ceb3ecSAnna Dabrowska 'xhtml', 130*22ceb3ecSAnna Dabrowska p_get_instructions($message_with_replacements), 131*22ceb3ecSAnna Dabrowska $info 132*22ceb3ecSAnna Dabrowska ); 133922aade7SSzymon Olewniczak $event->data['notifications'][] = [ 134922aade7SSzymon Olewniczak 'plugin' => 'structnotification', 135922aade7SSzymon Olewniczak 'id' => $predicate['id'] . ':' . $schema . ':' . $pid . ':' . $rawDate, 136c0d6a71fSSzymon Olewniczak 'full' => $message_with_replacements_html, 137c0d6a71fSSzymon Olewniczak 'brief' => $message_with_replacements_html, 138922aade7SSzymon Olewniczak 'timestamp' => (int) strtotime($rawDate) 139922aade7SSzymon Olewniczak ]; 140922aade7SSzymon Olewniczak } 141922aade7SSzymon Olewniczak } 142922aade7SSzymon Olewniczak } catch (Exception $e) { 143922aade7SSzymon Olewniczak msg($e->getMessage(), -1); 144922aade7SSzymon Olewniczak return; 145922aade7SSzymon Olewniczak } 146922aade7SSzymon Olewniczak } 147a1f4c7baSSzymon Olewniczak } 148a1f4c7baSSzymon Olewniczak 149a1f4c7baSSzymon Olewniczak /** 150922aade7SSzymon Olewniczak * @return array 151a1f4c7baSSzymon Olewniczak */ 152*22ceb3ecSAnna Dabrowska protected function usersSet($user_and_groups, $values) 153*22ceb3ecSAnna Dabrowska { 154922aade7SSzymon Olewniczak /** @var DokuWiki_Auth_Plugin $auth */ 155922aade7SSzymon Olewniczak global $auth; 156922aade7SSzymon Olewniczak 157cfa4f911SAnna Dabrowska // $auth is missing in CLI context 158cfa4f911SAnna Dabrowska if (is_null($auth)) { 159cfa4f911SAnna Dabrowska auth_setup(); 160cfa4f911SAnna Dabrowska } 161cfa4f911SAnna Dabrowska 1622c65b673SSzymon Olewniczak //make substitutions 1632c65b673SSzymon Olewniczak $user_and_groups = preg_replace_callback( 1642c65b673SSzymon Olewniczak '/@@(.*?)@@/', 1652c65b673SSzymon Olewniczak function ($matches) use ($values) { 166*22ceb3ecSAnna Dabrowska [$schema, $field] = explode('.', trim($matches[1])); 1672c65b673SSzymon Olewniczak if (!$field) return ''; 1682c65b673SSzymon Olewniczak /* @var Value $value */ 1692c65b673SSzymon Olewniczak foreach ($values as $value) { 17086ba6958SSzymon Olewniczak $column = $value->getColumn(); 17186ba6958SSzymon Olewniczak $colLabel = $column->getLabel(); 17286ba6958SSzymon Olewniczak $type = $column->getType(); 1732c65b673SSzymon Olewniczak if ($colLabel == $field) { 174*22ceb3ecSAnna Dabrowska if ( 175*22ceb3ecSAnna Dabrowska class_exists('\dokuwiki\plugin\structgroup\types\Group') && 176*22ceb3ecSAnna Dabrowska $type instanceof Group 177*22ceb3ecSAnna Dabrowska ) { 17886ba6958SSzymon Olewniczak if ($column->isMulti()) { 179*22ceb3ecSAnna Dabrowska return implode( 180*22ceb3ecSAnna Dabrowska ',', 181*22ceb3ecSAnna Dabrowska array_map(static fn($rawValue) => '@' . $rawValue, $value->getRawValue()) 182*22ceb3ecSAnna Dabrowska ); 18386ba6958SSzymon Olewniczak } else { 1842c65b673SSzymon Olewniczak return '@' . $value->getRawValue(); 1852c65b673SSzymon Olewniczak } 18686ba6958SSzymon Olewniczak } 18786ba6958SSzymon Olewniczak if ($column->isMulti()) { 18886ba6958SSzymon Olewniczak return implode(',', $value->getRawValue()); 18986ba6958SSzymon Olewniczak } else { 1902c65b673SSzymon Olewniczak return $value->getRawValue(); 1912c65b673SSzymon Olewniczak } 1922c65b673SSzymon Olewniczak } 19386ba6958SSzymon Olewniczak } 1942c65b673SSzymon Olewniczak return ''; 1952c65b673SSzymon Olewniczak }, 1962c65b673SSzymon Olewniczak $user_and_groups 1972c65b673SSzymon Olewniczak ); 1982c65b673SSzymon Olewniczak 199922aade7SSzymon Olewniczak $user_and_groups_set = array_map('trim', explode(',', $user_and_groups)); 200922aade7SSzymon Olewniczak $users = []; 201922aade7SSzymon Olewniczak $groups = []; 202922aade7SSzymon Olewniczak foreach ($user_and_groups_set as $user_or_group) { 203922aade7SSzymon Olewniczak if ($user_or_group[0] == '@') { 204922aade7SSzymon Olewniczak $groups[] = substr($user_or_group, 1); 205922aade7SSzymon Olewniczak } else { 206922aade7SSzymon Olewniczak $users[] = $user_or_group; 207a1f4c7baSSzymon Olewniczak } 208a1f4c7baSSzymon Olewniczak } 209922aade7SSzymon Olewniczak $set = []; 210922aade7SSzymon Olewniczak 211922aade7SSzymon Olewniczak $all_users = $auth->retrieveUsers(); 212922aade7SSzymon Olewniczak foreach ($all_users as $user => $info) { 213922aade7SSzymon Olewniczak if (in_array($user, $users)) { 214922aade7SSzymon Olewniczak $set[$user] = $info; 215922aade7SSzymon Olewniczak } elseif (array_intersect($groups, $info['grps'])) { 216922aade7SSzymon Olewniczak $set[$user] = $info; 217922aade7SSzymon Olewniczak } 218922aade7SSzymon Olewniczak } 219922aade7SSzymon Olewniczak 220922aade7SSzymon Olewniczak return $set; 221922aade7SSzymon Olewniczak } 222922aade7SSzymon Olewniczak 223*22ceb3ecSAnna Dabrowska protected function predicateTrue($date, $operator, $value) 224*22ceb3ecSAnna Dabrowska { 225922aade7SSzymon Olewniczak $date = date('Y-m-d', strtotime($date)); 226922aade7SSzymon Olewniczak 227922aade7SSzymon Olewniczak switch ($operator) { 228922aade7SSzymon Olewniczak case 'before': 2294677fc3cSSzymon Olewniczak $days = date('Y-m-d', strtotime("+$value days")); 230922aade7SSzymon Olewniczak return $days >= $date; 231922aade7SSzymon Olewniczak case 'after': 2324677fc3cSSzymon Olewniczak $days = date('Y-m-d', strtotime("-$value days")); 233922aade7SSzymon Olewniczak return $date <= $days; 2344677fc3cSSzymon Olewniczak case 'at': 2354677fc3cSSzymon Olewniczak $now = new DateTime(); 2364677fc3cSSzymon Olewniczak $at = new DateTime(date($value, strtotime($date))); 2374677fc3cSSzymon Olewniczak return $now >= $at; 238922aade7SSzymon Olewniczak default: 239922aade7SSzymon Olewniczak return false; 240922aade7SSzymon Olewniczak } 241a1f4c7baSSzymon Olewniczak } 242a1f4c7baSSzymon Olewniczak 243*22ceb3ecSAnna Dabrowska protected function replacePlaceholders($message, $values) 244*22ceb3ecSAnna Dabrowska { 245ac63f65fSSzymon Olewniczak $patterns = []; 246ac63f65fSSzymon Olewniczak $replacements = []; 247ac63f65fSSzymon Olewniczak /* @var Value $value */ 248ac63f65fSSzymon Olewniczak foreach ($values as $value) { 249ac63f65fSSzymon Olewniczak $schema = $value->getColumn()->getTable(); 250ac63f65fSSzymon Olewniczak $label = $value->getColumn()->getLabel(); 251ac63f65fSSzymon Olewniczak $patterns[] = "/@@$schema.$label@@/"; 252ac63f65fSSzymon Olewniczak $replacements[] = $value->getDisplayValue(); 253ac63f65fSSzymon Olewniczak } 254ac63f65fSSzymon Olewniczak 255ac63f65fSSzymon Olewniczak return preg_replace($patterns, $replacements, $message); 256ac63f65fSSzymon Olewniczak } 257ac63f65fSSzymon Olewniczak 258e96b00ceSAnna Dabrowska /** 259e96b00ceSAnna Dabrowska * @param Search $search 260e96b00ceSAnna Dabrowska * @param string $filters 261e96b00ceSAnna Dabrowska */ 262e96b00ceSAnna Dabrowska protected function addFiltersToSearch(&$search, $filters) 263e96b00ceSAnna Dabrowska { 264e96b00ceSAnna Dabrowska if (!$filters) return; 265a1f4c7baSSzymon Olewniczak 266e96b00ceSAnna Dabrowska /** @var \helper_plugin_struct_config $confHelper */ 267e96b00ceSAnna Dabrowska $confHelper = plugin_load('helper', 'struct_config'); 268e96b00ceSAnna Dabrowska 269e96b00ceSAnna Dabrowska $filterConfigs = explode("\r\n", $filters); 270e96b00ceSAnna Dabrowska 271e96b00ceSAnna Dabrowska foreach ($filterConfigs as $config) { 272*22ceb3ecSAnna Dabrowska [$colname, $comp, $value, ] = $confHelper->parseFilterLine('AND', $config); 273e96b00ceSAnna Dabrowska $search->addFilter($colname, $value, $comp, 'AND'); 274e96b00ceSAnna Dabrowska } 275e96b00ceSAnna Dabrowska } 276e96b00ceSAnna Dabrowska} 277