Node.js 博客实例(十五)转载和转载统计功能

原教程 https://github.com/nswbmw/N-blog/wiki/_pages的第十五章,由于版本等的原因,在原教程基础上稍加改动即可实现。

          我们的设计思路是:当在线用户满足特定条件时,文章页面才会显示 转载 链接字样,当用户点击 转载 后,复制一份存储当前文章的文档,修改后以新文档的形式存入数据库,而不是单纯的添加一条指向被转载的文档的 "引用" ,这种设计是合理的,因为这样我们也可以将转载来的文章进行修改。
         首先,我们来完成转载文章的功能。
打开 post.js ,将 Post.prototype.save 内的:

var post = {
    name: this.name,
    head: this.head,
    time: time,
    title:this.title,
    tags: this.tags,
    post: this.post,
    comments: [],
    pv: 0
};
修改为:
var post = {
    name: this.name,
    head: this.head,
    time: time,
    title:this.title,
    tags: this.tags,
    post: this.post,
    comments: [],
    reprint_info: {},
    pv: 0
};
打开 post.js ,在最后添加如下代码:

//转载一篇文章
Post.reprint = function(reprint_from, reprint_to, callback) {
  mongodb.open(function (err, db) {
    if (err) {
      return callback(err);
    }
    db.collection('posts', function (err, collection) {
      if (err) {
        mongodb.close();
        return callback(err);
      }
      //找到被转载的文章的原文档
      collection.findOne({
        "name": reprint_from.name,
        "time.day": reprint_from.day,
        "title": reprint_from.title
      }, function (err, doc) {
        if (err) {
          mongodb.close();
          return callback(err);
        }
        var date = new Date();
        var time = {
            date: date,
            year : date.getFullYear(),
            month : date.getFullYear() + "-" + (date.getMonth() + 1),
            day : date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(),
            minute : date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + 
            date.getHours() + ":" + (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())
        }

        delete doc._id;//注意要删掉原来的 _id
        doc.name = reprint_to.name;
        doc.head = reprint_to.head;
        doc.time = time;
        doc.title = (doc.title.search(/[reprint]/) > -1) ? doc.title : "[reprint]" + doc.title;
        doc.comments = [];
        doc.reprint_info = {"reprint_from": reprint_from};
        doc.pv = 0;

        //更新被转载的原文档的 reprint_info 内的 reprint_to
        collection.update({
          "name": reprint_from.name,
          "time.day": reprint_from.day,
          "title": reprint_from.title
        }, {
          $push: {
            "reprint_info.reprint_to": {
              "name": doc.name,
              "day": time.day,
              "title": doc.title
          }}
        }, function (err) {
          if (err) {
            mongodb.close();
            return callback(err);
          }
        });
        //将转载生成的副本修改后存入数据库,并返回存储后的文档
        collection.insert(doc, {
          safe: true
        }, function (err, post) {
          mongodb.close();
          if (err) {
            return callback(err);
          }
          callback(err, post[0]);
        });
      });
    });
  });
};

这里我们在 Post.reprint() 内实现了被转载的原文章的更新和转载后文章的存储。

注意这一句:doc.title = (doc.title.search(/[reprint]/) > -1) ? doc.title : "[reprint]" + doc.title;

原教程里是doc.title = (doc.title.search(/[转载]/) > -1) ? doc.title : "[转载]" + doc.title;

汉字的话就会报错:

Express
500 TypeError: E:\nodejs\blog\views\article.ejs:3
   1| <%- include header %> 
   2| <p> 
>> 3|   <% if (user && (user.name == post.name)) { %> 
   4|   <span><a class="edit" href="/edit/<%= post.name %>/<%= post.time.day %>/<%= post.title %>">编辑</a></span> 
   5|   <span><a class="edit" href="/remove/<%= post.name %>/<%= post.time.day %>/<%= post.title %>">删除</a></span>
   6| <% } %> 

Cannot read property 'name' of null
   1| <%- include header %>
   2| <p>
>> 3|   <% if (user && (user.name == post.name)) { %>
   4|   <span><a class="edit" href="/edit/<%= post.name %>/<%= post.time.day %>/<%= post.title %>">编辑</a></span>
   5|   <span><a class="edit" href="/remove/<%= post.name %>/<%= post.time.day %>/<%= post.title %>">删除</a></span>
   6| <% } %>
Cannot read property 'name' of null
   at eval (eval at <anonymous> (E:\nodejs\blog\node_modules\ejs\lib\ejs.js:242:14), <anonymous>:31:89)
   at eval (eval at <anonymous> (E:\nodejs\blog\node_modules\ejs\lib\ejs.js:242:14), <anonymous>:35:35)
   at E:\nodejs\blog\node_modules\ejs\lib\ejs.js:255:15
   at Object.exports.render (E:\nodejs\blog\node_modules\ejs\lib\ejs.js:293:13)
   at View.exports.renderFile [as engine] (E:\nodejs\blog\node_modules\ejs\lib\ejs.js:323:20)
   at View.render (E:\nodejs\blog\node_modules\express\lib\view.js:76:8)
   at Function.app.render (E:\nodejs\blog\node_modules\express\lib\application.js:523:10)
   at ServerResponse.res.render (E:\nodejs\blog\node_modules\express\lib\response.js:828:7)
   at E:\nodejs\blog\routes\index.js:282:8
   at E:\nodejs\blog\models\post.js:146:5
问题是处在转载之后返回的URL,本来是http://localhost:3000/u/123/2014-8-4/title   的形式,到但是如果这里title包含汉字就会报错。数据库里的数据是正常的。

打开 index.js ,在 app.get('/remove/:name/:day/:title') 后添加如下代码:
app.get('/reprint/:name/:day/:title', checkLogin);
	app.get('/reprint/:name/:day/:title', function (req, res) {
		Post.edit(req.params.name, req.params.day, req.params.title, function (err, post) {
			if (err) {
				req.flash('error', err); 
				return res.redirect(back);
			}
			var currentUser = req.session.user,
			reprint_from = {name: post.name, day: post.time.day, title: post.title},
			reprint_to = {name: currentUser.name, head: currentUser.head};
			Post.reprint(reprint_from, reprint_to, function (err, post) {
				if (err) {
					req.flash('error', err); 
					return res.redirect('back');
				}
				req.flash('success', '转载成功!');
				var url = '/u/' + post.name + '/' + post.time.day + '/' + post.title;
				//跳转到转载后的文章页面
				res.redirect(url);
			});
		});
	});
         至此,我们给 转载 链接注册了路由响应。
         注意:我们需要通过 Post.edit() 返回一篇文章 markdown 格式的文本,而不是通过 Post.getOne 返回一篇转义后的 HTML 文本,因为我们还要将修改后的文档存入数据库,而数据库中应该存储 markdown 格式的文本。
         最后,我们在文章页(article.ejs)添加转载链接。
打开 article.ejs ,在:
<% if (user && (user.name == post.name)) { %>
  <span><a class="edit" href="/edit/<%= post.name %>/<%= post.time.day %>/<%= post.title %>">编辑</a></span>
  <span><a class="edit" href="/remove/<%= post.name %>/<%= post.time.day %>/<%= post.title %>">删除</a></span>
<% } %>
后添加如下代码:
<% var flag = 1 %>
<% if (user && (user.name != post.name)) { %>
  <% if ((post.reprint_info.reprint_from != undefined) && (user.name == post.reprint_info.reprint_from.name)) { %>
    <% flag = 0 %>
  <% } %>
  <% if ((post.reprint_info.reprint_to != undefined)) { %>
    <% post.reprint_info.reprint_to.forEach(function (reprint_to, index) { %>
      <% if (user.name == reprint_to.name) { %>
        <% flag = 0 %>
      <% } %>
    <% }) %>
  <% } %>
<% } else { %>
  <% flag = 0 %>
<% } %>
<% if (flag) { %>
  <span><a class="edit" href="/reprint/<%= post.name %>/<%= post.time.day %>/<%= post.title %>">转载</a></span>
<% } %>
        以上代码的意思是:我们设置一个 flag 标志,如果用户是游客,或者是该文章的目前作者,或者是该文章的上一级作者,或者已经转载过该文章,都会将 flag 设置为 0 ,即不显示 转载 链接,即不能转载该文章。最后判断 flag 为 1 时才会显示 转载 链接,即才可以转载这篇文章。
        最后,我们需要添加一个 原文链接 来指向被转载的文章。
打开 index.ejs 、user.ejs 、article.ejs,在第一个 <p class="info"> 里最后添加如下代码:
<% if (post.reprint_info.reprint_from) { %>
  <br><a href="/u/<%= post.reprint_info.reprint_from.name %>/<%= post.reprint_info.reprint_from.day %>/<%= post.reprint_info.reprint_from.title %>">原文链接</a>
<% } %>
现在我们就给博客添加了转载功能。
       接下来我们添加转载统计。

打开 index.ejs 、user.ejs 、article.ejs ,将:

<p class="info">阅读:<%= post.pv %> | 评论:<%= post.comments.length %></p>

修改为:

<p class="info">
  阅读:<%= post.pv %> | 
  评论:<%= post.comments.length %> | 
  转载:
  <% if (post.reprint_info.reprint_to) { %>
    <%= post.reprint_info.reprint_to.length %>
  <% } else { %>
    <%= 0 %>
  <% } %>
</p>

现在我们就给博客添加了转载统计的功能。但工作还没有完成,假如我们要删除一篇转载来的文章时,还要将被转载的原文章所在文档的 reprint_to 删除遗留的转载信息。
打开 post.js ,将 Post.remove 修改为:
Post.remove = function(name, day, title, callback) {
  //打开数据库
  mongodb.open(function (err, db) {
    if (err) {
      return callback(err);
    }
    //读取 posts 集合
    db.collection('posts', function (err, collection) {
      if (err) {
        mongodb.close();
        return callback(err);
      }
      //查询要删除的文档
      collection.findOne({
        "name": name,
        "time.day": day,
        "title": title
      }, function (err, doc) {
        if (err) {
          mongodb.close();
          return callback(err);
        }
        //如果有 reprint_from,即该文章是转载来的,先保存下来 reprint_from
        var reprint_from = "";
        if (doc.reprint_info.reprint_from) {
          reprint_from = doc.reprint_info.reprint_from;
        }
        if (reprint_from != "") {
          //更新原文章所在文档的 reprint_to
          collection.update({
            "name": reprint_from.name,
            "time.day": reprint_from.day,
            "title": reprint_from.title
          }, {
            $pull: {
              "reprint_info.reprint_to": {
                "name": name,
                "day": day,
                "title": title
            }}
          }, function (err) {
            if (err) {
              mongodb.close();
              return callback(err);
            }
          });
        }

        //删除转载来的文章所在的文档
        collection.remove({
          "name": name,
          "time.day": day,
          "title": title
        }, {
          w: 1
        }, function (err) {
          mongodb.close();
          if (err) {
            return callback(err);
          }
          callback(null);
        });
      });
    });
  });
};
看看效果,用户‘123’将转载用户‘dss’的一片文章:


再登录用户‘123’,转载这篇文章:


转载:


          顺着教程做到这里,其实还是存在一些问题的,将在最后一篇总结里贴出这些问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值