*/
// must be run within Dokuwiki
if (!defined('DOKU_INC')) die();
if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'action.php');
if (!defined('NL')) define('NL',"\n");
class action_plugin_discussion extends DokuWiki_Action_Plugin{
/**
* Return some info
*/
function getInfo(){
return array(
'author' => 'Esther Brunner',
'email' => 'wikidesign@gmail.com',
'date' => '2006-11-13',
'name' => 'Discussion Plugin',
'desc' => 'Enables discussion features',
'url' => 'http://wiki:splitbrain.org/plugin:discussion',
);
}
/**
* Register the eventhandlers
*/
function register(&$contr){
$contr->register_hook(
'ACTION_ACT_PREPROCESS',
'BEFORE',
$this,
'handle_act_preprocess',
array()
);
$contr->register_hook(
'TPL_ACT_RENDER',
'AFTER',
$this,
'comments',
array()
);
}
/**
* Main function; dispatches the comment actions
*/
function comments(&$event, $param){
if ($event->data != 'show') return; // nothing to do for us
$cid = $_REQUEST['cid'];
switch ($_REQUEST['comment']){
case 'add':
$comment = array(
'user' => $_REQUEST['user'],
'name' => $_REQUEST['name'],
'mail' => $_REQUEST['mail'],
'url' => $_REQUEST['url'],
'address' => $_REQUEST['address'],
'date' => $_REQUEST['date'],
'raw' => cleanText($_REQUEST['text'])
);
$repl = $_REQUEST['reply'];
$this->_add($comment, $repl);
break;
case 'edit':
$this->_show(NULL, $cid);
break;
case 'save':
$raw = cleanText($_REQUEST['text']);
$this->_save($cid, $raw);
break;
case 'delete':
$this->_save($cid, '');
case 'toogle':
$this->_save($cid, '', true);
break;
default: // 'show' => $this->_show(), 'reply' => $this->_show($cid)
$this->_show($cid);
}
}
/**
* Shows all comments of the current page
*/
function _show($reply = NULL, $edit = NULL){
global $ID;
// get discussion meta file name
$file = metaFN($ID, '.comments');
if (!file_exists($file)) return true; // no comments at all
$data = unserialize(io_readFile($file, false));
if ($data['status'] == 0) return true; // comments are off
// section title
$title = $this->getLang('discussion');
$secid = cleanID($title);
echo '
';
echo '';
// now display the comments
if (isset($data['comments'])){
foreach ($data['comments'] as $key => $value){
if ($key == $edit) $this->_form($value['raw'], 'save', $edit); // edit form
else $this->_print($key, $data, '', $reply);
}
}
// comment form
if (($data['status'] == 1) && !$reply && !$edit) $this->_form('');
echo '
';
return true;
}
/**
* Adds a new comment and then displays all comments
*/
function _add($comment, $parent){
global $ID;
global $TEXT;
$otxt = $TEXT; // set $TEXT to comment text for wordblock check
$TEXT = $comment['raw'];
// spamcheck against the DokuWiki blacklist
if (checkwordblock()){
msg($this->getLang('wordblock'), -1);
$this->_show();
return false;
}
$TEXT = $otxt; // restore global $TEXT
// get discussion meta file name
$file = metaFN($ID, '.comments');
$data = array();
$data = unserialize(io_readFile($file, false));
if ($data['status'] != 1) return false; // comments off or closed
if ((!$this->getConf('allowguests'))
&& ($comment['user'] != $_SERVER['REMOTE_USER']))
return false; // guest comments not allowed
if ($comment['date']) $date = strtotime($comment['date']);
else $date = time();
if ($date == -1) $date = time();
$cid = md5($comment['user'].$date); // create a unique id
if (!is_array($data['comments'][$parent])) $parent = NULL; // invalid parent comment
// render the comment
$xhtml = $this->_render($comment['raw']);
// fill in the new comment
$data['comments'][$cid] = array(
'user' => htmlspecialchars($comment['user']),
'name' => htmlspecialchars($comment['name']),
'mail' => htmlspecialchars($comment['mail']),
'date' => $date,
'show' => true,
'raw' => trim($comment['raw']),
'xhtml' => $xhtml,
'parent' => $parent,
'replies' => array()
);
if ($comment['url'])
$data['comments'][$cid]['url'] = htmlspecialchars($comment['url']);
if ($comment['address'])
$data['comments'][$cid]['address'] = htmlspecialchars($comment['address']);
// update parent comment
if ($parent) $data['comments'][$parent]['replies'][] = $cid;
// update the number of comments
$data['number']++;
// save the comment metadata file
io_saveFile($file, serialize($data));
$this->_addLogEntry($date, $ID, 'cc', '', $cid);
// notify subscribers of the page
$this->_notify($data['comments'][$cid]);
$this->_show();
return true;
}
/**
* Saves the comment with the given ID and then displays all comments
*/
function _save($cid, $raw, $toogle = false){
global $ID;
global $TEXT;
global $INFO;
$otxt = $TEXT; // set $TEXT to comment text for wordblock check
$TEXT = $raw;
// spamcheck against the DokuWiki blacklist
if (checkwordblock()){
msg($this->getLang('wordblock'), -1);
$this->_show();
return false;
}
$TEXT = $otxt; // restore global $TEXT
// get discussion meta file name
$file = metaFN($ID, '.comments');
$data = array();
$data = unserialize(io_readFile($file, false));
// someone else was trying to edit our comment -> abort
if (($data['comments'][$cid]['user'] != $_SERVER['REMOTE_USER'])
&& ($INFO['perm'] != AUTH_ADMIN)) return false;
$date = time();
if ($toogle){ // toogle visibility
$now = $data['comments'][$cid]['show'];
$data['comments'][$cid]['show'] = !$now;
$data['number'] = $this->_count($data);
$type = ($data['comments'][$cid]['show'] ? 'sc' : 'hc');
} elseif (!$raw){ // remove the comment
unset($data['comments'][$cid]);
$data['number'] = $this->_count($data);
$type = 'dc';
} else { // save changed comment
$xhtml = $this->_render($raw);
// now change the comment's content
$data['comments'][$cid]['edited'] = $date;
$data['comments'][$cid]['raw'] = trim($raw);
$data['comments'][$cid]['xhtml'] = $xhtml;
$type = 'ec';
}
// save the comment metadata file
io_saveFile($file, serialize($data));
$this->_addLogEntry($date, $ID, $type, '', $cid);
$this->_show();
return true;
}
/**
* Prints an individual comment
*/
function _print($cid, &$data, $parent = '', $reply = '', $visible = true){
global $conf;
global $lang;
global $ID;
global $INFO;
$comment = $data['comments'][$cid];
if (!is_array($comment)) return false; // corrupt datatype
if ($comment['parent'] != $parent) return true; // reply to an other comment
if (!$comment['show']){ // comment hidden
if ($INFO['perm'] == AUTH_ADMIN) echo ''.NL; // class="comment_hidden"
// reply form
if ($reply == $cid){
echo ''.NL; // class="comment_replies"
}
}
/**
* Outputs the comment form
*/
function _form($raw = '', $act = 'add', $cid = NULL){
global $lang;
global $conf;
global $ID;
global $INFO;
// not for unregistered users when guest comments aren't allowed
if (!$_SERVER['REMOTE_USER'] && !$this->getConf('allowguests')) return false;
?>
getConf('usecocomment')) echo $this->_coComment();
}
/**
* Adds a javascript to interact with coComments
*/
function _coComment(){
global $ID;
global $conf;
global $INFO;
$user = $_SERVER['REMOTE_USER'];
?>
* @author Ben Coburn
*/
function _addLogEntry($date, $id, $type = 'cc', $summary = '', $extra = ''){
global $conf;
$changelog = $conf['metadir'].'/_comments.changes';
if(!$date) $date = time(); //use current time if none supplied
$remote = $_SERVER['REMOTE_ADDR'];
$user = $_SERVER['REMOTE_USER'];
$strip = array("\t", "\n");
$logline = array(
'date' => $date,
'ip' => $remote,
'type' => str_replace($strip, '', $type),
'id' => $id,
'user' => $user,
'sum' => str_replace($strip, '', $summary),
'extra' => str_replace($strip, '', $extra)
);
// add changelog line
$logline = implode("\t", $logline)."\n";
io_saveFile($changelog, $logline, true); //global changelog cache
$this->_trimRecentCommentsLog($changelog);
}
/**
* Trims the recent comments cache to the last $conf['changes_days'] recent
* changes or $conf['recent'] items, which ever is larger.
* The trimming is only done once a day.
*
* @author Ben Coburn
*/
function _trimRecentCommentsLog($changelog){
global $conf;
if (@file_exists($changelog) &&
(filectime($changelog) + 86400) < time() &&
!@file_exists($changelog.'_tmp')){
io_lock($changelog);
$lines = file($changelog);
if (count($lines)<$conf['recent']) {
// nothing to trim
io_unlock($changelog);
return true;
}
io_saveFile($changelog.'_tmp', ''); // presave tmp as 2nd lock
$trim_time = time() - $conf['recent_days']*86400;
$out_lines = array();
for ($i=0; $i 0) {
ksort($old_lines);
$out_lines = array_merge(array_slice($old_lines,-$extra),$out_lines);
}
// save trimmed changelog
io_saveFile($changelog.'_tmp', implode('', $out_lines));
@unlink($changelog);
if (!rename($changelog.'_tmp', $changelog)) {
// rename failed so try another way...
io_unlock($changelog);
io_saveFile($changelog, implode('', $out_lines));
@unlink($changelog.'_tmp');
} else {
io_unlock($changelog);
}
return true;
}
}
/**
* Sends a notify mail on new comment
*
* @param array $comment data array of the new comment
*
* @author Andreas Gohr
* @author Esther Brunner
*/
function _notify($comment){
global $conf;
global $ID;
if (!$conf['subscribers']) return; //subscribers enabled?
$bcc = subscriber_addresslist($ID);
if (empty($bcc)) return;
$to = '';
$text = io_readFile($this->localFN('subscribermail'));
$text = str_replace('@PAGE@', $ID, $text);
$text = str_replace('@TITLE@', $conf['title'], $text);
$text = str_replace('@DATE@', date($conf['dformat'], $comment['date']), $text);
$text = str_replace('@NAME@', $comment['name'], $text);
$text = str_replace('@TEXT@', $comment['raw'], $text);
$text = str_replace('@UNSUBSCRIBE@', wl($ID, 'do=unsubscribe', true, '&'), $text);
$text = str_replace('@DOKUWIKIURL@', DOKU_URL, $text);
$subject = '['.$conf['title'].'] '.$this->getLang('mail_newcomment');
mail_send($to, $subject, $text, $conf['mailfrom'], '', $bcc);
}
/**
* Counts the number of visible comments
*/
function _count($data){
$number = 0;
foreach ($data['comments'] as $cid => $comment){
if ($comment['parent']) continue;
if (!$comment['show']) continue;
$number++;
$rids = $comment['replies'];
if (count($rids)) $number = $number + $this->_countReplies($data, $rids);
}
return $number;
}
function _countReplies(&$data, $rids){
$number = 0;
foreach ($rids as $rid){
if (!$data['comments'][$rid]['show']) continue;
$number++;
$rids = $data['comments'][$rid]['replies'];
if (count($rids)) $number = $number + $this->_countReplies($data, $rids);
}
return $number;
}
/**
* Renders the comment text
*/
function _render($raw){
if ($this->getConf('wikisyntaxok')){
$xhtml = $this->render($raw);
} else { // wiki syntax not allowed -> just encode special chars
$xhtml = htmlspecialchars(trim($raw));
}
return $xhtml;
}
/**
* Checks if 'newthread' was given as action, if so we
* do handle the event our self and no further checking takes place
*/
function handle_act_preprocess(&$event, $param){
if ($event->data != 'newthread') return; // nothing to do for us
global $ACT;
global $ID;
// we can handle it -> prevent others
$event->stopPropagation();
$event->preventDefault();
$ns = $_REQUEST['ns'];
$title = str_replace(':', '', $_REQUEST['title']);
$id = ($ns ? $ns.':' : '').cleanID($title);
// check if we are allowed to create this file
if (auth_quickaclcheck($id) >= AUTH_CREATE){
$back = $ID;
$ID = $id;
$file = wikiFN($ID);
//check if locked by anyone - if not lock for my self
if (checklock($ID)){
$ACT = 'locked';
} else {
lock($ID);
}
// prepare the new thread file with default stuff
if (!@file_exists($file)){
global $TEXT;
global $INFO;
global $conf;
$TEXT = pageTemplate(array($ns.':'.$title));
if (!$TEXT) $TEXT = "<- [[:$back]]\n\n====== $title ======\n\n".
"{{gravatar>".$INFO['userinfo']['mail']." }} ".
"//".$INFO['userinfo']['name'].", ".
date($conf['dformat']).": //\n\n\n\n".
"~~DISCUSSION~~\n";
$ACT = 'preview';
} else {
$ACT = 'edit';
}
} else {
$ACT = 'show';
}
}
}
//Setup VIM: ex: et ts=4 enc=utf-8 :