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