1<?php 2/** 3 * DokuWiki Plugin feedback (Action Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Andreas Gohr <gohr@cosmocode.de> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12class action_plugin_feedback extends DokuWiki_Action_Plugin { 13 14 /* 15 * @var true if we are on the detail page 16 */ 17 protected $detail_page = false; 18 19 /** 20 * Registers a callback function for a given event 21 * 22 * @param Doku_Event_Handler $controller DokuWiki's event controller object 23 * @return void 24 */ 25 public function register(Doku_Event_Handler $controller) { 26 27 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax'); 28 $controller->register_hook('DETAIL_STARTED', 'BEFORE', $this, 'handle_detail_started'); 29 $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'handleDokuStarted'); 30 } 31 32 /** 33 * Chceck if we can give a feedback 34 * 35 * @return bool 36 */ 37 protected function feedback_possible() { 38 global $ACT, $ID; 39 40 // only on show and detail pages 41 if($ACT != 'show' && !$this->detail_page) return false; 42 // allow anonymous feedback? 43 if(!$_SERVER['REMOTE_USER'] && !$this->getConf('allowanon')) return false; 44 // any contact defined? 45 if(!$this->getFeedbackContact($ID)) return false; 46 47 return true; 48 } 49 50 /** 51 * [Custom event handler which performs action] 52 * 53 * @param Doku_Event $event event object by reference 54 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 55 * handler was registered] 56 * @return void 57 */ 58 59 public function handle_ajax(Doku_Event &$event, $param) { 60 // our event? 61 if($event->data != 'plugin_feedback') return; 62 $event->preventDefault(); 63 $event->stopPropagation(); 64 65 // allow anonymous feedback? 66 if(!$_SERVER['REMOTE_USER'] && !$this->getConf('allowanon')) { 67 http_status(400); 68 die('no anonymous access'); 69 } 70 71 // get submitted data 72 global $INPUT; 73 $id = $INPUT->str('id'); 74 $feedback = $INPUT->str('feedback'); 75 $media = $INPUT->bool('media'); 76 77 // get the responsible contact 78 $contact = $this->getFeedbackContact($id); 79 if(!$contact) { 80 http_status(400); 81 die('no contact defined'); 82 } 83 84 // get info on user 85 $user = null; 86 if($_SERVER['REMOTE_USER']) { 87 /** @var DokuWiki_Auth_Plugin $auth */ 88 global $auth; 89 $user = $auth->getUserData($_SERVER['REMOTE_USER']); 90 if(!$user) $user = null; 91 } 92 93 // send the mail 94 $mailer = new Mailer(); 95 $mailer->to($contact); 96 if($user) $mailer->setHeader('Reply-To', $user['mail']); 97 $mailer->subject($this->getLang('subject')); 98 if ($media) { 99 $url = ml($id, '', false, '&', true); 100 } else { 101 $url = wl($id, '', true); 102 } 103 $mailer->setBody( 104 io_readFile($this->localFN('mail')), 105 array('PAGE' => $id, 'FEEDBACK' => $feedback, 'URL' => $url), 106 array('FEEDBACK' => nl2br($feedback)) 107 ); 108 $success = $mailer->send(); 109 //send a copy to the author 110 if ($success && $user && $this->getConf('send_copy')) { 111 //insert headers unseted by send() 112 $mailer->to($user['mail']); 113 $mailer->subject($this->getLang('subject')); 114 $success = $mailer->send(); 115 } 116 header('Content-Type: text/html; charset=utf-8'); 117 118 if (!$success) { 119 echo $this->getLang('error'); 120 return; 121 } 122 123 echo $this->getLang('thanks'); 124 } 125 126 /** 127 * set our JSINFO isMedia-flag to false 128 * 129 * this simplifies our js, becuase now window.JSINFO.plugins.feedback.isMedia is always defined 130 */ 131 public function handleDokuStarted() { 132 global $JSINFO; 133 134 if (empty($JSINFO['plugins'])) { 135 $JSINFO['plugins'] = []; 136 } 137 $JSINFO['plugins']['feedback'] = [ 138 'isMedia' => false, 139 ]; 140 } 141 142 /** 143 * Set the flag when we are on the detail page 144 */ 145 public function handle_detail_started() { 146 global $JSINFO, $ID, $IMG; 147 148 $this->detail_page = true; 149 150 if (empty($JSINFO)) { 151 $JSINFO = [ 152 'id' => $ID, 153 'namespace' => getNS($ID), 154 'plugins' => [], 155 ]; 156 } 157 if (empty($JSINFO['plugins'])) { 158 $JSINFO['plugins'] = []; 159 } 160 161 $JSINFO['plugins']['feedback'] = [ 162 'isMedia' => true, 163 'mediaID' => $IMG 164 ]; 165 } 166 167 /** 168 * Get the responsible contact for givven ID 169 * 170 * @param $id 171 * @return false|string 172 */ 173 public function getFeedbackContact($id) { 174 $conf = confToHash(DOKU_CONF . 'plugin_feedback.conf'); 175 176 $ns = $id; 177 178 if ($this->getConf('span_translations')) { 179 $ns = $this->adjustForTanslations($id); 180 } 181 182 if ($this->getConf('include_parent_startpage')) { 183 if(isset($conf[$ns])) { 184 return $conf[$ns]; 185 } 186 } 187 188 do { 189 $ns = getNS($ns); 190 if(!$ns) $ns = '*'; 191 if(isset($conf[$ns])) return $conf[$ns]; 192 } while($ns != '*'); 193 194 return false; 195 } 196 197 /** 198 * prints or returns the the action link 199 * 200 * Alternatively you can add the plugin_feedback class to any object in the DOM and it will be used 201 * for triggering the feedback dialog 202 * 203 * @param bool $return 204 * @return string 205 */ 206 public function tpl($return = false) { 207 208 if(!$this->feedback_possible()) return; 209 210 $html = '<a href="#" class="plugin_feedback">' . $this->getLang('feedback') . '</a>'; 211 if($return) return $html; 212 echo $html; 213 return ''; 214 } 215 216 /** 217 * If this is a translated page, remove the language-prefix 218 * 219 * @param string $id 220 * 221 * @return string 222 */ 223 protected function adjustForTanslations($id) { 224 /** @var helper_plugin_translation $trans */ 225 $trans = plugin_load('helper', 'translation', defined('DOKU_UNITTEST')); 226 if ($trans) { 227 list(, $id) = $trans->getTransParts($id); 228 } else { 229 msg('Option span_translations has been activated in feedback-plugin, but translation-plugin is not installed/enabled!', -1); 230 } 231 return $id; 232 } 233 234} 235 236// vim:ts=4:sw=4:et: 237