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, '&amp;', 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