1<?php 2if (!defined('DOKU_INC')) { die(); } 3 4/** 5 * DokuWiki Plugin matrixnotifier (Helper Component) 6 * 7 * @license GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html) 8 * 9 * @author Wilhelm/ JPTV.club 10 */ 11 12class helper_plugin_matrixnotifier extends \dokuwiki\Extension\Plugin 13{ 14 CONST __PLUGIN_VERSION__ = '1.2'; 15 16 private $_event = null; 17 private $_summary = null; 18 private $_payload = null; 19 20 private function valid_namespace() 21 { 22 $validNamespaces = $this->getConf('namespaces'); 23 if (!empty($validNamespaces)) 24 { 25 $validNamespacesArr = array_map('trim', explode(',', $validNamespaces)); 26 $thisNamespaceArr = explode(':', $GLOBALS['INFO']['namespace']); 27 28 return in_array($thisNamespaceArr[0], $validNamespacesArr); 29 } 30 31 return true; 32 } 33 34 private function check_event($event) 35 { 36 $etype = $event->data['changeType']; 37 38 if (($etype == 'C') && ($this->getConf('notify_create') == 1)) 39 { 40 $this->_event = 'create'; 41 } 42 elseif (($etype == 'E') && ($this->getConf('notify_edit') == 1)) 43 { 44 $this->_event = 'edit'; 45 } 46 elseif (($etype == 'e') && ($this->getConf('notify_edit') == 1) && ($this->getConf('notify_edit_minor') == 1)) 47 { 48 $this->_event = 'edit minor'; 49 } 50 elseif (($etype == 'D') && ($this->getConf('notify_delete') == 1)) 51 { 52 $this->_event = 'delete'; 53 } 54 /* 55 elseif (($etype == 'R') && ($this->getConf('notify_revert') == 1)) 56 { 57 $this->_event = 'revert'; 58 return true; 59 } 60 */ 61 else 62 { 63 return false; 64 } 65 66 $summary = $event->data['summary']; 67 if (!empty($summary)) 68 { 69 $this->_summary = $summary; 70 } 71 72 return true; 73 } 74 75 private function update_payload($event) 76 { 77 $user = $GLOBALS['INFO']['userinfo']['name']; 78 $link = $this->compose_url($event, null); 79 $page = $event->data['id']; 80 81 $data = [ 82 'create' => ['loc_title' => 't_created', 'loc_event' => 'e_created', 'emoji' => ''], 83 'edit' => ['loc_title' => 't_updated', 'loc_event' => 'e_updated', 'emoji' => ''], 84 'edit minor' => ['loc_title' => 't_minor_upd', 'loc_event' => 'e_minor_upd', 'emoji' => ''], 85 'delete' => ['loc_title' => 't_removed', 'loc_event' => 'e_removed', 'emoji' => "\u{1F5D1}"], /* 'Wastebasket' emoji */ 86 ]; 87 88 $d = $data[$this->_event]; 89 $title = $this->getLang($d['loc_title']); 90 $useraction = $user.' '.$this->getLang($d['loc_event']); 91 92 $descr_raw = $title.' · '.$useraction.' "'.$page.'" ('.$link.')'; 93 $descr_html = $d['emoji'].' <strong>'.htmlspecialchars($title).'</strong> · '.htmlspecialchars($useraction).' "<a href="'.$link.'">'.htmlspecialchars($page).'</a>"'; 94 95 if (($this->_event != 'delete') && ($this->_event != 'create')) 96 { 97 $oldRev = $GLOBALS['INFO']['meta']['last_change']['date']; 98 99 if (!empty($oldRev)) 100 { 101 $diffURL = $this->compose_url($event, $oldRev); 102 $descr_raw .= ' ('.$this->getLang('compare').': '.$diffURL.')'; 103 $descr_html .= ' (<a href="'.$diffURL.'">'.$this->getLang('compare').'</a>)'; 104 } 105 } 106 107 if (($this->_event != 'delete') && $this->getConf('notify_show_summary')) 108 { 109 $summary = strip_tags($this->_summary); 110 111 if ($summary) 112 { 113 $descr_raw .= ' · '.$this->getLang('l_summary').': '.$summary; 114 $descr_html .= ' · '.$this->getLang('l_summary').': <i>'.$summary.'</i>'; 115 } 116 } 117 118 $this->_payload = array( 119 'msgtype' => 'm.text', 120 'body' => $descr_raw, 121 'format' => 'org.matrix.custom.html', 122 'formatted_body' => $descr_html, 123 ); 124 } 125 126 private function compose_url($event = null, $rev = null) 127 { 128 $page = $event->data['id']; 129 $userewrite = $GLOBALS['conf']['userewrite']; /* 0 = no rewrite, 1 = htaccess, 2 = internal */ 130 131 if ((($userewrite == 1) || ($userewrite == 2)) && $GLOBALS['conf']['useslash'] == true) 132 { 133 $page = str_replace(":", "/", $page); 134 } 135 136 $url = sprintf(['%sdoku.php?id=%s', '%s%s', '%sdoku.php/%s'][$userewrite], DOKU_URL, $page); 137 138 if ($rev != null) 139 { 140 $url .= ('&??'[$userewrite])."do=diff&rev={$rev}"; 141 } 142 143 return $url; 144 } 145 146 private function submit_payload() 147 { 148 $homeserver = $this->getConf('homeserver'); 149 $roomid = $this->getConf('room'); 150 $accesstoken = $this->getConf('accesstoken'); 151 152 if (!($homeserver && $roomid && $accesstoken)) 153 { 154 error_log('matrixnotifer: At least one of the required configuration options \'homeserver\', \'room\', or \'accesstoken\' is not set.'); 155 return; 156 } 157 158 $homeserver = rtrim(trim($homeserver), '/'); 159 $endpoint = $homeserver.'/_matrix/client/r0/rooms/'.$roomid.'/send/m.room.message/'.uniqid('docuwiki', true).'-'.md5(strval(random_int(0, PHP_INT_MAX))); 160 161 $json_payload = json_encode($this->_payload); 162 if (!is_string($json_payload)) 163 { 164 return; 165 } 166 167 $ch = curl_init($endpoint); 168 if ($ch) 169 { 170 /* Use a proxy, if defined 171 * 172 * Note: still entirely untested, was full of very obvious bugs, so nobody 173 * has ever used this succesfully anyway 174 */ 175 $proxy = $GLOBALS['conf']['proxy']; 176 if (!empty($proxy['host'])) 177 { 178 /* configure proxy address and port 179 */ 180 $proxyAddress = $proxy['host'].':'.$proxy['port']; 181 curl_setopt($ch, CURLOPT_PROXY, $proxyAddress); 182 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 183 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 184 185 /* include username and password if defined 186 */ 187 if (!empty($proxy['user']) && !empty($proxy['pass'])) 188 { 189 $proxyAuth = $proxy['user'].':'.conf_decodeString($proxy['pass']); 190 curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyAuth ); 191 } 192 } 193 194 /* Submit Payload 195 */ 196 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); 197 curl_setopt($ch, CURLOPT_HTTPHEADER, array( 198 'Content-type: application/json', 199 'Content-length: '.strlen($json_payload), 200 'User-agent: DocuWiki Matrix Notifier Plugin '.self::__PLUGIN_VERSION__, 201 'Authorization: Bearer '.$accesstoken, 202 'Cache-control: no-cache', 203 )); 204 curl_setopt($ch, CURLOPT_POSTFIELDS, $json_payload); 205 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 206 curl_exec($ch); 207 208 curl_close($ch); 209 } 210 } 211 212 public function sendUpdate($event) 213 { 214 if((strpos($event->data['file'], 'data/attic') === false) && $this->valid_namespace() && $this->check_event($event)) 215 { 216 $this->update_payload($event); 217 $this->submit_payload(); 218 } 219 } 220} 221