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 10a768ba62Swikidesignif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11264b7327Swikidesign 12a768ba62Swikidesignrequire_once(DOKU_PLUGIN.'admin.php'); 13a768ba62Swikidesign 14a768ba62Swikidesignclass admin_plugin_discussion extends DokuWiki_Admin_Plugin { 15a768ba62Swikidesign 16a768ba62Swikidesign function getInfo(){ 17a768ba62Swikidesign return array( 18264b7327Swikidesign 'author' => 'Esther Brunner', 19264b7327Swikidesign 'email' => 'wikidesign@gmail.com', 20*573e23a1Swikidesign 'date' => '2007-03-01', 21264b7327Swikidesign 'name' => 'Discussion Plugin (admin component)', 22*573e23a1Swikidesign 'desc' => 'Moderate discussions', 23264b7327Swikidesign 'url' => 'http://www.wikidesign.ch/en/plugin/discussion/start', 24a768ba62Swikidesign ); 25a768ba62Swikidesign } 26a768ba62Swikidesign 27264b7327Swikidesign function getMenuSort(){ return 200; } 28264b7327Swikidesign function forAdminOnly(){ return false; } 29a768ba62Swikidesign 30*573e23a1Swikidesign function handle(){ 31*573e23a1Swikidesign global $lang; 32*573e23a1Swikidesign 33*573e23a1Swikidesign $cid = $_REQUEST['cid']; 34*573e23a1Swikidesign if (is_array($cid)) $cid = array_keys($cid); 35*573e23a1Swikidesign 36*573e23a1Swikidesign $action =& plugin_load('action', 'discussion'); 37*573e23a1Swikidesign if (!$action) return; // couldn't load action plugin component 38*573e23a1Swikidesign 39*573e23a1Swikidesign switch ($_REQUEST['comment']){ 40*573e23a1Swikidesign case $lang['btn_delete']: 41*573e23a1Swikidesign $action->_save($cid, ''); 42*573e23a1Swikidesign break; 43*573e23a1Swikidesign 44*573e23a1Swikidesign case $this->getLang('btn_show'): 45*573e23a1Swikidesign $action->_save($cid, '', 'show'); 46*573e23a1Swikidesign break; 47*573e23a1Swikidesign 48*573e23a1Swikidesign case $this->getLang('btn_hide'): 49*573e23a1Swikidesign $action->_save($cid, '', 'hide'); 50*573e23a1Swikidesign break; 51*573e23a1Swikidesign 52*573e23a1Swikidesign case $this->getLang('btn_change'): 53*573e23a1Swikidesign $new = $_REQUEST['status']; 54*573e23a1Swikidesign $this->_changeStatus($new); 55*573e23a1Swikidesign break; 56*573e23a1Swikidesign } 57*573e23a1Swikidesign } 58*573e23a1Swikidesign 59a768ba62Swikidesign function html(){ 60264b7327Swikidesign global $conf; 61264b7327Swikidesign 62*573e23a1Swikidesign $first = $_REQUEST['first']; 63*573e23a1Swikidesign if (!is_numeric($first)) $first = 0; 64*573e23a1Swikidesign $num = $conf['recent']; 65264b7327Swikidesign 66*573e23a1Swikidesign ptln('<h1>'.$this->getLang('menu').'</h1>'); 67264b7327Swikidesign 68*573e23a1Swikidesign $threads = $this->_getThreads(); 69*573e23a1Swikidesign 70*573e23a1Swikidesign // slice the needed chunk of discussion pages 71*573e23a1Swikidesign $more = ((count($threads) > ($first + $num)) ? true : false); 72*573e23a1Swikidesign $threads = array_slice($threads, $first, $num); 73*573e23a1Swikidesign 74264b7327Swikidesign foreach ($threads as $thread){ 75*573e23a1Swikidesign $comments = $this->_getComments($thread); 76*573e23a1Swikidesign $this->_threadHead($thread); 77*573e23a1Swikidesign if ($comments === false){ 78*573e23a1Swikidesign ptln('</div>'); // class="level2" 79264b7327Swikidesign continue; 80264b7327Swikidesign } 81264b7327Swikidesign 82*573e23a1Swikidesign ptln('<form method="post" action="'.wl($thread['id']).'">', 2); 83*573e23a1Swikidesign ptln('<div class="no">', 4); 84*573e23a1Swikidesign ptln('<input type="hidden" name="do" value="admin" />', 6); 85*573e23a1Swikidesign ptln('<input type="hidden" name="page" value="discussion" />', 6); 86264b7327Swikidesign echo html_buildlist($comments, 'admin_discussion', array($this, '_commentItem'), array($this, '_li_comment')); 87*573e23a1Swikidesign $this->_actionButtons($thread['id']); 88264b7327Swikidesign } 89*573e23a1Swikidesign $this->_browseDiscussionLinks($more, $first, $num); 90264b7327Swikidesign } 91264b7327Swikidesign 92264b7327Swikidesign /** 93*573e23a1Swikidesign * Returns an array of pages with discussion sections, sorted by recent comments 94*573e23a1Swikidesign */ 95*573e23a1Swikidesign function _getThreads(){ 96*573e23a1Swikidesign global $conf; 97*573e23a1Swikidesign 98*573e23a1Swikidesign require_once(DOKU_INC.'inc/search.php'); 99*573e23a1Swikidesign 100*573e23a1Swikidesign // returns the list of pages in the given namespace and it's subspaces 101*573e23a1Swikidesign $items = array(); 102*573e23a1Swikidesign search($items, $conf['datadir'], 'search_allpages', ''); 103*573e23a1Swikidesign 104*573e23a1Swikidesign // add pages with comments to result 105*573e23a1Swikidesign $result = array(); 106*573e23a1Swikidesign foreach ($items as $item){ 107*573e23a1Swikidesign $id = $item['id']; 108*573e23a1Swikidesign 109*573e23a1Swikidesign // some checks 110*573e23a1Swikidesign $file = metaFN($id, '.comments'); 111*573e23a1Swikidesign if (!@file_exists($file)) continue; // skip if no comments file 112*573e23a1Swikidesign 113*573e23a1Swikidesign $date = filemtime($file); 114*573e23a1Swikidesign $result[$date] = array( 115*573e23a1Swikidesign 'id' => $id, 116*573e23a1Swikidesign 'file' => $file, 117*573e23a1Swikidesign 'date' => $date, 118*573e23a1Swikidesign ); 119*573e23a1Swikidesign } 120*573e23a1Swikidesign 121*573e23a1Swikidesign // finally sort by time of last comment 122*573e23a1Swikidesign krsort($result); 123*573e23a1Swikidesign 124*573e23a1Swikidesign return $result; 125*573e23a1Swikidesign } 126*573e23a1Swikidesign 127*573e23a1Swikidesign /** 128*573e23a1Swikidesign * Outputs header, page ID and status of a discussion thread 129264b7327Swikidesign */ 130264b7327Swikidesign function _threadHead($thread){ 131*573e23a1Swikidesign $id = $thread['id']; 132*573e23a1Swikidesign 133*573e23a1Swikidesign $labels = array( 134*573e23a1Swikidesign 0 => $this->getLang('off'), 135*573e23a1Swikidesign 1 => $this->getLang('open'), 136*573e23a1Swikidesign 2 => $this->getLang('closed') 137264b7327Swikidesign ); 138*573e23a1Swikidesign ptln('<h2 name="'.$id.'" id="'.$id.'">'.hsc(p_get_metadata($id, 'title')).'</h2>'); 139*573e23a1Swikidesign ptln('<form method="post" action="'.wl($id).'">'); 140*573e23a1Swikidesign ptln('<div class="mediaright">', 2); 141*573e23a1Swikidesign ptln('<input type="hidden" name="do" value="admin" />', 4); 142*573e23a1Swikidesign ptln('<input type="hidden" name="page" value="discussion" />', 4); 143*573e23a1Swikidesign ptln($this->getLang('status').': <select name="status" size="1">', 4); 144*573e23a1Swikidesign foreach ($labels as $key => $label){ 145*573e23a1Swikidesign $selected = (($key == $thread['status']) ? ' selected="selected"' : ''); 146*573e23a1Swikidesign ptln('<option value="'.$key.'"'.$selected.'>'.$label.'</option>', 6); 147*573e23a1Swikidesign } 148*573e23a1Swikidesign ptln('</select> ', 4); 149*573e23a1Swikidesign ptln('<input type="submit" value="'.$this->getLang('btn_change').'" />', 4); 150*573e23a1Swikidesign ptln('</div>', 2); 151*573e23a1Swikidesign ptln('</form>'); 152*573e23a1Swikidesign ptln('<div class="level2">'); 153*573e23a1Swikidesign ptln('<a href="'.wl($id).'" class="wikilink1">'.$id.'</a> ', 2); 154*573e23a1Swikidesign return true; 155*573e23a1Swikidesign } 156*573e23a1Swikidesign 157*573e23a1Swikidesign /** 158*573e23a1Swikidesign * Returns the full comments data for a given wiki page 159*573e23a1Swikidesign */ 160*573e23a1Swikidesign function _getComments(&$thread){ 161*573e23a1Swikidesign $id = $thread['id']; 162*573e23a1Swikidesign 163*573e23a1Swikidesign if (!$thread['file']) $thread['file'] = metaFN($id, '.comments'); 164*573e23a1Swikidesign if (!@file_exists($thread['file'])) return false; // no discussion thread at all 165*573e23a1Swikidesign 166*573e23a1Swikidesign $data = unserialize(io_readFile($thread['file'], false)); 167*573e23a1Swikidesign 168*573e23a1Swikidesign $thread['status'] = $data['status']; 169*573e23a1Swikidesign $thread['number'] = $data['number']; 170*573e23a1Swikidesign if (!$data['status']) return false; // comments are turned off 171*573e23a1Swikidesign if (!isset($data['comments']) || !$data['number']) return false; // no comments 172*573e23a1Swikidesign 173*573e23a1Swikidesign $result = array(); 174*573e23a1Swikidesign foreach ($data['comments'] as $cid => $comment){ 175*573e23a1Swikidesign $this->_addComment($cid, $data, $result); 176*573e23a1Swikidesign } 177*573e23a1Swikidesign 178*573e23a1Swikidesign return $result; 179*573e23a1Swikidesign } 180*573e23a1Swikidesign 181*573e23a1Swikidesign /** 182*573e23a1Swikidesign * Recursive function to add the comment hierarchy to the result 183*573e23a1Swikidesign */ 184*573e23a1Swikidesign function _addComment($cid, &$data, &$result, $parent = '', $level = 1){ 185*573e23a1Swikidesign if (!is_array($data['comments'][$cid])) return; // corrupt datatype 186*573e23a1Swikidesign $comment = $data['comments'][$cid]; 187*573e23a1Swikidesign if ($comment['parent'] != $parent) return; // answer to another comment 188*573e23a1Swikidesign 189*573e23a1Swikidesign // okay, add the comment to the result 190*573e23a1Swikidesign $comment['id'] = $cid; 191*573e23a1Swikidesign $comment['level'] = $level; 192*573e23a1Swikidesign $result[] = $comment; 193*573e23a1Swikidesign 194*573e23a1Swikidesign // check answers to this comment 195*573e23a1Swikidesign if (count($comment['replies'])){ 196*573e23a1Swikidesign foreach ($comment['replies'] as $rid){ 197*573e23a1Swikidesign $this->_addComment($rid, $data, $result, $cid, $level + 1); 198*573e23a1Swikidesign } 199*573e23a1Swikidesign } 200264b7327Swikidesign } 201264b7327Swikidesign 202264b7327Swikidesign /** 203264b7327Swikidesign * Checkbox and info about a comment item 204264b7327Swikidesign */ 205264b7327Swikidesign function _commentItem($comment){ 206264b7327Swikidesign global $conf; 207264b7327Swikidesign 208264b7327Swikidesign // prepare variables 209264b7327Swikidesign if (is_array($comment['user'])){ // new format 210264b7327Swikidesign $name = $comment['user']['name']; 211264b7327Swikidesign $mail = $comment['user']['mail']; 212264b7327Swikidesign } else { // old format 213264b7327Swikidesign $name = $comment['name']; 214264b7327Swikidesign $mail = $comment['mail']; 215264b7327Swikidesign } 216264b7327Swikidesign if (is_array($comment['date'])){ // new format 217264b7327Swikidesign $created = $comment['date']['created']; 218264b7327Swikidesign } else { // old format 219264b7327Swikidesign $created = $comment['date']; 220264b7327Swikidesign } 221*573e23a1Swikidesign $abstract = preg_replace('/\s+?/', ' ', strip_tags($comment['xhtml'])); 222264b7327Swikidesign if (utf8_strlen($abstract) > 160) $abstract = utf8_substr($abstract, 0, 160).'...'; 223264b7327Swikidesign 224*573e23a1Swikidesign return '<input type="checkbox" name="cid['.$comment['id'].']" value="1" /> '. 225*573e23a1Swikidesign $this->email($mail, $name, 'email').', '.date($conf['dformat'], $created).': '. 226*573e23a1Swikidesign '<span class="abstract">'.$abstract.'</span>'; 227264b7327Swikidesign } 228264b7327Swikidesign 229264b7327Swikidesign /** 230264b7327Swikidesign * list item tag 231264b7327Swikidesign */ 232264b7327Swikidesign function _li_comment($comment){ 233264b7327Swikidesign $show = ($comment['show'] ? '' : ' hidden'); 234264b7327Swikidesign return '<li class="level'.$comment['level'].$show.'">'; 235264b7327Swikidesign } 236264b7327Swikidesign 237264b7327Swikidesign /** 238264b7327Swikidesign * Show buttons to bulk remove, hide or show comments 239264b7327Swikidesign */ 240264b7327Swikidesign function _actionButtons($id){ 241264b7327Swikidesign global $lang; 242264b7327Swikidesign 243*573e23a1Swikidesign ptln('<div class="comment_buttons">', 6); 244*573e23a1Swikidesign ptln('<input type="submit" name="comment" value="'.$this->getLang('btn_show').'" class="button" title="'.$this->getLang('btn_show').'" />', 8); 245*573e23a1Swikidesign ptln('<input type="submit" name="comment" value="'.$this->getLang('btn_hide').'" class="button" title="'.$this->getLang('btn_hide').'" />', 8); 246*573e23a1Swikidesign ptln('<input type="submit" name="comment" value="'.$lang['btn_delete'].'" class="button" title="'.$lang['btn_delete'].'" />', 8); 247*573e23a1Swikidesign ptln('</div>', 6); // class="comment_buttons" 248*573e23a1Swikidesign ptln('</div>', 4); // class="no" 249*573e23a1Swikidesign ptln('</form>', 2); 250*573e23a1Swikidesign ptln('</div>'); // class="level2" 251*573e23a1Swikidesign return true; 252*573e23a1Swikidesign } 253*573e23a1Swikidesign 254*573e23a1Swikidesign /** 255*573e23a1Swikidesign * Displays links to older newer discussions 256*573e23a1Swikidesign */ 257*573e23a1Swikidesign function _browseDiscussionLinks($more, $first, $num){ 258*573e23a1Swikidesign global $ID; 259*573e23a1Swikidesign 260*573e23a1Swikidesign $params = array('do' => 'admin', 'page' => 'discussion'); 261*573e23a1Swikidesign $last = $first+$num; 262*573e23a1Swikidesign if ($first > 0){ 263*573e23a1Swikidesign $first -= $num; 264*573e23a1Swikidesign if ($first < 0) $first = 0; 265*573e23a1Swikidesign $params['first'] = $first; 266*573e23a1Swikidesign ptln('<p class="centeralign">', 2); 267*573e23a1Swikidesign $ret = '<a href="'.wl($ID, $params).'" class="wikilink1"><< '.$this->getLang('newer').'</a>'; 268*573e23a1Swikidesign if ($more){ 269*573e23a1Swikidesign $ret .= ' | '; 270*573e23a1Swikidesign } else { 271*573e23a1Swikidesign ptln($ret, 4); 272*573e23a1Swikidesign ptln('</p>', 2); 273*573e23a1Swikidesign } 274*573e23a1Swikidesign } else if ($more){ 275*573e23a1Swikidesign ptln('<p class="centeralign">', 2); 276*573e23a1Swikidesign } 277*573e23a1Swikidesign if ($more){ 278*573e23a1Swikidesign $params['first'] = $last; 279*573e23a1Swikidesign $ret .= '<a href="'.wl($ID, $params).'" class="wikilink1">'.$this->getLang('older').' >></a>'; 280*573e23a1Swikidesign ptln($ret, 4); 281*573e23a1Swikidesign ptln('</p>', 2); 282*573e23a1Swikidesign } 283*573e23a1Swikidesign return true; 284*573e23a1Swikidesign } 285*573e23a1Swikidesign 286*573e23a1Swikidesign /** 287*573e23a1Swikidesign * Changes the status of a comment 288*573e23a1Swikidesign */ 289*573e23a1Swikidesign function _changeStatus($new){ 290*573e23a1Swikidesign global $ID; 291*573e23a1Swikidesign 292*573e23a1Swikidesign // get discussion meta file name 293*573e23a1Swikidesign $file = metaFN($ID, '.comments'); 294*573e23a1Swikidesign $data = unserialize(io_readFile($file, false)); 295*573e23a1Swikidesign 296*573e23a1Swikidesign $old = $data['status']; 297*573e23a1Swikidesign if ($old == $new) return true; 298*573e23a1Swikidesign 299*573e23a1Swikidesign // save the comment metadata file 300*573e23a1Swikidesign $data['status'] = $new; 301*573e23a1Swikidesign io_saveFile($file, serialize($data)); 302*573e23a1Swikidesign 303*573e23a1Swikidesign // look for ~~DISCUSSION~~ command in page file and change it accordingly 304*573e23a1Swikidesign $patterns = array('~~DISCUSSION:off~~', '~~DISCUSSION~~', '~~DISCUSSION:closed~~'); 305*573e23a1Swikidesign $replace = $patterns[$new]; 306*573e23a1Swikidesign $wiki = preg_replace('/~~DISCUSSION(?:|:off|:closed)~~/', $replace, rawWiki($ID)); 307*573e23a1Swikidesign saveWikiText($ID, $wiki, $this->getLang('statuschanged'), true); 308*573e23a1Swikidesign 309*573e23a1Swikidesign return true; 310264b7327Swikidesign } 311264b7327Swikidesign 312264b7327Swikidesign /** 313264b7327Swikidesign * function by iDo 314264b7327Swikidesign */ 315264b7327Swikidesign function _html() { 316434f5172SiLoveiDo require_once(DOKU_PLUGIN.'action.php'); 317434f5172SiLoveiDo $actionDiscussion= new action_plugin_discussion(); 318434f5172SiLoveiDo 319a768ba62Swikidesign global $conf; 320434f5172SiLoveiDo global $INFO; 321434f5172SiLoveiDo global $ID; 322434f5172SiLoveiDo global $ADMDISCUSSION; 323434f5172SiLoveiDo 3245ef1705fSiLoveiDo $oID=$ID; 3255ef1705fSiLoveiDo $ADMDISCUSSION['page']="adm"; 3265ef1705fSiLoveiDo //Execute action for page 3275ef1705fSiLoveiDo if (isset($_REQUEST['comment'])) { 3285ef1705fSiLoveiDo if ($_REQUEST['comment']!='edit') { 3295ef1705fSiLoveiDo 3305ef1705fSiLoveiDo if (($_REQUEST['comment']=='add') && (isset($_REQUEST['cid']))) { 3315ef1705fSiLoveiDo 3325ef1705fSiLoveiDo } else { 3335ef1705fSiLoveiDo $obj=new unusedclass(); 3345ef1705fSiLoveiDo $actionDiscussion->comments($obj, null); 3355ef1705fSiLoveiDo } 3365ef1705fSiLoveiDo } 3375ef1705fSiLoveiDo } 3385ef1705fSiLoveiDo 339a768ba62Swikidesign $chem=DOKU_INC.$conf['savedir']."/meta/"; 340a768ba62Swikidesign $arr=$this->globr($chem,"*.comments"); 341a768ba62Swikidesign $com =array(); 342a768ba62Swikidesign foreach ($arr as $v) { 343a768ba62Swikidesign $ap=unserialize(io_readFile($v, false)); 344a768ba62Swikidesign if (isset($ap['comments'])){ 345434f5172SiLoveiDo $ID=substr(str_replace(array($chem,".comments",'/'),array("","",':'),$v),1); 346434f5172SiLoveiDo $ADMDISCUSSION['page']=' : <a href="'.wl($ID,'').'">'.str_replace("/doku.php/","",wl($ID,'')).'</a>'; 347a768ba62Swikidesign 3485ef1705fSiLoveiDo if ((isset($_REQUEST['comment'])) && ($_REQUEST['comment']=='edit')) 3495ef1705fSiLoveiDo $actionDiscussion->_show(NULL, $_REQUEST['cid']); 3505ef1705fSiLoveiDo else 3515ef1705fSiLoveiDo $actionDiscussion->_show((($oID==$ID)?@$_REQUEST['cid']:null)); 3525ef1705fSiLoveiDo 3535ef1705fSiLoveiDo } 3545ef1705fSiLoveiDo } 3555ef1705fSiLoveiDo $ID = $oID; 3565ef1705fSiLoveiDo $ADMDISCUSSION['breakaction']=true; 357a768ba62Swikidesign } 358a768ba62Swikidesign 359a768ba62Swikidesign /** 360a768ba62Swikidesign * Recursive version of glob 361a768ba62Swikidesign * 362a768ba62Swikidesign * @return array containing all pattern-matched files. 363a768ba62Swikidesign * 364a768ba62Swikidesign * @param string $sDir Directory to start with. 365a768ba62Swikidesign * @param string $sPattern Pattern to glob for. 366a768ba62Swikidesign * @param int $nFlags Flags sent to glob. 367a768ba62Swikidesign */ 368*573e23a1Swikidesign function _globr($sDir, $sPattern, $nFlags = NULL) { 369a768ba62Swikidesign $sDir = escapeshellcmd($sDir); 370a768ba62Swikidesign // Get the list of all matching files currently in the 371a768ba62Swikidesign // directory. 372a768ba62Swikidesign $aFiles = glob("$sDir/$sPattern", $nFlags); 373a768ba62Swikidesign // Then get a list of all directories in this directory, and 374a768ba62Swikidesign // run ourselves on the resulting array. This is the 375a768ba62Swikidesign // recursion step, which will not execute if there are no 376a768ba62Swikidesign // directories. 377a768ba62Swikidesign foreach (glob("$sDir/*", GLOB_ONLYDIR) as $sSubDir) { 378a768ba62Swikidesign $aSubFiles = $this->globr($sSubDir, $sPattern, $nFlags); 379a768ba62Swikidesign $aFiles = array_merge($aFiles, $aSubFiles); 380a768ba62Swikidesign } 381a768ba62Swikidesign // The array we return contains the files we found, and the 382a768ba62Swikidesign // files all of our children found. 383a768ba62Swikidesign return $aFiles; 384a768ba62Swikidesign } 385a768ba62Swikidesign 3865ef1705fSiLoveiDo} 387264b7327Swikidesign 388264b7327Swikidesign//Setup VIM: ex: et ts=4 enc=utf-8 :