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
10/**
11 * Class admin_plugin_blogtng
12 */
13class admin_plugin_blogtng extends DokuWiki_Admin_Plugin {
14
15    /** @var helper_plugin_blogtng_comments */
16    protected $commenthelper = null;
17    /** @var helper_plugin_blogtng_entry */
18    protected $entryhelper   = null;
19    /** @var helper_plugin_blogtng_sqlite */
20    protected $sqlitehelper  = null;
21    /** @var helper_plugin_blogtng_tags */
22    protected $taghelper     = null;
23
24    /**
25     * Determine position in list in admin window
26     * Lower values are sorted up
27     *
28     * @return int
29     */
30    public function getMenuSort() { return 200; }
31
32    /**
33     * Return true for access only by admins (config:superuser) or false if managers are allowed as well
34     *
35     * @return bool
36     */
37    public function forAdminOnly() { return false; }
38
39    /**
40     * Constructor
41     */
42    public function __construct() {
43        $this->commenthelper = plugin_load('helper', 'blogtng_comments');
44        $this->entryhelper   = plugin_load('helper', 'blogtng_entry');
45        $this->sqlitehelper  = plugin_load('helper', 'blogtng_sqlite');
46        $this->taghelper     = plugin_load('helper', 'blogtng_tags');
47    }
48
49    /**
50     * Handles all actions of the admin component
51     *
52     * @author Michael Klier <chi@chimeric.de>
53     */
54    public function handle() {
55        if(!isset($_REQUEST['btng']['admin'])) {
56            $admin = null;
57        } else {
58            $admin = (is_array($_REQUEST['btng']['admin'])) ? key($_REQUEST['btng']['admin']) : $_REQUEST['btng']['admin'];
59        }
60        //skip actions when no valid security token given
61        $noSecTokenNeeded = array('search', 'comment_edit', 'comment_preview', null);
62        if(!in_array($admin, $noSecTokenNeeded) && !checkSecurityToken()) {
63            $admin = null;
64        }
65
66        // handle actions
67        switch($admin) {
68
69            case 'comment_save':
70                // FIXME error handling?
71                $comment = $_REQUEST['btng']['comment'];
72                $this->commenthelper->save($comment);
73                msg($this->getLang('msg_comment_save'), 1);
74                break;
75
76            case 'comment_delete':
77                // FIXME error handling
78                $comment = $_REQUEST['btng']['comment'];
79                $this->commenthelper->delete($comment['cid']);
80                msg($this->getLang('msg_comment_delete'), 1);
81                break;
82
83            case 'comment_batch_edit':
84                $batch = $_REQUEST['btng']['admin']['comment_batch_edit'];
85                $cids  = $_REQUEST['btng']['comments']['cids'];
86                if($cids) {
87                    foreach($cids as $cid) {
88                        switch($batch) {
89                            // FIXME messages
90                            case 'delete':
91                                $this->commenthelper->delete($cid);
92                                msg($this->getLang('msg_comment_delete'), 1);
93                                break;
94                            case 'status_hidden':
95                                $this->commenthelper->moderate($cid, 'hidden');
96                                msg($this->getLang('msg_comment_status_change'), 1);
97                                break;
98                            case 'status_visible':
99                                $this->commenthelper->moderate($cid, 'visible');
100                                msg($this->getLang('msg_comment_status_change'), 1);
101                                break;
102                        }
103                    }
104                }
105                break;
106
107            case 'entry_set_blog':
108                // FIXME errors?
109                $pid = $_REQUEST['btng']['entry']['pid'];
110                $blog = $_REQUEST['btng']['entry']['blog'];
111                if($pid) {
112                    $blogs = $this->entryhelper->get_blogs();
113                    if(in_array($blog, $blogs)) {
114                        $this->entryhelper->load_by_pid($pid);
115                        $this->entryhelper->entry['blog'] = $blog;
116                        $this->entryhelper->save();
117                    }
118                }
119                msg($this->getLang('msg_entry_blog_change'), 1);
120                break;
121
122            case 'entry_set_commentstatus':
123                $pid = $_REQUEST['btng']['entry']['pid'];
124                $status = $_REQUEST['btng']['entry']['commentstatus'];
125                if($pid) {
126                    if(in_array($status, array('disabled', 'enabled', 'closed'))) {
127                        $this->entryhelper->load_by_pid($pid);
128                        $this->entryhelper->entry['commentstatus'] = $status;
129                        $this->entryhelper->save();
130                    }
131                }
132                msg($this->getLang('msg_comment_status_change'), 1);
133                break;
134
135            default:
136                // do nothing - show dashboard
137                break;
138        }
139    }
140
141    /**
142     * Handles the XHTML output of the admin component
143     *
144     * @author Michael Klier <chi@chimeric.de>
145     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
146     */
147    public function html() {
148        global $ID;
149
150        ptln('<h1>'.$this->getLang('menu').'</h1>');
151
152        $admin = (is_array($_REQUEST['btng']['admin'])) ? key($_REQUEST['btng']['admin']) : $_REQUEST['btng']['admin'];
153
154        ptln('<div id="blogtng__admin">');
155
156        // display link back to dashboard
157        if($admin) {
158            ptln('<div class="level1">');
159            ptln('<p><a href="' . wl($ID, array('do'=>'admin', 'page'=>'blogtng')) . '" title="' . $this->getLang('dashboard') . '">&larr; ' . $this->getLang('dashboard') . '</a></p>');
160            ptln('</div>');
161
162        }
163
164        switch($admin) {
165            case 'search':
166                // display search form
167                $this->xhtml_search_form();
168
169                ptln('<h2>' . $this->getLang('searchresults') . '</h2>');
170
171                $query = $_REQUEST['btng']['query'];
172                $query['resultset'] = 'query';
173
174                $this->xhtml_search_results($query);
175                break;
176
177            case 'comment_edit':
178            case 'comment_preview':
179                if($admin == 'comment_edit') {
180                    $obj = $this->commenthelper->comment_by_cid($_REQUEST['btng']['comment']['cid']);
181                    $comment = $obj->data;
182                    if($comment) {
183                        $this->xhtml_comment_edit_form($comment);
184                    }
185                }
186                if($admin == 'comment_preview') {
187                    $this->xhtml_comment_edit_form($_REQUEST['btng']['comment']);
188                    $this->xhtml_comment_preview($_REQUEST['btng']['comment']);
189                }
190                break;
191
192            default:
193                // display search form
194                $this->xhtml_search_form();
195
196                // print latest 'x' comments/entries
197                $query = $_REQUEST['btng']['comment'];
198                $query['resultset'] = 'comment';
199                $this->xhtml_latest_items($query);
200
201                $query = $_REQUEST['btng']['entry'];
202                $query['resultset']  = 'entry';
203                $this->xhtml_latest_items($query);
204                break;
205        }
206
207        ptln('</div>');
208    }
209
210    /**
211     * Displays a list of comments or entries for a given search term
212     *
213     * @param array $query url parameters for query
214     */
215    private function xhtml_search_results($query) {
216        if(!$this->sqlitehelper->ready()) return;
217
218        $db = $this->sqlitehelper->getDB();
219
220        switch($query['filter']) {
221            case 'entry_title':
222            case 'entry_author':
223                $select  = 'SELECT * ';
224                $from    = 'FROM entries ';
225                $orderby = 'ORDER BY created DESC ';
226                $itemdisplaycallback = 'xhtml_entry_list';
227                break;
228            case 'comment':
229            case 'comment_ip':
230                $select = 'SELECT cid, comments.pid as pid, ip, source, name, comments.mail as mail, web, avatar, comments.created as created, text, status ';
231                $from   = 'FROM comments LEFT JOIN entries ON comments.pid = entries.pid ';
232                $orderby = 'ORDER BY comments.created DESC ';
233                $itemdisplaycallback = 'xhtml_comment_list';
234            break;
235            case 'tags':
236                $select = 'SELECT DISTINCT entries.pid as pid, page, title, blog, image, created, lastmod, author, login, mail ';
237                $from   = 'FROM entries  LEFT JOIN tags ON entries.pid = tags.pid ';
238                $orderby = 'ORDER BY created DESC ';
239                $itemdisplaycallback = 'xhtml_entry_list';
240                break;
241            default:
242                return;
243        }
244        $count  = 'SELECT COUNT(*) as count ';
245
246        if(isset($query['blog']) && $query['blog']) {
247            $where = 'WHERE blog = ' . $db->quote_string($query['blog']) . ' ';
248        } else {
249            $where = 'WHERE blog != "" ';
250        }
251
252        if(isset($query['string']) && $query['string'] != '') {
253            switch($query['filter']) {
254                case 'entry_title':
255                    $where .= 'AND ( title LIKE \'%'.$db->escape_string($query['string']).'%\' ) ';
256                    break;
257                case 'entry_author':
258                    $where .= 'AND ( author LIKE \'%'.$db->escape_string($query['string']).'%\' ) ';
259                    break;
260                case 'comment':
261                    $where .= 'AND ( comments.text LIKE \'%'.$db->escape_string($query['string']).'%\' ) ';
262                    break;
263                case 'comment_ip':
264                    $where .= 'AND ( comments.ip LIKE \'%'.$db->escape_string($query['string']).'%\' ) ';
265                    break;
266                case 'tags':
267                    $where .= 'AND ( tags.tag LIKE \'%'.$db->escape_string($query['string']).'%\' ) ';
268                    break;
269            }
270        }
271
272        //comments: if pid is given limit to give page
273        if(isset($query['pid']) && $query['pid'] != '') {
274            $where .= 'AND ( comments.pid = ' . $db->quote_string($query['pid']) . ' ) ';
275        }
276
277        $sqlcount  = $count . $from . $where;
278        $sqlselect = $select . $from . $where . $orderby;
279
280
281        $sqlselect .= ' LIMIT '.$this->getLimitParam($query, 20);
282        $offset = $this->getOffsetParam($query);
283        if($offset > 0) {
284            $sqlselect .= ' OFFSET '.$offset;
285        }
286
287        $res = $db->query($sqlcount);
288        $count = $db->res2single($res);
289
290        $resid = $db->query($sqlselect);
291        if($resid) {
292            $this->xhtml_show_paginated_result($resid, $query, $itemdisplaycallback, $count);
293        }
294    }
295
296    /**
297     * Display paginated results
298     *
299     * @param object $resid    Database resource object
300     * @param array  $query    Query parameters
301     * @param string $itemdisplaycallback called for each item, to display content of item
302     * @param int    $count    Number of total items
303     * @param int    $limit    Number of results to display per page (page size)
304     *
305     * @author Michael Klier <chi@chimeric.de>
306     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
307     */
308    private function xhtml_show_paginated_result($resid, $query, $itemdisplaycallback, $count, $limit = 20) {
309        global $lang;
310        if(!$resid) return;
311
312        $offset = $this->getOffsetParam($query);
313        $currentpage   =  floor($offset / $limit) + 1;
314
315        $items = $this->sqlitehelper->getDB()->res2arr($resid);
316
317        if($items) {
318            ptln('<div class="level2"><p><strong>' . $this->getLang('numhits') . ':</strong> ' . $count .'</p></div>');
319
320            // show pagination only when enough items
321            if($count > $limit) {
322                $this->xhtml_pagination($query, $currentpage, $count, $limit);
323            }
324
325            call_user_func(array($this, $itemdisplaycallback), $items, $query);
326
327        } else {
328            ptln('<div class="level2">');
329            ptln($lang['nothingfound']);
330            ptln('</div>');
331        }
332
333        // show pagination only when enough items
334        if($count > $limit) {
335            $this->xhtml_pagination($query, $currentpage, $count, $limit);
336        }
337    }
338
339    /**
340     * Diplays that pagination links of a query
341     *
342     * @param array  $query       Query parameters
343     * @param int    $currentpage number of current page
344     * @param int    $maximum     maximum number of items available
345     * @param int    $limit       number of items per page
346     *
347     * @author Michael Klier <chi@chimeric.de>
348     */
349    private function xhtml_pagination($query, $currentpage, $maximum, $limit) {
350        $lastpage = (int) ceil($maximum / $limit);
351
352        $pages[] = 1;             // first always
353        $pages[] = $lastpage;     // last page always
354        $pages[] = $currentpage;  // current page always
355
356        if($lastpage > 1){            // if enough pages
357            $pages[] = 2;             // second and ..
358            $pages[] = $lastpage-1;   // one before last
359        }
360
361        // three around current
362        if($currentpage-1 > 0) $pages[] = $currentpage-1;
363        if($currentpage-2 > 0) $pages[] = $currentpage-2;
364        if($currentpage-3 > 0) $pages[] = $currentpage-3;
365        if($currentpage+1 < $lastpage) $pages[] = $currentpage+1;
366        if($currentpage+2 < $lastpage) $pages[] = $currentpage+2;
367        if($currentpage+3 < $lastpage) $pages[] = $currentpage+3;
368
369        $pages = array_unique($pages);
370        sort($pages);
371
372        ptln('<div class="level2"><p>');
373
374        if($currentpage > 1) {
375            $this->xhtml_paginationurl($query, ($currentpage - 2) * $limit, $limit, '&laquo;', $currentpage - 1);
376        }
377
378        $last = 0;
379        foreach($pages as $page) {
380            if($page - $last > 1) {
381                ptln('<span class="sep">...</span>');
382            }
383            if($page == $currentpage) {
384                ptln('<span class="cur">' . $page . '</span>');
385            } else {
386                $this->xhtml_paginationurl($query, ($page - 1) * $limit, $limit, $page, $page);
387            }
388            $last = $page;
389        }
390
391        if($currentpage < $lastpage) {
392            $this->xhtml_paginationurl($query, $currentpage * $limit, $limit, '&raquo;', $currentpage + 1);
393        }
394
395        ptln('</p></div>');
396    }
397
398    /**
399     * Print a pagination link.
400     *
401     * @param array   $query   Query parameters
402     * @param int     $offset  number of previous items
403     * @param int     $limit   number of items at this page
404     * @param string  $text    text of url
405     * @param string  $title   title of url
406     * @internal param string $anchor url anchor
407     */
408    private function xhtml_paginationurl($query, $offset, $limit, $text, $title) {
409        global $ID;
410        list($params, $anchor) = $this->buildUrlParams($query, $offset, $limit);
411        ptln("<a href='".wl($ID, $params).'#'.$anchor."' title='$title'>$text</a>");
412    }
413
414    /**
415     * Build URL parameters.
416     *
417     * @param array  $query   Query parameters
418     * @param int    $offset  number of previous items
419     * @param int    $limit   number of items at this page
420     * @return array($params, $anchor)
421     */
422    private function buildUrlParams($query, $offset, $limit) {
423        $params = array(
424            'do' => 'admin',
425            'page' => 'blogtng',
426            'btng[' . $query['resultset'] . '][limit]' => $limit ,
427            'btng[' . $query['resultset'] . '][offset]' => $offset
428        );
429        $anchor = $query['resultset'] . '_latest';
430
431        if($query['resultset'] == 'query') {
432            $params = $params + array(
433                    'btng[admin]' => 'search',
434                    'btng[query][filter]' => $query['filter'],
435                    'btng[query][blog]'   => $query['blog'],
436                    'btng[query][string]' => $query['string'],
437                    'btng[query][pid]'    => $query['pid']
438                );
439            $anchor = '';
440        }
441        return array($params, $anchor);
442    }
443
444    /**
445     * Display the latest comments or entries
446     *
447     * @param  $query  Query parameters
448     *
449     * @author Michael Klier <chi@chimeric.de>
450     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
451     */
452    private function xhtml_latest_items($query) {
453        $resultset = $query['resultset'];
454
455        printf("<h2 id='{$resultset}_latest'>".$this->getLang($resultset.'_latest').'</h2>', $this->getLimitParam($query));
456        $this->xhtml_limit_form($query);
457
458        if(!$this->sqlitehelper->ready()) return;
459
460        $count = 'SELECT COUNT(pid) as count ';
461        $select = 'SELECT * ';
462
463        if($resultset == 'entry') {
464            $from = 'FROM entries ';
465            $where = 'WHERE blog != "" ';
466            $itemdisplaycallback = 'xhtml_entry_list';
467        } else {
468            $from = 'FROM comments ';
469            $where = '';
470            $itemdisplaycallback = 'xhtml_comment_list';
471        }
472
473        $orderby = 'ORDER BY created DESC ';
474
475        $sqlcount = $count . $from . $where;
476        $sqlselect = $select . $from . $where . $orderby;
477
478        $limit = $this->getLimitParam($query);
479        $offset = $this->getOffsetParam($query);
480        $sqlselect .= 'LIMIT ' . $limit;
481        if($offset) {
482            $sqlselect .= ' OFFSET ' . $offset;
483        }
484
485        $res = $this->sqlitehelper->getDB()->query($sqlcount);
486        $count = $this->sqlitehelper->getDB()->res2single($res);
487
488        $resid = $this->sqlitehelper->getDB()->query($sqlselect);
489        if(!$resid) {
490            return;
491        }
492        $this->xhtml_show_paginated_result($resid, $query, $itemdisplaycallback, $count, $limit);
493    }
494
495    /**
496     * Displays a list of entries, as callback of xhtml_search_results()
497     *
498     * @param $entries Array of entries
499     * @param $query   Query parameters
500     *
501     * @author Michael Klier <chi@chimeric.de>
502     */
503    private function xhtml_entry_list($entries, $query) {
504        ptln('<div class="level2">');
505        ptln('<table class="inline">');
506
507        ptln('<th>' . $this->getLang('created') . '</th>');
508        ptln('<th>' . $this->getLang('author') . '</th>');
509        ptln('<th>' . $this->getLang('entry') . '</th>');
510        ptln('<th>' . $this->getLang('blog') . '</th>');
511        ptln('<th>' . $this->getLang('commentstatus') . '</th>');
512        ptln('<th>' . $this->getLang('comments') . '</th>');
513        ptln('<th>' . $this->getLang('tags') . '</th>');
514        ptln('<th></th>');
515        foreach($entries as $entry) {
516            $this->xhtml_entry_item($entry, $query);
517        }
518        ptln('</table>');
519        ptln('</div>');
520    }
521
522    /**
523     * Displays a single entry and related actions
524     *
525     * @param $entry Single entry
526     * @param $query Query parameters
527     *
528     * @author Michael Klier <chi@chimeric.de>
529     */
530    private function xhtml_entry_item($entry, $query) {
531        global $lang;
532        global $ID;
533
534        static $class = 'odd';
535        ptln('<tr class="' . $class . '">');
536        $class = ($class == 'odd') ? 'even' : 'odd';
537
538        ptln('<td class="entry_created">' . dformat($entry['created']) . '</td>');
539        ptln('<td class="entry_author">' . hsc($entry['author']) . '</td>');
540        ptln('<td class="entry_title">' . html_wikilink(':'.$entry['page'], $entry['title']) . '</td>');
541        ptln('<td class="entry_set_blog">' . $this->xhtml_entry_edit_form($entry, $query, 'blog') . '</th>');
542        ptln('<td class="entry_set_commentstatus">' . $this->xhtml_entry_edit_form($entry, $query, 'commentstatus') . '</th>');
543
544        $this->commenthelper->setPid($entry['pid']);
545
546        // search comments of this entry link
547        ptln('<td class="entry_comments">');
548        $count = $this->commenthelper->get_count(null, true);
549        if($count > 0) {
550            $params = array('do' => 'admin',
551                            'page' => 'blogtng',
552                            'btng[admin]' => 'search',
553                            'btng[query][filter]' => 'comment',
554                            'btng[query][pid]' => $entry['pid']);
555            ptln('<a href="' . wl($ID, $params) . '" title="' . $this->getLang('comments') . '">' . $count . '</a>');
556        } else {
557            ptln($count);
558        }
559        ptln('</td>');
560
561        // tags filter links
562        ptln('<td class="entry_tags">');
563        $this->taghelper->load($entry['pid']);
564        $tags = $this->taghelper->getTags();
565        $count = count($tags);
566        for($i = 0; $i < $count; $i++) {
567            $params = array('do' => 'admin',
568                            'page' => 'blogtng',
569                            'btng[admin]' => 'search',
570                            'btng[query][filter]' => 'tags',
571                            'btng[query][string]' => $tags[$i]);
572            $link = '<a href="' . wl($ID, $params) . '" title="' . $tags[$i] . '">' . $tags[$i] . '</a>';
573            if($i < ($count - 1)) $link .= ', ';
574            ptln($link);
575        }
576        ptln('</td>');
577
578        // edit links
579        ptln('<td class="entry_edit">');
580        $params = array('id' => $entry['page'],
581                        'do' => 'edit');
582        ptln('<a href="' . wl($ID, $params) . '" class="blogtng_btn_edit" title="' . $lang['btn_secedit'] . '">' . $lang['btn_secedit'] . '</a>');
583        ptln('</td>');
584
585        ptln('</tr>');
586    }
587
588    /**
589     * Displays a list of comments, as callback of xhtml_search_results()
590     *
591     * @param $comments List of comments
592     * @param $query    Query parameters
593     *
594     * @author Michael Klier <chi@chimeric.de>
595     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
596     */
597    private function xhtml_comment_list($comments, $query) {
598        global $lang;
599
600        ptln('<div class="level2">');
601
602        ptln('<form action="' . DOKU_SCRIPT . '" method="post" id="blogtng__comment_batch_edit_form">');
603        ptln('<input type="hidden" name="page" value="blogtng" />');
604        ptln('<input type="hidden" name="btng[comment][limit]" value="' .$this->getLimitParam($query). '" />');
605        ptln('<input type="hidden" name="btng[comment][offset]" value="' .$this->getLimitParam($query). '" />');
606        ptln('<input type="hidden" name="sectok" value="' .getSecurityToken(). '" />');
607
608        ptln('<table class="inline">');
609
610        ptln('<th id="blogtng__admin_checkall_th"></th>');
611        ptln('<th>' . $this->getLang('comments') . '</th>');
612        ptln('<th></th>');
613
614        foreach($comments as $comment) {
615            $this->xhtml_comment_item($comment);
616        }
617
618        ptln('</table>');
619
620        ptln('<select name="btng[admin][comment_batch_edit]">');
621            ptln('<option value="status_visible">Visible</option>');
622            ptln('<option value="status_hidden">Hidden</option>');
623            ptln('<option value="delete">'.$lang['btn_delete'].'</option>');
624        ptln('</select>');
625        ptln('<input type="submit" class="edit button" name="do[admin]" value="' . $lang['btn_update'] . '" />');
626        ptln('</form>');
627
628        ptln('</div>');
629    }
630
631    /**
632     * Displays a single comment and related actions
633     *
634     * @param $comment A single comment
635     *
636     * @author Michael Klier <chi@chimeric.de>
637     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
638     */
639    private function xhtml_comment_item($comment) {
640        global $lang;
641        global $ID;
642
643        static $class = 'odd';
644        ptln('<tr class="' . $class . '">');
645        $class = ($class == 'odd') ? 'even' : 'odd';
646
647        $cmt = new blogtng_comment();
648        $cmt->init($comment);
649        ptln('<td class="admin_checkbox">');
650            ptln('<input type="checkbox" class="comment_cid" name="btng[comments][cids][]" value="' . $comment['cid'] . '" />');
651        ptln('</td>');
652
653        ptln('<td class="comment_row">');
654        ptln('<div class="comment_text" title="'.$this->getLang('comment_text').'">' . hsc($comment['text']) . '</div>');
655        ptln('<div class="comment_metadata">');
656            ptln('<span class="comment_created" title="'.$this->getLang('created').'">' . dformat($comment['created']) . '</span>');
657            ptln('<span class="comment_ip" title="'.$this->getLang('comment_ip').'">' . hsc($comment['ip']) . '</span>');
658
659            ptln('<span class="comment_name" title="'.$this->getLang('comment_name').'">');
660                $avatar = $cmt->tpl_avatar(16,16,true);
661                if($avatar) ptln('<img src="' . $avatar . '" alt="' . hsc($comment['name']) . '" class="avatar" /> ');
662                if($comment['mail']){
663                    ptln('<a href="mailto:' . hsc($comment['mail']) . '" class="mail" title="' . hsc($comment['mail']) . '">' . hsc($comment['name']) . '</a>');
664                }else{
665                    ptln(hsc($comment['name']));
666                }
667            ptln('</span>');
668
669            $weburl = '';
670            if($comment['web']) $weburl = '<a href="' . hsc($comment['web']) . '" title="' . hsc($comment['web']) . '">' . hsc($comment['web']) . '</a>';
671            ptln('<span class="comment_web" title="'.$this->getLang('comment_web').'">'.$weburl.'</span>');
672
673            ptln('<span class="comment_status" title="'.$this->getLang('comment_status').'">' . hsc($comment['status']) . '</span>');
674            ptln('<span class="comment_source" title="'.$this->getLang('comment_source').'">' . hsc($comment['source']) . '</span>');
675
676            $this->entryhelper->load_by_pid($comment['pid']);
677            $pagelink = html_wikilink(':'.$this->entryhelper->entry['page'], $this->entryhelper->entry['title']);
678            ptln('<span class="comment_entry" title="'.$this->getLang('comment_entry').'">' . $pagelink . '</span>');
679        ptln('</div>');
680        ptln('</td>');
681
682        ptln('<td class="comment_edit">');
683            $params = array('do'=>'admin',
684                            'page'=>'blogtng',
685                            'btng[comment][cid]'=>$comment['cid'],
686                            'btng[admin]'=>'comment_edit');
687            ptln('<a href="' . wl($ID, $params). '" class="blogtng_btn_edit" title="' . $lang['btn_edit'] . '">' . $lang['btn_secedit'] . '</a>');
688        ptln('</td>');
689
690        ptln('</tr>');
691    }
692
693    /**
694     * Displays a preview of the comment
695     *
696     * @param array $data submitted comment properties
697     */
698    private function xhtml_comment_preview($data) {
699        $this->entryhelper->load_by_pid($data['pid']);
700        $blogname = $this->entryhelper->get_blog();
701
702        ptln('<div id="blogtng__comment_preview">');
703        ptln(p_locale_xhtml('preview'));
704        ptln('<br />');
705        $comment = new blogtng_comment();
706        $comment->init($data);
707        $comment->output($blogname);
708        ptln('</div>');
709    }
710
711    /**
712     * Displays the form to change the comment status of a blog entry
713     *
714     * @param $entry Blog entry
715     * @param $query Query parameters
716     * @param string $field
717     * @return Doku_Form|string
718     *
719     * @author Michael Klier <chi@chimeric.de>
720     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
721     */
722    private function xhtml_entry_edit_form($entry, $query, $field = 'commentstatus') {
723        global $lang;
724
725        $changablefields = array('commentstatus', 'blog');
726        if(!in_array($field, $changablefields)) return hsc($entry[$field]);
727
728        $form = new Doku_Form(array('id'=>"blogtng__entry_set_{$field}_form"));
729        $form->addHidden('do', 'admin');
730        $form->addHidden('page', 'blogtng');
731        $form->addHidden('btng[entry][pid]', $entry['pid']);
732        $form->addHidden('btng[entry][limit]', $this->getLimitParam($query));
733        $form->addHidden('btng[entry][offset]', $this->getOffsetParam($query));
734
735        if($field == 'commentstatus') {
736            $availableoptions = array('enabled', 'disabled', 'closed');
737        } else {
738            $availableoptions = $this->entryhelper->get_blogs();
739        }
740        $form->addElement(form_makeListBoxField("btng[entry][$field]", $availableoptions, $entry[$field], ''));
741        $form->addElement('<input type="submit" name="btng[admin][entry_set_'.$field.']" class="edit button" value="' . $lang['btn_update'] . '" />');
742
743        ob_start();
744        html_form("blotng__btn_entry_set_{$field}", $form);
745        $form = ob_get_clean();
746        return $form;
747    }
748
749    /**
750     * Displays the comment edit form
751     *
752     * @param $comment
753     *
754     * @author Michael Klier <chi@chimeric.de>
755     */
756    private function xhtml_comment_edit_form($comment) {
757        global $lang;
758
759        ptln('<div class="level1">');
760        $form = new Doku_Form(array('id'=>'blogtng__comment_edit_form'));
761        $form->startFieldset($this->getLang('act_comment_edit'));
762        $form->addHidden('page', 'blogtng');
763        $form->addHidden('do', 'admin');
764        $form->addHidden('btng[comment][cid]', $comment['cid']);
765        $form->addHidden('btng[comment][pid]', $comment['pid']);
766        $form->addHidden('btng[comment][created]', $comment['created']);
767        $form->addElement(form_makeListBoxField('btng[comment][status]', array('visible', 'hidden'), $comment['status'], $this->getLang('comment_status')));
768        $form->addElement('<br />');
769        $form->addElement(form_makeListBoxField('btng[comment][source]', array('comment', 'trackback', 'pingback'), $comment['source'], $this->getLang('comment_source')));
770        $form->addElement('<br />');
771        $form->addElement(form_makeTextField('btng[comment][name]', $comment['name'], $this->getLang('comment_name')));
772        $form->addElement('<br />');
773        $form->addElement(form_makeTextField('btng[comment][mail]', $comment['mail'], $this->getLang('comment_mail')));
774        $form->addElement('<br />');
775        $form->addElement(form_makeTextField('btng[comment][web]', $comment['web'], $this->getLang('comment_web')));
776        $form->addElement('<br />');
777        $form->addElement(form_makeTextField('btng[comment][avatar]', $comment['avatar'], $this->getLang('comment_avatar')));
778        $form->addElement('<br />');
779        $form->addElement('<textarea class="edit" name="btng[comment][text]" rows="10" cols="80">' . $comment['text'] . '</textarea>');
780        $form->addElement('<input type="submit" name="btng[admin][comment_save]" class="edit button" value="' . $lang['btn_save'] . '" />');
781        $form->addElement('<input type="submit" name="btng[admin][comment_preview]" class="edit button" value="' . $lang['btn_preview'] . '" />');
782        $form->addElement('<input type="submit" name="btng[admin][comment_delete]" class="edit button" value="' . $lang['btn_delete'] . '" />');
783        $form->endFieldset();
784        html_form('blogtng__edit_comment', $form);
785        ptln('</div>');
786    }
787
788    /**
789     * Displays the search form
790     *
791     * @author Michael Klier <chi@chimeric.de>
792     */
793    private function xhtml_search_form() {
794        global $lang;
795
796        ptln('<div class="level1">');
797
798        $blogs = $this->entryhelper->get_blogs();
799
800        $form = new Doku_Form(array('id'=>'blogtng__search_form'));
801        $form->startFieldset($lang['btn_search']);
802        $form->addHidden('page', 'blogtng');
803        $form->addHidden('btng[admin]', 'search');
804
805        $form->addElement(form_makeListBoxField('btng[query][blog]', $blogs, $_REQUEST['btng']['query']['blog'], $this->getLang('blog')));
806        $form->addElement(form_makeListBoxField('btng[query][filter]', array('entry_title', 'entry_author', 'comment', 'comment_ip', 'tags'), $_REQUEST['btng']['query']['filter'], $this->getLang('filter')));
807        $form->addElement(form_makeTextField('btng[query][string]', $_REQUEST['btng']['query']['string'],''));
808
809        $form->addElement(form_makeButton('submit', 'admin', $lang['btn_search']));
810        $form->endFieldset();
811        html_form('blogtng__search_form', $form);
812
813        ptln('</div>');
814    }
815
816    /**
817     * Displays the limit selection form
818     *
819     * @param string $query
820     *
821     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
822     */
823    private function xhtml_limit_form($query='') {
824        global $lang;
825
826        $limit = $this->getLimitParam($query);
827        $resultset = $query['resultset'];
828
829        ptln('<div class="level1">');
830
831        $form = new Doku_Form(array('id'=>'blogtng__'.$resultset.'_limit_form'));
832        $form->startFieldset("");
833        $form->addHidden('page', 'blogtng');
834        $form->addElement(
835                form_makeListBoxField("btng[{$resultset}][limit]",
836                    array(5,10,15,20,25,30,40,50,100),
837                    $limit,
838                    $this->getLang('numhits')));
839        $form->addHidden("btng[{$resultset}][offset]", $this->getOffsetParam($query));
840        $form->addElement(form_makeButton('submit', 'admin', $lang['btn_update']));
841        $form->endFieldset();
842        html_form('blogtng__'.$resultset.'_cnt_form', $form);
843
844        ptln('</div>');
845    }
846
847    /**
848     * Get submitted value of items per page
849     *
850     * @param array $query   url parameters
851     * @param int   $default value
852     * @return int number of items per page
853     *
854     * @author hArpanet <dokuwiki-blogtng@harpanet.com>
855     */
856    private function getLimitParam($query, $default = 5) {
857        return (int) (isset($query['limit']) ? $query['limit'] : $default);
858    }
859
860    /**
861     * Get the offset of the pagination
862     *
863     * @param array $query    url parameters
864     * @param int   $default  value
865     * @return int offset number of items
866     */
867    public function getOffsetParam($query, $default = 0) {
868        return (int) (isset($query['offset']) ? $query['offset'] : $default);
869    }
870}
871// vim:ts=4:sw=4:et:
872