xref: /plugin/discussion/action.php (revision 8657566c7a82367eda4174d762749d54f0b1cfc9)
1f0fda08aSwikidesign<?php
2f0fda08aSwikidesign/**
3f0fda08aSwikidesign * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4f0fda08aSwikidesign * @author     Esther Brunner <wikidesign@gmail.com>
5f0fda08aSwikidesign */
6f0fda08aSwikidesign
7c3413364SGerrit Uitslaguse dokuwiki\Extension\Event;
8c3413364SGerrit Uitslaguse dokuwiki\Subscriptions\SubscriberManager;
9c3413364SGerrit Uitslaguse dokuwiki\Utf8\PhpString;
10c3413364SGerrit Uitslag
11de7e6f00SGerrit Uitslag/**
12de7e6f00SGerrit Uitslag * Class action_plugin_discussion
13c3413364SGerrit Uitslag *
14c3413364SGerrit Uitslag * Data format of file metadir/<id>.comments:
15c3413364SGerrit Uitslag * array = [
16c3413364SGerrit Uitslag *  'status' => int whether comments are 0=disabled/1=open/2=closed,
17c3413364SGerrit Uitslag *  'number' => int number of visible comments,
1876fdd2cdSGerrit Uitslag *  'title' => string|null alternative title for discussion section
19c3413364SGerrit Uitslag *  'comments' => [
20c3413364SGerrit Uitslag *      '<cid>'=> [
21c3413364SGerrit Uitslag *          'cid' => string comment id - long random string
22c3413364SGerrit Uitslag *          'raw' => string comment text,
23c3413364SGerrit Uitslag *          'xhtml' => string rendered html,
24c3413364SGerrit Uitslag *          'parent' => null|string null or empty string at highest level, otherwise comment id of parent
25c3413364SGerrit Uitslag *          'replies' => string[] array with comment ids
26c3413364SGerrit Uitslag *          'user' => [
27c3413364SGerrit Uitslag *              'id' => string,
28c3413364SGerrit Uitslag *              'name' => string,
29c3413364SGerrit Uitslag *              'mail' => string,
30c3413364SGerrit Uitslag *              'address' => string,
31c3413364SGerrit Uitslag *              'url' => string
32c3413364SGerrit Uitslag *          ],
33c3413364SGerrit Uitslag *          'date' => [
34c3413364SGerrit Uitslag *              'created' => int timestamp,
35c3413364SGerrit Uitslag *              'modified' => int (not defined if not modified)
36c3413364SGerrit Uitslag *          ],
37c3413364SGerrit Uitslag *          'show' => bool, whether shown (still be moderated, or hidden by moderator or user self)
38c3413364SGerrit Uitslag *      ],
39c3413364SGerrit Uitslag *      ...
40c3413364SGerrit Uitslag *   ]
41c3413364SGerrit Uitslag *   'subscribers' => [
425494dd20SGerrit Uitslag *      '<email>' => [
43c3413364SGerrit Uitslag *          'hash' => string unique token,
44c3413364SGerrit Uitslag *          'active' => bool, true if confirmed
45c3413364SGerrit Uitslag *          'confirmsent' => bool, true if confirmation mail is sent
46c3413364SGerrit Uitslag *      ],
47c3413364SGerrit Uitslag *      ...
48c3413364SGerrit Uitslag *   ]
49de7e6f00SGerrit Uitslag */
50283a3029SGerrit Uitslagclass action_plugin_discussion extends DokuWiki_Action_Plugin
51283a3029SGerrit Uitslag{
52f0fda08aSwikidesign
53de7e6f00SGerrit Uitslag    /** @var helper_plugin_avatar */
54c3413364SGerrit Uitslag    protected $avatar = null;
55c3413364SGerrit Uitslag    /** @var null|string */
56c3413364SGerrit Uitslag    protected $style = null;
57c3413364SGerrit Uitslag    /** @var null|bool */
58c3413364SGerrit Uitslag    protected $useAvatar = null;
59de7e6f00SGerrit Uitslag    /** @var helper_plugin_discussion */
60c3413364SGerrit Uitslag    protected $helper = null;
61e6b2f142Slupo49
62de7e6f00SGerrit Uitslag    /**
63de7e6f00SGerrit Uitslag     * load helper
64de7e6f00SGerrit Uitslag     */
65283a3029SGerrit Uitslag    public function __construct()
66283a3029SGerrit Uitslag    {
67e6b2f142Slupo49        $this->helper = plugin_load('helper', 'discussion');
68e6b2f142Slupo49    }
69f1c6610eSpierre.spring
70de7e6f00SGerrit Uitslag    /**
71de7e6f00SGerrit Uitslag     * Register the handlers
72de7e6f00SGerrit Uitslag     *
73c3413364SGerrit Uitslag     * @param Doku_Event_Handler $controller DokuWiki's event controller object.
74de7e6f00SGerrit Uitslag     */
75283a3029SGerrit Uitslag    public function register(Doku_Event_Handler $controller)
76283a3029SGerrit Uitslag    {
77c3413364SGerrit Uitslag        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleCommentActions');
78c3413364SGerrit Uitslag        $controller->register_hook('TPL_ACT_RENDER', 'AFTER', $this, 'renderCommentsSection');
79c3413364SGerrit Uitslag        $controller->register_hook('INDEXER_PAGE_ADD', 'AFTER', $this, 'addCommentsToIndex', ['id' => 'page', 'text' => 'body']);
80c3413364SGerrit Uitslag        $controller->register_hook('FULLTEXT_SNIPPET_CREATE', 'BEFORE', $this, 'addCommentsToIndex', ['id' => 'id', 'text' => 'text']);
81c3413364SGerrit Uitslag        $controller->register_hook('INDEXER_VERSION_GET', 'BEFORE', $this, 'addIndexVersion', []);
82c3413364SGerrit Uitslag        $controller->register_hook('FULLTEXT_PHRASE_MATCH', 'AFTER', $this, 'fulltextPhraseMatchInComments', []);
831ce4168fSGerrit Uitslag        $controller->register_hook('PARSER_METADATA_RENDER', 'AFTER', $this, 'updateCommentStatusFromMetadata', []);
84c3413364SGerrit Uitslag        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'addToolbarToCommentfield', []);
85c3413364SGerrit Uitslag        $controller->register_hook('TOOLBAR_DEFINE', 'AFTER', $this, 'modifyToolbar', []);
86c3413364SGerrit Uitslag        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'ajaxPreviewComments', []);
87c3413364SGerrit Uitslag        $controller->register_hook('TPL_TOC_RENDER', 'BEFORE', $this, 'addDiscussionToTOC', []);
88b8fdc796SMichael Klier    }
89b8fdc796SMichael Klier
90b8fdc796SMichael Klier    /**
91b8fdc796SMichael Klier     * Preview Comments
92b8fdc796SMichael Klier     *
93de7e6f00SGerrit Uitslag     * @param Doku_Event $event
94283a3029SGerrit Uitslag     * @author Michael Klier <chi@chimeric.de>
95b8fdc796SMichael Klier     */
96283a3029SGerrit Uitslag    public function ajaxPreviewComments(Doku_Event $event)
97283a3029SGerrit Uitslag    {
98c3413364SGerrit Uitslag        global $INPUT;
99b8fdc796SMichael Klier        if ($event->data != 'discussion_preview') return;
100c3413364SGerrit Uitslag
101b8fdc796SMichael Klier        $event->preventDefault();
102b8fdc796SMichael Klier        $event->stopPropagation();
103b8fdc796SMichael Klier        print p_locale_xhtml('preview');
104b8fdc796SMichael Klier        print '<div class="comment_preview">';
105c3413364SGerrit Uitslag        if (!$INPUT->server->str('REMOTE_USER') && !$this->getConf('allowguests')) {
106b8fdc796SMichael Klier            print p_locale_xhtml('denied');
107b8fdc796SMichael Klier        } else {
108c3413364SGerrit Uitslag            print $this->renderComment($INPUT->post->str('comment'));
109b8fdc796SMichael Klier        }
110b8fdc796SMichael Klier        print '</div>';
1112d4bee9aSMichael Klier    }
1122d4bee9aSMichael Klier
1132d4bee9aSMichael Klier    /**
1145886c85bSMichael Klier     * Adds a TOC item if a discussion exists
1155886c85bSMichael Klier     *
116de7e6f00SGerrit Uitslag     * @param Doku_Event $event
117283a3029SGerrit Uitslag     * @author Michael Klier <chi@chimeric.de>
1185886c85bSMichael Klier     */
119283a3029SGerrit Uitslag    public function addDiscussionToTOC(Doku_Event $event)
120283a3029SGerrit Uitslag    {
1218c057533SMichael Klier        global $ACT;
122c3413364SGerrit Uitslag        if ($this->hasDiscussion($title) && $event->data && $ACT != 'admin') {
1235494dd20SGerrit Uitslag            $tocitem = [
1245494dd20SGerrit Uitslag                'hid' => 'discussion__section',
12576fdd2cdSGerrit Uitslag                'title' => $title ?: $this->getLang('discussion'),
1265886c85bSMichael Klier                'type' => 'ul',
1275494dd20SGerrit Uitslag                'level' => 1
1285494dd20SGerrit Uitslag            ];
1295886c85bSMichael Klier
130c3413364SGerrit Uitslag            $event->data[] = $tocitem;
1315886c85bSMichael Klier        }
1325886c85bSMichael Klier    }
1335886c85bSMichael Klier
1345886c85bSMichael Klier    /**
135c3413364SGerrit Uitslag     * Modify Toolbar for use with discussion plugin
1362d4bee9aSMichael Klier     *
137de7e6f00SGerrit Uitslag     * @param Doku_Event $event
138283a3029SGerrit Uitslag     * @author Michael Klier <chi@chimeric.de>
1392d4bee9aSMichael Klier     */
140283a3029SGerrit Uitslag    public function modifyToolbar(Doku_Event $event)
141283a3029SGerrit Uitslag    {
1422d4bee9aSMichael Klier        global $ACT;
1432d4bee9aSMichael Klier        if ($ACT != 'show') return;
1442d4bee9aSMichael Klier
145c3413364SGerrit Uitslag        if ($this->hasDiscussion($title) && $this->getConf('wikisyntaxok')) {
146c3413364SGerrit Uitslag            $toolbar = [];
1472d4bee9aSMichael Klier            foreach ($event->data as $btn) {
1482d4bee9aSMichael Klier                if ($btn['type'] == 'mediapopup') continue;
1492d4bee9aSMichael Klier                if ($btn['type'] == 'signature') continue;
150310210d6SGina Haeussge                if ($btn['type'] == 'linkwiz') continue;
1511dc736fbSGerrit Uitslag                if ($btn['type'] == 'NewTable') continue; //skip button for Edittable Plugin
152f7bcfbedSGerrit Uitslag                //FIXME does nothing. Checks for '=' on toplevel, but today it are special buttons and a picker with subarray
153c3413364SGerrit Uitslag                if (isset($btn['open']) && preg_match("/=+?/", $btn['open'])) continue;
154c3413364SGerrit Uitslag
155c3413364SGerrit Uitslag                $toolbar[] = $btn;
1562d4bee9aSMichael Klier            }
1572d4bee9aSMichael Klier            $event->data = $toolbar;
1582d4bee9aSMichael Klier        }
1592d4bee9aSMichael Klier    }
1602d4bee9aSMichael Klier
1612d4bee9aSMichael Klier    /**
1622d4bee9aSMichael Klier     * Dirty workaround to add a toolbar to the discussion plugin
1632d4bee9aSMichael Klier     *
164de7e6f00SGerrit Uitslag     * @param Doku_Event $event
165283a3029SGerrit Uitslag     * @author Michael Klier <chi@chimeric.de>
1662d4bee9aSMichael Klier     */
167283a3029SGerrit Uitslag    public function addToolbarToCommentfield(Doku_Event $event)
168283a3029SGerrit Uitslag    {
1692d4bee9aSMichael Klier        global $ACT;
1702d4bee9aSMichael Klier        global $ID;
1712d4bee9aSMichael Klier        if ($ACT != 'show') return;
1722d4bee9aSMichael Klier
173c3413364SGerrit Uitslag        if ($this->hasDiscussion($title) && $this->getConf('wikisyntaxok')) {
1742d4bee9aSMichael Klier            // FIXME ugly workaround, replace this once DW the toolbar code is more flexible
1752d4bee9aSMichael Klier            @require_once(DOKU_INC . 'inc/toolbar.php');
1762d4bee9aSMichael Klier            ob_start();
1772d4bee9aSMichael Klier            print 'NS = "' . getNS($ID) . '";'; // we have to define NS, otherwise we get get JS errors
1782d4bee9aSMichael Klier            toolbar_JSdefines('toolbar');
1792d4bee9aSMichael Klier            $script = ob_get_clean();
180c3413364SGerrit Uitslag            $event->data['script'][] = ['type' => 'text/javascript', 'charset' => "utf-8", '_data' => $script];
1812d4bee9aSMichael Klier        }
182f0fda08aSwikidesign    }
183f0fda08aSwikidesign
184f0fda08aSwikidesign    /**
185a1d93126SGina Haeussge     * Handles comment actions, dispatches data processing routines
186de7e6f00SGerrit Uitslag     *
187de7e6f00SGerrit Uitslag     * @param Doku_Event $event
188f0fda08aSwikidesign     */
189283a3029SGerrit Uitslag    public function handleCommentActions(Doku_Event $event)
190283a3029SGerrit Uitslag    {
191c3413364SGerrit Uitslag        global $ID, $INFO, $lang, $INPUT;
192573e23a1Swikidesign
193a1d93126SGina Haeussge        // handle newthread ACTs
194a1d93126SGina Haeussge        if ($event->data == 'newthread') {
195a1d93126SGina Haeussge            // we can handle it -> prevent others
196c3413364SGerrit Uitslag            $event->data = $this->newThread();
197a1d93126SGina Haeussge        }
198a1d93126SGina Haeussge
199a1d93126SGina Haeussge        // enable captchas
200c3413364SGerrit Uitslag        if (in_array($INPUT->str('comment'), ['add', 'save'])) {
201c3413364SGerrit Uitslag            $this->captchaCheck();
202c3413364SGerrit Uitslag            $this->recaptchaCheck();
203bd6dc08eSAdrian Schlegel        }
204a1d93126SGina Haeussge
2053011fb8bSMichael Klier        // if we are not in show mode or someone wants to unsubscribe, that was all for now
206c3413364SGerrit Uitslag        if ($event->data != 'show'
207c3413364SGerrit Uitslag            && $event->data != 'discussion_unsubscribe'
208c3413364SGerrit Uitslag            && $event->data != 'discussion_confirmsubscribe') {
209c3413364SGerrit Uitslag            return;
210c3413364SGerrit Uitslag        }
211a1d93126SGina Haeussge
212b2f2e866SMichael Klier        if ($event->data == 'discussion_unsubscribe' or $event->data == 'discussion_confirmsubscribe') {
213c3413364SGerrit Uitslag            if ($INPUT->has('hash')) {
2143011fb8bSMichael Klier                $file = metaFN($ID, '.comments');
2153011fb8bSMichael Klier                $data = unserialize(io_readFile($file));
216c3413364SGerrit Uitslag                $matchedMail = '';
2179881d835SMichael Klier                foreach ($data['subscribers'] as $mail => $info) {
2189881d835SMichael Klier                    // convert old style subscribers just in case
2199881d835SMichael Klier                    if (!is_array($info)) {
2209881d835SMichael Klier                        $hash = $data['subscribers'][$mail];
2219881d835SMichael Klier                        $data['subscribers'][$mail]['hash'] = $hash;
2229881d835SMichael Klier                        $data['subscribers'][$mail]['active'] = true;
2239881d835SMichael Klier                        $data['subscribers'][$mail]['confirmsent'] = true;
2249881d835SMichael Klier                    }
2259881d835SMichael Klier
226c3413364SGerrit Uitslag                    if ($data['subscribers'][$mail]['hash'] == $INPUT->str('hash')) {
227c3413364SGerrit Uitslag                        $matchedMail = $mail;
2280c54624fSGina Haeussge                    }
2290c54624fSGina Haeussge                }
2300c54624fSGina Haeussge
231c3413364SGerrit Uitslag                if ($matchedMail != '') {
232b2f2e866SMichael Klier                    if ($event->data == 'discussion_unsubscribe') {
233c3413364SGerrit Uitslag                        unset($data['subscribers'][$matchedMail]);
234c3413364SGerrit Uitslag                        msg(sprintf($lang['subscr_unsubscribe_success'], $matchedMail, $ID), 1);
235c3413364SGerrit Uitslag                    } else { //$event->data == 'discussion_confirmsubscribe'
236c3413364SGerrit Uitslag                        $data['subscribers'][$matchedMail]['active'] = true;
237c3413364SGerrit Uitslag                        msg(sprintf($lang['subscr_subscribe_success'], $matchedMail, $ID), 1);
2389881d835SMichael Klier                    }
2399881d835SMichael Klier                    io_saveFile($file, serialize($data));
2403011fb8bSMichael Klier                    $event->data = 'show';
2413011fb8bSMichael Klier                }
242de7e6f00SGerrit Uitslag
2439881d835SMichael Klier            }
244c3413364SGerrit Uitslag            return;
245f7bcfbedSGerrit Uitslag        }
246f7bcfbedSGerrit Uitslag
247a1d93126SGina Haeussge        // do the data processing for comments
248c3413364SGerrit Uitslag        $cid = $INPUT->str('cid');
249c3413364SGerrit Uitslag        switch ($INPUT->str('comment')) {
250f0fda08aSwikidesign            case 'add':
251c3413364SGerrit Uitslag                if (empty($INPUT->str('text'))) return; // don't add empty comments
252c3413364SGerrit Uitslag
253c3413364SGerrit Uitslag                if ($INPUT->server->has('REMOTE_USER') && !$this->getConf('adminimport')) {
254c3413364SGerrit Uitslag                    $comment['user']['id'] = $INPUT->server->str('REMOTE_USER');
25594c5d164SMichael Klier                    $comment['user']['name'] = $INFO['userinfo']['name'];
25694c5d164SMichael Klier                    $comment['user']['mail'] = $INFO['userinfo']['mail'];
25783f28d9bSGerrit Uitslag                } elseif (($INPUT->server->has('REMOTE_USER') && $this->getConf('adminimport') && $this->helper->isDiscussionModerator())
258c3413364SGerrit Uitslag                    || !$INPUT->server->has('REMOTE_USER')) {
259c3413364SGerrit Uitslag                    // don't add anonymous comments
260c3413364SGerrit Uitslag                    if (empty($INPUT->str('name')) or empty($INPUT->str('mail'))) {
261c3413364SGerrit Uitslag                        return;
262c3413364SGerrit Uitslag                    }
263c3413364SGerrit Uitslag
264c3413364SGerrit Uitslag                    if (!mail_isvalid($INPUT->str('mail'))) {
265c9d36b5eSMichael Klier                        msg($lang['regbadmail'], -1);
266c9d36b5eSMichael Klier                        return;
267c9d36b5eSMichael Klier                    } else {
26800d916a2SGerrit Uitslag                        $comment['user']['id'] = ''; //prevent overlap with loggedin users, before: 'test<ipadress>'
269c3413364SGerrit Uitslag                        $comment['user']['name'] = hsc($INPUT->str('name'));
270c3413364SGerrit Uitslag                        $comment['user']['mail'] = hsc($INPUT->str('mail'));
27194c5d164SMichael Klier                    }
272c9d36b5eSMichael Klier                }
273c3413364SGerrit Uitslag                $comment['user']['address'] = ($this->getConf('addressfield')) ? hsc($INPUT->str('address')) : '';
274c3413364SGerrit Uitslag                $comment['user']['url'] = ($this->getConf('urlfield')) ? $this->checkURL($INPUT->str('url')) : '';
275c3413364SGerrit Uitslag                $comment['subscribe'] = ($this->getConf('subscribe')) ? $INPUT->has('subscribe') : '';
276c3413364SGerrit Uitslag                $comment['date'] = ['created' => $INPUT->str('date')];
277c3413364SGerrit Uitslag                $comment['raw'] = cleanText($INPUT->str('text'));
278c3413364SGerrit Uitslag                $reply = $INPUT->str('reply');
27983f28d9bSGerrit Uitslag                if ($this->getConf('moderate') && !$this->helper->isDiscussionModerator()) {
280a44bc9f7SMichael Klier                    $comment['show'] = false;
281a44bc9f7SMichael Klier                } else {
282a44bc9f7SMichael Klier                    $comment['show'] = true;
283a44bc9f7SMichael Klier                }
284c3413364SGerrit Uitslag                $this->add($comment, $reply);
285f0fda08aSwikidesign                break;
286f0fda08aSwikidesign
287f0fda08aSwikidesign            case 'save':
288c3413364SGerrit Uitslag                $raw = cleanText($INPUT->str('text'));
289c3413364SGerrit Uitslag                $this->save([$cid], $raw);
290f0fda08aSwikidesign                break;
291f0fda08aSwikidesign
2921e46d176Swikidesign            case 'delete':
293c3413364SGerrit Uitslag                $this->save([$cid], '');
2942ee3dca3Swikidesign                break;
2951e46d176Swikidesign
296f0fda08aSwikidesign            case 'toogle':
297c3413364SGerrit Uitslag                $this->save([$cid], '', 'toogle');
298f0fda08aSwikidesign                break;
299a1d93126SGina Haeussge        }
3003011fb8bSMichael Klier    }
301a1d93126SGina Haeussge
302a1d93126SGina Haeussge    /**
303a1d93126SGina Haeussge     * Main function; dispatches the visual comment actions
304f7bcfbedSGerrit Uitslag     *
305f7bcfbedSGerrit Uitslag     * @param Doku_Event $event
306a1d93126SGina Haeussge     */
307283a3029SGerrit Uitslag    public function renderCommentsSection(Doku_Event $event)
308283a3029SGerrit Uitslag    {
309c3413364SGerrit Uitslag        global $INPUT;
310a1d93126SGina Haeussge        if ($event->data != 'show') return; // nothing to do for us
311a1d93126SGina Haeussge
312c3413364SGerrit Uitslag        $cid = $INPUT->str('cid');
313c3413364SGerrit Uitslag
314aa0d6cd7SGerrit Uitslag        if (!$cid) {
315c3413364SGerrit Uitslag            $cid = $INPUT->str('reply');
316aa0d6cd7SGerrit Uitslag        }
317283a3029SGerrit Uitslag
318c3413364SGerrit Uitslag        switch ($INPUT->str('comment')) {
319a1d93126SGina Haeussge            case 'edit':
320c3413364SGerrit Uitslag                $this->showDiscussionSection(null, $cid);
321a1d93126SGina Haeussge                break;
322283a3029SGerrit Uitslag            default: //'reply' or no action specified
323c3413364SGerrit Uitslag                $this->showDiscussionSection($cid);
3242b18adb9SMichael Klier                break;
325f0fda08aSwikidesign        }
326f0fda08aSwikidesign    }
327f0fda08aSwikidesign
328f0fda08aSwikidesign    /**
329a1d93126SGina Haeussge     * Redirects browser to given comment anchor
330f7bcfbedSGerrit Uitslag     *
331f7bcfbedSGerrit Uitslag     * @param string $cid comment id
332a1d93126SGina Haeussge     */
333283a3029SGerrit Uitslag    protected function redirect($cid)
334283a3029SGerrit Uitslag    {
335a1d93126SGina Haeussge        global $ID;
336a1d93126SGina Haeussge        global $ACT;
337a1d93126SGina Haeussge
338a1d93126SGina Haeussge        if ($ACT !== 'show') return;
339a44bc9f7SMichael Klier
34083f28d9bSGerrit Uitslag        if ($this->getConf('moderate') && !$this->helper->isDiscussionModerator()) {
341a44bc9f7SMichael Klier            msg($this->getLang('moderation'), 1);
342a44bc9f7SMichael Klier            @session_start();
343a44bc9f7SMichael Klier            global $MSG;
344a44bc9f7SMichael Klier            $_SESSION[DOKU_COOKIE]['msg'] = $MSG;
345a44bc9f7SMichael Klier            session_write_close();
346a44bc9f7SMichael Klier            $url = wl($ID);
347a44bc9f7SMichael Klier        } else {
348a44bc9f7SMichael Klier            $url = wl($ID) . '#comment_' . $cid;
349a44bc9f7SMichael Klier        }
350e88de38aSAxel Beckert
351e88de38aSAxel Beckert        if (function_exists('send_redirect')) {
352e88de38aSAxel Beckert            send_redirect($url);
353e88de38aSAxel Beckert        } else {
35469f42a0bSAxel Beckert            header('Location: ' . $url);
355e88de38aSAxel Beckert        }
3566d1f4f20SGina Haeussge        exit();
357a1d93126SGina Haeussge    }
358a1d93126SGina Haeussge
359a1d93126SGina Haeussge    /**
360d406a452SGerrit Uitslag     * Checks config settings to enable/disable discussions
361f0bcde18SGerrit Uitslag     *
362f7bcfbedSGerrit Uitslag     * @return bool true if enabled
363f0bcde18SGerrit Uitslag     */
364283a3029SGerrit Uitslag    public function isDiscussionEnabled()
365283a3029SGerrit Uitslag    {
366f8c74f2eSGerrit Uitslag        global $ID;
367f0bcde18SGerrit Uitslag
368d406a452SGerrit Uitslag        if ($this->getConf('excluded_ns') == '') {
369d406a452SGerrit Uitslag            $isNamespaceExcluded = false;
370d406a452SGerrit Uitslag        } else {
371283a3029SGerrit Uitslag            $ns = getNS($ID); // $INFO['namespace'] is not yet available, if used in update_comment_status()
372f8c74f2eSGerrit Uitslag            $isNamespaceExcluded = preg_match($this->getConf('excluded_ns'), $ns);
373d406a452SGerrit Uitslag        }
374f0bcde18SGerrit Uitslag
375f0bcde18SGerrit Uitslag        if ($this->getConf('automatic')) {
376f0bcde18SGerrit Uitslag            if ($isNamespaceExcluded) {
377f0bcde18SGerrit Uitslag                return false;
378f0bcde18SGerrit Uitslag            } else {
379f0bcde18SGerrit Uitslag                return true;
380f0bcde18SGerrit Uitslag            }
381f0bcde18SGerrit Uitslag        } else {
382f0bcde18SGerrit Uitslag            if ($isNamespaceExcluded) {
383f0bcde18SGerrit Uitslag                return true;
384f0bcde18SGerrit Uitslag            } else {
385f0bcde18SGerrit Uitslag                return false;
386f0bcde18SGerrit Uitslag            }
387f0bcde18SGerrit Uitslag        }
388f0bcde18SGerrit Uitslag    }
389f0bcde18SGerrit Uitslag
390f0bcde18SGerrit Uitslag    /**
391c3413364SGerrit Uitslag     * Shows all comments of the current page, if no reply or edit requested, then comment form is shown on the end
392c3413364SGerrit Uitslag     *
393c3413364SGerrit Uitslag     * @param null|string $reply comment id on which the user requested a reply
394c3413364SGerrit Uitslag     * @param null|string $edit comment id which the user requested for editing
395f0fda08aSwikidesign     */
396283a3029SGerrit Uitslag    protected function showDiscussionSection($reply = null, $edit = null)
397283a3029SGerrit Uitslag    {
398c3413364SGerrit Uitslag        global $ID, $INFO, $INPUT;
399573e23a1Swikidesign
400479dd10fSwikidesign        // get .comments meta file name
401f0fda08aSwikidesign        $file = metaFN($ID, '.comments');
402f0fda08aSwikidesign
403c3413364SGerrit Uitslag        if (!$INFO['exists']) return;
404c3413364SGerrit Uitslag        if (!@file_exists($file) && !$this->isDiscussionEnabled()) return;
405c3413364SGerrit Uitslag        if (!$INPUT->server->has('REMOTE_USER') && !$this->getConf('showguests')) return;
406f0fda08aSwikidesign
4072b18adb9SMichael Klier        // load data
408c3413364SGerrit Uitslag        $data = [];
4092b18adb9SMichael Klier        if (@file_exists($file)) {
4102b18adb9SMichael Klier            $data = unserialize(io_readFile($file, false));
411c3413364SGerrit Uitslag            // comments are turned off
412c3413364SGerrit Uitslag            if (!$data['status']) {
413c3413364SGerrit Uitslag                return;
414c3413364SGerrit Uitslag            }
415f7bcfbedSGerrit Uitslag        } elseif (!@file_exists($file) && $this->isDiscussionEnabled()) {
4162b18adb9SMichael Klier            // set status to show the comment form
4172b18adb9SMichael Klier            $data['status'] = 1;
4182b18adb9SMichael Klier            $data['number'] = 0;
4191ce4168fSGerrit Uitslag            $data['title'] = null;
4202b18adb9SMichael Klier        }
421f0fda08aSwikidesign
422a1599850SMichael Klier        // show discussion wrapper only on certain circumstances
423c3413364SGerrit Uitslag        if (empty($data['comments']) || !is_array($data['comments'])) {
424c3413364SGerrit Uitslag            $cnt = 0;
4255494dd20SGerrit Uitslag            $cids = [];
426c3413364SGerrit Uitslag        } else {
427c3413364SGerrit Uitslag            $cnt = count($data['comments']);
4285494dd20SGerrit Uitslag            $cids = array_keys($data['comments']);
4293e19949cSMark Prins        }
430c3413364SGerrit Uitslag
431de7e6f00SGerrit Uitslag        $show = false;
4325494dd20SGerrit Uitslag        if ($cnt > 1 || ($cnt == 1 && $data['comments'][$cids[0]]['show'] == 1)
4335494dd20SGerrit Uitslag            || $this->getConf('allowguests')
4345494dd20SGerrit Uitslag            || $INPUT->server->has('REMOTE_USER')) {
435a1599850SMichael Klier            $show = true;
436f0fda08aSwikidesign            // section title
43776fdd2cdSGerrit Uitslag            $title = (!empty($data['title']) ? hsc($data['title']) : $this->getLang('discussion'));
43846178401Slupo49            ptln('<div class="comment_wrapper" id="comment_wrapper">'); // the id value is used for visibility toggling the section
4394a0a1bd2Swikidesign            ptln('<h2><a name="discussion__section" id="discussion__section">', 2);
4404a0a1bd2Swikidesign            ptln($title, 4);
4414a0a1bd2Swikidesign            ptln('</a></h2>', 2);
4424a0a1bd2Swikidesign            ptln('<div class="level2 hfeed">', 2);
443a1599850SMichael Klier        }
444a1599850SMichael Klier
445f0fda08aSwikidesign        // now display the comments
446f0fda08aSwikidesign        if (isset($data['comments'])) {
44731aab30eSGina Haeussge            if (!$this->getConf('usethreading')) {
448c3413364SGerrit Uitslag                $data['comments'] = $this->flattenThreads($data['comments']);
449283a3029SGerrit Uitslag                uasort($data['comments'], [$this, 'sortThreadsOnCreation']);
45031aab30eSGina Haeussge            }
451dbd9d5cdSMichael Klier            if ($this->getConf('newestfirst')) {
452dbd9d5cdSMichael Klier                $data['comments'] = array_reverse($data['comments']);
453dbd9d5cdSMichael Klier            }
454c3413364SGerrit Uitslag            foreach ($data['comments'] as $cid => $value) {
455c3413364SGerrit Uitslag                if ($cid == $edit) { // edit form
456c3413364SGerrit Uitslag                    $this->showCommentForm($value['raw'], 'save', $edit);
457c3413364SGerrit Uitslag                } else {
458c3413364SGerrit Uitslag                    $this->showCommentWithReplies($cid, $data, '', $reply);
459c3413364SGerrit Uitslag                }
460f0fda08aSwikidesign            }
461f0fda08aSwikidesign        }
462f0fda08aSwikidesign
463c3413364SGerrit Uitslag        // comment form shown on the end, if no comment form of $reply or $edit is requested before
464c3413364SGerrit Uitslag        if ($data['status'] == 1 && (!$reply || !$this->getConf('usethreading')) && !$edit) {
465f7bcfbedSGerrit Uitslag            $this->showCommentForm('', 'add');
466c3413364SGerrit Uitslag        }
467f0fda08aSwikidesign
468a1599850SMichael Klier        if ($show) {
4694a0a1bd2Swikidesign            ptln('</div>', 2); // level2 hfeed
4704a0a1bd2Swikidesign            ptln('</div>'); // comment_wrapper
471a1599850SMichael Klier        }
472f0fda08aSwikidesign
47346178401Slupo49        // check for toggle print configuration
47446178401Slupo49        if ($this->getConf('visibilityButton')) {
47546178401Slupo49            // print the hide/show discussion section button
476c3413364SGerrit Uitslag            $this->showDiscussionToggleButton();
47746178401Slupo49        }
478f0fda08aSwikidesign    }
479f0fda08aSwikidesign
480de7e6f00SGerrit Uitslag    /**
481c3413364SGerrit Uitslag     * Remove the parent-child relation, such that the comment structure becomes flat
482c3413364SGerrit Uitslag     *
483c3413364SGerrit Uitslag     * @param array $comments array with all comments
484c3413364SGerrit Uitslag     * @param null|array $cids comment ids of replies, which should be flatten
485c3413364SGerrit Uitslag     * @return array returned array with flattened comment structure
486de7e6f00SGerrit Uitslag     */
487283a3029SGerrit Uitslag    protected function flattenThreads($comments, $cids = null)
488283a3029SGerrit Uitslag    {
489c3413364SGerrit Uitslag        if (is_null($cids)) {
490c3413364SGerrit Uitslag            $cids = array_keys($comments);
491c3413364SGerrit Uitslag        }
49231aab30eSGina Haeussge
493c3413364SGerrit Uitslag        foreach ($cids as $cid) {
49431aab30eSGina Haeussge            if (!empty($comments[$cid]['replies'])) {
49531aab30eSGina Haeussge                $rids = $comments[$cid]['replies'];
496c3413364SGerrit Uitslag                $comments = $this->flattenThreads($comments, $rids);
497c3413364SGerrit Uitslag                $comments[$cid]['replies'] = [];
49831aab30eSGina Haeussge            }
49931aab30eSGina Haeussge            $comments[$cid]['parent'] = '';
50031aab30eSGina Haeussge        }
50131aab30eSGina Haeussge        return $comments;
50231aab30eSGina Haeussge    }
50331aab30eSGina Haeussge
504f0fda08aSwikidesign    /**
505f0fda08aSwikidesign     * Adds a new comment and then displays all comments
506de7e6f00SGerrit Uitslag     *
507c3413364SGerrit Uitslag     * @param array $comment with
508c3413364SGerrit Uitslag     *  'raw' => string comment text,
509c3413364SGerrit Uitslag     *  'user' => [
510c3413364SGerrit Uitslag     *      'id' => string,
511c3413364SGerrit Uitslag     *      'name' => string,
512c3413364SGerrit Uitslag     *      'mail' => string
513c3413364SGerrit Uitslag     *  ],
514c3413364SGerrit Uitslag     *  'date' => [
515c3413364SGerrit Uitslag     *      'created' => int timestamp
516c3413364SGerrit Uitslag     *  ]
517c3413364SGerrit Uitslag     *  'show' => bool
518c3413364SGerrit Uitslag     *  'subscribe' => bool
519c3413364SGerrit Uitslag     * @param string $parent comment id of parent
520de7e6f00SGerrit Uitslag     * @return bool
521f0fda08aSwikidesign     */
522283a3029SGerrit Uitslag    protected function add($comment, $parent)
523283a3029SGerrit Uitslag    {
524c3413364SGerrit Uitslag        global $ID, $TEXT, $INPUT;
525f0fda08aSwikidesign
526c3413364SGerrit Uitslag        $originalTxt = $TEXT; // set $TEXT to comment text for wordblock check
527f0fda08aSwikidesign        $TEXT = $comment['raw'];
528f0fda08aSwikidesign
529f0fda08aSwikidesign        // spamcheck against the DokuWiki blacklist
530f0fda08aSwikidesign        if (checkwordblock()) {
531f0fda08aSwikidesign            msg($this->getLang('wordblock'), -1);
532f0fda08aSwikidesign            return false;
533f0fda08aSwikidesign        }
534f0fda08aSwikidesign
535c3413364SGerrit Uitslag        if (!$this->getConf('allowguests')
536c3413364SGerrit Uitslag            && $comment['user']['id'] != $INPUT->server->str('REMOTE_USER')
5374cded5e1SGerrit Uitslag        ) {
5383011fb8bSMichael Klier            return false; // guest comments not allowed
5394cded5e1SGerrit Uitslag        }
5403011fb8bSMichael Klier
541c3413364SGerrit Uitslag        $TEXT = $originalTxt; // restore global $TEXT
542f0fda08aSwikidesign
543f0fda08aSwikidesign        // get discussion meta file name
544f0fda08aSwikidesign        $file = metaFN($ID, '.comments');
545f0fda08aSwikidesign
5462b18adb9SMichael Klier        // create comments file if it doesn't exist yet
5472b18adb9SMichael Klier        if (!@file_exists($file)) {
5481ce4168fSGerrit Uitslag            $data = [
5491ce4168fSGerrit Uitslag                'status' => 1,
5501ce4168fSGerrit Uitslag                'number' => 0,
5511ce4168fSGerrit Uitslag                'title' => null
5521ce4168fSGerrit Uitslag            ];
5532b18adb9SMichael Klier            io_saveFile($file, serialize($data));
5542b18adb9SMichael Klier        } else {
555f0fda08aSwikidesign            $data = unserialize(io_readFile($file, false));
556c3413364SGerrit Uitslag            // comments off or closed
557c3413364SGerrit Uitslag            if ($data['status'] != 1) {
558c3413364SGerrit Uitslag                return false;
559c3413364SGerrit Uitslag            }
5602b18adb9SMichael Klier        }
5612b18adb9SMichael Klier
5623011fb8bSMichael Klier        if ($comment['date']['created']) {
5633011fb8bSMichael Klier            $date = strtotime($comment['date']['created']);
5643011fb8bSMichael Klier        } else {
5653011fb8bSMichael Klier            $date = time();
5663011fb8bSMichael Klier        }
567f0fda08aSwikidesign
5683011fb8bSMichael Klier        if ($date == -1) {
5693011fb8bSMichael Klier            $date = time();
5703011fb8bSMichael Klier        }
5713011fb8bSMichael Klier
5726046f25cSwikidesign        $cid = md5($comment['user']['id'] . $date); // create a unique id
573f0fda08aSwikidesign
57476fdd2cdSGerrit Uitslag        if (!isset($data['comments'][$parent]) || !is_array($data['comments'][$parent])) {
575c3413364SGerrit Uitslag            $parent = null; // invalid parent comment
5763011fb8bSMichael Klier        }
577f0fda08aSwikidesign
578f0fda08aSwikidesign        // render the comment
579c3413364SGerrit Uitslag        $xhtml = $this->renderComment($comment['raw']);
580f0fda08aSwikidesign
581f0fda08aSwikidesign        // fill in the new comment
582c3413364SGerrit Uitslag        $data['comments'][$cid] = [
5836046f25cSwikidesign            'user' => $comment['user'],
584c3413364SGerrit Uitslag            'date' => ['created' => $date],
5856046f25cSwikidesign            'raw' => $comment['raw'],
586f0fda08aSwikidesign            'xhtml' => $xhtml,
587f0fda08aSwikidesign            'parent' => $parent,
588c3413364SGerrit Uitslag            'replies' => [],
589a44bc9f7SMichael Klier            'show' => $comment['show']
590c3413364SGerrit Uitslag        ];
591f0fda08aSwikidesign
5923011fb8bSMichael Klier        if ($comment['subscribe']) {
5933011fb8bSMichael Klier            $mail = $comment['user']['mail'];
5945494dd20SGerrit Uitslag            if (isset($data['subscribers'])) {
5953011fb8bSMichael Klier                if (!$data['subscribers'][$mail]) {
5969881d835SMichael Klier                    $data['subscribers'][$mail]['hash'] = md5($mail . mt_rand());
5979881d835SMichael Klier                    $data['subscribers'][$mail]['active'] = false;
5989881d835SMichael Klier                    $data['subscribers'][$mail]['confirmsent'] = false;
5999881d835SMichael Klier                } else {
6009881d835SMichael Klier                    // convert old style subscribers and set them active
6019881d835SMichael Klier                    if (!is_array($data['subscribers'][$mail])) {
6029881d835SMichael Klier                        $hash = $data['subscribers'][$mail];
6039881d835SMichael Klier                        $data['subscribers'][$mail]['hash'] = $hash;
6049881d835SMichael Klier                        $data['subscribers'][$mail]['active'] = true;
6059881d835SMichael Klier                        $data['subscribers'][$mail]['confirmsent'] = true;
6069881d835SMichael Klier                    }
6073011fb8bSMichael Klier                }
6083011fb8bSMichael Klier            } else {
6099881d835SMichael Klier                $data['subscribers'][$mail]['hash'] = md5($mail . mt_rand());
6109881d835SMichael Klier                $data['subscribers'][$mail]['active'] = false;
6119881d835SMichael Klier                $data['subscribers'][$mail]['confirmsent'] = false;
6123011fb8bSMichael Klier            }
6133011fb8bSMichael Klier        }
6143011fb8bSMichael Klier
615f0fda08aSwikidesign        // update parent comment
6164cded5e1SGerrit Uitslag        if ($parent) {
6174cded5e1SGerrit Uitslag            $data['comments'][$parent]['replies'][] = $cid;
6184cded5e1SGerrit Uitslag        }
619f0fda08aSwikidesign
620f0fda08aSwikidesign        // update the number of comments
621f0fda08aSwikidesign        $data['number']++;
622f0fda08aSwikidesign
623f0fda08aSwikidesign        // notify subscribers of the page
6248b42cefbSMichael Klier        $data['comments'][$cid]['cid'] = $cid;
625c3413364SGerrit Uitslag        $this->notify($data['comments'][$cid], $data['subscribers']);
626f0fda08aSwikidesign
6279881d835SMichael Klier        // save the comment metadata file
6289881d835SMichael Klier        io_saveFile($file, serialize($data));
629c3413364SGerrit Uitslag        $this->addLogEntry($date, $ID, 'cc', '', $cid);
6309881d835SMichael Klier
631c3413364SGerrit Uitslag        $this->redirect($cid);
632f0fda08aSwikidesign        return true;
633f0fda08aSwikidesign    }
634f0fda08aSwikidesign
635f0fda08aSwikidesign    /**
636f0fda08aSwikidesign     * Saves the comment with the given ID and then displays all comments
637de7e6f00SGerrit Uitslag     *
638c3413364SGerrit Uitslag     * @param array|string $cids array with comment ids to save, or a single string comment id
639c3413364SGerrit Uitslag     * @param string $raw if empty comment is deleted, otherwise edited text is stored (note: storing is per one cid!)
640c3413364SGerrit Uitslag     * @param string|null $act 'toogle', 'show', 'hide', null. If null, it depends on $raw
641c3413364SGerrit Uitslag     * @return bool succeed?
642f0fda08aSwikidesign     */
643283a3029SGerrit Uitslag    public function save($cids, $raw, $act = null)
644283a3029SGerrit Uitslag    {
645c3413364SGerrit Uitslag        global $ID, $INPUT;
646f0fda08aSwikidesign
647c3413364SGerrit Uitslag        if (empty($cids)) return false; // do nothing if we get no comment id
648757550e8SMichael Klier
6492ee3dca3Swikidesign        if ($raw) {
6502ee3dca3Swikidesign            global $TEXT;
6512ee3dca3Swikidesign
652f0fda08aSwikidesign            $otxt = $TEXT; // set $TEXT to comment text for wordblock check
653f0fda08aSwikidesign            $TEXT = $raw;
654f0fda08aSwikidesign
655f0fda08aSwikidesign            // spamcheck against the DokuWiki blacklist
656f0fda08aSwikidesign            if (checkwordblock()) {
657f0fda08aSwikidesign                msg($this->getLang('wordblock'), -1);
658f0fda08aSwikidesign                return false;
659f0fda08aSwikidesign            }
660f0fda08aSwikidesign
661f0fda08aSwikidesign            $TEXT = $otxt; // restore global $TEXT
6622ee3dca3Swikidesign        }
663f0fda08aSwikidesign
664f0fda08aSwikidesign        // get discussion meta file name
665f0fda08aSwikidesign        $file = metaFN($ID, '.comments');
666f0fda08aSwikidesign        $data = unserialize(io_readFile($file, false));
667f0fda08aSwikidesign
668c3413364SGerrit Uitslag        if (!is_array($cids)) {
669c3413364SGerrit Uitslag            $cids = [$cids];
670c3413364SGerrit Uitslag        }
671264b7327Swikidesign        foreach ($cids as $cid) {
672264b7327Swikidesign
6736046f25cSwikidesign            if (is_array($data['comments'][$cid]['user'])) {
6746046f25cSwikidesign                $user = $data['comments'][$cid]['user']['id'];
6756046f25cSwikidesign                $convert = false;
6766046f25cSwikidesign            } else {
6776046f25cSwikidesign                $user = $data['comments'][$cid]['user'];
6786046f25cSwikidesign                $convert = true;
6796046f25cSwikidesign            }
6806046f25cSwikidesign
681f0fda08aSwikidesign            // someone else was trying to edit our comment -> abort
68283f28d9bSGerrit Uitslag            if ($user != $INPUT->server->str('REMOTE_USER') && !$this->helper->isDiscussionModerator()) {
683c3413364SGerrit Uitslag                return false;
684c3413364SGerrit Uitslag            }
685f0fda08aSwikidesign
686f0fda08aSwikidesign            $date = time();
687f0fda08aSwikidesign
6886046f25cSwikidesign            // need to convert to new format?
6896046f25cSwikidesign            if ($convert) {
690c3413364SGerrit Uitslag                $data['comments'][$cid]['user'] = [
6916046f25cSwikidesign                    'id' => $user,
6926046f25cSwikidesign                    'name' => $data['comments'][$cid]['name'],
6936046f25cSwikidesign                    'mail' => $data['comments'][$cid]['mail'],
6946046f25cSwikidesign                    'url' => $data['comments'][$cid]['url'],
6956046f25cSwikidesign                    'address' => $data['comments'][$cid]['address'],
696c3413364SGerrit Uitslag                ];
697c3413364SGerrit Uitslag                $data['comments'][$cid]['date'] = [
6986046f25cSwikidesign                    'created' => $data['comments'][$cid]['date']
699c3413364SGerrit Uitslag                ];
7006046f25cSwikidesign            }
7016046f25cSwikidesign
702264b7327Swikidesign            if ($act == 'toogle') {     // toogle visibility
703f0fda08aSwikidesign                $now = $data['comments'][$cid]['show'];
704f0fda08aSwikidesign                $data['comments'][$cid]['show'] = !$now;
705c3413364SGerrit Uitslag                $data['number'] = $this->countVisibleComments($data);
706f0fda08aSwikidesign
707f0fda08aSwikidesign                $type = ($data['comments'][$cid]['show'] ? 'sc' : 'hc');
708f0fda08aSwikidesign
709264b7327Swikidesign            } elseif ($act == 'show') { // show comment
710264b7327Swikidesign                $data['comments'][$cid]['show'] = true;
711c3413364SGerrit Uitslag                $data['number'] = $this->countVisibleComments($data);
712264b7327Swikidesign
713573e23a1Swikidesign                $type = 'sc'; // show comment
714264b7327Swikidesign
715264b7327Swikidesign            } elseif ($act == 'hide') { // hide comment
716264b7327Swikidesign                $data['comments'][$cid]['show'] = false;
717c3413364SGerrit Uitslag                $data['number'] = $this->countVisibleComments($data);
718264b7327Swikidesign
719573e23a1Swikidesign                $type = 'hc'; // hide comment
720264b7327Swikidesign
721f0fda08aSwikidesign            } elseif (!$raw) {          // remove the comment
722c3413364SGerrit Uitslag                $data['comments'] = $this->removeComment($cid, $data['comments']);
723c3413364SGerrit Uitslag                $data['number'] = $this->countVisibleComments($data);
724f0fda08aSwikidesign
725573e23a1Swikidesign                $type = 'dc'; // delete comment
726f0fda08aSwikidesign
727f0fda08aSwikidesign            } else {                   // save changed comment
728c3413364SGerrit Uitslag                $xhtml = $this->renderComment($raw);
729f0fda08aSwikidesign
730f0fda08aSwikidesign                // now change the comment's content
7316046f25cSwikidesign                $data['comments'][$cid]['date']['modified'] = $date;
7326046f25cSwikidesign                $data['comments'][$cid]['raw'] = $raw;
733f0fda08aSwikidesign                $data['comments'][$cid]['xhtml'] = $xhtml;
734f0fda08aSwikidesign
735573e23a1Swikidesign                $type = 'ec'; // edit comment
736f0fda08aSwikidesign            }
737264b7327Swikidesign        }
738264b7327Swikidesign
739f0fda08aSwikidesign        // save the comment metadata file
740f0fda08aSwikidesign        io_saveFile($file, serialize($data));
741c3413364SGerrit Uitslag        $this->addLogEntry($date, $ID, $type, '', $cid);
742f0fda08aSwikidesign
743c3413364SGerrit Uitslag        $this->redirect($cid);
744f0fda08aSwikidesign        return true;
745f0fda08aSwikidesign    }
746f0fda08aSwikidesign
747f0fda08aSwikidesign    /**
748c3413364SGerrit Uitslag     * Recursive function to remove a comment from the data array
749c3413364SGerrit Uitslag     *
750c3413364SGerrit Uitslag     * @param string $cid comment id to be removed
751c3413364SGerrit Uitslag     * @param array $comments array with all comments
752c3413364SGerrit Uitslag     * @return array returns modified array with all remaining comments
753efbe59d0Swikidesign     */
754283a3029SGerrit Uitslag    protected function removeComment($cid, $comments)
755283a3029SGerrit Uitslag    {
756efbe59d0Swikidesign        if (is_array($comments[$cid]['replies'])) {
757efbe59d0Swikidesign            foreach ($comments[$cid]['replies'] as $rid) {
758c3413364SGerrit Uitslag                $comments = $this->removeComment($rid, $comments);
759efbe59d0Swikidesign            }
760efbe59d0Swikidesign        }
761efbe59d0Swikidesign        unset($comments[$cid]);
762efbe59d0Swikidesign        return $comments;
763efbe59d0Swikidesign    }
764efbe59d0Swikidesign
765efbe59d0Swikidesign    /**
766f0fda08aSwikidesign     * Prints an individual comment
767de7e6f00SGerrit Uitslag     *
768c3413364SGerrit Uitslag     * @param string $cid comment id
769c3413364SGerrit Uitslag     * @param array $data array with all comments by reference
770c3413364SGerrit Uitslag     * @param string $parent comment id of parent
771c3413364SGerrit Uitslag     * @param string $reply comment id on which the user requested a reply
772c3413364SGerrit Uitslag     * @param bool $isVisible is marked as visible
773f0fda08aSwikidesign     */
774283a3029SGerrit Uitslag    protected function showCommentWithReplies($cid, &$data, $parent = '', $reply = '', $isVisible = true)
775283a3029SGerrit Uitslag    {
776c3413364SGerrit Uitslag        // comment was removed
777c3413364SGerrit Uitslag        if (!isset($data['comments'][$cid])) {
778c3413364SGerrit Uitslag            return;
779c3413364SGerrit Uitslag        }
780f0fda08aSwikidesign        $comment = $data['comments'][$cid];
781f0fda08aSwikidesign
782c3413364SGerrit Uitslag        // corrupt datatype
783c3413364SGerrit Uitslag        if (!is_array($comment)) {
784c3413364SGerrit Uitslag            return;
785c3413364SGerrit Uitslag        }
786f0fda08aSwikidesign
787c3413364SGerrit Uitslag        // handle only replies to given parent comment
788c3413364SGerrit Uitslag        if ($comment['parent'] != $parent) {
789c3413364SGerrit Uitslag            return;
790c3413364SGerrit Uitslag        }
791f0fda08aSwikidesign
79203ab0c03SGerrit Uitslag        // comment hidden, only shown for moderators
79303ab0c03SGerrit Uitslag        if (!$comment['show'] && !$this->helper->isDiscussionModerator()) {
794c3413364SGerrit Uitslag            return;
795c3413364SGerrit Uitslag        }
796f0fda08aSwikidesign
797f1c6610eSpierre.spring        // print the actual comment
79803ab0c03SGerrit Uitslag        $this->showComment($cid, $data, $reply, $isVisible);
799f1c6610eSpierre.spring        // replies to this comment entry?
800c3413364SGerrit Uitslag        $this->showReplies($cid, $data, $reply, $isVisible);
801f1c6610eSpierre.spring        // reply form
802c3413364SGerrit Uitslag        $this->showReplyForm($cid, $reply);
803f1c6610eSpierre.spring    }
804f1c6610eSpierre.spring
805de7e6f00SGerrit Uitslag    /**
806c3413364SGerrit Uitslag     * Print the comment
807c3413364SGerrit Uitslag     *
808c3413364SGerrit Uitslag     * @param string $cid comment id
80920d55e56SGerrit Uitslag     * @param array $data array with all comments
810c3413364SGerrit Uitslag     * @param string $reply comment id on which the user requested a reply
81103ab0c03SGerrit Uitslag     * @param bool $isVisible (grand)parent is marked as visible
812de7e6f00SGerrit Uitslag     */
81303ab0c03SGerrit Uitslag    protected function showComment($cid, $data, $reply, $isVisible)
814283a3029SGerrit Uitslag    {
815c3413364SGerrit Uitslag        global $conf, $lang, $HIGH, $INPUT;
816f1c6610eSpierre.spring        $comment = $data['comments'][$cid];
817f1c6610eSpierre.spring
81803ab0c03SGerrit Uitslag        //only moderators can arrive here if hidden
81947f99603SGerrit Uitslag        $class = '';
82003ab0c03SGerrit Uitslag        if (!$comment['show'] || !$isVisible) {
82147f99603SGerrit Uitslag            $class = ' comment_hidden';
82247f99603SGerrit Uitslag        }
82347f99603SGerrit Uitslag        if($cid === $reply) {
82447f99603SGerrit Uitslag            $class .= ' reply';
82503ab0c03SGerrit Uitslag        }
826f0fda08aSwikidesign        // comment head with date and user data
82747f99603SGerrit Uitslag        ptln('<div class="hentry' . $class . '">', 4);
8284a0a1bd2Swikidesign        ptln('<div class="comment_head">', 6);
8291810ba9cSMichael Klier        ptln('<a name="comment_' . $cid . '" id="comment_' . $cid . '"></a>', 8);
8304a0a1bd2Swikidesign        $head = '<span class="vcard author">';
831f0fda08aSwikidesign
8326046f25cSwikidesign        // prepare variables
8336046f25cSwikidesign        if (is_array($comment['user'])) { // new format
8346046f25cSwikidesign            $user = $comment['user']['id'];
8356046f25cSwikidesign            $name = $comment['user']['name'];
8366046f25cSwikidesign            $mail = $comment['user']['mail'];
8376046f25cSwikidesign            $url = $comment['user']['url'];
8386046f25cSwikidesign            $address = $comment['user']['address'];
8396046f25cSwikidesign        } else {                         // old format
8406046f25cSwikidesign            $user = $comment['user'];
8416046f25cSwikidesign            $name = $comment['name'];
8426046f25cSwikidesign            $mail = $comment['mail'];
8436046f25cSwikidesign            $url = $comment['url'];
8446046f25cSwikidesign            $address = $comment['address'];
8456046f25cSwikidesign        }
8466046f25cSwikidesign        if (is_array($comment['date'])) { // new format
8476046f25cSwikidesign            $created = $comment['date']['created'];
848283a3029SGerrit Uitslag            $modified = $comment['date']['modified'] ?? null;
8496046f25cSwikidesign        } else {                         // old format
8506046f25cSwikidesign            $created = $comment['date'];
8516046f25cSwikidesign            $modified = $comment['edited'];
8526046f25cSwikidesign        }
8536046f25cSwikidesign
8540ff5ab97SMichael Klier        // show username or real name?
855c3413364SGerrit Uitslag        if (!$this->getConf('userealname') && $user) {
85600d916a2SGerrit Uitslag            //not logged-in users have currently username set to '', but before 'test<Ipaddress>'
85700d916a2SGerrit Uitslag            if(substr($user, 0,4) === 'test'
85800d916a2SGerrit Uitslag                && (strpos($user, ':', 4) !== false || strpos($user, '.', 4) !== false)) {
85900d916a2SGerrit Uitslag                $showname = $name;
86000d916a2SGerrit Uitslag            } else {
8610ff5ab97SMichael Klier                $showname = $user;
86200d916a2SGerrit Uitslag            }
8630ff5ab97SMichael Klier        } else {
8640ff5ab97SMichael Klier            $showname = $name;
8650ff5ab97SMichael Klier        }
8660ff5ab97SMichael Klier
867ce7b17cfSwikidesign        // show avatar image?
868c3413364SGerrit Uitslag        if ($this->useAvatar()) {
86912ca6034SMichael Klier            $user_data['name'] = $name;
87012ca6034SMichael Klier            $user_data['user'] = $user;
87112ca6034SMichael Klier            $user_data['mail'] = $mail;
872b6725686SGerrit Uitslag            $align = $lang['direction'] === 'ltr' ? 'left' : 'right';
873b6725686SGerrit Uitslag            $avatar = $this->avatar->getXHTML($user_data, $name, $align);
874c3413364SGerrit Uitslag            if ($avatar) {
875c3413364SGerrit Uitslag                $head .= $avatar;
876c3413364SGerrit Uitslag            }
877f0fda08aSwikidesign        }
878f0fda08aSwikidesign
8796046f25cSwikidesign        if ($this->getConf('linkemail') && $mail) {
8800ff5ab97SMichael Klier            $head .= $this->email($mail, $showname, 'email fn');
8816046f25cSwikidesign        } elseif ($url) {
882c3413364SGerrit Uitslag            $head .= $this->external_link($this->checkURL($url), $showname, 'urlextern url fn');
883f0fda08aSwikidesign        } else {
8840ff5ab97SMichael Klier            $head .= '<span class="fn">' . $showname . '</span>';
885f0fda08aSwikidesign        }
8866f1f13d6SMatthias Schulte
8874cded5e1SGerrit Uitslag        if ($address) {
8884cded5e1SGerrit Uitslag            $head .= ', <span class="adr">' . $address . '</span>';
8894cded5e1SGerrit Uitslag        }
8904a0a1bd2Swikidesign        $head .= '</span>, ' .
891f014bc86SMichael Klier            '<abbr class="published" title="' . strftime('%Y-%m-%dT%H:%M:%SZ', $created) . '">' .
8926f1f13d6SMatthias Schulte            dformat($created, $conf['dformat']) . '</abbr>';
8939c0e06f3SMoisés Braga Ribeiro        if ($modified) {
8949c0e06f3SMoisés Braga Ribeiro            $head .= ', <abbr class="updated" title="' .
8956f1f13d6SMatthias Schulte                strftime('%Y-%m-%dT%H:%M:%SZ', $modified) . '">' . dformat($modified, $conf['dformat']) .
8969c0e06f3SMoisés Braga Ribeiro                '</abbr>';
8974cded5e1SGerrit Uitslag        }
898f014bc86SMichael Klier        ptln($head, 8);
8994a0a1bd2Swikidesign        ptln('</div>', 6); // class="comment_head"
900f0fda08aSwikidesign
901f0fda08aSwikidesign        // main comment content
9024a0a1bd2Swikidesign        ptln('<div class="comment_body entry-content"' .
903c3413364SGerrit Uitslag            ($this->useAvatar() ? $this->getWidthStyle() : '') . '>', 6);
90415cdad37Spierre.spring        echo ($HIGH ? html_hilight($comment['xhtml'], $HIGH) : $comment['xhtml']) . DOKU_LF;
9054a0a1bd2Swikidesign        ptln('</div>', 6); // class="comment_body"
906f0fda08aSwikidesign
907c3413364SGerrit Uitslag        if ($isVisible) {
9081184c36aSwikidesign            ptln('<div class="comment_buttons">', 6);
909f0fda08aSwikidesign
910f0fda08aSwikidesign            // show reply button?
911c3413364SGerrit Uitslag            if ($data['status'] == 1 && !$reply && $comment['show']
912c3413364SGerrit Uitslag                && ($this->getConf('allowguests') || $INPUT->server->has('REMOTE_USER'))
913c3413364SGerrit Uitslag                && $this->getConf('usethreading')
9144cded5e1SGerrit Uitslag            ) {
915c3413364SGerrit Uitslag                $this->showButton($cid, $this->getLang('btn_reply'), 'reply', true);
9164cded5e1SGerrit Uitslag            }
917f0fda08aSwikidesign
9181184c36aSwikidesign            // show edit, show/hide and delete button?
91983f28d9bSGerrit Uitslag            if (($user == $INPUT->server->str('REMOTE_USER') && $user != '') || $this->helper->isDiscussionModerator()) {
920c3413364SGerrit Uitslag                $this->showButton($cid, $lang['btn_secedit'], 'edit', true);
9211184c36aSwikidesign                $label = ($comment['show'] ? $this->getLang('btn_hide') : $this->getLang('btn_show'));
922c3413364SGerrit Uitslag                $this->showButton($cid, $label, 'toogle');
923c3413364SGerrit Uitslag                $this->showButton($cid, $lang['btn_delete'], 'delete');
924f0fda08aSwikidesign            }
9251184c36aSwikidesign            ptln('</div>', 6); // class="comment_buttons"
9261184c36aSwikidesign        }
9271184c36aSwikidesign        ptln('</div>', 4); // class="hentry"
928f0fda08aSwikidesign    }
929f0fda08aSwikidesign
930de7e6f00SGerrit Uitslag    /**
931c3413364SGerrit Uitslag     * If requested by user, show comment form to write a reply
932c3413364SGerrit Uitslag     *
933c3413364SGerrit Uitslag     * @param string $cid current comment id
934c3413364SGerrit Uitslag     * @param string $reply comment id on which the user requested a reply
935de7e6f00SGerrit Uitslag     */
936c3413364SGerrit Uitslag    protected function showReplyForm($cid, $reply)
937f1c6610eSpierre.spring    {
93831aab30eSGina Haeussge        if ($this->getConf('usethreading') && $reply == $cid) {
93947f99603SGerrit Uitslag            ptln('<div class="comment_replies reply">', 4);
940c3413364SGerrit Uitslag            $this->showCommentForm('', 'add', $cid);
9414a0a1bd2Swikidesign            ptln('</div>', 4); // class="comment_replies"
942f0fda08aSwikidesign        }
943f0fda08aSwikidesign    }
944f0fda08aSwikidesign
945de7e6f00SGerrit Uitslag    /**
946f7bcfbedSGerrit Uitslag     * Show the replies to the given comment
947c3413364SGerrit Uitslag     *
948c3413364SGerrit Uitslag     * @param string $cid comment id
949c3413364SGerrit Uitslag     * @param array $data array with all comments by reference
950f7bcfbedSGerrit Uitslag     * @param string $reply comment id on which the user requested a reply
951f7bcfbedSGerrit Uitslag     * @param bool $isVisible is marked as visible by reference
952de7e6f00SGerrit Uitslag     */
953c3413364SGerrit Uitslag    protected function showReplies($cid, &$data, $reply, &$isVisible)
954f1c6610eSpierre.spring    {
955f1c6610eSpierre.spring        $comment = $data['comments'][$cid];
956f1c6610eSpierre.spring        if (!count($comment['replies'])) {
957f1c6610eSpierre.spring            return;
958f1c6610eSpierre.spring        }
959c3413364SGerrit Uitslag        ptln('<div class="comment_replies"' . $this->getWidthStyle() . '>', 4);
960c3413364SGerrit Uitslag        $isVisible = ($comment['show'] && $isVisible);
961f1c6610eSpierre.spring        foreach ($comment['replies'] as $rid) {
962c3413364SGerrit Uitslag            $this->showCommentWithReplies($rid, $data, $cid, $reply, $isVisible);
963f1c6610eSpierre.spring        }
964f1c6610eSpierre.spring        ptln('</div>', 4);
965f1c6610eSpierre.spring    }
966f1c6610eSpierre.spring
967de7e6f00SGerrit Uitslag    /**
968de7e6f00SGerrit Uitslag     * Is an avatar displayed?
969de7e6f00SGerrit Uitslag     *
970de7e6f00SGerrit Uitslag     * @return bool
971de7e6f00SGerrit Uitslag     */
972c3413364SGerrit Uitslag    protected function useAvatar()
973f1c6610eSpierre.spring    {
974c3413364SGerrit Uitslag        if (is_null($this->useAvatar)) {
975c3413364SGerrit Uitslag            $this->useAvatar = $this->getConf('useavatar')
976c3413364SGerrit Uitslag                && ($this->avatar = $this->loadHelper('avatar', false));
977f1c6610eSpierre.spring        }
978c3413364SGerrit Uitslag        return $this->useAvatar;
979f1c6610eSpierre.spring    }
980f1c6610eSpierre.spring
981de7e6f00SGerrit Uitslag    /**
982de7e6f00SGerrit Uitslag     * Calculate width of indent
983de7e6f00SGerrit Uitslag     *
984de7e6f00SGerrit Uitslag     * @return string
985de7e6f00SGerrit Uitslag     */
986283a3029SGerrit Uitslag    protected function getWidthStyle()
987283a3029SGerrit Uitslag    {
988b6725686SGerrit Uitslag        global $lang;
989b6725686SGerrit Uitslag
990f1c6610eSpierre.spring        if (is_null($this->style)) {
991b6725686SGerrit Uitslag            $side = $lang['direction'] === 'ltr' ? 'left' : 'right';
992b6725686SGerrit Uitslag
993c3413364SGerrit Uitslag            if ($this->useAvatar()) {
994b6725686SGerrit Uitslag                $this->style = ' style="margin-' . $side . ': ' . ($this->avatar->getConf('size') + 14) . 'px;"';
995f1c6610eSpierre.spring            } else {
996b6725686SGerrit Uitslag                $this->style = ' style="margin-' . $side . ': 20px;"';
997f1c6610eSpierre.spring            }
998f1c6610eSpierre.spring        }
999f1c6610eSpierre.spring        return $this->style;
1000f1c6610eSpierre.spring    }
1001f1c6610eSpierre.spring
1002f0fda08aSwikidesign    /**
1003c3413364SGerrit Uitslag     * Show the button which toggles between show/hide of the entire discussion section
100446178401Slupo49     */
1005283a3029SGerrit Uitslag    protected function showDiscussionToggleButton()
1006283a3029SGerrit Uitslag    {
1007b6725686SGerrit Uitslag        ptln('<div id="toggle_button" class="toggle_button">');
1008283a3029SGerrit Uitslag        ptln('<input type="submit" id="discussion__btn_toggle_visibility" title="Toggle Visibiliy" class="button"'
1009283a3029SGerrit Uitslag            . 'value="' . $this->getLang('toggle_display') . '">');
101046178401Slupo49        ptln('</div>');
101146178401Slupo49    }
101246178401Slupo49
101346178401Slupo49    /**
1014f0fda08aSwikidesign     * Outputs the comment form
1015f7bcfbedSGerrit Uitslag     *
1016f7bcfbedSGerrit Uitslag     * @param string $raw the existing comment text in case of edit
1017f7bcfbedSGerrit Uitslag     * @param string $act action 'add' or 'save'
1018f7bcfbedSGerrit Uitslag     * @param string|null $cid comment id to be responded to or null
1019f0fda08aSwikidesign     */
1020f7bcfbedSGerrit Uitslag    protected function showCommentForm($raw, $act, $cid = null)
1021283a3029SGerrit Uitslag    {
1022c3413364SGerrit Uitslag        global $lang, $conf, $ID, $INPUT;
1023f0fda08aSwikidesign
1024f0fda08aSwikidesign        // not for unregistered users when guest comments aren't allowed
1025c3413364SGerrit Uitslag        if (!$INPUT->server->has('REMOTE_USER') && !$this->getConf('allowguests')) {
10267c8e18ffSGina Haeussge            ?>
10277c8e18ffSGina Haeussge            <div class="comment_form">
10287c8e18ffSGina Haeussge                <?php echo $this->getLang('noguests'); ?>
10297c8e18ffSGina Haeussge            </div>
10307c8e18ffSGina Haeussge            <?php
1031de7e6f00SGerrit Uitslag            return;
10327c8e18ffSGina Haeussge        }
1033f0fda08aSwikidesign
1034c3413364SGerrit Uitslag        // fill $raw with $INPUT->str('text') if it's empty (for failed CAPTCHA check)
1035c3413364SGerrit Uitslag        if (!$raw && $INPUT->str('comment') == 'show') {
1036c3413364SGerrit Uitslag            $raw = $INPUT->str('text');
10374cded5e1SGerrit Uitslag        }
1038f0fda08aSwikidesign        ?>
10395ef1705fSiLoveiDo
1040f0fda08aSwikidesign        <div class="comment_form">
1041283a3029SGerrit Uitslag            <form id="discussion__comment_form" method="post" action="<?php echo script() ?>"
1042283a3029SGerrit Uitslag                  accept-charset="<?php echo $lang['encoding'] ?>">
1043f0fda08aSwikidesign                <div class="no">
1044f0fda08aSwikidesign                    <input type="hidden" name="id" value="<?php echo $ID ?>"/>
104561437513Swikidesign                    <input type="hidden" name="do" value="show"/>
1046f0fda08aSwikidesign                    <input type="hidden" name="comment" value="<?php echo $act ?>"/>
1047530693fbSMichael Klier                    <?php
1048f0fda08aSwikidesign                    // for adding a comment
1049f0fda08aSwikidesign                    if ($act == 'add') {
1050f0fda08aSwikidesign                        ?>
1051f0fda08aSwikidesign                        <input type="hidden" name="reply" value="<?php echo $cid ?>"/>
1052f0fda08aSwikidesign                        <?php
105320c152acSMichael Klier                        // for guest/adminimport: show name, e-mail and subscribe to comments fields
105483f28d9bSGerrit Uitslag                        if (!$INPUT->server->has('REMOTE_USER') or ($this->getConf('adminimport') && $this->helper->isDiscussionModerator())) {
1055f0fda08aSwikidesign                            ?>
105600d916a2SGerrit Uitslag                            <input type="hidden" name="user" value=""/>
1057f0fda08aSwikidesign                            <div class="comment_name">
1058f0fda08aSwikidesign                                <label class="block" for="discussion__comment_name">
1059f0fda08aSwikidesign                                    <span><?php echo $lang['fullname'] ?>:</span>
1060283a3029SGerrit Uitslag                                    <input type="text"
1061283a3029SGerrit Uitslag                                           class="edit<?php if ($INPUT->str('comment') == 'add' && empty($INPUT->str('name'))) echo ' error' ?>"
1062283a3029SGerrit Uitslag                                           name="name" id="discussion__comment_name" size="50" tabindex="1"
1063283a3029SGerrit Uitslag                                           value="<?php echo hsc($INPUT->str('name')) ?>"/>
1064f0fda08aSwikidesign                                </label>
1065f0fda08aSwikidesign                            </div>
1066f0fda08aSwikidesign                            <div class="comment_mail">
1067f0fda08aSwikidesign                                <label class="block" for="discussion__comment_mail">
1068f0fda08aSwikidesign                                    <span><?php echo $lang['email'] ?>:</span>
1069283a3029SGerrit Uitslag                                    <input type="text"
1070283a3029SGerrit Uitslag                                           class="edit<?php if ($INPUT->str('comment') == 'add' && empty($INPUT->str('mail'))) echo ' error' ?>"
1071283a3029SGerrit Uitslag                                           name="mail" id="discussion__comment_mail" size="50" tabindex="2"
1072283a3029SGerrit Uitslag                                           value="<?php echo hsc($INPUT->str('mail')) ?>"/>
1073f0fda08aSwikidesign                                </label>
1074f0fda08aSwikidesign                            </div>
1075f0fda08aSwikidesign                            <?php
1076f0fda08aSwikidesign                        }
1077f0fda08aSwikidesign
1078f0fda08aSwikidesign                        // allow entering an URL
1079f0fda08aSwikidesign                        if ($this->getConf('urlfield')) {
1080f0fda08aSwikidesign                            ?>
1081f0fda08aSwikidesign                            <div class="comment_url">
1082f0fda08aSwikidesign                                <label class="block" for="discussion__comment_url">
1083f0fda08aSwikidesign                                    <span><?php echo $this->getLang('url') ?>:</span>
1084283a3029SGerrit Uitslag                                    <input type="text" class="edit" name="url" id="discussion__comment_url" size="50"
1085283a3029SGerrit Uitslag                                           tabindex="3" value="<?php echo hsc($INPUT->str('url')) ?>"/>
1086f0fda08aSwikidesign                                </label>
1087f0fda08aSwikidesign                            </div>
1088f0fda08aSwikidesign                            <?php
1089f0fda08aSwikidesign                        }
1090f0fda08aSwikidesign
1091f0fda08aSwikidesign                        // allow entering an address
1092f0fda08aSwikidesign                        if ($this->getConf('addressfield')) {
1093f0fda08aSwikidesign                            ?>
1094f0fda08aSwikidesign                            <div class="comment_address">
1095f0fda08aSwikidesign                                <label class="block" for="discussion__comment_address">
1096f0fda08aSwikidesign                                    <span><?php echo $this->getLang('address') ?>:</span>
1097283a3029SGerrit Uitslag                                    <input type="text" class="edit" name="address" id="discussion__comment_address"
1098283a3029SGerrit Uitslag                                           size="50" tabindex="4" value="<?php echo hsc($INPUT->str('address')) ?>"/>
1099f0fda08aSwikidesign                                </label>
1100f0fda08aSwikidesign                            </div>
1101f0fda08aSwikidesign                            <?php
1102f0fda08aSwikidesign                        }
1103f0fda08aSwikidesign
1104f0fda08aSwikidesign                        // allow setting the comment date
110583f28d9bSGerrit Uitslag                        if ($this->getConf('adminimport') && ($this->helper->isDiscussionModerator())) {
1106f0fda08aSwikidesign                            ?>
1107f0fda08aSwikidesign                            <div class="comment_date">
1108f0fda08aSwikidesign                                <label class="block" for="discussion__comment_date">
1109f0fda08aSwikidesign                                    <span><?php echo $this->getLang('date') ?>:</span>
1110283a3029SGerrit Uitslag                                    <input type="text" class="edit" name="date" id="discussion__comment_date"
1111283a3029SGerrit Uitslag                                           size="50"/>
1112f0fda08aSwikidesign                                </label>
1113f0fda08aSwikidesign                            </div>
1114f0fda08aSwikidesign                            <?php
1115f0fda08aSwikidesign                        }
1116f0fda08aSwikidesign
1117f0fda08aSwikidesign                        // for saving a comment
1118f0fda08aSwikidesign                    } else {
1119f0fda08aSwikidesign                        ?>
1120f0fda08aSwikidesign                        <input type="hidden" name="cid" value="<?php echo $cid ?>"/>
1121f0fda08aSwikidesign                        <?php
1122f0fda08aSwikidesign                    }
1123f0fda08aSwikidesign                    ?>
1124f0fda08aSwikidesign                    <div class="comment_text">
1125283a3029SGerrit Uitslag                        <?php echo $this->getLang('entercomment');
1126283a3029SGerrit Uitslag                        echo($this->getConf('wikisyntaxok') ? "" : ":");
112743ade360Slupo49                        if ($this->getConf('wikisyntaxok')) echo '. ' . $this->getLang('wikisyntax') . ':'; ?>
112843ade360Slupo49
112943ade360Slupo49                        <!-- Fix for disable the toolbar when wikisyntaxok is set to false. See discussion's script.jss -->
113043ade360Slupo49                        <?php if ($this->getConf('wikisyntaxok')) { ?>
113164901b8eSGerrit Uitslag                        <div id="discussion__comment_toolbar" class="toolbar group">
113243ade360Slupo49                            <?php } else { ?>
113343ade360Slupo49                            <div id="discussion__comment_toolbar_disabled">
113443ade360Slupo49                                <?php } ?>
11351de52da1SMichael Klier                            </div>
1136283a3029SGerrit Uitslag                            <textarea
1137283a3029SGerrit Uitslag                                class="edit<?php if ($INPUT->str('comment') == 'add' && empty($INPUT->str('text'))) echo ' error' ?>"
1138283a3029SGerrit Uitslag                                name="text" cols="80" rows="10" id="discussion__comment_text" tabindex="5"><?php
113937e3c825SMichael Klier                                if ($raw) {
114037e3c825SMichael Klier                                    echo formText($raw);
114137e3c825SMichael Klier                                } else {
1142c3413364SGerrit Uitslag                                    echo hsc($INPUT->str('text'));
114337e3c825SMichael Klier                                }
114437e3c825SMichael Klier                                ?></textarea>
1145f0fda08aSwikidesign                        </div>
11460c613822SMichael Hamann
11470c613822SMichael Hamann                        <?php
11480c613822SMichael Hamann                        /** @var helper_plugin_captcha $captcha */
11490c613822SMichael Hamann                        $captcha = $this->loadHelper('captcha', false);
11500c613822SMichael Hamann                        if ($captcha && $captcha->isEnabled()) {
11510c613822SMichael Hamann                            echo $captcha->getHTML();
11520c613822SMichael Hamann                        }
11530c613822SMichael Hamann
11540c613822SMichael Hamann                        /** @var helper_plugin_recaptcha $recaptcha */
11550c613822SMichael Hamann                        $recaptcha = $this->loadHelper('recaptcha', false);
11560c613822SMichael Hamann                        if ($recaptcha && $recaptcha->isEnabled()) {
11570c613822SMichael Hamann                            echo $recaptcha->getHTML();
11580c613822SMichael Hamann                        }
1159e7c760b3Swikidesign                        ?>
11600c613822SMichael Hamann
1161283a3029SGerrit Uitslag                        <input class="button comment_submit" id="discussion__btn_submit" type="submit" name="submit"
1162283a3029SGerrit Uitslag                               accesskey="s" value="<?php echo $lang['btn_save'] ?>"
1163283a3029SGerrit Uitslag                               title="<?php echo $lang['btn_save'] ?> [S]" tabindex="7"/>
11641bb14180SGerrit Uitslag                        <?php
11651bb14180SGerrit Uitslag                        //if enabled, let not logged-in users subscribe, and logged-in only if no page-subcriptions are used
11661bb14180SGerrit Uitslag                        if ((!$INPUT->server->has('REMOTE_USER')
11671bb14180SGerrit Uitslag                                || $INPUT->server->has('REMOTE_USER') && !$conf['subscribers'])
11681bb14180SGerrit Uitslag                            && $this->getConf('subscribe')) { ?>
11691bb14180SGerrit Uitslag                            <label class="nowrap" for="discussion__comment_subscribe">
11701bb14180SGerrit Uitslag                                <input type="checkbox" id="discussion__comment_subscribe" name="subscribe"
11711bb14180SGerrit Uitslag                                       tabindex="6"/>
11721bb14180SGerrit Uitslag                                <span><?php echo $this->getLang('subscribe') ?></span>
11731bb14180SGerrit Uitslag                            </label>
11741bb14180SGerrit Uitslag                        <?php } ?>
1175283a3029SGerrit Uitslag                        <input class="button comment_preview_button" id="discussion__btn_preview" type="button"
1176283a3029SGerrit Uitslag                               name="preview" accesskey="p" value="<?php echo $lang['btn_preview'] ?>"
1177283a3029SGerrit Uitslag                               title="<?php echo $lang['btn_preview'] ?> [P]"/>
11781bb14180SGerrit Uitslag                        <?php if ($cid) { ?>
11791bb14180SGerrit Uitslag                            <a class="button comment_cancel" href="<?php echo wl($ID) . '#comment_' . $cid ?>" ><?php echo $lang['btn_cancel'] ?></a>
11803011fb8bSMichael Klier                        <?php } ?>
11813011fb8bSMichael Klier
11823011fb8bSMichael Klier                        <div class="clearer"></div>
1183ed4846efSMichael Klier                        <div id="discussion__comment_preview">&nbsp;</div>
1184f0fda08aSwikidesign                    </div>
1185f0fda08aSwikidesign            </form>
1186f0fda08aSwikidesign        </div>
1187f0fda08aSwikidesign        <?php
1188f0fda08aSwikidesign    }
1189f0fda08aSwikidesign
1190f0fda08aSwikidesign    /**
1191c3413364SGerrit Uitslag     * Action button below a comment
1192de7e6f00SGerrit Uitslag     *
1193c3413364SGerrit Uitslag     * @param string $cid comment id
1194c3413364SGerrit Uitslag     * @param string $label translated label
1195c3413364SGerrit Uitslag     * @param string $act action
1196c3413364SGerrit Uitslag     * @param bool $jump whether to scroll to the commentform
1197f0fda08aSwikidesign     */
1198283a3029SGerrit Uitslag    protected function showButton($cid, $label, $act, $jump = false)
1199283a3029SGerrit Uitslag    {
1200f0fda08aSwikidesign        global $ID;
12015ef1705fSiLoveiDo
12021e46d176Swikidesign        $anchor = ($jump ? '#discussion__comment_form' : '');
1203f0fda08aSwikidesign
12040980d234SGerrit Uitslag        $submitClass = '';
12050980d234SGerrit Uitslag        if($act === 'delete') {
12060980d234SGerrit Uitslag            $submitClass = ' dcs_confirmdelete';
12070980d234SGerrit Uitslag        }
1208f0fda08aSwikidesign        ?>
12096d1f4f20SGina Haeussge        <form class="button discussion__<?php echo $act ?>" method="get" action="<?php echo script() . $anchor ?>">
1210f0fda08aSwikidesign            <div class="no">
1211f0fda08aSwikidesign                <input type="hidden" name="id" value="<?php echo $ID ?>"/>
121261437513Swikidesign                <input type="hidden" name="do" value="show"/>
1213f0fda08aSwikidesign                <input type="hidden" name="comment" value="<?php echo $act ?>"/>
1214f0fda08aSwikidesign                <input type="hidden" name="cid" value="<?php echo $cid ?>"/>
12150980d234SGerrit Uitslag                <input type="submit" value="<?php echo $label ?>" class="button<?php echo $submitClass ?>" title="<?php echo $label ?>"/>
1216f0fda08aSwikidesign            </div>
1217f0fda08aSwikidesign        </form>
1218f0fda08aSwikidesign        <?php
1219f0fda08aSwikidesign    }
1220f0fda08aSwikidesign
1221f0fda08aSwikidesign    /**
1222f0fda08aSwikidesign     * Adds an entry to the comments changelog
1223f0fda08aSwikidesign     *
1224de7e6f00SGerrit Uitslag     * @param int $date
1225de7e6f00SGerrit Uitslag     * @param string $id page id
1226c3413364SGerrit Uitslag     * @param string $type create/edit/delete/show/hide comment 'cc', 'ec', 'dc', 'sc', 'hc'
1227de7e6f00SGerrit Uitslag     * @param string $summary
1228de7e6f00SGerrit Uitslag     * @param string $extra
1229283a3029SGerrit Uitslag     * @author Ben Coburn <btcoburn@silicodon.net>
1230283a3029SGerrit Uitslag     *
1231283a3029SGerrit Uitslag     * @author Esther Brunner <wikidesign@gmail.com>
1232f0fda08aSwikidesign     */
1233283a3029SGerrit Uitslag    protected function addLogEntry($date, $id, $type = 'cc', $summary = '', $extra = '')
1234283a3029SGerrit Uitslag    {
1235c3413364SGerrit Uitslag        global $conf, $INPUT;
1236f0fda08aSwikidesign
1237f0fda08aSwikidesign        $changelog = $conf['metadir'] . '/_comments.changes';
1238f0fda08aSwikidesign
12394cded5e1SGerrit Uitslag        //use current time if none supplied
12404cded5e1SGerrit Uitslag        if (!$date) {
12414cded5e1SGerrit Uitslag            $date = time();
12424cded5e1SGerrit Uitslag        }
1243c3413364SGerrit Uitslag        $remote = $INPUT->server->str('REMOTE_ADDR');
1244c3413364SGerrit Uitslag        $user = $INPUT->server->str('REMOTE_USER');
1245f0fda08aSwikidesign
1246c3413364SGerrit Uitslag        $strip = ["\t", "\n"];
1247c3413364SGerrit Uitslag        $logline = [
1248f0fda08aSwikidesign            'date' => $date,
1249f0fda08aSwikidesign            'ip' => $remote,
1250f0fda08aSwikidesign            'type' => str_replace($strip, '', $type),
1251f0fda08aSwikidesign            'id' => $id,
1252f0fda08aSwikidesign            'user' => $user,
1253f0fda08aSwikidesign            'sum' => str_replace($strip, '', $summary),
1254f0fda08aSwikidesign            'extra' => str_replace($strip, '', $extra)
1255c3413364SGerrit Uitslag        ];
1256f0fda08aSwikidesign
1257f0fda08aSwikidesign        // add changelog line
1258f0fda08aSwikidesign        $logline = implode("\t", $logline) . "\n";
1259f0fda08aSwikidesign        io_saveFile($changelog, $logline, true); //global changelog cache
1260c3413364SGerrit Uitslag        $this->trimRecentCommentsLog($changelog);
126177a22ba2Swikidesign
126277a22ba2Swikidesign        // tell the indexer to re-index the page
126377a22ba2Swikidesign        @unlink(metaFN($id, '.indexed'));
1264f0fda08aSwikidesign    }
1265f0fda08aSwikidesign
1266f0fda08aSwikidesign    /**
1267f0fda08aSwikidesign     * Trims the recent comments cache to the last $conf['changes_days'] recent
1268f0fda08aSwikidesign     * changes or $conf['recent'] items, which ever is larger.
1269f0fda08aSwikidesign     * The trimming is only done once a day.
1270f0fda08aSwikidesign     *
1271de7e6f00SGerrit Uitslag     * @param string $changelog file path
1272de7e6f00SGerrit Uitslag     * @return bool
1273283a3029SGerrit Uitslag     * @author Ben Coburn <btcoburn@silicodon.net>
1274283a3029SGerrit Uitslag     *
1275f0fda08aSwikidesign     */
1276283a3029SGerrit Uitslag    protected function trimRecentCommentsLog($changelog)
1277283a3029SGerrit Uitslag    {
1278f0fda08aSwikidesign        global $conf;
1279f0fda08aSwikidesign
1280283a3029SGerrit Uitslag        if (@file_exists($changelog)
1281283a3029SGerrit Uitslag            && (filectime($changelog) + 86400) < time()
1282283a3029SGerrit Uitslag            && !@file_exists($changelog . '_tmp')
12834cded5e1SGerrit Uitslag        ) {
1284f0fda08aSwikidesign
1285f0fda08aSwikidesign            io_lock($changelog);
1286f0fda08aSwikidesign            $lines = file($changelog);
1287f0fda08aSwikidesign            if (count($lines) < $conf['recent']) {
1288f0fda08aSwikidesign                // nothing to trim
1289f0fda08aSwikidesign                io_unlock($changelog);
1290f0fda08aSwikidesign                return true;
1291f0fda08aSwikidesign            }
1292f0fda08aSwikidesign
1293283a3029SGerrit Uitslag            // presave tmp as 2nd lock
1294283a3029SGerrit Uitslag            io_saveFile($changelog . '_tmp', '');
1295f0fda08aSwikidesign            $trim_time = time() - $conf['recent_days'] * 86400;
1296c3413364SGerrit Uitslag            $out_lines = [];
1297f0fda08aSwikidesign
1298e49085a2SMichael Klier            $num = count($lines);
1299e49085a2SMichael Klier            for ($i = 0; $i < $num; $i++) {
1300f0fda08aSwikidesign                $log = parseChangelogLine($lines[$i]);
1301f0fda08aSwikidesign                if ($log === false) continue;                      // discard junk
1302f0fda08aSwikidesign                if ($log['date'] < $trim_time) {
1303f0fda08aSwikidesign                    $old_lines[$log['date'] . ".$i"] = $lines[$i]; // keep old lines for now (append .$i to prevent key collisions)
1304f0fda08aSwikidesign                } else {
1305f0fda08aSwikidesign                    $out_lines[$log['date'] . ".$i"] = $lines[$i]; // definitely keep these lines
1306f0fda08aSwikidesign                }
1307f0fda08aSwikidesign            }
1308f0fda08aSwikidesign
1309f0fda08aSwikidesign            // sort the final result, it shouldn't be necessary,
1310f0fda08aSwikidesign            // however the extra robustness in making the changelog cache self-correcting is worth it
1311f0fda08aSwikidesign            ksort($out_lines);
1312f0fda08aSwikidesign            $extra = $conf['recent'] - count($out_lines);        // do we need extra lines do bring us up to minimum
1313f0fda08aSwikidesign            if ($extra > 0) {
1314f0fda08aSwikidesign                ksort($old_lines);
1315f0fda08aSwikidesign                $out_lines = array_merge(array_slice($old_lines, -$extra), $out_lines);
1316f0fda08aSwikidesign            }
1317f0fda08aSwikidesign
1318f0fda08aSwikidesign            // save trimmed changelog
1319f0fda08aSwikidesign            io_saveFile($changelog . '_tmp', implode('', $out_lines));
1320f0fda08aSwikidesign            @unlink($changelog);
1321f0fda08aSwikidesign            if (!rename($changelog . '_tmp', $changelog)) {
1322f0fda08aSwikidesign                // rename failed so try another way...
1323f0fda08aSwikidesign                io_unlock($changelog);
1324f0fda08aSwikidesign                io_saveFile($changelog, implode('', $out_lines));
1325f0fda08aSwikidesign                @unlink($changelog . '_tmp');
1326f0fda08aSwikidesign            } else {
1327f0fda08aSwikidesign                io_unlock($changelog);
1328f0fda08aSwikidesign            }
1329f0fda08aSwikidesign            return true;
1330f0fda08aSwikidesign        }
1331de7e6f00SGerrit Uitslag        return true;
1332f0fda08aSwikidesign    }
1333f0fda08aSwikidesign
1334f0fda08aSwikidesign    /**
1335f0fda08aSwikidesign     * Sends a notify mail on new comment
1336f0fda08aSwikidesign     *
1337f0fda08aSwikidesign     * @param array $comment data array of the new comment
1338c3413364SGerrit Uitslag     * @param array $subscribers data of the subscribers by reference
1339f0fda08aSwikidesign     *
1340f0fda08aSwikidesign     * @author Andreas Gohr <andi@splitbrain.org>
1341f0fda08aSwikidesign     * @author Esther Brunner <wikidesign@gmail.com>
1342f0fda08aSwikidesign     */
1343283a3029SGerrit Uitslag    protected function notify($comment, &$subscribers)
1344283a3029SGerrit Uitslag    {
1345c3413364SGerrit Uitslag        global $conf, $ID, $INPUT, $auth;
1346f0fda08aSwikidesign
13479881d835SMichael Klier        $notify_text = io_readfile($this->localfn('subscribermail'));
13489881d835SMichael Klier        $confirm_text = io_readfile($this->localfn('confirmsubscribe'));
13499881d835SMichael Klier        $subject_notify = '[' . $conf['title'] . '] ' . $this->getLang('mail_newcomment');
13509881d835SMichael Klier        $subject_subscribe = '[' . $conf['title'] . '] ' . $this->getLang('subscribe');
1351f0fda08aSwikidesign
1352451c1100SMichael Hamann        $mailer = new Mailer();
1353c3413364SGerrit Uitslag        if (!$INPUT->server->has('REMOTE_USER')) {
1354451c1100SMichael Hamann            $mailer->from($conf['mailfromnobody']);
1355f4a5ed1cSMatthias Schulte        }
1356f4a5ed1cSMatthias Schulte
1357c3413364SGerrit Uitslag        $replace = [
13583c4953e9SMichael Hamann            'PAGE' => $ID,
13593c4953e9SMichael Hamann            'TITLE' => $conf['title'],
13603c4953e9SMichael Hamann            'DATE' => dformat($comment['date']['created'], $conf['dformat']),
13613c4953e9SMichael Hamann            'NAME' => $comment['user']['name'],
13623c4953e9SMichael Hamann            'TEXT' => $comment['raw'],
13633c4953e9SMichael Hamann            'COMMENTURL' => wl($ID, '', true) . '#comment_' . $comment['cid'],
136406644a74SMichael Hamann            'UNSUBSCRIBE' => wl($ID, 'do=subscribe', true, '&'),
13653c4953e9SMichael Hamann            'DOKUWIKIURL' => DOKU_URL
1366c3413364SGerrit Uitslag        ];
1367451c1100SMichael Hamann
1368c3413364SGerrit Uitslag        $confirm_replace = [
13693c4953e9SMichael Hamann            'PAGE' => $ID,
13703c4953e9SMichael Hamann            'TITLE' => $conf['title'],
13713c4953e9SMichael Hamann            'DOKUWIKIURL' => DOKU_URL
1372c3413364SGerrit Uitslag        ];
1373451c1100SMichael Hamann
1374451c1100SMichael Hamann
1375451c1100SMichael Hamann        $mailer->subject($subject_notify);
1376451c1100SMichael Hamann        $mailer->setBody($notify_text, $replace);
1377451c1100SMichael Hamann
1378f4a5ed1cSMatthias Schulte        // send mail to notify address
1379f4a5ed1cSMatthias Schulte        if ($conf['notify']) {
1380451c1100SMichael Hamann            $mailer->bcc($conf['notify']);
1381451c1100SMichael Hamann            $mailer->send();
1382f4a5ed1cSMatthias Schulte        }
1383f4a5ed1cSMatthias Schulte
1384ca785d71SMichael Hamann        // send email to moderators
1385ca785d71SMichael Hamann        if ($this->getConf('moderatorsnotify')) {
1386c3413364SGerrit Uitslag            $moderatorgrpsString = trim($this->getConf('moderatorgroups'));
1387c3413364SGerrit Uitslag            if (!empty($moderatorgrpsString)) {
1388ca785d71SMichael Hamann                // create a clean mods list
1389c3413364SGerrit Uitslag                $moderatorgroups = explode(',', $moderatorgrpsString);
1390c3413364SGerrit Uitslag                $moderatorgroups = array_map('trim', $moderatorgroups);
1391c3413364SGerrit Uitslag                $moderatorgroups = array_unique($moderatorgroups);
1392c3413364SGerrit Uitslag                $moderatorgroups = array_filter($moderatorgroups);
1393ca785d71SMichael Hamann                // search for moderators users
1394c3413364SGerrit Uitslag                foreach ($moderatorgroups as $moderatorgroup) {
1395c3413364SGerrit Uitslag                    if (!$auth->isCaseSensitive()) {
1396c3413364SGerrit Uitslag                        $moderatorgroup = PhpString::strtolower($moderatorgroup);
1397c3413364SGerrit Uitslag                    }
1398ca785d71SMichael Hamann                    // create a clean mailing list
1399c3413364SGerrit Uitslag                    $bccs = [];
1400c3413364SGerrit Uitslag                    if ($moderatorgroup[0] == '@') {
1401c3413364SGerrit Uitslag                        foreach ($auth->retrieveUsers(0, 0, ['grps' => $auth->cleanGroup(substr($moderatorgroup, 1))]) as $user) {
1402ca785d71SMichael Hamann                            if (!empty($user['mail'])) {
1403c3413364SGerrit Uitslag                                $bccs[] = $user['mail'];
1404ca785d71SMichael Hamann                            }
1405ca785d71SMichael Hamann                        }
1406ca785d71SMichael Hamann                    } else {
1407c3413364SGerrit Uitslag                        //it is an user
1408c3413364SGerrit Uitslag                        $userdata = $auth->getUserData($auth->cleanUser($moderatorgroup));
1409ca785d71SMichael Hamann                        if (!empty($userdata['mail'])) {
1410c3413364SGerrit Uitslag                            $bccs[] = $userdata['mail'];
1411ca785d71SMichael Hamann                        }
1412ca785d71SMichael Hamann                    }
1413c3413364SGerrit Uitslag                    $bccs = array_unique($bccs);
1414ca785d71SMichael Hamann                    // notify the users
1415c3413364SGerrit Uitslag                    $mailer->bcc(implode(',', $bccs));
1416ca785d71SMichael Hamann                    $mailer->send();
1417ca785d71SMichael Hamann                }
1418ca785d71SMichael Hamann            }
1419ca785d71SMichael Hamann        }
1420ca785d71SMichael Hamann
1421f4a5ed1cSMatthias Schulte        // notify page subscribers
1422451c1100SMichael Hamann        if (actionOK('subscribe')) {
1423c3413364SGerrit Uitslag            $data = ['id' => $ID, 'addresslist' => '', 'self' => false];
1424c3413364SGerrit Uitslag            //FIXME default callback, needed to mentioned it again?
1425c3413364SGerrit Uitslag            Event::createAndTrigger(
1426451c1100SMichael Hamann                'COMMON_NOTIFY_ADDRESSLIST', $data,
1427c3413364SGerrit Uitslag                [new SubscriberManager(), 'notifyAddresses']
1428451c1100SMichael Hamann            );
1429c3413364SGerrit Uitslag
1430451c1100SMichael Hamann            $to = $data['addresslist'];
1431451c1100SMichael Hamann            if (!empty($to)) {
1432451c1100SMichael Hamann                $mailer->bcc($to);
1433451c1100SMichael Hamann                $mailer->send();
1434451c1100SMichael Hamann            }
14353011fb8bSMichael Klier        }
1436f0fda08aSwikidesign
14373011fb8bSMichael Klier        // notify comment subscribers
14383011fb8bSMichael Klier        if (!empty($subscribers)) {
14393011fb8bSMichael Klier
14409881d835SMichael Klier            foreach ($subscribers as $mail => $data) {
1441451c1100SMichael Hamann                $mailer->bcc($mail);
14429881d835SMichael Klier                if ($data['active']) {
14433c4953e9SMichael Hamann                    $replace['UNSUBSCRIBE'] = wl($ID, 'do=discussion_unsubscribe&hash=' . $data['hash'], true, '&');
14443011fb8bSMichael Klier
1445451c1100SMichael Hamann                    $mailer->subject($subject_notify);
1446451c1100SMichael Hamann                    $mailer->setBody($notify_text, $replace);
1447451c1100SMichael Hamann                    $mailer->send();
1448c3413364SGerrit Uitslag                } elseif (!$data['confirmsent']) {
14493c4953e9SMichael Hamann                    $confirm_replace['SUBSCRIBE'] = wl($ID, 'do=discussion_confirmsubscribe&hash=' . $data['hash'], true, '&');
14509881d835SMichael Klier
1451451c1100SMichael Hamann                    $mailer->subject($subject_subscribe);
1452451c1100SMichael Hamann                    $mailer->setBody($confirm_text, $confirm_replace);
1453451c1100SMichael Hamann                    $mailer->send();
14549881d835SMichael Klier                    $subscribers[$mail]['confirmsent'] = true;
14559881d835SMichael Klier                }
14563011fb8bSMichael Klier            }
14573011fb8bSMichael Klier        }
1458f0fda08aSwikidesign    }
1459f0fda08aSwikidesign
1460f0fda08aSwikidesign    /**
1461f0fda08aSwikidesign     * Counts the number of visible comments
1462de7e6f00SGerrit Uitslag     *
1463c3413364SGerrit Uitslag     * @param array $data array with all comments
1464de7e6f00SGerrit Uitslag     * @return int
1465f0fda08aSwikidesign     */
1466283a3029SGerrit Uitslag    protected function countVisibleComments($data)
1467283a3029SGerrit Uitslag    {
1468f0fda08aSwikidesign        $number = 0;
1469de7e6f00SGerrit Uitslag        foreach ($data['comments'] as $comment) {
1470f0fda08aSwikidesign            if ($comment['parent']) continue;
1471f0fda08aSwikidesign            if (!$comment['show']) continue;
1472c3413364SGerrit Uitslag
1473f0fda08aSwikidesign            $number++;
1474f0fda08aSwikidesign            $rids = $comment['replies'];
14754cded5e1SGerrit Uitslag            if (count($rids)) {
1476c3413364SGerrit Uitslag                $number = $number + $this->countVisibleReplies($data, $rids);
14774cded5e1SGerrit Uitslag            }
1478f0fda08aSwikidesign        }
1479f0fda08aSwikidesign        return $number;
1480f0fda08aSwikidesign    }
1481f0fda08aSwikidesign
1482de7e6f00SGerrit Uitslag    /**
1483c3413364SGerrit Uitslag     * Count visible replies on the comments
1484c3413364SGerrit Uitslag     *
1485de7e6f00SGerrit Uitslag     * @param array $data
1486de7e6f00SGerrit Uitslag     * @param array $rids
1487c3413364SGerrit Uitslag     * @return int counted replies
1488de7e6f00SGerrit Uitslag     */
1489283a3029SGerrit Uitslag    protected function countVisibleReplies(&$data, $rids)
1490283a3029SGerrit Uitslag    {
1491f0fda08aSwikidesign        $number = 0;
1492f0fda08aSwikidesign        foreach ($rids as $rid) {
14932ee3dca3Swikidesign            if (!isset($data['comments'][$rid])) continue; // reply was removed
1494f0fda08aSwikidesign            if (!$data['comments'][$rid]['show']) continue;
1495c3413364SGerrit Uitslag
1496f0fda08aSwikidesign            $number++;
1497f0fda08aSwikidesign            $rids = $data['comments'][$rid]['replies'];
14984cded5e1SGerrit Uitslag            if (count($rids)) {
1499c3413364SGerrit Uitslag                $number = $number + $this->countVisibleReplies($data, $rids);
15004cded5e1SGerrit Uitslag            }
1501f0fda08aSwikidesign        }
1502f0fda08aSwikidesign        return $number;
1503f0fda08aSwikidesign    }
1504f0fda08aSwikidesign
1505f0fda08aSwikidesign    /**
1506c3413364SGerrit Uitslag     * Renders the raw comment (wiki)text to html
1507de7e6f00SGerrit Uitslag     *
1508c3413364SGerrit Uitslag     * @param string $raw comment text
1509de7e6f00SGerrit Uitslag     * @return null|string
1510f0fda08aSwikidesign     */
1511283a3029SGerrit Uitslag    protected function renderComment($raw)
1512283a3029SGerrit Uitslag    {
1513f0fda08aSwikidesign        if ($this->getConf('wikisyntaxok')) {
1514efccf6b0SJeffrey Bergamini            // Note the warning for render_text:
1515efccf6b0SJeffrey Bergamini            //   "very ineffecient for small pieces of data - try not to use"
1516efccf6b0SJeffrey Bergamini            // in dokuwiki/inc/plugin.php
1517efccf6b0SJeffrey Bergamini            $xhtml = $this->render_text($raw);
1518f0fda08aSwikidesign        } else { // wiki syntax not allowed -> just encode special chars
151987bb4e97SMichael Klier            $xhtml = hsc(trim($raw));
152087bb4e97SMichael Klier            $xhtml = str_replace("\n", '<br />', $xhtml);
1521f0fda08aSwikidesign        }
1522f0fda08aSwikidesign        return $xhtml;
1523f0fda08aSwikidesign    }
1524f0fda08aSwikidesign
1525f0fda08aSwikidesign    /**
1526479dd10fSwikidesign     * Finds out whether there is a discussion section for the current page
1527de7e6f00SGerrit Uitslag     *
152876fdd2cdSGerrit Uitslag     * @param string $title set to title from metadata or empty string
152976fdd2cdSGerrit Uitslag     * @return bool discussion section is shown?
1530479dd10fSwikidesign     */
1531283a3029SGerrit Uitslag    protected function hasDiscussion(&$title)
1532283a3029SGerrit Uitslag    {
1533b2ac3b3bSwikidesign        global $ID;
15344a0a1bd2Swikidesign
1535c3413364SGerrit Uitslag        $file = metaFN($ID, '.comments');
1536479dd10fSwikidesign
1537c3413364SGerrit Uitslag        if (!@file_exists($file)) {
1538f0bcde18SGerrit Uitslag            if ($this->isDiscussionEnabled()) {
15392b18adb9SMichael Klier                return true;
15402b18adb9SMichael Klier            } else {
15412b18adb9SMichael Klier                return false;
15422b18adb9SMichael Klier            }
1543479dd10fSwikidesign        }
1544479dd10fSwikidesign
154576fdd2cdSGerrit Uitslag        $data = unserialize(io_readFile($file, false));
1546479dd10fSwikidesign
154776fdd2cdSGerrit Uitslag        $title = $data['title'] ?? '';
154876fdd2cdSGerrit Uitslag
1549*8657566cSGerrit Uitslag        $num = $data['number'] ?? 0;
155076fdd2cdSGerrit Uitslag        if (!$data['status'] || ($data['status'] == 2 && $num == 0)) {
1551c3413364SGerrit Uitslag            //disabled, or closed and no comments
1552c3413364SGerrit Uitslag            return false;
1553c3413364SGerrit Uitslag        } else {
1554c3413364SGerrit Uitslag            return true;
1555c3413364SGerrit Uitslag        }
1556479dd10fSwikidesign    }
1557479dd10fSwikidesign
1558479dd10fSwikidesign    /**
1559e7c760b3Swikidesign     * Creates a new thread page
1560de7e6f00SGerrit Uitslag     *
1561de7e6f00SGerrit Uitslag     * @return string
1562e7c760b3Swikidesign     */
1563283a3029SGerrit Uitslag    protected function newThread()
1564283a3029SGerrit Uitslag    {
1565c3413364SGerrit Uitslag        global $ID, $INFO, $INPUT;
1566f0fda08aSwikidesign
1567c3413364SGerrit Uitslag        $ns = cleanID($INPUT->str('ns'));
1568c3413364SGerrit Uitslag        $title = str_replace(':', '', $INPUT->str('title'));
15692e80cd5fSwikidesign        $back = $ID;
15702e80cd5fSwikidesign        $ID = ($ns ? $ns . ':' : '') . cleanID($title);
15712e80cd5fSwikidesign        $INFO = pageinfo();
1572f0fda08aSwikidesign
1573f0fda08aSwikidesign        // check if we are allowed to create this file
15742e80cd5fSwikidesign        if ($INFO['perm'] >= AUTH_CREATE) {
1575f0fda08aSwikidesign
1576f0fda08aSwikidesign            //check if locked by anyone - if not lock for my self
15774cded5e1SGerrit Uitslag            if ($INFO['locked']) {
15784cded5e1SGerrit Uitslag                return 'locked';
15794cded5e1SGerrit Uitslag            } else {
15804cded5e1SGerrit Uitslag                lock($ID);
15814cded5e1SGerrit Uitslag            }
1582f0fda08aSwikidesign
1583f0fda08aSwikidesign            // prepare the new thread file with default stuff
15842e80cd5fSwikidesign            if (!@file_exists($INFO['filepath'])) {
1585f0fda08aSwikidesign                global $TEXT;
1586f0fda08aSwikidesign
1587c3413364SGerrit Uitslag                $TEXT = pageTemplate(($ns ? $ns . ':' : '') . $title);
15881433886fSwikidesign                if (!$TEXT) {
1589c3413364SGerrit Uitslag                    $data = ['id' => $ID, 'ns' => $ns, 'title' => $title, 'back' => $back];
1590c3413364SGerrit Uitslag                    $TEXT = $this->pageTemplate($data);
15912e80cd5fSwikidesign                }
15922e80cd5fSwikidesign                return 'preview';
1593f0fda08aSwikidesign            } else {
15942e80cd5fSwikidesign                return 'edit';
1595f0fda08aSwikidesign            }
1596f0fda08aSwikidesign        } else {
15972e80cd5fSwikidesign            return 'show';
1598f0fda08aSwikidesign        }
1599f0fda08aSwikidesign    }
1600f0fda08aSwikidesign
1601e7c760b3Swikidesign    /**
160261437513Swikidesign     * Adapted version of pageTemplate() function
1603de7e6f00SGerrit Uitslag     *
1604de7e6f00SGerrit Uitslag     * @param array $data
1605de7e6f00SGerrit Uitslag     * @return string
160661437513Swikidesign     */
1607283a3029SGerrit Uitslag    protected function pageTemplate($data)
1608283a3029SGerrit Uitslag    {
1609c3413364SGerrit Uitslag        global $conf, $INFO, $INPUT;
161061437513Swikidesign
161161437513Swikidesign        $id = $data['id'];
1612c3413364SGerrit Uitslag        $user = $INPUT->server->str('REMOTE_USER');
161361437513Swikidesign        $tpl = io_readFile(DOKU_PLUGIN . 'discussion/_template.txt');
161461437513Swikidesign
161561437513Swikidesign        // standard replacements
1616c3413364SGerrit Uitslag        $replace = [
161761437513Swikidesign            '@NS@' => $data['ns'],
161861437513Swikidesign            '@PAGE@' => strtr(noNS($id), '_', ' '),
161961437513Swikidesign            '@USER@' => $user,
162061437513Swikidesign            '@NAME@' => $INFO['userinfo']['name'],
162161437513Swikidesign            '@MAIL@' => $INFO['userinfo']['mail'],
1622d5530824SMichael Hamann            '@DATE@' => dformat(time(), $conf['dformat']),
1623c3413364SGerrit Uitslag        ];
162461437513Swikidesign
162561437513Swikidesign        // additional replacements
162661437513Swikidesign        $replace['@BACK@'] = $data['back'];
162761437513Swikidesign        $replace['@TITLE@'] = $data['title'];
162861437513Swikidesign
162961437513Swikidesign        // avatar if useavatar and avatar plugin available
1630c3413364SGerrit Uitslag        if ($this->getConf('useavatar') && !plugin_isdisabled('avatar')) {
163161437513Swikidesign            $replace['@AVATAR@'] = '{{avatar>' . $user . ' }} ';
163261437513Swikidesign        } else {
163361437513Swikidesign            $replace['@AVATAR@'] = '';
163461437513Swikidesign        }
163561437513Swikidesign
163661437513Swikidesign        // tag if tag plugin is available
1637c3413364SGerrit Uitslag        if (!plugin_isdisabled('tag')) {
163861437513Swikidesign            $replace['@TAG@'] = "\n\n{{tag>}}";
163961437513Swikidesign        } else {
164061437513Swikidesign            $replace['@TAG@'] = '';
164161437513Swikidesign        }
164261437513Swikidesign
1643c3413364SGerrit Uitslag        // perform the replacements in tpl
1644c3413364SGerrit Uitslag        return str_replace(array_keys($replace), array_values($replace), $tpl);
164561437513Swikidesign    }
164661437513Swikidesign
164761437513Swikidesign    /**
1648f7bcfbedSGerrit Uitslag     * Checks if the CAPTCHA string submitted is valid, modifies action if needed
1649e7c760b3Swikidesign     */
1650283a3029SGerrit Uitslag    protected function captchaCheck()
1651283a3029SGerrit Uitslag    {
1652c3413364SGerrit Uitslag        global $INPUT;
165396bc68a7SMichael Hamann        /** @var helper_plugin_captcha $captcha */
1654c3413364SGerrit Uitslag        if (!$captcha = $this->loadHelper('captcha', false)) {
1655c3413364SGerrit Uitslag            // CAPTCHA is disabled or not available
1656c3413364SGerrit Uitslag            return;
1657c3413364SGerrit Uitslag        }
1658e7c760b3Swikidesign
1659d578a059SMichael Hamann        if ($captcha->isEnabled() && !$captcha->check()) {
1660c3413364SGerrit Uitslag            if ($INPUT->str('comment') == 'save') {
1661c3413364SGerrit Uitslag                $INPUT->set('comment', 'edit');
1662c3413364SGerrit Uitslag            } elseif ($INPUT->str('comment') == 'add') {
1663c3413364SGerrit Uitslag                $INPUT->set('comment', 'show');
16644cded5e1SGerrit Uitslag            }
1665e7c760b3Swikidesign        }
1666e7c760b3Swikidesign    }
1667e7c760b3Swikidesign
1668a1ca9e44Swikidesign    /**
1669f7bcfbedSGerrit Uitslag     * checks if the submitted reCAPTCHA string is valid, modifies action if needed
1670bd6dc08eSAdrian Schlegel     *
1671bd6dc08eSAdrian Schlegel     * @author Adrian Schlegel <adrian@liip.ch>
1672bd6dc08eSAdrian Schlegel     */
1673283a3029SGerrit Uitslag    protected function recaptchaCheck()
1674283a3029SGerrit Uitslag    {
1675c3413364SGerrit Uitslag        global $INPUT;
1676c3413364SGerrit Uitslag        /** @var helper_plugin_recaptcha $recaptcha */
1677c3413364SGerrit Uitslag        if (!$recaptcha = plugin_load('helper', 'recaptcha'))
1678bd6dc08eSAdrian Schlegel            return; // reCAPTCHA is disabled or not available
1679bd6dc08eSAdrian Schlegel
1680bd6dc08eSAdrian Schlegel        // do nothing if logged in user and no reCAPTCHA required
1681c3413364SGerrit Uitslag        if (!$recaptcha->getConf('forusers') && $INPUT->server->has('REMOTE_USER')) return;
1682bd6dc08eSAdrian Schlegel
1683c3413364SGerrit Uitslag        $response = $recaptcha->check();
1684c3413364SGerrit Uitslag        if (!$response->is_valid) {
1685bd6dc08eSAdrian Schlegel            msg($recaptcha->getLang('testfailed'), -1);
1686c3413364SGerrit Uitslag            if ($INPUT->str('comment') == 'save') {
1687c3413364SGerrit Uitslag                $INPUT->str('comment', 'edit');
1688c3413364SGerrit Uitslag            } elseif ($INPUT->str('comment') == 'add') {
1689c3413364SGerrit Uitslag                $INPUT->str('comment', 'show');
16904cded5e1SGerrit Uitslag            }
1691bd6dc08eSAdrian Schlegel        }
1692bd6dc08eSAdrian Schlegel    }
1693bd6dc08eSAdrian Schlegel
1694bd6dc08eSAdrian Schlegel    /**
1695ac818938SMichael Hamann     * Add discussion plugin version to the indexer version
1696ac818938SMichael Hamann     * This means that all pages will be indexed again in order to add the comments
1697ac818938SMichael Hamann     * to the index whenever there has been a change that concerns the index content.
1698de7e6f00SGerrit Uitslag     *
1699de7e6f00SGerrit Uitslag     * @param Doku_Event $event
1700ac818938SMichael Hamann     */
1701283a3029SGerrit Uitslag    public function addIndexVersion(Doku_Event $event)
1702283a3029SGerrit Uitslag    {
1703ac818938SMichael Hamann        $event->data['discussion'] = '0.1';
1704ac818938SMichael Hamann    }
1705ac818938SMichael Hamann
1706ac818938SMichael Hamann    /**
1707a1ca9e44Swikidesign     * Adds the comments to the index
1708de7e6f00SGerrit Uitslag     *
1709de7e6f00SGerrit Uitslag     * @param Doku_Event $event
1710c3413364SGerrit Uitslag     * @param array $param with
1711c3413364SGerrit Uitslag     *  'id' => string 'page'/'id' for respectively INDEXER_PAGE_ADD and FULLTEXT_SNIPPET_CREATE event
1712c3413364SGerrit Uitslag     *  'text' => string 'body'/'text'
1713a1ca9e44Swikidesign     */
1714283a3029SGerrit Uitslag    public function addCommentsToIndex(Doku_Event $event, $param)
1715283a3029SGerrit Uitslag    {
1716a1ca9e44Swikidesign        // get .comments meta file name
171710b5d61eSMichael Hamann        $file = metaFN($event->data[$param['id']], '.comments');
1718a1ca9e44Swikidesign
171996b3951aSMichael Hamann        if (!@file_exists($file)) return;
172096b3951aSMichael Hamann        $data = unserialize(io_readFile($file, false));
1721c3413364SGerrit Uitslag
1722c3413364SGerrit Uitslag        // comments are turned off or no comments available to index
1723c3413364SGerrit Uitslag        if (!$data['status'] || $data['number'] == 0) return;
1724a1ca9e44Swikidesign
1725a1ca9e44Swikidesign        // now add the comments
1726a1ca9e44Swikidesign        if (isset($data['comments'])) {
1727a1ca9e44Swikidesign            foreach ($data['comments'] as $key => $value) {
1728c3413364SGerrit Uitslag                $event->data[$param['text']] .= DOKU_LF . $this->addCommentWords($key, $data);
1729a1ca9e44Swikidesign            }
1730a1ca9e44Swikidesign        }
1731a1ca9e44Swikidesign    }
1732a1ca9e44Swikidesign
1733c3413364SGerrit Uitslag    /**
1734c3413364SGerrit Uitslag     * Checks if the phrase occurs in the comments and return event result true if matching
1735c3413364SGerrit Uitslag     *
1736c3413364SGerrit Uitslag     * @param Doku_Event $event
1737c3413364SGerrit Uitslag     */
1738283a3029SGerrit Uitslag    public function fulltextPhraseMatchInComments(Doku_Event $event)
1739283a3029SGerrit Uitslag    {
174010b5d61eSMichael Hamann        if ($event->result === true) return;
174110b5d61eSMichael Hamann
174210b5d61eSMichael Hamann        // get .comments meta file name
174310b5d61eSMichael Hamann        $file = metaFN($event->data['id'], '.comments');
174410b5d61eSMichael Hamann
174510b5d61eSMichael Hamann        if (!@file_exists($file)) return;
174610b5d61eSMichael Hamann        $data = unserialize(io_readFile($file, false));
1747c3413364SGerrit Uitslag
1748c3413364SGerrit Uitslag        // comments are turned off or no comments available to match
1749c3413364SGerrit Uitslag        if (!$data['status'] || $data['number'] == 0) return;
175010b5d61eSMichael Hamann
175110b5d61eSMichael Hamann        $matched = false;
175210b5d61eSMichael Hamann
175310b5d61eSMichael Hamann        // now add the comments
175410b5d61eSMichael Hamann        if (isset($data['comments'])) {
1755c3413364SGerrit Uitslag            foreach ($data['comments'] as $cid => $value) {
1756c3413364SGerrit Uitslag                $matched = $this->phraseMatchInComment($event->data['phrase'], $cid, $data);
175710b5d61eSMichael Hamann                if ($matched) break;
175810b5d61eSMichael Hamann            }
175910b5d61eSMichael Hamann        }
176010b5d61eSMichael Hamann
1761c3413364SGerrit Uitslag        if ($matched) {
176210b5d61eSMichael Hamann            $event->result = true;
176310b5d61eSMichael Hamann        }
1764c3413364SGerrit Uitslag    }
176510b5d61eSMichael Hamann
1766c3413364SGerrit Uitslag    /**
1767c3413364SGerrit Uitslag     * Match the phrase in the comment and its replies
1768c3413364SGerrit Uitslag     *
1769c3413364SGerrit Uitslag     * @param string $phrase phrase to search
1770c3413364SGerrit Uitslag     * @param string $cid comment id
1771c3413364SGerrit Uitslag     * @param array $data array with all comments by reference
1772c3413364SGerrit Uitslag     * @param string $parent cid of parent
1773c3413364SGerrit Uitslag     * @return bool if match true, otherwise false
1774c3413364SGerrit Uitslag     */
1775283a3029SGerrit Uitslag    protected function phraseMatchInComment($phrase, $cid, &$data, $parent = '')
1776283a3029SGerrit Uitslag    {
177710b5d61eSMichael Hamann        if (!isset($data['comments'][$cid])) return false; // comment was removed
1778c3413364SGerrit Uitslag
177910b5d61eSMichael Hamann        $comment = $data['comments'][$cid];
178010b5d61eSMichael Hamann
178110b5d61eSMichael Hamann        if (!is_array($comment)) return false;             // corrupt datatype
178210b5d61eSMichael Hamann        if ($comment['parent'] != $parent) return false;   // reply to an other comment
178310b5d61eSMichael Hamann        if (!$comment['show']) return false;               // hidden comment
178410b5d61eSMichael Hamann
1785c3413364SGerrit Uitslag        $text = PhpString::strtolower($comment['raw']);
178610b5d61eSMichael Hamann        if (strpos($text, $phrase) !== false) {
178710b5d61eSMichael Hamann            return true;
178810b5d61eSMichael Hamann        }
178910b5d61eSMichael Hamann
179010b5d61eSMichael Hamann        if (is_array($comment['replies'])) {               // and the replies
179110b5d61eSMichael Hamann            foreach ($comment['replies'] as $rid) {
1792c3413364SGerrit Uitslag                if ($this->phraseMatchInComment($phrase, $rid, $data, $cid)) {
179310b5d61eSMichael Hamann                    return true;
179410b5d61eSMichael Hamann                }
179510b5d61eSMichael Hamann            }
179610b5d61eSMichael Hamann        }
179710b5d61eSMichael Hamann        return false;
179810b5d61eSMichael Hamann    }
179910b5d61eSMichael Hamann
1800a1ca9e44Swikidesign    /**
1801c3413364SGerrit Uitslag     * Saves the current comment status and title from metadata into the .comments file
1802de7e6f00SGerrit Uitslag     *
1803de7e6f00SGerrit Uitslag     * @param Doku_Event $event
1804c1530f74SMichael Hamann     */
18051ce4168fSGerrit Uitslag    public function updateCommentStatusFromMetadata(Doku_Event $event)
1806283a3029SGerrit Uitslag    {
1807c1530f74SMichael Hamann        global $ID;
1808c1530f74SMichael Hamann
1809c1530f74SMichael Hamann        $meta = $event->data['current'];
18101ce4168fSGerrit Uitslag
1811c1530f74SMichael Hamann        $file = metaFN($ID, '.comments');
18121ce4168fSGerrit Uitslag        $configurationStatus = ($this->isDiscussionEnabled() ? 1 : 0); // 0=off, 1=enabled
1813c3413364SGerrit Uitslag        $title = null;
1814c1530f74SMichael Hamann        if (isset($meta['plugin_discussion'])) {
18151ce4168fSGerrit Uitslag            $status = (int) $meta['plugin_discussion']['status']; // 0=off, 1=enabled or 2=closed
1816c1530f74SMichael Hamann            $title = $meta['plugin_discussion']['title'];
18171ce4168fSGerrit Uitslag
18181ce4168fSGerrit Uitslag            // do we have metadata that differs from general config?
18191ce4168fSGerrit Uitslag            $saveNeededFromMetadata = $configurationStatus !== $status || ($status > 0 && $title);
18201ce4168fSGerrit Uitslag        } else {
18211ce4168fSGerrit Uitslag            $status = $configurationStatus;
18221ce4168fSGerrit Uitslag            $saveNeededFromMetadata = false;
1823c1530f74SMichael Hamann        }
1824c1530f74SMichael Hamann
18251ce4168fSGerrit Uitslag        // if .comment file exists always update it with latest status
18261ce4168fSGerrit Uitslag        if ($saveNeededFromMetadata || file_exists($file)) {
18271ce4168fSGerrit Uitslag
1828c3413364SGerrit Uitslag            $data = [];
1829c1530f74SMichael Hamann            if (@file_exists($file)) {
1830c1530f74SMichael Hamann                $data = unserialize(io_readFile($file, false));
1831c1530f74SMichael Hamann            }
1832c1530f74SMichael Hamann
1833c1530f74SMichael Hamann            if (!array_key_exists('title', $data) || $data['title'] !== $title || !isset($data['status']) || $data['status'] !== $status) {
18341ce4168fSGerrit Uitslag                //title can be only set from metadata
1835c1530f74SMichael Hamann                $data['title'] = $title;
1836c1530f74SMichael Hamann                $data['status'] = $status;
1837c3413364SGerrit Uitslag                if (!isset($data['number'])) {
1838c1530f74SMichael Hamann                    $data['number'] = 0;
1839c3413364SGerrit Uitslag                }
1840c1530f74SMichael Hamann                io_saveFile($file, serialize($data));
1841c1530f74SMichael Hamann            }
1842c1530f74SMichael Hamann        }
1843c1530f74SMichael Hamann    }
1844c1530f74SMichael Hamann
1845c1530f74SMichael Hamann    /**
1846c3413364SGerrit Uitslag     * Return words of a given comment and its replies, suitable to be added to the index
1847de7e6f00SGerrit Uitslag     *
1848c3413364SGerrit Uitslag     * @param string $cid comment id
1849c3413364SGerrit Uitslag     * @param array $data array with all comments by reference
1850c3413364SGerrit Uitslag     * @param string $parent cid of parent
1851de7e6f00SGerrit Uitslag     * @return string
1852a1ca9e44Swikidesign     */
1853283a3029SGerrit Uitslag    protected function addCommentWords($cid, &$data, $parent = '')
1854283a3029SGerrit Uitslag    {
1855a1ca9e44Swikidesign
1856efbe59d0Swikidesign        if (!isset($data['comments'][$cid])) return ''; // comment was removed
1857c3413364SGerrit Uitslag
1858a1ca9e44Swikidesign        $comment = $data['comments'][$cid];
1859a1ca9e44Swikidesign
1860efbe59d0Swikidesign        if (!is_array($comment)) return '';             // corrupt datatype
1861efbe59d0Swikidesign        if ($comment['parent'] != $parent) return '';   // reply to an other comment
1862efbe59d0Swikidesign        if (!$comment['show']) return '';               // hidden comment
1863a1ca9e44Swikidesign
1864efbe59d0Swikidesign        $text = $comment['raw'];                        // we only add the raw comment text
1865efbe59d0Swikidesign        if (is_array($comment['replies'])) {            // and the replies
1866efbe59d0Swikidesign            foreach ($comment['replies'] as $rid) {
1867c3413364SGerrit Uitslag                $text .= $this->addCommentWords($rid, $data, $cid);
1868a1ca9e44Swikidesign            }
1869a1ca9e44Swikidesign        }
1870efbe59d0Swikidesign        return ' ' . $text;
1871efbe59d0Swikidesign    }
1872a10b5c98SMichael Klier
1873a10b5c98SMichael Klier    /**
1874a10b5c98SMichael Klier     * Only allow http(s) URLs and append http:// to URLs if needed
1875de7e6f00SGerrit Uitslag     *
1876de7e6f00SGerrit Uitslag     * @param string $url
1877de7e6f00SGerrit Uitslag     * @return string
1878a10b5c98SMichael Klier     */
1879283a3029SGerrit Uitslag    protected function checkURL($url)
1880283a3029SGerrit Uitslag    {
1881a10b5c98SMichael Klier        if (preg_match("#^http://|^https://#", $url)) {
1882a10b5c98SMichael Klier            return hsc($url);
1883a10b5c98SMichael Klier        } elseif (substr($url, 0, 4) == 'www.') {
1884c3413364SGerrit Uitslag            return hsc('https://' . $url);
1885a10b5c98SMichael Klier        } else {
1886a10b5c98SMichael Klier            return '';
1887a10b5c98SMichael Klier        }
1888a10b5c98SMichael Klier    }
188931aab30eSGina Haeussge
1890de7e6f00SGerrit Uitslag    /**
1891de7e6f00SGerrit Uitslag     * Sort threads
1892de7e6f00SGerrit Uitslag     *
1893283a3029SGerrit Uitslag     * @param array $a array with comment properties
1894283a3029SGerrit Uitslag     * @param array $b array with comment properties
1895de7e6f00SGerrit Uitslag     * @return int
1896de7e6f00SGerrit Uitslag     */
1897283a3029SGerrit Uitslag    function sortThreadsOnCreation($a, $b)
1898283a3029SGerrit Uitslag    {
18997e018cb6Slpaulsen93        if (is_array($a['date'])) {
19007e018cb6Slpaulsen93            // new format
190131aab30eSGina Haeussge            $createdA = $a['date']['created'];
19027e018cb6Slpaulsen93        } else {
19037e018cb6Slpaulsen93            // old format
190431aab30eSGina Haeussge            $createdA = $a['date'];
190531aab30eSGina Haeussge        }
190631aab30eSGina Haeussge
19077e018cb6Slpaulsen93        if (is_array($b['date'])) {
19087e018cb6Slpaulsen93            // new format
190931aab30eSGina Haeussge            $createdB = $b['date']['created'];
19107e018cb6Slpaulsen93        } else {
19117e018cb6Slpaulsen93            // old format
191231aab30eSGina Haeussge            $createdB = $b['date'];
191331aab30eSGina Haeussge        }
191431aab30eSGina Haeussge
19154cded5e1SGerrit Uitslag        if ($createdA == $createdB) {
191631aab30eSGina Haeussge            return 0;
19174cded5e1SGerrit Uitslag        } else {
191831aab30eSGina Haeussge            return ($createdA < $createdB) ? -1 : 1;
191931aab30eSGina Haeussge        }
19204cded5e1SGerrit Uitslag    }
192131aab30eSGina Haeussge
1922c3413364SGerrit Uitslag}
1923c3413364SGerrit Uitslag
1924c3413364SGerrit Uitslag
1925