phpDocumentor pond
[ class tree: pond ] [ index: pond ] [ all elements ]

Source for file comment_post.php

Documentation is available at comment_post.php

  1. <?php
  2. /**
  3.  * This file posts a comment!
  4.  *
  5.  * This file is part of Quam Plures - {@link http://quamplures.net/}
  6.  * See also {@link https://launchpad.net/quam-plures}.
  7.  *
  8.  * @copyright (c) 2009 - 2011 by the Quam Plures developers - {@link http://quamplures.net/}
  9.  * @copyright (c)2003-2009 by Francois PLANQUE - {@link http://fplanque.net/}
  10.  *
  11.  *  {@internal License choice
  12.  *  - If you have received this file as part of a package, please find the license.txt file in
  13.  *    the same folder or the closest folder above for complete license terms.
  14.  *  - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15.  *    then you must choose one of the following licenses before using the file:
  16.  *    - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17.  *    - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18.  *  }}}
  19.  *
  20.  *  {@internal Open Source relicensing agreement:
  21.  *  }}}
  22.  *
  23.  * @package pond
  24.  */
  25.  
  26.  
  27. /**
  28.  * Initialize everything:
  29.  */
  30. require_once dirname(__FILE__).'/../qp_config/_config.php';
  31.  
  32. require_once $inc_path.'_main.inc.php';
  33.  
  34. header'Content-Type: text/html; charset='.$io_charset );
  35.  
  36. // Getting GET or POST parameters:
  37. param'comment_post_ID''integer'true )// required
  38. param'redirect_to''string''' );
  39.  
  40.  
  41. $action param_arrayindex'submit_comment_post_'.$comment_post_ID'save' );
  42.  
  43.  
  44. $ItemCache get_Cache'ItemCache' );
  45. $commented_Item $ItemCache->get_by_ID$comment_post_ID );
  46.  
  47. if$commented_Item->can_commentNULL ) )
  48. {
  49.     $Messages->addT_('You cannot leave comments on this post!')'error' );
  50. }
  51.  
  52. // Note: we use funky field names to defeat the most basic guestbook spam bots and/or their most basic authors
  53. $comment param'p''html' );
  54.  
  55. param'comment_autobr''integer'($comments_use_autobr == 'always');
  56.  
  57. ifis_logged_in() )
  58. {
  59.     /**
  60.      * @var User 
  61.      */
  62.     $User $current_User;
  63.     $author null;
  64.     $email null;
  65.     $url null;
  66.     $comment_cookies null;
  67.     $comment_allow_msgform null;
  68. }
  69. else
  70. {    // User is not logged in (registered users), we need some id info from him:
  71.     $User NULL;
  72.     // Note: we use funky field names to defeat the most basic guestbook spam bots and/or their most basic authors
  73.     $author param'u''string' );
  74.     $email param'i''string' );
  75.     $url param'o''string' );
  76.     param'comment_cookies''integer');
  77.     param'comment_allow_msgform''integer')// checkbox
  78. }
  79.  
  80. param'comment_rating''integer'NULL );
  81.  
  82.  
  83. $now date'Y-m-d H:i:s'$localtimenow );
  84.  
  85.  
  86. // VALIDATION:
  87.  
  88. $original_comment $comment;
  89.  
  90. // Trigger event: a Plugin could add a $category="error" message here..
  91. // This must get triggered before any internal validation and must pass all relevant params.
  92. // openID plugin will validate a given OpenID here
  93. $Plugins->trigger_event'CommentFormSent'array(
  94.         'comment_post_ID' => $comment_post_ID,
  95.         'comment' => $comment,
  96.         'original_comment' => $original_comment,
  97.         'comment_autobr' => $comment_autobr,
  98.         'action' => $action,
  99.         'anon_name' => $author,
  100.         'anon_email' => $email,
  101.         'anon_url' => $url,
  102.         'rating' => $comment_rating,
  103.         'anon_allow_msgform' => $comment_allow_msgform,
  104.         'anon_cookies' => $comment_cookies,
  105.         'User' => $User,
  106.         'redirect_to' => $redirect_to,
  107.     ) );
  108.  
  109. $commented_Item->get_Blog()// Make sure Blog is loaded (will be needed wether logged in or not)
  110.  
  111. if$User )
  112. {    // User is logged in
  113.     // Does user have permission to edit?
  114.     $perm_comment_edit $User->check_perm'blog_comments''edit'false$commented_Item->Blog->ID );
  115. }
  116. else
  117. {    // User is still not logged in
  118.     // NO permission to edit!
  119.     $perm_comment_edit false;
  120.  
  121.     // we need some id info from the anonymous user:
  122.     if ($require_name_email)
  123.     // We want Name and EMail with comments
  124.         ifempty($author) )
  125.         {
  126.             $Messages->addT_('Please fill in your name.')'error' );
  127.         }
  128.         ifempty($email) )
  129.         {
  130.             $Messages->addT_('Please fill in your email.')'error' );
  131.         }
  132.     }
  133.  
  134.     if!empty($author&& antispam_check$author ) )
  135.     {
  136.         $Messages->addT_('Supplied name is invalid.')'error' );
  137.     }
  138.  
  139.     if!empty($email)
  140.         && !is_email($email)|| antispam_check$email ) ) )
  141.     {
  142.         $Messages->addT_('Supplied email address is invalid.')'error' );
  143.     }
  144.  
  145.  
  146.     if!stristr($url'://'&& !stristr($url'@') )
  147.     // add 'http://' if no protocol defined for URL; but not if the user seems to be entering an email address alone
  148.         $url 'http://'.$url;
  149.     }
  150.  
  151.     ifstrlen($url<= )
  152.     {    // ex: https:// is 8 chars
  153.         $url '';
  154.     }
  155.  
  156.     // Note: as part of the validation we require the url to be absolute; otherwise we cannot detect bozos typing in
  157.     // a title for their comment or whatever...
  158.     if$error validate_url$url'commenting' ) )
  159.     {
  160.         $Messages->addT_('Supplied website address is invalid: ').$error'error' );
  161.     }
  162. }
  163.  
  164. // CHECK and FORMAT content
  165. // TODO: AutoBR should really be a "comment renderer" (like with Items)
  166. // OLD stub: $comment = format_to_post( $comment, $comment_autobr, 1 ); // includes antispam
  167. $saved_comment $comment;
  168. $comment check_html_sanity$comment$perm_comment_edit 'posting' 'commenting',
  169.                 $comment_autobrNULLtrue /* escape comments */ );
  170. if$comment === false )
  171. {    // ERROR
  172.     $comment $saved_comment;
  173. }
  174.  
  175. ifempty($comment) )
  176. // comment should not be empty!
  177.     $Messages->addT_('Please do not send empty comments.')'error' );
  178. }
  179.  
  180. // Flood protection was here and SHOULD NOT have moved down!
  181.  
  182. /**
  183.  * Create comment object. Gets validated, before recording it into DB:
  184.  */
  185. $Comment new Comment();
  186. $Comment->set'type''comment' );
  187. $Comment->set_Item$commented_Item );
  188. if$User )
  189. // User is logged in, we'll use his ID
  190.     $Comment->set_author_User$User );
  191. }
  192. else
  193. {    // User is not logged in:
  194.     $Comment->set'author'$author );
  195.     $Comment->set'author_email'$email );
  196.     $Comment->set'author_url'$url );
  197.     $Comment->set'allow_msgform'$comment_allow_msgform );
  198. }
  199.  
  200. if$commented_Item->can_rate() )
  201. {    // Comment rating:
  202.     $Comment->set'rating'$comment_rating );
  203. }
  204. $Comment->set'author_IP'$Hit->IP );
  205. $Comment->set'date'$now );
  206. $Comment->set'content'$comment );
  207.  
  208. if$perm_comment_edit )
  209. {    // User has perm to moderate comments, publish automatically:
  210.     $Comment->set'status''published' );
  211. }
  212. else
  213. // Assign default status for new comments:
  214.     $Comment->set'status'$commented_Item->Blog->get_setting('new_feedback_status') );
  215. }
  216.  
  217. if$action != 'preview' )
  218. {
  219.     /*
  220.      * Flood-protection
  221.      * NOTE: devs can override the flood protection delay in /qp_config/_overrides_TEST.php
  222.      * TODO: Put time check into query?
  223.      * TODO: move that as far !!UP!! as possible! We want to waste minimum resources on Floods
  224.      * TODO: have several thresholds. For example:
  225.      * 1 comment max every 30 sec + 5 comments max every 10 minutes + 15 comments max every 24 hours
  226.      * TODO: factorize with trackback
  227.      */
  228.     $query 'SELECT MAX(comment_date)
  229.                             FROM T_comments
  230.                          WHERE comment_author_IP = '.$DB->quote($Hit->IP).'
  231.                                 OR comment_author_email = '.$DB->quote($Comment->get_author_email());
  232.     $ok 1;
  233.     if$then $DB->get_var$query ) )
  234.     {
  235.         $time_lastcomment mysql2date("U",$then);
  236.         $time_newcomment mysql2date("U",$now);
  237.         if( ($time_newcomment $time_lastcomment$minimum_comment_interval )
  238.             $ok 0;
  239.     }
  240.     if!$ok )
  241.     {
  242.         $Messages->addsprintfT_('You can only post a new comment every %d seconds.')$minimum_comment_interval )'error' );
  243.     }
  244.     /* end flood-protection */
  245. }
  246.  
  247.  
  248. // Trigger event: a Plugin could add a $category="error" message here..
  249. $Plugins->trigger_event'BeforeCommentFormInsert'array(
  250.     'Comment' => $Comment,
  251.     'original_comment' => $original_comment,
  252.     'is_preview' => ($action == 'preview'),
  253.     'action' => $action ) );
  254.  
  255.  
  256. /*
  257.  * Display error messages:
  258.  */
  259. if$Messages->count('error') )
  260. {
  261.     ifisset($page_title) )
  262.     {
  263.         $page_title T_('Errors while processing your comment');
  264.     }
  265.     // TODO: dh> HEAD part should be some global front end include file..
  266.     // fp> actually, I'd like the error messages to de displayed in a templatenable file. Something that looks like the _main template file but with minimum extra gadgets (in order to save on DB requests at each "spam denied" error)
  267.     // fp> So please don't waste time on implementing a half baked solution.
  268.     // fp> We may want to rethink templates more deeply beofre implementing this.
  269.     ?>
  270.     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  271.     <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php locale_lang(?>" lang="<?php locale_lang(?>">
  272.     <head>
  273.         <title><?php echo $app_shortname.' &rsaquo; '.$page_title ?></title>
  274.         <meta name="ROBOTS" content="NOINDEX" />
  275.         <meta http-equiv="Content-Type" content="text/html; charset=<?php echo $io_charset ?>" />
  276.     </head>
  277.     <body>
  278.     <?php
  279.     $Messages->displayT_('Cannot post comment, please correct these errors:'),
  280.     '[<a href="javascript:history.go(-1)">'.T_('Back to comment editing').'</a>]' );
  281.     ?>
  282.     </body>
  283.     </html>
  284.     <?php
  285.     exit(0);
  286. }
  287.  
  288. if$action == 'preview' )
  289. // set the Comment into user's session and redirect.
  290.     $Comment->set'original_content'$original_comment )// used in the textarea input field again
  291.     $Session->set'core.preview_Comment'$Comment );
  292.     $Session->set'core.no_CachePageContent');
  293.     $Session->dbsave();
  294.  
  295.     // This message serves the purpose that the next page will not even try to retrieve preview from cache... (and won't collect data to be cached)
  296.     // This is session based, so it's not 100% safe to prevent caching. We are also using explicit caching prevention whenever personal data is displayed
  297.     $Messages->addT_('This is a preview only! Do not forget to send your comment!')'error' );
  298.  
  299.     // Passthrough comment_cookies & comment_allow_msgform params:
  300.     // fp> moved this down here in order to keep return URLs clean whenever this is not needed.
  301.     $redirect_to url_add_param($redirect_to'redir=no&comment_cookies='.$comment_cookies
  302.         .'&comment_allow_msgform='.$comment_allow_msgform'&');
  303.  
  304.     $redirect_to .= '#comment_preview';
  305.  
  306.     header_nocache();
  307.     header_redirect();
  308.     exit(0);
  309. }
  310. else
  311. // delete any preview comment from session data:
  312.     $Session->delete'core.preview_Comment' );
  313. }
  314.  
  315.  
  316. // RECORD comment:
  317.  
  318. $Comment->dbinsert();
  319.  
  320.  
  321. /*
  322.  * ---------------
  323.  * Handle cookies:
  324.  * ---------------
  325.  */
  326. if!is_logged_in() )
  327. {
  328.     if$comment_cookies )
  329.     {    // Set cookies:
  330.         if ($email == '')
  331.             $email ' '// this to make sure a cookie is set for 'no email'
  332.         if ($url == '')
  333.             $url ' '// this to make sure a cookie is set for 'no url'
  334.  
  335.         // fplanque: made cookies available for whole site
  336.         setcookie$cookie_name$author$cookie_expires$cookie_path$cookie_domain);
  337.         setcookie$cookie_email$email$cookie_expires$cookie_path$cookie_domain);
  338.         setcookie$cookie_url$url$cookie_expires$cookie_path$cookie_domain);
  339.     }
  340.     else
  341.     {    // Erase cookies:
  342.         if!empty($_COOKIE[$cookie_name]) )
  343.         {
  344.             setcookie$cookie_name''$cookie_expired$cookie_path$cookie_domain);
  345.         }
  346.         if!empty($_COOKIE[$cookie_email]) )
  347.         {
  348.             setcookie$cookie_email''$cookie_expired$cookie_path$cookie_domain);
  349.         }
  350.         if!empty($_COOKIE[$cookie_url]) )
  351.         {
  352.             setcookie$cookie_url''$cookie_expired$cookie_path$cookie_domain);
  353.         }
  354.     }
  355. }
  356.  
  357. // Note: we don't give any clue that we have automatically deleted a comment. It would only give spammers the perfect tool to find out how to pass the filter.
  358.  
  359. if$Comment->ID )
  360. // comment has not been deleted
  361.     // Trigger event: a Plugin should cleanup any temporary data here..
  362.     $Plugins->trigger_event'AfterCommentFormInsert'array'Comment' => $Comment'original_comment' => $original_comment ) );
  363.  
  364.     /*
  365.      * --------------------------
  366.      * New comment notifications:
  367.      * --------------------------
  368.      */
  369.     // TODO: dh> this should only send published feedback probably and should also use "outbound_notifications_mode"
  370.     // fp> yes for general users, but comment moderators need to receive notifications for new unpublished comments
  371.     $Comment->send_email_notifications();
  372.  
  373.  
  374.     // Add a message, according to the comment's status:
  375.     if$Comment->status == 'published' )
  376.     {
  377.         $Messages->addT_('Your comment has been submitted.')'success' );
  378.  
  379.         // Append anchor to the redirect_to param, so the user sees his comment:
  380.         $redirect_to .= '#'.$Comment->get_anchor();
  381.     }
  382.     else
  383.     {
  384.         $Messages->addT_('Your comment has been submitted. It will appear once it has been approved.')'success' );
  385.     }
  386.  
  387.     if!is_logged_in() )
  388.     // Not logged in user. We want him to see his comment has not vanished if he checks back on the Item page
  389.         // before the cache has expired. Invalidate cache for that page:
  390.         // Note: this is approximative and may not cover all URLs where the user expects to see the comment...
  391.         // TODO: fp> solution: touch dates?
  392.         load_class'_core/model/_pagecache.class.php' );
  393.         $PageCache new PageCache$Comment->Item->Blog );
  394.         $PageCache->invalidate$Comment->Item->get_single_url() );
  395.     }
  396. }
  397.  
  398.  
  399. header_redirect()// Will save $Messages into Session
  400.  
  401.  
  402. ?>