1<?php 2/** 3 * Discussion Plugin, threads component: displays a list of recently active discussions 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Esther Brunner <wikidesign@gmail.com> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 13require_once(DOKU_PLUGIN.'syntax.php'); 14 15/** 16 * All DokuWiki plugins to extend the parser/rendering mechanism 17 * need to inherit from this class 18 */ 19class syntax_plugin_discussion_threads extends DokuWiki_Syntax_Plugin { 20 21 /** 22 * return some info 23 */ 24 function getInfo(){ 25 return array( 26 'author' => 'Esther Brunner', 27 'email' => 'wikidesign@gmail.com', 28 'date' => '2006-12-04', 29 'name' => 'Discussion Plugin (threads component)', 30 'desc' => 'Displays a list of recently active discussions', 31 'url' => 'http://www.wikidesign.ch/en/plugin/discussion/start', 32 ); 33 } 34 35 function getType(){ return 'substition'; } 36 function getPType(){ return 'block'; } 37 function getSort(){ return 306; } 38 function connectTo($mode) { $this->Lexer->addSpecialPattern('\{\{threads>.+?\}\}',$mode,'plugin_discussion_threads'); } 39 40 /** 41 * Handle the match 42 */ 43 function handle($match, $state, $pos, &$handler){ 44 $match = substr($match, 10, -2); // strip {{threads> from start and }} from end 45 return cleanID($match); 46 } 47 48 /** 49 * Create output 50 */ 51 function render($mode, &$renderer, $ns) { 52 global $ID; 53 global $conf; 54 55 if ($ns == ':') $ns = ''; 56 elseif ($ns == '.') $ns = getNS($ID); 57 58 $pages = $this->_threadList($ns); 59 60 if (!count($pages)) return true; // nothing to display 61 62 if ($mode == 'xhtml'){ 63 64 // prevent caching to ensure content is always fresh 65 $renderer->info['cache'] = false; 66 67 // show form to start a new discussion thread? 68 $perm_create = (auth_quickaclcheck($ns.':*') >= AUTH_CREATE); 69 if ($perm_create && ($this->getConf('threads_formposition') == 'top')) 70 $renderer->doc .= $this->_newThreadForm($ns); 71 72 // main table 73 $renderer->doc .= '<table class="threads">'; 74 foreach ($pages as $page){ 75 $renderer->doc .= '<tr><td class="page">'; 76 77 // page title 78 $id = $page['id']; 79 $title = $page['title']; 80 if (!$title) $title = str_replace('_', ' ', noNS($id)); 81 $renderer->doc .= $renderer->internallink(':'.$id, $title).'</td>'; 82 83 // last comment date 84 if ($this->getConf('threads_showdate')){ 85 $renderer->doc .= '<td class="date">'.date($conf['dformat'], $page['date']). 86 '</td>'; 87 } 88 89 // topic starter 90 if ($this->getConf('threads_showuser')){ 91 if ($page['user']) $renderer->doc .= '<td class="user">'.$page['user'].'</td>'; 92 else $renderer->doc .= '<td class="user"> </td>'; 93 } 94 95 // number of replies 96 if ($page['num'] == 0) $repl = ''; 97 elseif ($page['num'] == 1) $repl = '1 '.$this->getLang('reply'); 98 else $repl = $page['num'].' '.$this->getLang('replies'); 99 $renderer->doc .= '<td class="num">'.$repl.'</td>'; 100 101 $renderer->doc .= '</tr>'; 102 } 103 $renderer->doc .= '</table>'; 104 105 // show form to start a new discussion thread? 106 if ($perm_create && ($this->getConf('threads_formposition') == 'bottom')) 107 $renderer->doc .= $this->_newThreadForm($ns); 108 109 return true; 110 111 // for metadata renderer 112 } elseif ($mode == 'metadata'){ 113 foreach ($pages as $page){ 114 $id = $page['id']; 115 $renderer->meta['relation']['references'][$id] = true; 116 } 117 118 return true; 119 } 120 return false; 121 } 122 123 /** 124 * Returns an array of files with discussion sections, sorted by recent comments 125 */ 126 function _threadList($ns){ 127 global $conf; 128 129 require_once(DOKU_INC.'inc/search.php'); 130 131 $dir = $conf['datadir'].($ns ? '/'.str_replace(':', '/', $ns): ''); 132 133 // returns the list of pages in the given namespace and it's subspaces 134 $items = array(); 135 search($items, $dir, 'search_allpages', ''); 136 137 // add pages with comments to result 138 $result = array(); 139 foreach ($items as $item){ 140 $id = ($ns ? $ns.':' : '').$item['id']; 141 if (auth_quickaclcheck($id) < AUTH_READ) continue; // skip if no permission 142 $file = metaFN($id, '.comments'); 143 if (!@file_exists($file)) continue; // skip if no comments file 144 $data = unserialize(io_readFile($file, false)); 145 if ($data['status'] == 0) continue; // skip if comments are off 146 $date = filemtime($file); 147 $meta = p_get_metadata($id); 148 $result[$date] = array( 149 'id' => $id, 150 'title' => $meta['title'], 151 'user' => $meta['creator'], 152 'num' => $data['number'], 153 'date' => $date, 154 ); 155 } 156 157 // finally sort by time of last comment 158 krsort($result); 159 160 return $result; 161 } 162 163 /** 164 * Show the form to start a new discussion thread 165 */ 166 function _newThreadForm($ns){ 167 global $ID; 168 global $lang; 169 170 return '<div class="newthread_form">'. 171 '<form id="discussion__newthread_form" method="post" action="'.script().'" accept-charset="'.$lang['encoding'].'">'. 172 '<div class="no">'. 173 '<input type="hidden" name="id" value="'.$ID.'" />'. 174 '<input type="hidden" name="do" value="newthread" />'. 175 '<input type="hidden" name="ns" value="'.$ns.'" />'. 176 '<label class="block" for="discussion__newthread_title">'. 177 '<span>'.$this->getLang('newthread').':</span> '. 178 '<input class="edit" type="text" name="title" id="discussion__newthread_title" size="40" tabindex="1" />'. 179 '</label>'. 180 '<input class="button" type="submit" value="'.$lang['btn_create'].'" tabindex="2" />'. 181 '</div>'. 182 '</form>'. 183 '</div>'; 184 } 185 186} 187 188//Setup VIM: ex: et ts=4 enc=utf-8 : 189