PageRenderTime 56ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/wkp_Comments.php

https://github.com/pastak/jichikai_web
PHP | 354 lines | 237 code | 85 blank | 32 comment | 38 complexity | bcafdbb48420ebfe1e44a0470106235b MD5 | raw file
  1. <?php
  2. /*
  3. * Comments plugin for LionWiki, (c) 2009 Adam Zivner, adam.zivner@gmail.com
  4. *
  5. * There are two ways to add comments section to your pages:
  6. * 1) insert {plugin:COMMENTS} on appropriate place in your template
  7. * If you then don't want comments on some particular page, insert {NO_COMMENTS} into that page content.
  8. * 2) insert {COMMENTS} into your page content. Good if you want discussion only on some pages.
  9. *
  10. * Comments are almost totally templatable, see plugins/Comments/template.html, it
  11. * should be quite self explanatory.
  12. *
  13. * Comments are stored in var/comments/ in the same directory structure as history.
  14. */
  15. class Comments
  16. {
  17. var $desc = array(
  18. array("Comments", "adds support for comments.")
  19. );
  20. var $sorting_order = "asc"; // "asc" means from oldest to newest, "desc" means newest first
  21. var $data_dir;
  22. var $comments_dir;
  23. var $template = "template.html";
  24. var $rss_use = true; // turn on/off creating comments RSS
  25. var $rss_file;
  26. var $rss_max_comments = 50;
  27. var $rss_template = '<rss version="2.0">
  28. <channel>
  29. <title>{WIKI_TITLE}</title>
  30. <link>{PAGE_LINK}</link>
  31. <description>{WIKI_DESCRIPTION}</description>
  32. <language>{LANG}</language>
  33. {CONTENT_RSS}
  34. </channel>
  35. </rss>'; // don't change template. This exact form is needed for correct functioning.
  36. function Comments()
  37. {
  38. $this->data_dir = $GLOBALS["PLUGINS_DIR"] . "Comments/"; // CSS and JS for comments
  39. $this->comments_dir = $GLOBALS["VAR_DIR"] . "comments/"; // actual comments
  40. $this->rss_file = $GLOBALS["VAR_DIR"] . "comments-rss.xml";
  41. $this->localize();
  42. }
  43. /*
  44. * Process comment content, i.e. turn wiki syntax into HTML.
  45. *
  46. * Because of LionWiki core structure, we need to duplicate the functionality :(
  47. *
  48. * Supported features are: bold, italics, underscore, strikethrough, link
  49. */
  50. function processComment($txt)
  51. {
  52. global $PG_DIR;
  53. $txt = preg_replace("#\[([^\]\|]+)\|(\./([^\]]+)|(https?://[^\]]+))\]#U", '<a href="$2" class="external">$1</a>', $txt);
  54. $txt = preg_replace("#(?<!\")(https?://[^\]]+)#i", '<a href="$0" class="external">$1</a>', $txt);
  55. preg_match_all("/\[(?:([^|\]]+)\|)?([^\]#]+)(?:#([^\]]+))?\]/", $txt, $matches, PREG_SET_ORDER); // matching Wiki links
  56. foreach($matches as $m) {
  57. $m[1] = $m[1] ? $m[1] : $m[2]; // is page label same as its name?
  58. $m[3] = $m[3] ? "#".u(preg_replace("/[^\da-z]/i", "_", $m[3])) : ""; // anchor
  59. $attr = file_exists("$PG_DIR$m[2].txt") ? $m[3] : '&amp;action=edit" class="pending"';
  60. $txt = str_replace($m[0], '<a href="'.$self.'?page='.u($m[2]).$attr.'">'.$m[1].'</a>', $txt);
  61. }
  62. $txt = preg_replace('#([0-9a-zA-Z\./~\-_]+@[0-9a-z/~\-_]+\.[0-9a-z\./~\-_]+)#i', '<a href="mailto:$0">$0</a>', $txt);
  63. $txt = preg_replace("/'--(.*)--'/Um", '<del>$1</del>', $txt); // strikethrough
  64. $txt = str_replace("--", "&mdash;", $txt); // --
  65. $txt = preg_replace("/'__(.*)__'/Um", '<u>$1</u>', $txt); // underlining
  66. $txt = preg_replace("/'''(.*)'''/Um", '<strong>$1</strong>', $txt); // bold
  67. $txt = preg_replace("/''(.*)''/Um", '<em>$1</em>', $txt); // italic
  68. $txt = preg_replace("/(\r\n|\r)/", "\n", $txt); // unifying newlines to Unix ones
  69. $txt = str_replace("\n", "<br />", $txt);
  70. return $txt;
  71. }
  72. function template()
  73. {
  74. global $CON, $html, $action, $preview, $page, $PG_DIR, $HEAD, $self, $comments_html, $comment_captcha_failed;
  75. /*
  76. * Include comments if:
  77. * - {plugin:COMMENTS} is in template and {NO_COMMENTS} is not in page content
  78. * - {COMMENTS} is in page content
  79. */
  80. if($action == "" && !$preview && ((template_match("plugin:COMMENTS", $html, $null) && strpos($CON, "{NO_COMMENTS}") === false)
  81. || strpos($CON, "{COMMENTS}") !== false)) {
  82. $HEAD .= '<script type="text/javascript" src="plugins/Comments/comments.js"></script>';
  83. $HEAD .= '<style type="text/css" media="all">@import url("plugins/Comments/comments.css");</style>';
  84. $tmpl = file_get_contents($this->data_dir . $this->template);
  85. $tmpl = strtr($tmpl, array(
  86. "{FORM_NAME}" => $this->TP_FORM_NAME,
  87. "{FORM_EMAIL}" => $this->TP_FORM_EMAIL,
  88. "{FORM_CONTENT}" => $this->TP_FORM_CONTENT,
  89. // Following 3 are for failed captcha test
  90. "{FORM_NAME_VALUE}" => $comment_captcha_failed ? h($_POST["name"]) : "",
  91. "{FORM_EMAIL_VALUE}" => $comment_captcha_failed ? h($_POST["email"]) : "",
  92. "{FORM_CONTENT_VALUE}" => $comment_captcha_failed ? h($_POST["content"]) : "",
  93. "{FORM_SUBMIT}" => $this->TP_FORM_SUBMIT,
  94. "{FORM_SELF}" => h($self),
  95. "{FORM_PAGE}" => h($page),
  96. "{COMMENTS}" => $this->TP_COMMENTS
  97. ));
  98. $items_str = "";
  99. if($dir = @opendir($this->comments_dir . $page)) {
  100. $item_tmpl = "";
  101. if(preg_match("/\{item\}(.*)\{\/item\}/Us", $tmpl, $m))
  102. $item_tmpl = $m[1];
  103. $filenames = array();
  104. while($filename = @readdir($dir))
  105. if(preg_match("/([0-9]{8}-[0-9]{4}-[0-9]{2})\.txt/", $filename, $m))
  106. $filenames[] = $filename;
  107. if($this->sorting_order == "asc")
  108. sort($filenames);
  109. else if($this->sorting_order == "desc")
  110. rsort($filenames);
  111. $comment_num = 0;
  112. foreach($filenames as $filename) {
  113. $comment_num++;
  114. $file = file_get_contents($this->comments_dir . $page . "/" . $filename);
  115. $delimiter = strpos($file, "\n");
  116. $meta = substr($file, 0, $delimiter);
  117. $content = substr($file, $delimiter + 1);
  118. list($ip, $name, $email) = explode("\t", $meta);
  119. $processed_content = $this->processComment($content);
  120. $items_str .= strtr($item_tmpl, array(
  121. "{CONTENT}" => $processed_content,
  122. "{NAME}" => h($name),
  123. "{EMAIL}" => h($email),
  124. "{NAME_TO_EMAIL}" => $email == "" ? $name : ("<a href=\"mailto:".h($email)."\">" . h($name) . "</a>"),
  125. "{IP}" => $ip,
  126. "{DATE}" => rev_time(basename($filename, ".txt")),
  127. "{ID}" => basename($filename, ".txt"),
  128. "{NUMBER}" => $comment_num,
  129. "{DELETE}" => h($this->TP_DELETE),
  130. "{DELETE_LINK}" => "$self?action=admin-deletecomment&amp;page=" . u($page) . "&amp;filename=" . u($filename),
  131. "{DELETE_CONFIRM}" => h($this->TP_DELETE_CONFIRM)
  132. ));
  133. }
  134. }
  135. $tmpl = str_replace("{NUMBER_OF_COMMENTS}", count($filenames), $tmpl);
  136. $comments_html = preg_replace("/\{item\}.*\{\/item\}/Us", $items_str, $tmpl);
  137. plugin("commentsTemplate");
  138. $html = template_replace("plugin:COMMENTS", $comments_html, $html);
  139. $CON = str_replace("{COMMENTS}", $comments_html, $CON);
  140. }
  141. $CON = str_replace("{NO_COMMENTS}", "", $CON);
  142. $HEAD .= "\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS ".h($this->TP_COMMENTS)."\" href=\"$this->rss_file\" />\n";
  143. }
  144. function actionBegin()
  145. {
  146. global $page, $LOCAL_HOUR, $plugins, $action, $plugin_saveok, $error, $comment_captcha_failed;
  147. if($action == "save-comment") {
  148. if(isset($plugins["Captcha"]) && $plugins["Captcha"]->checkCaptcha() == true) {
  149. $comment_captcha_failed = true;
  150. $action = "";
  151. $error = ""; // suppress error messages
  152. unset($_REQUEST["qid"]); // don't check captcha again
  153. return true;
  154. }
  155. if(!is_dir(rtrim($this->comments_dir, "/"))) {
  156. mkdir(rtrim($this->comments_dir, "/"));
  157. $f = fopen($this->comments_dir . ".htaccess", "w");
  158. fwrite($f, "deny from all");
  159. fclose($f);
  160. }
  161. $c_dir = $this->comments_dir . urldecode($page);
  162. if(!is_dir(rtrim($c_dir, "/")))
  163. mkdir(rtrim($c_dir, "/"));
  164. function prepare($txt) {
  165. return strtr($txt, array(
  166. "\t" => "",
  167. "\n" => ""
  168. ));
  169. }
  170. $meta = prepare($_SERVER["REMOTE_ADDR"]) . "\t" . prepare($_POST["name"]) . "\t" . prepare($_POST["email"]) . "\n";
  171. $rightnow = date("Ymd-Hi-s", time() + $LOCAL_HOUR * 3600);
  172. $h = fopen($c_dir . "/" . $rightnow . ".txt", "w");
  173. if(!$h)
  174. return;
  175. fwrite($h, $meta . $_POST["content"]);
  176. fclose($h);
  177. $this->writeRSS($page, $rightnow, $meta . $_POST["content"]);
  178. header("Location: $self?page=$page#$rightnow");
  179. die();
  180. }
  181. }
  182. function writeRSS($page, $id, $content)
  183. {
  184. global $PROTECTED_READ, $WIKI_TITLE, $LANG;
  185. if(!$this->rss_use || $PROTECTED_READ)
  186. return;
  187. $pagelink = ($_SERVER["HTTPS"] ? "https://" : "http://") . $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["SCRIPT_NAME"];
  188. preg_match("/<\/language>(.*)<\/channel>/s", @file_get_contents($this->rss_file), $matches);
  189. $items = $matches[1];
  190. $pos = -1;
  191. // count items
  192. for($i = 0; $i < $this->rss_max_comments - 1; $i++)
  193. if(!($pos = strpos($items, "</item>", $pos + 1)))
  194. break;
  195. if($pos) // if count is higher than $max_changes - 1, cut out the rest
  196. $items = substr($items, 0, $pos + 7);
  197. $n_item = "
  198. <item>
  199. <title>".h($page)."</title>
  200. <pubDate>". date("r")."</pubDate>
  201. <link>$pagelink?page=".u($page)."#$id</link>
  202. <description><pre>".h($content)."</pre></description>
  203. </item>";
  204. $rss = str_replace('{WIKI_TITLE}', $WIKI_TITLE . ": " . $this->TP_COMMENTS, $this->rss_template);
  205. $rss = str_replace('{PAGE_LINK}', $pagelink, $rss);
  206. $rss = str_replace('{LANG}', $LANG, $rss);
  207. $rss = str_replace('{WIKI_DESCRIPTION}', "RSS comments feed from " . $WIKI_TITLE, $rss);
  208. $rss = str_replace('{CONTENT_RSS}', $n_item . $items, $rss);
  209. if(!$file = @fopen($this->rss_file, "w")) {
  210. echo "Opening file for writing RSS comments file is not possible! Please create file rss.xml in your var directory and make it writable (chmod 666).";
  211. return true;
  212. }
  213. fwrite($file, $rss);
  214. fclose($file);
  215. }
  216. /*
  217. * Need to move comments together with the page (during rename operation)
  218. */
  219. function pageWritten()
  220. {
  221. global $moveto, $PG_DIR;
  222. // page is already set to $moveto, we need to take original page name from the request
  223. $orig_name = $_REQUEST["page"];
  224. if(!$moveto || $moveto == $orig_name) // page was not moved, nothing to do
  225. return;
  226. if(file_exists($this->comments_dir . $orig_name))
  227. rename($this->comments_dir . $orig_name, $this->comments_dir . $moveto);
  228. }
  229. // Localization strings
  230. var $cs_strings = array(
  231. array("TP_FORM_NAME", "Jméno"),
  232. array("TP_FORM_EMAIL", "E-mail"),
  233. array("TP_FORM_CONTENT", "Obsah"),
  234. array("TP_FORM_SUBMIT", "Přidat"),
  235. array("TP_REPLY_TO", "Odpovědět na tento komentář"),
  236. array("TP_COMMENTS", "Komentáře"),
  237. array("TP_DELETE", "Smazat"),
  238. array("TP_DELETE_CONFIRM", "Chcete opravdu smazat tento komentář?"),
  239. );
  240. var $en_strings = array(
  241. array("TP_FORM_NAME", "Name"),
  242. array("TP_FORM_EMAIL", "E-mail"),
  243. array("TP_FORM_CONTENT", "Content"),
  244. array("TP_FORM_SUBMIT", "Save"),
  245. array("TP_REPLY_TO", "Reply to this comment"),
  246. array("TP_COMMENTS", "Comments"),
  247. array("TP_DELETE", "Delete"),
  248. array("TP_DELETE_CONFIRM", "Do you really want to delete this comment?")
  249. );
  250. var $fr_strings = array(
  251. array("TP_FORM_NAME", "Votre nom"),
  252. array("TP_FORM_EMAIL", "E-mail"),
  253. array("TP_FORM_CONTENT", "Votre commentaire"),
  254. array("TP_FORM_SUBMIT", "Envoyer"),
  255. array("TP_REPLY_TO", "Répondre à ce commentaire"),
  256. array("TP_COMMENTS", "Commentaires"),
  257. array("TP_DELETE", "Supprimer"),
  258. array("TP_DELETE_CONFIRM", "Voulez-vous supprimer ce commentaire ?")
  259. );
  260. function localize()
  261. {
  262. global $LANG;
  263. foreach($this->en_strings as $str)
  264. $this->$str[0] = $str[1];
  265. if($LANG != "en" && isset($this->{$LANG . "_strings"}))
  266. foreach($this->{$LANG . "_strings"} as $str)
  267. $this->$str[0] = $str[1];
  268. }
  269. }