xref: /plugin/discussion/helper.php (revision 138eaa04d083d045f49bf5663c9caed513db644a)
1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Esther Brunner <wikidesign@gmail.com>
5 */
6
7// must be run within Dokuwiki
8if (!defined('DOKU_INC')) die();
9
10if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
11if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
12
13class helper_plugin_discussion extends DokuWiki_Plugin {
14
15    function getInfo() {
16        return array(
17                'author' => 'Gina Häußge, Michael Klier, Esther Brunner',
18                'email'  => 'dokuwiki@chimeric.de',
19                'date'   => @file_get_contents(DOKU_PLUGIN.'discussion/VERSION'),
20                'name'   => 'Discussion Plugin (helper class)',
21                'desc'   => 'Functions to get info about comments to a wiki page',
22                'url'    => 'http://wiki.splitbrain.org/plugin:discussion',
23                );
24    }
25
26    function getMethods() {
27        $result = array();
28        $result[] = array(
29                'name'   => 'th',
30                'desc'   => 'returns the header of the comments column for pagelist',
31                'return' => array('header' => 'string'),
32                );
33        $result[] = array(
34                'name'   => 'td',
35                'desc'   => 'returns the link to the discussion section with number of comments',
36                'params' => array(
37                    'id' => 'string',
38                    'number of comments (optional)' => 'integer'),
39                'return' => array('link' => 'string'),
40                );
41        $result[] = array(
42                'name'   => 'getThreads',
43                'desc'   => 'returns pages with discussion sections, sorted by recent comments',
44                'params' => array(
45                    'namespace' => 'string',
46                    'number (optional)' => 'integer'),
47                'return' => array('pages' => 'array'),
48                );
49        $result[] = array(
50                'name'   => 'getComments',
51                'desc'   => 'returns recently added or edited comments individually',
52                'params' => array(
53                    'namespace' => 'string',
54                    'number (optional)' => 'integer'),
55                'return' => array('pages' => 'array'),
56                );
57        return $result;
58    }
59
60    /**
61     * Returns the column header for the Pagelist Plugin
62     */
63    function th() {
64        return $this->getLang('discussion');
65    }
66
67    /**
68     * Returns the link to the discussion section of a page
69     */
70    function td($id, $num = NULL) {
71        $section = '#discussion__section';
72
73        if (!isset($num)) {
74            $cfile = metaFN($id, '.comments');
75            $comments = unserialize(io_readFile($cfile, false));
76
77            $num = $comments['number'];
78            if ((!$comments['status']) || (($comments['status'] == 2) && (!$num))) return '';
79        }
80
81        if ($num == 0) $comment = '0&nbsp;'.$this->getLang('nocomments');
82        elseif ($num == 1) $comment = '1&nbsp;'.$this->getLang('comment');
83        else $comment = $num.'&nbsp;'.$this->getLang('comments');
84
85        return '<a href="'.wl($id).$section.'" class="wikilink1" title="'.$id.$section.'">'.
86            $comment.'</a>';
87    }
88
89    /**
90     * Returns an array of pages with discussion sections, sorted by recent comments
91     */
92    function getThreads($ns, $num = NULL) {
93        global $conf;
94
95        require_once(DOKU_INC.'inc/search.php');
96
97        $dir = $conf['datadir'].($ns ? '/'.str_replace(':', '/', $ns): '');
98
99        // returns the list of pages in the given namespace and it's subspaces
100        $items = array();
101        search($items, $dir, 'search_allpages', '');
102
103        // add pages with comments to result
104        $result = array();
105        foreach ($items as $item) {
106            $id   = ($ns ? $ns.':' : '').$item['id'];
107
108            // some checks
109            $perm = auth_quickaclcheck($id);
110            if ($perm < AUTH_READ) continue;    // skip if no permission
111            $file = metaFN($id, '.comments');
112            if (!@file_exists($file)) continue; // skip if no comments file
113            $data = unserialize(io_readFile($file, false));
114            $status = $data['status'];
115            $number = $data['number']; // skip if comments are off or closed without comments
116            if (!$status || (($status == 2) && (!$number))) continue;
117
118            $date = filemtime($file);
119            $meta = p_get_metadata($id);
120            $result[$date] = array(
121                    'id'       => $id,
122                    'file'     => $file,
123                    'title'    => $meta['title'],
124                    'date'     => $date,
125                    'user'     => $meta['creator'],
126                    'desc'     => $meta['description']['abstract'],
127                    'num'      => $number,
128                    'comments' => $this->td($id, $number),
129                    'status'   => $status,
130                    'perm'     => $perm,
131                    'exists'   => true,
132                    'anchor'   => 'discussion__section',
133                    );
134        }
135
136        // finally sort by time of last comment
137        krsort($result);
138
139        if (is_numeric($num)) $result = array_slice($result, 0, $num);
140
141        return $result;
142    }
143
144    /**
145     * Returns an array of recently added comments to a given page or namespace
146     */
147    function getComments($ns, $num = NULL) {
148        global $conf;
149
150        $first  = $_REQUEST['first'];
151        if (!is_numeric($first)) $first = 0;
152
153        if ((!$num) || (!is_numeric($num))) $num = $conf['recent'];
154
155        $result = array();
156        $count  = 0;
157
158        if (!@file_exists($conf['metadir'].'/_comments.changes')) return $result;
159
160        // read all recent changes. (kept short)
161        $lines = file($conf['metadir'].'/_comments.changes');
162
163        $seen = array(); //caches seen pages in order to skip them
164        // handle lines
165        $line_num = count($lines);
166        for ($i = ($line_num - 1); $i >= 0; $i--) {
167            $rec = $this->_handleRecentComment($lines[$i], $ns, $seen);
168            if ($rec !== false) {
169                if (--$first >= 0) continue; // skip first entries
170                $result[$rec['date']] = $rec;
171                $count++;
172                // break when we have enough entries
173                if ($count >= $num) break;
174            }
175        }
176
177        // finally sort by time of last comment
178        krsort($result);
179
180        return $result;
181    }
182
183    /* ---------- Changelog function adapted for the Discussion Plugin ---------- */
184
185    /**
186     * Internal function used by $this->getComments()
187     *
188     * don't call directly
189     *
190     * @see getRecentComments()
191     * @author Andreas Gohr <andi@splitbrain.org>
192     * @author Ben Coburn <btcoburn@silicodon.net>
193     * @author Esther Brunner <wikidesign@gmail.com>
194     */
195    function _handleRecentComment($line, $ns, &$seen) {
196        if (empty($line)) return false;  //skip empty lines
197
198        // split the line into parts
199        $recent = parseChangelogLine($line);
200        if ($recent === false) return false;
201
202        $cid     = $recent['extra'];
203        $fullcid = $recent['id'].'#'.$recent['extra'];
204
205        // skip seen ones
206        if (isset($seen[$fullcid])) return false;
207
208        // skip 'show comment' log entries
209        if ($recent['type'] === 'sc') return false;
210
211        // remember in seen to skip additional sights
212        $seen[$fullcid] = 1;
213
214        // check if it's a hidden page or comment
215        if (isHiddenPage($recent['id'])) return false;
216        if ($recent['type'] === 'hc') return false;
217
218        // filter namespace or id
219        if (($ns) && (strpos($recent['id'].':', $ns.':') !== 0)) return false;
220
221        // check ACL
222        $recent['perm'] = auth_quickaclcheck($recent['id']);
223        if ($recent['perm'] < AUTH_READ) return false;
224
225        // check existance
226        $recent['file'] = wikiFN($recent['id']);
227        $recent['exists'] = @file_exists($recent['file']);
228        if (!$recent['exists']) return false;
229        if ($recent['type'] === 'dc') return false;
230
231        // get discussion meta file name
232        $data = unserialize(io_readFile(metaFN($recent['id'], '.comments'), false));
233
234        // check if discussion is turned off
235        if ($data['status'] === 0) return false;
236
237        // check if the comment still exists
238        if (!isset($data['comments'][$cid])) return false;
239
240        // okay, then add some additional info
241        if (is_array($data['comments'][$cid]['user']))
242            $recent['name'] = $data['comments'][$cid]['user']['name'];
243        else $recent['name'] = $data['comments'][$cid]['name'];
244        $recent['desc'] = strip_tags($data['comments'][$cid]['xhtml']);
245        $recent['anchor'] = 'comment__'.$cid;
246
247        return $recent;
248    }
249}
250// vim:ts=4:sw=4:et:enc=utf-8:
251