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 10if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 11require_once(DOKU_PLUGIN.'action.php'); 12 13if (!defined('NL')) define('NL',"\n"); 14 15class action_plugin_discussion extends DokuWiki_Action_Plugin{ 16 17 /** 18 * Return some info 19 */ 20 function getInfo(){ 21 return array( 22 'author' => 'Esther Brunner', 23 'email' => 'wikidesign@gmail.com', 24 'date' => '2006-12-12', 25 'name' => 'Discussion Plugin', 26 'desc' => 'Enables discussion features', 27 'url' => 'http://www.wikidesign.ch/en/plugin/discussion/start', 28 ); 29 } 30 31 /** 32 * Register the eventhandlers 33 */ 34 function register(&$contr){ 35 $contr->register_hook( 36 'ACTION_ACT_PREPROCESS', 37 'BEFORE', 38 $this, 39 'handle_act_preprocess', 40 array() 41 ); 42 $contr->register_hook( 43 'TPL_ACT_RENDER', 44 'AFTER', 45 $this, 46 'comments', 47 array() 48 ); 49 } 50 51 /** 52 * Main function; dispatches the comment actions 53 */ 54 function comments(&$event, $param){ 55 if ($event->data != 'show') return; // nothing to do for us 56 57 $cid = $_REQUEST['cid']; 58 59 switch ($_REQUEST['comment']){ 60 61 case 'add': 62 $comment = array( 63 'user' => $_REQUEST['user'], 64 'name' => $_REQUEST['name'], 65 'mail' => $_REQUEST['mail'], 66 'url' => $_REQUEST['url'], 67 'address' => $_REQUEST['address'], 68 'date' => $_REQUEST['date'], 69 'raw' => cleanText($_REQUEST['text']) 70 ); 71 $repl = $_REQUEST['reply']; 72 $this->_add($comment, $repl); 73 break; 74 75 case 'edit': 76 $this->_show(NULL, $cid); 77 break; 78 79 case 'save': 80 $raw = cleanText($_REQUEST['text']); 81 $this->_save($cid, $raw); 82 break; 83 84 case 'delete': 85 $this->_save($cid, ''); 86 break; 87 88 case 'toogle': 89 $this->_save($cid, '', true); 90 break; 91 92 default: // 'show' => $this->_show(), 'reply' => $this->_show($cid) 93 $this->_show($cid); 94 } 95 } 96 97 /** 98 * Shows all comments of the current page 99 */ 100 function _show($reply = NULL, $edit = NULL){ 101 global $ID; 102 103 // get discussion meta file name 104 $file = metaFN($ID, '.comments'); 105 106 if (!file_exists($file)) return true; // no comments at all 107 108 $data = unserialize(io_readFile($file, false)); 109 110 if ($data['status'] == 0) return true; // comments are off 111 112 // section title 113 $title = $this->getLang('discussion'); 114 $secid = cleanID($title); 115 echo '<div class="comment_wrapper">'; 116 echo '<h2><a name="'.$secid.'" id="'.$secid.'">'.$title.'</a></h2>'; 117 echo '<div class="level2">'; 118 119 // now display the comments 120 if (isset($data['comments'])){ 121 foreach ($data['comments'] as $key => $value){ 122 if ($key == $edit) $this->_form($value['raw'], 'save', $edit); // edit form 123 else $this->_print($key, $data, '', $reply); 124 } 125 } 126 127 // comment form 128 if (($data['status'] == 1) && !$reply && !$edit) $this->_form(''); 129 130 echo '</div>'; // level2 131 echo '</div>'; // comment_wrapper 132 133 return true; 134 } 135 136 /** 137 * Adds a new comment and then displays all comments 138 */ 139 function _add($comment, $parent){ 140 global $ID; 141 global $TEXT; 142 143 $otxt = $TEXT; // set $TEXT to comment text for wordblock check 144 $TEXT = $comment['raw']; 145 146 // spamcheck against the DokuWiki blacklist 147 if (checkwordblock()){ 148 msg($this->getLang('wordblock'), -1); 149 $this->_show(); 150 return false; 151 } 152 153 $TEXT = $otxt; // restore global $TEXT 154 155 // get discussion meta file name 156 $file = metaFN($ID, '.comments'); 157 158 $data = array(); 159 $data = unserialize(io_readFile($file, false)); 160 161 if ($data['status'] != 1) return false; // comments off or closed 162 if ((!$this->getConf('allowguests')) 163 && ($comment['user'] != $_SERVER['REMOTE_USER'])) 164 return false; // guest comments not allowed 165 166 if ($comment['date']) $date = strtotime($comment['date']); 167 else $date = time(); 168 if ($date == -1) $date = time(); 169 $cid = md5($comment['user'].$date); // create a unique id 170 171 if (!is_array($data['comments'][$parent])) $parent = NULL; // invalid parent comment 172 173 // render the comment 174 $xhtml = $this->_render($comment['raw']); 175 176 // fill in the new comment 177 $data['comments'][$cid] = array( 178 'user' => htmlspecialchars($comment['user']), 179 'name' => htmlspecialchars($comment['name']), 180 'mail' => htmlspecialchars($comment['mail']), 181 'date' => $date, 182 'show' => true, 183 'raw' => trim($comment['raw']), 184 'xhtml' => $xhtml, 185 'parent' => $parent, 186 'replies' => array() 187 ); 188 if ($comment['url']) 189 $data['comments'][$cid]['url'] = htmlspecialchars($comment['url']); 190 if ($comment['address']) 191 $data['comments'][$cid]['address'] = htmlspecialchars($comment['address']); 192 193 // update parent comment 194 if ($parent) $data['comments'][$parent]['replies'][] = $cid; 195 196 // update the number of comments 197 $data['number']++; 198 199 // save the comment metadata file 200 io_saveFile($file, serialize($data)); 201 $this->_addLogEntry($date, $ID, 'cc', '', $cid); 202 203 // notify subscribers of the page 204 $this->_notify($data['comments'][$cid]); 205 206 $this->_show(); 207 return true; 208 } 209 210 /** 211 * Saves the comment with the given ID and then displays all comments 212 */ 213 function _save($cid, $raw, $toogle = false){ 214 global $ID; 215 global $INFO; 216 217 if ($raw){ 218 global $TEXT; 219 220 $otxt = $TEXT; // set $TEXT to comment text for wordblock check 221 $TEXT = $raw; 222 223 // spamcheck against the DokuWiki blacklist 224 if (checkwordblock()){ 225 msg($this->getLang('wordblock'), -1); 226 $this->_show(); 227 return false; 228 } 229 230 $TEXT = $otxt; // restore global $TEXT 231 } 232 233 // get discussion meta file name 234 $file = metaFN($ID, '.comments'); 235 236 $data = array(); 237 $data = unserialize(io_readFile($file, false)); 238 239 // someone else was trying to edit our comment -> abort 240 if (($data['comments'][$cid]['user'] != $_SERVER['REMOTE_USER']) 241 && ($INFO['perm'] != AUTH_ADMIN)) return false; 242 243 $date = time(); 244 245 if ($toogle){ // toogle visibility 246 $now = $data['comments'][$cid]['show']; 247 $data['comments'][$cid]['show'] = !$now; 248 $data['number'] = $this->_count($data); 249 250 $type = ($data['comments'][$cid]['show'] ? 'sc' : 'hc'); 251 252 } elseif (!$raw){ // remove the comment 253 unset($data['comments'][$cid]); 254 $data['number'] = $this->_count($data); 255 256 $type = 'dc'; 257 258 } else { // save changed comment 259 $xhtml = $this->_render($raw); 260 261 // now change the comment's content 262 $data['comments'][$cid]['edited'] = $date; 263 $data['comments'][$cid]['raw'] = trim($raw); 264 $data['comments'][$cid]['xhtml'] = $xhtml; 265 266 $type = 'ec'; 267 } 268 269 // save the comment metadata file 270 io_saveFile($file, serialize($data)); 271 $this->_addLogEntry($date, $ID, $type, '', $cid); 272 273 $this->_show(); 274 return true; 275 } 276 277 /** 278 * Prints an individual comment 279 */ 280 function _print($cid, &$data, $parent = '', $reply = '', $visible = true){ 281 global $conf; 282 global $lang; 283 global $ID; 284 global $INFO; 285 286 if (!isset($data['comments'][$cid])) return false; // comment was removed 287 $comment = $data['comments'][$cid]; 288 289 if (!is_array($comment)) return false; // corrupt datatype 290 291 if ($comment['parent'] != $parent) return true; // reply to an other comment 292 293 if (!$comment['show']){ // comment hidden 294 if ($INFO['perm'] == AUTH_ADMIN) echo '<div class="comment_hidden">'.NL; 295 else return true; 296 } 297 298 // comment head with date and user data 299 echo '<div class="comment_head">'.NL; 300 echo '<a name="comment__'.$cid.'" id="comment__'.$cid.'">'.NL; 301 302 // show gravatar image 303 if ($this->getConf('usegravatar')){ 304 $default = DOKU_URL.'lib/plugins/discussion/images/default.gif'; 305 $size = $this->getConf('gravatar_size'); 306 if ($comment['mail']) $src = ml('http://www.gravatar.com/avatar.php?'. 307 'gravatar_id='.md5($comment['mail']). 308 '&default='.urlencode($default). 309 '&size='.$size. 310 '&rating='.$this->getConf('gravatar_rating'). 311 '&.jpg', 'cache=recache'); 312 else $src = $default; 313 $title = ($comment['name'] ? $comment['name'] : obfuscate($comment['mail'])); 314 echo '<img src="'.$src.'" class="medialeft" title="'.$title.'"'. 315 ' alt="'.$title.'" width="'.$size.'" height="'.$size.'" />'.NL; 316 $style = ' style="margin-left: '.($size + 14).'px;"'; 317 } else { 318 $style = ' style="margin-left: 20px;"'; 319 } 320 321 echo '</a>'.NL; 322 if ($this->getConf('linkemail') && $comment['mail']){ 323 echo $this->email($comment['email'], $comment['name']); 324 } elseif ($comment['url']){ 325 echo $this->external_link($comment['url'], $comment['name'], 'urlextern'); 326 } else { 327 echo $comment['name']; 328 } 329 if ($comment['address']) echo ', '.htmlentities($comment['address']); 330 echo ', '.date($conf['dformat'], $comment['date']); 331 if ($comment['edited']) echo ' ('.date($conf['dformat'], $comment['edited']).')'; 332 echo ':'.NL; 333 echo '</div>'.NL; // class="comment_head" 334 335 // main comment content 336 echo '<div class="comment_body"'.($this->getConf('usegravatar') ? $style : '').'>'.NL; 337 echo $comment['xhtml'].NL; 338 echo '</div>'.NL; // class="comment_body" 339 340 341 if ($visible){ 342 // show hide/show toogle button? 343 echo '<div class="comment_buttons">'.NL; 344 if ($INFO['perm'] == AUTH_ADMIN){ 345 if (!$comment['show']) $label = $this->getLang('btn_show'); 346 else $label = $this->getLang('btn_hide'); 347 348 $this->_button($cid, $label, 'toogle'); 349 } 350 351 // show reply button? 352 if (($data['status'] == 1) && !$reply && $comment['show'] 353 && ($this->getConf('allowguests') || $_SERVER['REMOTE_USER'])) 354 $this->_button($cid, $this->getLang('btn_reply'), 'reply', true); 355 356 // show edit and delete button? 357 if ((($comment['user'] == $_SERVER['REMOTE_USER']) && ($comment['user'] != '')) 358 || ($INFO['perm'] == AUTH_ADMIN)) 359 $this->_button($cid, $lang['btn_secedit'], 'edit', true); 360 if ($INFO['perm'] == AUTH_ADMIN) 361 $this->_button($cid, $lang['btn_delete'], 'delete'); 362 echo '</div>'.NL; // class="comment_buttons" 363 echo '<div class="comment_line" '.($this->getConf('usegravatar') ? $style : '').'> </div>'.NL; 364 } 365 366 // replies to this comment entry? 367 if (count($comment['replies'])){ 368 echo '<div class="comment_replies"'.$style.'>'.NL; 369 $visible = ($comment['show'] && $visible); 370 foreach ($comment['replies'] as $rid){ 371 $this->_print($rid, $data, $cid, $reply, $visible); 372 } 373 echo '</div>'.NL; // class="comment_replies" 374 } 375 376 if (!$comment['show']) echo '</div>'.NL; // class="comment_hidden" 377 378 // reply form 379 if ($reply == $cid){ 380 echo '<div class="comment_replies">'.NL; 381 $this->_form('', 'add', $cid); 382 echo '</div>'.NL; // class="comment_replies" 383 } 384 } 385 386 /** 387 * Outputs the comment form 388 */ 389 function _form($raw = '', $act = 'add', $cid = NULL){ 390 global $lang; 391 global $conf; 392 global $ID; 393 global $INFO; 394 395 // not for unregistered users when guest comments aren't allowed 396 if (!$_SERVER['REMOTE_USER'] && !$this->getConf('allowguests')) return false; 397 398 // fill $raw with $_REQUEST['text'] if it's empty (for failed CAPTCHA check) 399 if (!$raw && ($_REQUEST['comment'] == 'show')) $raw = $_REQUEST['text']; 400 401 ?> 402 <div class="comment_form"> 403 <form id="discussion__comment_form" method="post" action="<?php echo script() ?>" accept-charset="<?php echo $lang['encoding'] ?>" onsubmit="return validate(this);"> 404 <div class="no"> 405 <input type="hidden" name="id" value="<?php echo $ID ?>" /> 406 <input type="hidden" name="do" value="show" /> 407 <input type="hidden" name="comment" value="<?php echo $act ?>" /> 408 <?php 409 410 // for adding a comment 411 if ($act == 'add'){ 412 ?> 413 <input type="hidden" name="reply" value="<?php echo $cid ?>" /> 414 <?php 415 // for registered user (and we're not in admin import mode) 416 if ($conf['useacl'] && $_SERVER['REMOTE_USER'] 417 && (!($this->getConf('adminimport') && ($INFO['perm'] == AUTH_ADMIN)))){ 418 ?> 419 <input type="hidden" name="user" value="<?php echo $_SERVER['REMOTE_USER'] ?>" /> 420 <input type="hidden" name="name" value="<?php echo $INFO['userinfo']['name'] ?>" /> 421 <input type="hidden" name="mail" value="<?php echo $INFO['userinfo']['mail'] ?>" /> 422 <?php 423 // for guest: show name and e-mail entry fields 424 } else { 425 ?> 426 <input type="hidden" name="user" value="<?php echo clientIP() ?>" /> 427 <div class="comment_name"> 428 <label class="block" for="discussion__comment_name"> 429 <span><?php echo $lang['fullname'] ?>:</span> 430 <input type="text" class="edit" name="name" id="discussion__comment_name" size="50" tabindex="1" value="<?php echo hsc($_REQUEST['name'])?>" /> 431 </label> 432 </div> 433 <div class="comment_mail"> 434 <label class="block" for="discussion__comment_mail"> 435 <span><?php echo $lang['email'] ?>:</span> 436 <input type="text" class="edit" name="mail" id="discussion__comment_mail" size="50" tabindex="2" value="<?php echo hsc($_REQUEST['mail'])?>" /> 437 </label> 438 </div> 439 <?php 440 } 441 442 // allow entering an URL 443 if ($this->getConf('urlfield')){ 444 ?> 445 <div class="comment_url"> 446 <label class="block" for="discussion__comment_url"> 447 <span><?php echo $this->getLang('url') ?>:</span> 448 <input type="text" class="edit" name="url" id="discussion__comment_url" size="50" tabindex="3" value="<?php echo hsc($_REQUEST['url'])?>" /> 449 </label> 450 </div> 451 <?php 452 } 453 454 // allow entering an address 455 if ($this->getConf('addressfield')){ 456 ?> 457 <div class="comment_address"> 458 <label class="block" for="discussion__comment_address"> 459 <span><?php echo $this->getLang('address') ?>:</span> 460 <input type="text" class="edit" name="address" id="discussion__comment_address" size="50" tabindex="4" value="<?php echo hsc($_REQUEST['address'])?>" /> 461 </label> 462 </div> 463 <?php 464 } 465 466 // allow setting the comment date 467 if ($this->getConf('adminimport') && ($INFO['perm'] == AUTH_ADMIN)){ 468 ?> 469 <div class="comment_date"> 470 <label class="block" for="discussion__comment_date"> 471 <span><?php echo $this->getLang('date') ?>:</span> 472 <input type="text" class="edit" name="date" id="discussion__comment_date" size="50" /> 473 </label> 474 </div> 475 <?php 476 } 477 478 // for saving a comment 479 } else { 480 ?> 481 <input type="hidden" name="cid" value="<?php echo $cid ?>" /> 482 <?php 483 } 484 ?> 485 <div class="comment_text"> 486 <textarea class="edit" name="text" cols="80" rows="10" id="discussion__comment_text" tabindex="5"><?php echo hsc($raw) ?></textarea> 487 </div> 488 <?php //bad and dirty event insert hook 489 $evdata = array('writable' => true); 490 trigger_event('HTML_EDITFORM_INJECTION', $evdata); 491 ?> 492 <input class="button" type="submit" name="submit" value="<?php echo $lang['btn_save'] ?>" tabindex="6" /> 493 </div> 494 </form> 495 </div> 496 <?php 497 if ($this->getConf('usecocomment')) echo $this->_coComment(); 498 } 499 500 /** 501 * Adds a javascript to interact with coComments 502 */ 503 function _coComment(){ 504 global $ID; 505 global $conf; 506 global $INFO; 507 508 $user = $_SERVER['REMOTE_USER']; 509 510 ?> 511 <script type="text/javascript"><!--//--><![CDATA[//><!-- 512 var blogTool = "DokuWiki"; 513 var blogURL = "<?php echo DOKU_URL ?>"; 514 var blogTitle = "<?php echo $conf['title'] ?>"; 515 var postURL = "<?php echo wl($ID, '', true) ?>"; 516 var postTitle = "<?php echo tpl_pagetitle($ID, true) ?>"; 517 <?php 518 if ($user){ 519 ?> 520 var commentAuthor = "<?php echo $INFO['userinfo']['name'] ?>"; 521 <?php 522 } else { 523 ?> 524 var commentAuthorFieldName = "name"; 525 <?php 526 } 527 ?> 528 var commentAuthorLoggedIn = <?php echo ($user ? 'true' : 'false') ?>; 529 var commentFormID = "discussion__comment_form"; 530 var commentTextFieldName = "text"; 531 var commentButtonName = "submit"; 532 var cocomment_force = false; 533 //--><!]]></script> 534 <script type="text/javascript" src="http://www.cocomment.com/js/cocomment.js"> 535 </script> 536 <?php 537 } 538 539 /** 540 * General button function 541 */ 542 function _button($cid, $label, $act, $jump = false){ 543 global $ID; 544 $anchor = ($jump ? '#discussion__comment_form' : '' ); 545 546 ?> 547 <form class="button" method="post" action="<?php echo script().$anchor ?>"> 548 <div class="no"> 549 <input type="hidden" name="id" value="<?php echo $ID ?>" /> 550 <input type="hidden" name="do" value="show" /> 551 <input type="hidden" name="comment" value="<?php echo $act ?>" /> 552 <input type="hidden" name="cid" value="<?php echo $cid ?>" /> 553 <input type="submit" value="<?php echo $label ?>" class="button" title="<?php echo $label ?>" /> 554 </div> 555 </form> 556 <?php 557 return true; 558 } 559 560 /** 561 * Adds an entry to the comments changelog 562 * 563 * @author Esther Brunner <wikidesign@gmail.com> 564 * @author Ben Coburn <btcoburn@silicodon.net> 565 */ 566 function _addLogEntry($date, $id, $type = 'cc', $summary = '', $extra = ''){ 567 global $conf; 568 569 $changelog = $conf['metadir'].'/_comments.changes'; 570 571 if(!$date) $date = time(); //use current time if none supplied 572 $remote = $_SERVER['REMOTE_ADDR']; 573 $user = $_SERVER['REMOTE_USER']; 574 575 $strip = array("\t", "\n"); 576 $logline = array( 577 'date' => $date, 578 'ip' => $remote, 579 'type' => str_replace($strip, '', $type), 580 'id' => $id, 581 'user' => $user, 582 'sum' => str_replace($strip, '', $summary), 583 'extra' => str_replace($strip, '', $extra) 584 ); 585 586 // add changelog line 587 $logline = implode("\t", $logline)."\n"; 588 io_saveFile($changelog, $logline, true); //global changelog cache 589 $this->_trimRecentCommentsLog($changelog); 590 } 591 592 /** 593 * Trims the recent comments cache to the last $conf['changes_days'] recent 594 * changes or $conf['recent'] items, which ever is larger. 595 * The trimming is only done once a day. 596 * 597 * @author Ben Coburn <btcoburn@silicodon.net> 598 */ 599 function _trimRecentCommentsLog($changelog){ 600 global $conf; 601 602 if (@file_exists($changelog) && 603 (filectime($changelog) + 86400) < time() && 604 !@file_exists($changelog.'_tmp')){ 605 606 io_lock($changelog); 607 $lines = file($changelog); 608 if (count($lines)<$conf['recent']) { 609 // nothing to trim 610 io_unlock($changelog); 611 return true; 612 } 613 614 io_saveFile($changelog.'_tmp', ''); // presave tmp as 2nd lock 615 $trim_time = time() - $conf['recent_days']*86400; 616 $out_lines = array(); 617 618 for ($i=0; $i<count($lines); $i++) { 619 $log = parseChangelogLine($lines[$i]); 620 if ($log === false) continue; // discard junk 621 if ($log['date'] < $trim_time) { 622 $old_lines[$log['date'].".$i"] = $lines[$i]; // keep old lines for now (append .$i to prevent key collisions) 623 } else { 624 $out_lines[$log['date'].".$i"] = $lines[$i]; // definitely keep these lines 625 } 626 } 627 628 // sort the final result, it shouldn't be necessary, 629 // however the extra robustness in making the changelog cache self-correcting is worth it 630 ksort($out_lines); 631 $extra = $conf['recent'] - count($out_lines); // do we need extra lines do bring us up to minimum 632 if ($extra > 0) { 633 ksort($old_lines); 634 $out_lines = array_merge(array_slice($old_lines,-$extra),$out_lines); 635 } 636 637 // save trimmed changelog 638 io_saveFile($changelog.'_tmp', implode('', $out_lines)); 639 @unlink($changelog); 640 if (!rename($changelog.'_tmp', $changelog)) { 641 // rename failed so try another way... 642 io_unlock($changelog); 643 io_saveFile($changelog, implode('', $out_lines)); 644 @unlink($changelog.'_tmp'); 645 } else { 646 io_unlock($changelog); 647 } 648 return true; 649 } 650 } 651 652 /** 653 * Sends a notify mail on new comment 654 * 655 * @param array $comment data array of the new comment 656 * 657 * @author Andreas Gohr <andi@splitbrain.org> 658 * @author Esther Brunner <wikidesign@gmail.com> 659 */ 660 function _notify($comment){ 661 global $conf; 662 global $ID; 663 664 if ((!$conf['subscribers']) && (!$conf['notify'])) return; //subscribers enabled? 665 $bcc = subscriber_addresslist($ID); 666 if ((empty($bcc)) && (!$conf['notify'])) return; 667 $to = $conf['notify']; 668 $text = io_readFile($this->localFN('subscribermail')); 669 670 $text = str_replace('@PAGE@', $ID, $text); 671 $text = str_replace('@TITLE@', $conf['title'], $text); 672 $text = str_replace('@DATE@', date($conf['dformat'], $comment['date']), $text); 673 $text = str_replace('@NAME@', $comment['name'], $text); 674 $text = str_replace('@TEXT@', $comment['raw'], $text); 675 $text = str_replace('@UNSUBSCRIBE@', wl($ID, 'do=unsubscribe', true, '&'), $text); 676 $text = str_replace('@DOKUWIKIURL@', DOKU_URL, $text); 677 678 $subject = '['.$conf['title'].'] '.$this->getLang('mail_newcomment'); 679 680 mail_send($to, $subject, $text, $conf['mailfrom'], '', $bcc); 681 } 682 683 /** 684 * Counts the number of visible comments 685 */ 686 function _count($data){ 687 $number = 0; 688 foreach ($data['comments'] as $cid => $comment){ 689 if ($comment['parent']) continue; 690 if (!$comment['show']) continue; 691 $number++; 692 $rids = $comment['replies']; 693 if (count($rids)) $number = $number + $this->_countReplies($data, $rids); 694 } 695 return $number; 696 } 697 698 function _countReplies(&$data, $rids){ 699 $number = 0; 700 foreach ($rids as $rid){ 701 if (!isset($data['comments'][$rid])) continue; // reply was removed 702 if (!$data['comments'][$rid]['show']) continue; 703 $number++; 704 $rids = $data['comments'][$rid]['replies']; 705 if (count($rids)) $number = $number + $this->_countReplies($data, $rids); 706 } 707 return $number; 708 } 709 710 /** 711 * Renders the comment text 712 */ 713 function _render($raw){ 714 if ($this->getConf('wikisyntaxok')){ 715 $xhtml = $this->render($raw); 716 } else { // wiki syntax not allowed -> just encode special chars 717 $xhtml = htmlspecialchars(trim($raw)); 718 } 719 return $xhtml; 720 } 721 722 /** 723 * Checks if 'newthread' was given as action or the comment form was submitted 724 */ 725 function handle_act_preprocess(&$event, $param){ 726 if ($event->data == 'newthread'){ 727 // we can handle it -> prevent others 728 // $event->stopPropagation(); 729 $event->preventDefault(); 730 731 $event->data = $this->_handle_newThread(); 732 } 733 if ((in_array($_REQUEST['comment'], array('add', 'save'))) 734 && (@file_exists(DOKU_PLUGIN.'captcha/action.php'))){ 735 $this->_handle_captchaCheck(); 736 } 737 } 738 739 /** 740 * Creates a new thread page 741 */ 742 function _handle_newThread(){ 743 global $ID; 744 global $INFO; 745 746 $ns = $_REQUEST['ns']; 747 $title = str_replace(':', '', $_REQUEST['title']); 748 $back = $ID; 749 $ID = ($ns ? $ns.':' : '').cleanID($title); 750 $INFO = pageinfo(); 751 752 // check if we are allowed to create this file 753 if ($INFO['perm'] >= AUTH_CREATE){ 754 755 //check if locked by anyone - if not lock for my self 756 if ($INFO['locked']) return 'locked'; 757 else lock($ID); 758 759 // prepare the new thread file with default stuff 760 if (!@file_exists($INFO['filepath'])){ 761 global $TEXT; 762 global $conf; 763 764 $TEXT = pageTemplate(array(($ns ? $ns.':' : '').$title)); 765 if (!$TEXT){ 766 $TEXT = "<- [[:$back]]\n\n====== $title ======\n\n"; 767 if ($this->getConf('usegravatar')) 768 $TEXT .= '{{gravatar>'.$INFO['userinfo']['mail'].' }} '; 769 $TEXT .= "//".$INFO['userinfo']['name'].", ".date($conf['dformat']).": //". 770 "\n\n\n\n~~DISCUSSION~~\n"; 771 } 772 return 'preview'; 773 } else { 774 return 'edit'; 775 } 776 } else { 777 return 'show'; 778 } 779 } 780 781 /** 782 * Checks if the CAPTCHA string submitted is valid 783 * 784 * @author Andreas Gohr <gohr@cosmocode.de> 785 * @adaption Esther Brunner <wikidesign@gmail.com> 786 */ 787 function _handle_captchaCheck(){ 788 if (@file_exists(DOKU_PLUGIN.'captcha/disabled')) return; // CAPTCHA is disabled 789 790 require_once(DOKU_PLUGIN.'captcha/action.php'); 791 $captcha = new action_plugin_captcha; 792 793 // do nothing if logged in user and no CAPTCHA required 794 if (!$captcha->getConf('forusers') && $_SERVER['REMOTE_USER']) return; 795 796 // compare provided string with decrypted captcha 797 $rand = PMA_blowfish_decrypt($_REQUEST['plugin__captcha_secret'], auth_cookiesalt()); 798 $code = $captcha->_generateCAPTCHA($captcha->_fixedIdent(), $rand); 799 800 if (!$_REQUEST['plugin__captcha_secret'] || 801 !$_REQUEST['plugin__captcha'] || 802 strtoupper($_REQUEST['plugin__captcha']) != $code){ 803 804 // CAPTCHA test failed! Continue to edit instead of saving 805 msg($captcha->getLang('testfailed'), -1); 806 if ($_REQUEST['comment'] == 'save') $_REQUEST['comment'] = 'edit'; 807 elseif ($_REQUEST['comment'] == 'add') $_REQUEST['comment'] = 'show'; 808 } 809 // if we arrive here it was a valid save 810 } 811 812} 813 814//Setup VIM: ex: et ts=4 enc=utf-8 : 815