1<?php 2/** 3 * DokuWiki Plugin Slack Notifier (Action Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 */ 7 8use dokuwiki\Extension\ActionPlugin; 9use dokuwiki\Extension\Event; 10use dokuwiki\Extension\EventHandler; 11use dokuwiki\HTTP\DokuHTTPClient; 12use dokuwiki\Logger; 13use dokuwiki\plugin\slacknotifier\event\PageMoveEvent; 14use dokuwiki\plugin\slacknotifier\event\PageSaveEvent; 15use dokuwiki\plugin\slacknotifier\helper\Config; 16use dokuwiki\plugin\slacknotifier\helper\Context; 17use dokuwiki\plugin\slacknotifier\helper\Formatter; 18 19class action_plugin_slacknotifier extends ActionPlugin 20{ 21 /** @var Event[] */ 22 private $changes = []; 23 /** @var PageMoveEvent[] */ 24 private $created = []; 25 /** @var PageMoveEvent[] */ 26 private $deleted = []; 27 private $inRename = false; 28 /** @var Config */ 29 private $config; 30 31 public function __construct() { 32 $this->config = new Config($this); 33 } 34 35 public function register(EventHandler $controller): void 36 { 37 if (!$this->config->webhook) { 38 return; 39 } 40 41 $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handleSave'); 42 $controller->register_hook('PLUGIN_MOVE_PAGE_RENAME', 'BEFORE', $this, 'handleRenameBefore', 'BEFORE'); 43 $controller->register_hook('PLUGIN_MOVE_PAGE_RENAME', 'AFTER', $this, 'handleRenameAfter', 'AFTER'); 44 } 45 46 public function handleRenameBefore(Event $rawEvent): void 47 { 48 $this->inRename = true; 49 $event = new PageMoveEvent($rawEvent); 50 $this->created[$event->dst_id] = $event; 51 $this->deleted[$event->src_id] = $event; 52 } 53 54 public function handleRenameAfter(): void 55 { 56 if (!$this->inRename) { 57 // Sanity check 58 throw new RuntimeException('Logic error: in rename is false after rename'); 59 } 60 $this->inRename = false; 61 62 foreach ($this->getEvents() as $event) { 63 $this->processEvent($event); 64 } 65 } 66 67 public function handleSave(Event $event): void 68 { 69 if ($this->inRename) { 70 $this->changes[] = $event; 71 return; 72 } 73 74 $this->processEvent(new PageSaveEvent($event)); 75 } 76 77 /** 78 * @return PageSaveEvent[] 79 */ 80 private function getEvents(): array 81 { 82 $events = []; 83 foreach ($this->changes as $rawEvent) { 84 $event = new PageSaveEvent($rawEvent); 85 $pageId = $event->id; 86 87 if ($event->isCreate() && isset($this->created[$pageId])) { 88 $moveEvent = $this->created[$pageId]; 89 $moveEvent->setCreatedPageEvent($event); 90 unset($this->created[$pageId]); 91 } elseif ($event->isDelete() && isset($this->deleted[$pageId])) { 92 $moveEvent = $this->deleted[$pageId]; 93 $createdEvent = $moveEvent->getCreatedPageEvent(); 94 $createdEvent->convertToRename($event); 95 unset($this->deleted[$pageId]); 96 // Skip delete event itself 97 continue; 98 } 99 100 $events[] = $event; 101 } 102 $this->changes = []; 103 104 return $events; 105 } 106 107 private function processEvent(PageSaveEvent $event): void 108 { 109 if (!$this->config->isValidNamespace($event->getNamespace())) { 110 return; 111 } 112 113 if (!$this->isValidEvent($event->getEventType())) { 114 return; 115 } 116 117 $formatter = new Formatter($this->config); 118 $formatted = $formatter->format($event, new Context()); 119 $this->submitPayload($this->config->webhook, $formatted); 120 } 121 122 private function isValidEvent(?string $eventType): bool 123 { 124 if ($eventType === 'create' && $this->config->notify_create) { 125 return true; 126 } elseif ($eventType === 'edit' && $this->config->notify_edit) { 127 return true; 128 } elseif ($eventType === 'edit minor' && $this->config->notify_edit && $this->config->notify_edit_minor) { 129 return true; 130 } elseif ($eventType === 'delete' && $this->config->notify_delete) { 131 return true; 132 } elseif ($eventType === 'rename' && $this->config->notify_create && $this->config->notify_delete) { 133 return true; 134 } 135 136 return false; 137 } 138 139 private function submitPayload(string $url, array $payload): void 140 { 141 $http = new DokuHTTPClient(); 142 $http->headers['Content-Type'] = 'application/json'; 143 // we do single ops here, no need for keep-alive 144 $http->keep_alive = false; 145 146 $result = $http->post($url, ['payload' => json_encode($payload)]); 147 if ($result !== 'ok') { 148 $ctx = [ 149 'resp_body' => $http->resp_body, 150 'result' => $result, 151 'http_error' => $http->error, 152 ]; 153 Logger::error('Error posting to Slack', $ctx, __FILE__, __LINE__); 154 } 155 } 156} 157