1<?php
2/**
3 * DokuWiki Plugin comment (Syntax Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Your Name
7 */
8
9if (!defined('DOKU_INC')) die();
10
11class syntax_plugin_comment extends DokuWiki_Syntax_Plugin {
12
13    protected static $comments = array();
14    protected static $commentCounter = 0;
15
16    public function getType() {
17        return 'formatting';
18    }
19
20    public function getPType() {
21        return 'normal';
22    }
23
24    public function getSort() {
25        return 195;
26    }
27
28    public function connectTo($mode) {
29        // Syntax for comment - must capture multiple pipes
30        $this->Lexer->addSpecialPattern('\{\{comment>.+?\}\}', $mode, 'plugin_comment');
31        // Syntax for list of all comments
32        $this->Lexer->addSpecialPattern('\{\{comments-list\}\}', $mode, 'plugin_comment');
33    }
34
35    public function handle($match, $state, $pos, Doku_Handler $handler) {
36        // Detect syntax type
37        if ($match === '{{comments-list}}') {
38            return array('type' => 'list');
39        }
40
41        // Process comment
42        $match = substr($match, 10, -2); // Remove {{comment> and }}
43
44        // Split into parts by |
45        $parts = explode('|', $match);
46
47        $text = isset($parts[0]) ? trim($parts[0]) : '';
48        $comment = isset($parts[1]) ? trim($parts[1]) : '';
49        $status = 'open';
50
51        // Check status in third part
52        if (isset($parts[2])) {
53            $statusPart = trim($parts[2]);
54            if (preg_match('/^status=(open|resolved|closed)$/i', $statusPart, $matches)) {
55                $status = strtolower($matches[1]);
56            }
57        }
58
59        return array(
60            'type' => 'comment',
61            'text' => $text,
62            'comment' => $comment,
63            'status' => $status
64        );
65    }
66
67    public function render($mode, Doku_Renderer $renderer, $data) {
68        if ($mode !== 'xhtml') return false;
69
70        // Render comments list
71        if ($data['type'] === 'list') {
72            $this->renderCommentsList($renderer);
73            return true;
74        }
75
76        // Render individual comment
77        self::$commentCounter++;
78        $commentNum = self::$commentCounter;
79
80        // Save comment for end-of-page list
81        self::$comments[] = array(
82            'num' => $commentNum,
83            'text' => $data['text'],
84            'comment' => $data['comment'],
85            'status' => $data['status']
86        );
87
88        // CSS classes by status
89        $statusClass = 'comment-' . $data['status'];
90
91        // Emoji for status
92        $statusEmoji = array(
93            'open' => '��',
94            'resolved' => '✅',
95            'closed' => '❌'
96        );
97        $emoji = isset($statusEmoji[$data['status']]) ? $statusEmoji[$data['status']] : '��';
98
99        // Render highlighted text with comment number
100        $renderer->doc .= '<span class="comment-highlight ' . $statusClass . '" data-comment="' . $commentNum . '" id="comment-ref-' . $commentNum . '">';
101        $renderer->doc .= hsc($data['text']);
102        $renderer->doc .= '<sup class="comment-number"><a href="#comment-bubble-' . $commentNum . '" class="comment-link">[��' . $commentNum . ']</a></sup>';
103        $renderer->doc .= '</span>';
104
105        // Side bubble with comment
106        $renderer->doc .= '<span class="comment-bubble ' . $statusClass . '" data-comment-id="' . $commentNum . '" id="comment-bubble-' . $commentNum . '">';
107        $renderer->doc .= '<span class="comment-num"><a href="#comment-ref-' . $commentNum . '" class="comment-backlink">[' . $commentNum . ']</a></span> ';
108        $renderer->doc .= '<span class="comment-status">' . $emoji . '</span><br>';
109        $renderer->doc .= hsc($data['comment']);
110        $renderer->doc .= '</span>';
111
112        return true;
113    }
114
115    /**
116     * Render list of all comments on the page
117     */
118    protected function renderCommentsList($renderer) {
119        if (empty(self::$comments)) {
120            $renderer->doc .= '<div class="comments-summary"><p><em>There are no comments on this page.</em></p></div>';
121            return;
122        }
123
124        $renderer->doc .= '<div class="comments-summary">';
125        $renderer->doc .= '<h3>Comments on this page</h3>';
126
127        foreach (self::$comments as $comment) {
128            $statusClass = 'comment-' . $comment['status'];
129            $renderer->doc .= '<aside><div name="Comment" class="comment-item ' . $statusClass . '">';
130            $renderer->doc .= '<div class="comment-header">';
131            $renderer->doc .= '<span class="comment-num">[' . $comment['num'] . ']</span> ';
132            $renderer->doc .= '<span class="comment-status-badge ' . $statusClass . '">' . hsc($comment['status']) . '</span>';
133            $renderer->doc .= '</div>';
134            $renderer->doc .= '<div class="comment-text-ref"><strong>Text:</strong> ' . hsc($comment['text']) . '</div>';
135            $renderer->doc .= '<div class="comment-content"><strong>Comment:</strong> ' . hsc($comment['comment']) . '</div>';
136            $renderer->doc .= '</div></aside> ';
137        }
138
139        $renderer->doc .= '</div>';
140    }
141}