1a768ba62Swikidesign<?php 2264b7327Swikidesign/** 3264b7327Swikidesign * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 4264b7327Swikidesign * @author Esther Brunner <wikidesign@gmail.com> 5264b7327Swikidesign */ 6264b7327Swikidesign 7264b7327Swikidesign// must be run within Dokuwiki 8264b7327Swikidesignif (!defined('DOKU_INC')) die(); 9264b7327Swikidesign 10de7e6f00SGerrit Uitslag/** 11de7e6f00SGerrit Uitslag * Class admin_plugin_discussion 12de7e6f00SGerrit Uitslag */ 13a768ba62Swikidesignclass admin_plugin_discussion extends DokuWiki_Admin_Plugin { 14a768ba62Swikidesign 15de7e6f00SGerrit Uitslag /** 16de7e6f00SGerrit Uitslag * @return int 17de7e6f00SGerrit Uitslag */ 18264b7327Swikidesign function getMenuSort() { return 200; } 19de7e6f00SGerrit Uitslag 20de7e6f00SGerrit Uitslag /** 21de7e6f00SGerrit Uitslag * @return bool 22de7e6f00SGerrit Uitslag */ 23264b7327Swikidesign function forAdminOnly() { return false; } 24a768ba62Swikidesign 25573e23a1Swikidesign function handle() { 26573e23a1Swikidesign global $lang; 27573e23a1Swikidesign 28573e23a1Swikidesign $cid = $_REQUEST['cid']; 29573e23a1Swikidesign if (is_array($cid)) $cid = array_keys($cid); 30573e23a1Swikidesign 31de7e6f00SGerrit Uitslag /** @var action_plugin_discussion $action */ 32573e23a1Swikidesign $action =& plugin_load('action', 'discussion'); 33573e23a1Swikidesign if (!$action) return; // couldn't load action plugin component 34573e23a1Swikidesign 35573e23a1Swikidesign switch ($_REQUEST['comment']) { 36573e23a1Swikidesign case $lang['btn_delete']: 37de7e6f00SGerrit Uitslag $action->save($cid, ''); 38573e23a1Swikidesign break; 39573e23a1Swikidesign 40573e23a1Swikidesign case $this->getLang('btn_show'): 41de7e6f00SGerrit Uitslag $action->save($cid, '', 'show'); 42573e23a1Swikidesign break; 43573e23a1Swikidesign 44573e23a1Swikidesign case $this->getLang('btn_hide'): 45de7e6f00SGerrit Uitslag $action->save($cid, '', 'hide'); 46573e23a1Swikidesign break; 47573e23a1Swikidesign 48573e23a1Swikidesign case $this->getLang('btn_change'): 496fd05bf5Swikidesign $this->_changeStatus($_REQUEST['status']); 50573e23a1Swikidesign break; 51573e23a1Swikidesign } 52573e23a1Swikidesign } 53573e23a1Swikidesign 54a768ba62Swikidesign function html() { 55264b7327Swikidesign global $conf; 56264b7327Swikidesign 57573e23a1Swikidesign $first = $_REQUEST['first']; 58573e23a1Swikidesign if (!is_numeric($first)) $first = 0; 59bc3cb1d5SMichael Klier $num = ($conf['recent']) ? $conf['recent'] : 20; 60264b7327Swikidesign 61573e23a1Swikidesign ptln('<h1>'.$this->getLang('menu').'</h1>'); 62264b7327Swikidesign 63573e23a1Swikidesign $threads = $this->_getThreads(); 64573e23a1Swikidesign 65573e23a1Swikidesign // slice the needed chunk of discussion pages 66573e23a1Swikidesign $more = ((count($threads) > ($first + $num)) ? true : false); 67573e23a1Swikidesign $threads = array_slice($threads, $first, $num); 68573e23a1Swikidesign 69264b7327Swikidesign foreach ($threads as $thread) { 70573e23a1Swikidesign $comments = $this->_getComments($thread); 71573e23a1Swikidesign $this->_threadHead($thread); 72573e23a1Swikidesign if ($comments === false) { 736fd05bf5Swikidesign ptln('</div>', 6); // class="level2" 74264b7327Swikidesign continue; 75264b7327Swikidesign } 76264b7327Swikidesign 776fd05bf5Swikidesign ptln('<form method="post" action="'.wl($thread['id']).'">', 8); 786fd05bf5Swikidesign ptln('<div class="no">', 10); 796fd05bf5Swikidesign ptln('<input type="hidden" name="do" value="admin" />', 10); 806fd05bf5Swikidesign ptln('<input type="hidden" name="page" value="discussion" />', 10); 81264b7327Swikidesign echo html_buildlist($comments, 'admin_discussion', array($this, '_commentItem'), array($this, '_li_comment')); 82573e23a1Swikidesign $this->_actionButtons($thread['id']); 83264b7327Swikidesign } 84573e23a1Swikidesign $this->_browseDiscussionLinks($more, $first, $num); 856fd05bf5Swikidesign 86264b7327Swikidesign } 87264b7327Swikidesign 88264b7327Swikidesign /** 89573e23a1Swikidesign * Returns an array of pages with discussion sections, sorted by recent comments 90e7ac9adaSGerrit Uitslag * 91e7ac9adaSGerrit Uitslag * @return array 92573e23a1Swikidesign */ 93573e23a1Swikidesign function _getThreads() { 94573e23a1Swikidesign global $conf; 95573e23a1Swikidesign 96573e23a1Swikidesign require_once(DOKU_INC.'inc/search.php'); 97573e23a1Swikidesign 98573e23a1Swikidesign // returns the list of pages in the given namespace and it's subspaces 99573e23a1Swikidesign $items = array(); 100387bc4d2SMichael Hamann search($items, $conf['datadir'], 'search_allpages', array()); 101573e23a1Swikidesign 102573e23a1Swikidesign // add pages with comments to result 103573e23a1Swikidesign $result = array(); 104573e23a1Swikidesign foreach ($items as $item) { 105573e23a1Swikidesign $id = $item['id']; 106573e23a1Swikidesign 107573e23a1Swikidesign // some checks 108573e23a1Swikidesign $file = metaFN($id, '.comments'); 109573e23a1Swikidesign if (!@file_exists($file)) continue; // skip if no comments file 110573e23a1Swikidesign 111573e23a1Swikidesign $date = filemtime($file); 11271a0ae95SGina Haeussge $result[] = array( 113573e23a1Swikidesign 'id' => $id, 114573e23a1Swikidesign 'file' => $file, 115573e23a1Swikidesign 'date' => $date, 116573e23a1Swikidesign ); 117573e23a1Swikidesign } 118573e23a1Swikidesign 119573e23a1Swikidesign // finally sort by time of last comment 12071a0ae95SGina Haeussge usort($result, array('admin_plugin_discussion', '_threadCmp')); 121573e23a1Swikidesign 122573e23a1Swikidesign return $result; 123573e23a1Swikidesign } 124573e23a1Swikidesign 125573e23a1Swikidesign /** 12671a0ae95SGina Haeussge * Callback for comparison of thread data. 12771a0ae95SGina Haeussge * 12871a0ae95SGina Haeussge * Used for sorting threads in descending order by date of last comment. 12971a0ae95SGina Haeussge * If this date happens to be equal for the compared threads, page id 13071a0ae95SGina Haeussge * is used as second comparison attribute. 131e7ac9adaSGerrit Uitslag * 132e7ac9adaSGerrit Uitslag * @param array $a 133e7ac9adaSGerrit Uitslag * @param array $b 134e7ac9adaSGerrit Uitslag * @return int 13571a0ae95SGina Haeussge */ 13671a0ae95SGina Haeussge function _threadCmp($a, $b) { 13771a0ae95SGina Haeussge if ($a['date'] == $b['date']) { 13871a0ae95SGina Haeussge return strcmp($a['id'], $b['id']); 13971a0ae95SGina Haeussge } 14071a0ae95SGina Haeussge return ($a['date'] < $b['date']) ? 1 : -1; 14171a0ae95SGina Haeussge } 14271a0ae95SGina Haeussge 14371a0ae95SGina Haeussge /** 144573e23a1Swikidesign * Outputs header, page ID and status of a discussion thread 145e7ac9adaSGerrit Uitslag * 146e7ac9adaSGerrit Uitslag * @param array $thread 147e7ac9adaSGerrit Uitslag * @return bool 148264b7327Swikidesign */ 149264b7327Swikidesign function _threadHead($thread) { 150573e23a1Swikidesign $id = $thread['id']; 151573e23a1Swikidesign 152573e23a1Swikidesign $labels = array( 153573e23a1Swikidesign 0 => $this->getLang('off'), 154573e23a1Swikidesign 1 => $this->getLang('open'), 155573e23a1Swikidesign 2 => $this->getLang('closed') 156264b7327Swikidesign ); 1576fd05bf5Swikidesign $title = p_get_metadata($id, 'title'); 158*4cded5e1SGerrit Uitslag if (!$title) { 159*4cded5e1SGerrit Uitslag $title = $id; 160*4cded5e1SGerrit Uitslag } 1616fd05bf5Swikidesign ptln('<h2 name="'.$id.'" id="'.$id.'">'.hsc($title).'</h2>', 6); 1626fd05bf5Swikidesign ptln('<form method="post" action="'.wl($id).'">', 6); 1636fd05bf5Swikidesign ptln('<div class="mediaright">', 8); 1646fd05bf5Swikidesign ptln('<input type="hidden" name="do" value="admin" />', 10); 1656fd05bf5Swikidesign ptln('<input type="hidden" name="page" value="discussion" />', 10); 1666fd05bf5Swikidesign ptln($this->getLang('status').': <select name="status" size="1">', 10); 167573e23a1Swikidesign foreach ($labels as $key => $label) { 168573e23a1Swikidesign $selected = (($key == $thread['status']) ? ' selected="selected"' : ''); 1696fd05bf5Swikidesign ptln('<option value="'.$key.'"'.$selected.'>'.$label.'</option>', 12); 170573e23a1Swikidesign } 1716fd05bf5Swikidesign ptln('</select> ', 10); 17214382ebaSMichael Klier ptln('<input type="submit" class="button" name="comment" value="'.$this->getLang('btn_change').'" class"button" title="'.$this->getLang('btn_change').'" />', 10); 1736fd05bf5Swikidesign ptln('</div>', 8); 1746fd05bf5Swikidesign ptln('</form>', 6); 1756fd05bf5Swikidesign ptln('<div class="level2">', 6); 1766fd05bf5Swikidesign ptln('<a href="'.wl($id).'" class="wikilink1">'.$id.'</a> ', 8); 177573e23a1Swikidesign return true; 178573e23a1Swikidesign } 179573e23a1Swikidesign 180573e23a1Swikidesign /** 181573e23a1Swikidesign * Returns the full comments data for a given wiki page 182e7ac9adaSGerrit Uitslag * 183e7ac9adaSGerrit Uitslag * @param array $thread 184e7ac9adaSGerrit Uitslag * @return array|bool 185573e23a1Swikidesign */ 186573e23a1Swikidesign function _getComments(&$thread) { 187573e23a1Swikidesign $id = $thread['id']; 188573e23a1Swikidesign 189*4cded5e1SGerrit Uitslag if (!$thread['file']) { 190*4cded5e1SGerrit Uitslag $thread['file'] = metaFN($id, '.comments'); 191*4cded5e1SGerrit Uitslag } 192573e23a1Swikidesign if (!@file_exists($thread['file'])) return false; // no discussion thread at all 193573e23a1Swikidesign 194573e23a1Swikidesign $data = unserialize(io_readFile($thread['file'], false)); 195573e23a1Swikidesign 196573e23a1Swikidesign $thread['status'] = $data['status']; 197573e23a1Swikidesign $thread['number'] = $data['number']; 198573e23a1Swikidesign if (!$data['status']) return false; // comments are turned off 1996fd05bf5Swikidesign if (!$data['comments']) return false; // no comments 200573e23a1Swikidesign 201573e23a1Swikidesign $result = array(); 202573e23a1Swikidesign foreach ($data['comments'] as $cid => $comment) { 203573e23a1Swikidesign $this->_addComment($cid, $data, $result); 204573e23a1Swikidesign } 205573e23a1Swikidesign 206*4cded5e1SGerrit Uitslag if (empty($result)) { 207*4cded5e1SGerrit Uitslag return false; 208*4cded5e1SGerrit Uitslag } else { 209*4cded5e1SGerrit Uitslag return $result; 210*4cded5e1SGerrit Uitslag } 211573e23a1Swikidesign } 212573e23a1Swikidesign 213573e23a1Swikidesign /** 214573e23a1Swikidesign * Recursive function to add the comment hierarchy to the result 215e7ac9adaSGerrit Uitslag * 216e7ac9adaSGerrit Uitslag * @param string $cid 217e7ac9adaSGerrit Uitslag * @param array $data 218e7ac9adaSGerrit Uitslag * @param array $result 219e7ac9adaSGerrit Uitslag * @param string $parent 220e7ac9adaSGerrit Uitslag * @param int $level 221573e23a1Swikidesign */ 222573e23a1Swikidesign function _addComment($cid, &$data, &$result, $parent = '', $level = 1) { 223573e23a1Swikidesign if (!is_array($data['comments'][$cid])) return; // corrupt datatype 224573e23a1Swikidesign $comment = $data['comments'][$cid]; 225573e23a1Swikidesign if ($comment['parent'] != $parent) return; // answer to another comment 226573e23a1Swikidesign 227573e23a1Swikidesign // okay, add the comment to the result 228573e23a1Swikidesign $comment['id'] = $cid; 229573e23a1Swikidesign $comment['level'] = $level; 230573e23a1Swikidesign $result[] = $comment; 231573e23a1Swikidesign 232573e23a1Swikidesign // check answers to this comment 233573e23a1Swikidesign if (count($comment['replies'])) { 234573e23a1Swikidesign foreach ($comment['replies'] as $rid) { 235573e23a1Swikidesign $this->_addComment($rid, $data, $result, $cid, $level + 1); 236573e23a1Swikidesign } 237573e23a1Swikidesign } 238264b7327Swikidesign } 239264b7327Swikidesign 240264b7327Swikidesign /** 241264b7327Swikidesign * Checkbox and info about a comment item 242e7ac9adaSGerrit Uitslag * 243e7ac9adaSGerrit Uitslag * @param array $comment 244e7ac9adaSGerrit Uitslag * @return string 245264b7327Swikidesign */ 246264b7327Swikidesign function _commentItem($comment) { 247264b7327Swikidesign global $conf; 248264b7327Swikidesign 249264b7327Swikidesign // prepare variables 250264b7327Swikidesign if (is_array($comment['user'])) { // new format 251264b7327Swikidesign $name = $comment['user']['name']; 252264b7327Swikidesign $mail = $comment['user']['mail']; 253264b7327Swikidesign } else { // old format 254264b7327Swikidesign $name = $comment['name']; 255264b7327Swikidesign $mail = $comment['mail']; 256264b7327Swikidesign } 257264b7327Swikidesign if (is_array($comment['date'])) { // new format 258264b7327Swikidesign $created = $comment['date']['created']; 259264b7327Swikidesign } else { // old format 260264b7327Swikidesign $created = $comment['date']; 261264b7327Swikidesign } 262573e23a1Swikidesign $abstract = preg_replace('/\s+?/', ' ', strip_tags($comment['xhtml'])); 263*4cded5e1SGerrit Uitslag if (utf8_strlen($abstract) > 160) { 264*4cded5e1SGerrit Uitslag $abstract = utf8_substr($abstract, 0, 160).'...'; 265*4cded5e1SGerrit Uitslag } 266264b7327Swikidesign 267573e23a1Swikidesign return '<input type="checkbox" name="cid['.$comment['id'].']" value="1" /> '. 268f014bc86SMichael Klier $this->email($mail, $name, 'email').', '.strftime($conf['dformat'], $created).': '. 269573e23a1Swikidesign '<span class="abstract">'.$abstract.'</span>'; 270264b7327Swikidesign } 271264b7327Swikidesign 272264b7327Swikidesign /** 273264b7327Swikidesign * list item tag 274e7ac9adaSGerrit Uitslag * 275e7ac9adaSGerrit Uitslag * @param array $comment 276e7ac9adaSGerrit Uitslag * @return string 277264b7327Swikidesign */ 278264b7327Swikidesign function _li_comment($comment) { 279264b7327Swikidesign $show = ($comment['show'] ? '' : ' hidden'); 280264b7327Swikidesign return '<li class="level'.$comment['level'].$show.'">'; 281264b7327Swikidesign } 282264b7327Swikidesign 283264b7327Swikidesign /** 284264b7327Swikidesign * Show buttons to bulk remove, hide or show comments 285e7ac9adaSGerrit Uitslag * 286e7ac9adaSGerrit Uitslag * @param string $id 287e7ac9adaSGerrit Uitslag * @return bool 288264b7327Swikidesign */ 289264b7327Swikidesign function _actionButtons($id) { 290264b7327Swikidesign global $lang; 291264b7327Swikidesign 2926fd05bf5Swikidesign ptln('<div class="comment_buttons">', 12); 2936fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="'.$this->getLang('btn_show').'" class="button" title="'.$this->getLang('btn_show').'" />', 14); 2946fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="'.$this->getLang('btn_hide').'" class="button" title="'.$this->getLang('btn_hide').'" />', 14); 2956fd05bf5Swikidesign ptln('<input type="submit" name="comment" value="'.$lang['btn_delete'].'" class="button" title="'.$lang['btn_delete'].'" />', 14); 2966fd05bf5Swikidesign ptln('</div>', 12); // class="comment_buttons" 2976fd05bf5Swikidesign ptln('</div>', 10); // class="no" 2986fd05bf5Swikidesign ptln('</form>', 8); 2996fd05bf5Swikidesign ptln('</div>', 6); // class="level2" 300573e23a1Swikidesign return true; 301573e23a1Swikidesign } 302573e23a1Swikidesign 303573e23a1Swikidesign /** 304573e23a1Swikidesign * Displays links to older newer discussions 305e7ac9adaSGerrit Uitslag * 306e7ac9adaSGerrit Uitslag * @param bool $more 307e7ac9adaSGerrit Uitslag * @param int $first 308e7ac9adaSGerrit Uitslag * @param int $num 309e7ac9adaSGerrit Uitslag * @return bool 310573e23a1Swikidesign */ 311573e23a1Swikidesign function _browseDiscussionLinks($more, $first, $num) { 312573e23a1Swikidesign global $ID; 313573e23a1Swikidesign 3143e02b3ffSwikidesign if (($first == 0) && (!$more)) return true; 3153e02b3ffSwikidesign 316573e23a1Swikidesign $params = array('do' => 'admin', 'page' => 'discussion'); 317573e23a1Swikidesign $last = $first+$num; 3186fd05bf5Swikidesign ptln('<div class="level1">', 8); 319de7e6f00SGerrit Uitslag $ret = ''; 320573e23a1Swikidesign if ($first > 0) { 321573e23a1Swikidesign $first -= $num; 322*4cded5e1SGerrit Uitslag if ($first < 0) { 323*4cded5e1SGerrit Uitslag $first = 0; 324*4cded5e1SGerrit Uitslag } 325573e23a1Swikidesign $params['first'] = $first; 3266fd05bf5Swikidesign ptln('<p class="centeralign">', 8); 327573e23a1Swikidesign $ret = '<a href="'.wl($ID, $params).'" class="wikilink1"><< '.$this->getLang('newer').'</a>'; 328573e23a1Swikidesign if ($more) { 329573e23a1Swikidesign $ret .= ' | '; 330573e23a1Swikidesign } else { 3316fd05bf5Swikidesign ptln($ret, 10); 3326fd05bf5Swikidesign ptln('</p>', 8); 333573e23a1Swikidesign } 334573e23a1Swikidesign } else if ($more) { 3356fd05bf5Swikidesign ptln('<p class="centeralign">', 8); 336573e23a1Swikidesign } 337573e23a1Swikidesign if ($more) { 338573e23a1Swikidesign $params['first'] = $last; 339573e23a1Swikidesign $ret .= '<a href="'.wl($ID, $params).'" class="wikilink1">'.$this->getLang('older').' >></a>'; 3406fd05bf5Swikidesign ptln($ret, 10); 3416fd05bf5Swikidesign ptln('</p>', 8); 342573e23a1Swikidesign } 3436fd05bf5Swikidesign ptln('</div>', 6); // class="level1" 344573e23a1Swikidesign return true; 345573e23a1Swikidesign } 346573e23a1Swikidesign 347573e23a1Swikidesign /** 348573e23a1Swikidesign * Changes the status of a comment 349e7ac9adaSGerrit Uitslag * 350b131df5eSGerrit Uitslag * @param $new 351e7ac9adaSGerrit Uitslag * @return bool 352573e23a1Swikidesign */ 353573e23a1Swikidesign function _changeStatus($new) { 354573e23a1Swikidesign global $ID; 355573e23a1Swikidesign 356573e23a1Swikidesign // get discussion meta file name 357573e23a1Swikidesign $file = metaFN($ID, '.comments'); 358573e23a1Swikidesign $data = unserialize(io_readFile($file, false)); 359573e23a1Swikidesign 360573e23a1Swikidesign $old = $data['status']; 361573e23a1Swikidesign if ($old == $new) return true; 362573e23a1Swikidesign 363573e23a1Swikidesign // save the comment metadata file 364573e23a1Swikidesign $data['status'] = $new; 365573e23a1Swikidesign io_saveFile($file, serialize($data)); 366573e23a1Swikidesign 367573e23a1Swikidesign // look for ~~DISCUSSION~~ command in page file and change it accordingly 3683e02b3ffSwikidesign $patterns = array('~~DISCUSSION:off\2~~', '~~DISCUSSION\2~~', '~~DISCUSSION:closed\2~~'); 369573e23a1Swikidesign $replace = $patterns[$new]; 3703e02b3ffSwikidesign $wiki = preg_replace('/~~DISCUSSION([\w:]*)(\|?.*?)~~/', $replace, rawWiki($ID)); 371573e23a1Swikidesign saveWikiText($ID, $wiki, $this->getLang('statuschanged'), true); 372573e23a1Swikidesign 373573e23a1Swikidesign return true; 374264b7327Swikidesign } 3755ef1705fSiLoveiDo} 376530693fbSMichael Klier// vim:ts=4:sw=4:et:enc=utf-8: 377