Typecho不使用插件实现Ajax评论功能

typecho教程 2020-08-25

  原文出自绛木子博客:https://lixianhua.com/te_ajax_comment_without_pluign.html

  为了不使用插件实现评论功能需要实现:

  1,监听评论表单,改用方式提交

  2,创建新的评论表单提交地址(用主题提供的系统方法实现)

  当访问文章加载主题时,方法首先被加载,可在此方法中判断是否为添加评论的操作,即新的评论表单地址为文章的链接().具体判断方法如下

  // 主题初始化

  function themeInit($archive){

  // 判断是否是添加评论的操作

  // 为文章或页面、post操作,且包含参数`themeAction=comment`(自定义)

  if($archive->is(‘single’) && $archive->request->isPost() && $archive->request->is(‘themeAction=comment’)){

  // 为添加评论的操作时

  ajaxComment($archive);

  }

  }

  要实现评论,则无法使用系统默认的,这里我们将复制功能并改造为我们需要的方法

  function ajaxComment($archive){

  $options = Helper::options();

  $user = Typecho_Widget::widget(‘Widget_User’);

  $db = Typecho_Db::get();

  // Security 验证不通过时会直接跳转,所以需要自己进行判断

  // 需要开启反垃圾保护,此时将不验证来源

  if($archive->request->get(‘_’) != Helper::security()->getToken($archive->request->getReferer())){

  $archive->response->throwJson(array(‘status’=>0,’msg’=>_t(‘非法请求’)));

  }

  if(!$archive->allow(‘comment’)){

  $archive->response->throwJson(array(‘status’=>0,’msg’=>_t(‘评论已关闭’)));

  }

  if (!$user->pass(‘editor’, true) && $archive->authorId != $user->uid &&

  $options->commentsPostIntervalEnable){

  $latestComment = $db->fetchRow($db->select(‘created’)->from(‘table.comments’)

  ->where(‘cid = ?’, $archive->cid)

  ->where(‘ip = ?’, $archive->request->getIp())

  ->order(‘created’, Typecho_Db::SORT_DESC)

  ->limit(1));

  if ($latestComment && ($options->gmtTime – $latestComment[‘created’] > 0 &&

  $options->gmtTime – $latestComment[‘created’] commentsPostInterval)) {

  $archive->response->throwJson(array(‘status’=>0,’msg’=>_t(‘对不起, 您的发言过于频繁, 请稍侯再次发布’)));

  }

  }

  $comment = array(

  ’cid’ => $archive->cid,

  ’created’ => $options->gmtTime,

  ’agent’ => $archive->request->getAgent(),

  ’ip’ => $archive->request->getIp(),

  ’ownerId’ => $archive->author->uid,

  ’type’ => ‘comment’,

  ’status’ => !$archive->allow(‘edit’) && $options->commentsRequireModeration ? ‘waiting’ : ‘approved’

  );

  if ($parentId = $archive->request->filter(‘int’)->get(‘parent’)) {

  if ($options->commentsThreaded && ($parent = $db->fetchRow($db->select(‘coid’, ‘cid’)->from(‘table.comments’)

  ->where(‘coid = ?’, $parentId))) && $archive->cid == $parent[‘cid’]) {

  $comment[‘parent’] = $parentId;

  } else {

  $archive->response->throwJson(array(‘status’=>0,’msg’=>_t(‘父级评论不存在’)));

  }

  }

  $feedback = Typecho_Widget::widget(‘Widget_Feedback’);

  //检验格式

  $validator = new Typecho_Validate();

  $validator->addRule(‘author’, ‘required’, _t(‘必须填写用户名’));

  $validator->addRule(‘author’, ‘xssCheck’, _t(‘请不要在用户名中使用特殊字符’));

  $validator->addRule(‘author’, array($feedback, ‘requireUserLogin’), _t(‘您所使用的用户名已经被注册,请登录后再次提交’));

  $validator->addRule(‘author’, ‘maxLength’, _t(‘用户名最多包含200个字符’), 200);

  if ($options->commentsRequireMail && !$user->hasLogin()) {

  $validator->addRule(‘mail’, ‘required’, _t(‘必须填写电子邮箱地址’));

  }

  $validator->addRule(‘mail’, ’email’, _t(‘邮箱地址不合法’));

  $validator->addRule(‘mail’, ‘maxLength’, _t(‘电子邮箱最多包含200个字符’), 200);

  if ($options->commentsRequireUrl && !$user->hasLogin()) {

  $validator->addRule(‘url’, ‘required’, _t(‘必须填写个人主页’));

  }

  $validator->addRule(‘url’, ‘url’, _t(‘个人主页地址格式错误’));

  $validator->addRule(‘url’, ‘maxLength’, _t(‘个人主页地址最多包含200个字符’), 200);

  $validator->addRule(‘text’, ‘required’, _t(‘必须填写评论内容’));

  $comment[‘text’] = $archive->request->text;

  if (!$user->hasLogin()) {

  $comment[‘author’] = $archive->request->filter(‘trim’)->author;

  $comment[‘mail’] = $archive->request->filter(‘trim’)->mail;

  $comment[‘url’] = $archive->request->filter(‘trim’)->url;

  if (!empty($comment[‘url’])) {

  $urlParams = parse_url($comment[‘url’]);

  if (!isset($urlParams[‘scheme’])) {

  $comment[‘url’] = ‘http://’ . $comment[‘url’];

  }

  }

  $expire = $options->gmtTime + $options->timezone + 30*24*3600;

  Typecho_Cookie::set(‘__typecho_remember_author’, $comment[‘author’], $expire);

  Typecho_Cookie::set(‘__typecho_remember_mail’, $comment[‘mail’], $expire);

  Typecho_Cookie::set(‘__typecho_remember_url’, $comment[‘url’], $expire);

  } else {

  $comment[‘author’] = $user->screenName;

  $comment[‘mail’] = $user->mail;

  $comment[‘url’] = $user->url;

  $comment[‘authorId’] = $user->uid;

  }

  if (!$options->commentsRequireModeration && $options->commentsWhitelist) {

  if ($feedback->size($feedback->select()->where(‘author = ? AND mail = ? AND status = ?’, $comment[‘author’], $comment[‘mail’], ‘approved’))) {

  $comment[‘status’] = ‘approved’;

  } else {

  $comment[‘status’] = ‘waiting’;

  }

  }

  if ($error = $validator->run($comment)) {

  $archive->response->throwJson(array(‘status’=>0,’msg’=> implode(‘;’,$error)));

  }

  $commentId = $feedback->insert($comment);

  if(!$commentId){

  $archive->response->throwJson(array(‘status’=>0,’msg’=>_t(‘评论失败’)));

  }

  Typecho_Cookie::delete(‘__typecho_remember_text’);

  $db->fetchRow($feedback->select()->where(‘coid = ?’, $commentId)

  ->limit(1), array($feedback, ‘push’));

  // 返回评论数据

  $data = array(

  ’cid’ => $feedback->cid,

  ’coid’ => $feedback->coid,

  ’parent’ => $feedback->parent,

  ’mail’ => $feedback->mail,

  ’url’ => $feedback->url,

  ’ip’ => $feedback->ip,

  ’agent’ => $feedback->agent,

  ’author’ => $feedback->author,

  ’authorId’ => $feedback->authorId,

  ’permalink’ => $feedback->permalink,

  ’created’ => $feedback->created,

  ’datetime’ => $feedback->date->format(‘Y-m-d H:i:s’),

  ’status’ => $feedback->status,

  );

  // 评论内容

  ob_start();

  $feedback->content();

  $data[‘content’] = ob_get_clean();

  $data[‘avatar’] = Typecho_Common::gravatarUrl($data[‘mail’], 48, Helper::options()->commentsAvatarRating, NULL, $archive->request->isSecure());

  $archive->response->throwJson(array(‘status’=>1,’comment’=>$data));

  }

  当已经在文件中添加完上述方法时,就已经可以接收评论了。

  此时我们需要修改评论表单添加的方式及提交的地址。

  在文件中添加方法

  // 依赖jquery,请自行加载

  $(function(){

  // 监听评论表单提交

  $(‘#comment-form’).submit(function(){

  var form = $(this), params = form.serialize();

  // 添加functions.php中定义的判断参数

  params += ‘&themeAction=comment’;

  // 解析新评论并附加到评论列表

  var appendComment = function(comment){

  // 评论列表

  var el = $(‘#comments > .comment-list’);

  if(0 != comment.parent){

  // 子评论则重新定位评论列表

  var el = $(‘#comment-‘+comment.parent);

  // 父评论不存在子评论时

  if(el.find(‘.comment-children’).length

  $(‘

    ‘).appendTo(el);

      }else if(el.find(‘.comment-children > .comment-list’).length

      $(‘

      ‘).appendTo(el.find(‘.comment-children’));

        }

        el = $(‘#comment-‘+comment.parent).find(‘.comment-children’).find(‘.comment-list’);

        }

        if(0 == el.length){

        $(‘

        ‘).appendTo($(‘#comments’));

          el = $(‘#comments > .comment-list’);

          }

          // 评论html模板,根据具体主题定制

          var html = ‘

      1. {author}{author}
        {content}
      2. ‘;

          $.each(comment,function(k,v){

          regExp = new RegExp(‘{‘+k+’}’, ‘g’);

          html = html.replace(regExp, v);

          });

          $(html).appendTo(el);

          }

          // ajax提交评论

          $.ajax({

          url: ‘permalink();?>’,

          type: ‘POST’,

          data: params,

          dataType: ‘json’,

          beforeSend: function() { form.find(‘.submit’).addClass(‘loading’).html(‘ 提交中…’).attr(‘disabled’,’disabled’);},

          complete: function() { form.find(‘.submit’).removeClass(‘loading’).html(‘提交评论’).removeAttr(‘disabled’);},

          success: function(result){

          if(1 == result.status){

          // 新评论附加到评论列表

          appendComment(result.comment);

          form.find(‘textarea’).val(”);

          }else{

          // 提醒错误消息

          alert(undefined === result.msg ? ‘‘ : result.msg);

          }

          },

          error:function(xhr, ajaxOptions, thrownError){

          alert(‘评论失败,请重试’);

          }

          });

          return false;

          });

          });

          评论需要自定义评论模板,获取使用其他方式拼接评论。

          评论表单的提交按钮需要添加或修改代码为其他自定义的

          注:需开启评论的反垃圾保护

          以上为转载内容,在实际投入使用时遇到了两点问题

          一,通过ajax进行评论,邮件通知插件并不会发出通知!

          原因是因为重写了评论的函数,而函数中没写评论完成后触发插件接口,所以邮件通知插件不会发邮件给作者。

          解决方法也很简单在上述php代码后面加入即可。

          二,评论过滤插件也会失效!

          原因也是没有写入评论过滤的接口

          这个接口我试着往里面写,没有成功,所以换了个方式来解决,直接在里面写过滤,而不是用插件过滤评论。

          比如在前面加入

          if (preg_match(“/[\x{4e00}-\x{9fa5}]/u”, $comment[‘text’]) == 0) {

          $archive->response->throwJson(array(‘status’=>0,’msg’=>_t(‘评论内容请不少于一个中文汉字’)));

          }

          即可屏蔽非中文评论,建议同时使用评论过滤插件,因为有些垃圾评论是通过评论机器人完成的,并不会经过ajax评论,所以还需要继续使用评论过滤插件,亦或者在模板层面上用上评论过滤接口(我是这么做的,目前来看大概是有效果的)

        评论 (0)
          Top https://www.laoyua.com/sitemap.xml