現(xiàn)在,我們來給博客添加轉(zhuǎn)載文章和轉(zhuǎn)載數(shù)統(tǒng)計的功能。
我們的設計思路是:當在線用戶滿足特定條件時,文章頁面才會顯示 轉(zhuǎn)載 鏈接字樣,當用戶點擊 轉(zhuǎn)載 后,復制一份存儲當前文章的文檔,修改后以新文檔的形式存入數(shù)據(jù)庫,而不是單純的添加一條指向被轉(zhuǎn)載的文檔的 "引用" ,這種設計是合理的,因為這樣我們也可以將轉(zhuǎn)載來的文章進行修改。
首先,我們來完成轉(zhuǎn)載文章的功能。
打開 post.js ,將 Post.prototype.save 內(nèi)的:
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
};
這里我們給文檔里添加了 reprint_info 鍵,最多為以下形式:
{
reprint_from: {name: xxx, day: xxx, title: xxx},
reprint_to: [
{name: xxx, day: xxx, title: xxx},
{name: xxx, day: xxx, title: xxx},
...
]
}
reprint_from 表示轉(zhuǎn)載來的原文章的信息,reprint_to 表示該文章被轉(zhuǎn)載的信息。為了節(jié)約存儲空間,我們初始設置 reprint_info 鍵為 {},而不是以下形式:
{
reprint_from: {},
reprint_to: []
}
這是因為大多數(shù)文章是沒有經(jīng)過任何轉(zhuǎn)載的,所以為每個文檔都添加以上形式的 reprint_info 是有點浪費的。假如某篇文章是轉(zhuǎn)載來的,我們只需給 reprint_info 添加上 reprint_from 鍵即可,假如某篇文章被轉(zhuǎn)載了,我們只需給 reprint_info 添加上 reprint_to 鍵即可,假如某篇文章是轉(zhuǎn)載來的且又被轉(zhuǎn)載了,那我們就給 reprint_info 添加上 reprint_from 和 reprint_to 鍵即可。
打開 post.js ,在最后添加如下代碼:
//轉(zhuǎn)載一篇文章
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);
}
//找到被轉(zhuǎn)載的文章的原文檔
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(/[轉(zhuǎn)載]/) > -1) ? doc.title : "[轉(zhuǎn)載]" + doc.title;
doc.comments = [];
doc.reprint_info = {"reprint_from": reprint_from};
doc.pv = 0;
//更新被轉(zhuǎn)載的原文檔的 reprint_info 內(nèi)的 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);
}
});
//將轉(zhuǎn)載生成的副本修改后存入數(shù)據(jù)庫,并返回存儲后的文檔
collection.insert(doc, {
safe: true
}, function (err, post) {
mongodb.close();
if (err) {
return callback(err);
}
callback(err, post[0]);
});
});
});
});
};
這里我們在 Post.reprint() 內(nèi)實現(xiàn)了被轉(zhuǎn)載的原文章的更新和轉(zhuǎn)載后文章的存儲。
打開 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', '轉(zhuǎn)載成功!');
var url = encodeURI('/u/' + post.name + '/' + post.time.day + '/' + post.title);
//跳轉(zhuǎn)到轉(zhuǎn)載后的文章頁面
res.redirect(url);
});
});
});
至此,我們給 轉(zhuǎn)載 鏈接注冊了路由響應。
注意:我們需要通過 Post.edit() 返回一篇文章 markdown 格式的文本,而不是通過 Post.getOne 返回一篇轉(zhuǎn)義后的 HTML 文本,因為我們還要將修改后的文檔存入數(shù)據(jù)庫,而數(shù)據(jù)庫中應該存儲 markdown 格式的文本。
最后,我們在文章頁(article.ejs)添加轉(zhuǎn)載鏈接。
打開 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 %>">轉(zhuǎn)載</a></span>
<% } %>
以上代碼的意思是:我們設置一個 flag 標志,如果用戶是游客,或者是該文章的目前作者,或者是該文章的上一級作者,或者已經(jīng)轉(zhuǎn)載過該文章,都會將 flag 設置為 0 ,即不顯示 轉(zhuǎn)載 鏈接,即不能轉(zhuǎn)載該文章。最后判斷 flag 為 1 時才會顯示 轉(zhuǎn)載 鏈接,即才可以轉(zhuǎn)載這篇文章。
最后,我們需要添加一個 原文鏈接 來指向被轉(zhuǎn)載的文章。 打開 index.ejs 、user.ejs 、article.ejs,在第一個
里最后添加如下代碼:
<% 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>
<% } %>
現(xiàn)在我們就給博客添加了轉(zhuǎn)載功能。
接下來我們添加轉(zhuǎn)載統(tǒng)計。 添加轉(zhuǎn)載統(tǒng)計就簡單多了,我們只需使用 reprint_info.reprint_to.length 即可。
打開 index.ejs 、user.ejs 、article.ejs ,將:
<p class="info">閱讀:<%= post.pv %> | 評論:<%= post.comments.length %></p>
修改為:
<p class="info">
閱讀:<%= post.pv %> |
評論:<%= post.comments.length %> |
轉(zhuǎn)載:
<% if (post.reprint_info.reprint_to) { %>
<%= post.reprint_info.reprint_to.length %>
<% } else { %>
<%= 0 %>
<% } %>
</p>
現(xiàn)在我們就給博客添加了轉(zhuǎn)載統(tǒng)計的功能。但工作還沒有完成,假如我們要刪除一篇轉(zhuǎn)載來的文章時,還要將被轉(zhuǎn)載的原文章所在文檔的 reprint_to 刪除遺留的轉(zhuǎn)載信息。
打開 post.js ,將 Post.remove 修改為:
//刪除一篇文章
Post.remove = function(name, day, title, callback) {
//打開數(shù)據(jù)庫
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,即該文章是轉(zhuǎn)載來的,先保存下來 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);
}
});
}
//刪除轉(zhuǎn)載來的文章所在的文檔
collection.remove({
"name": name,
"time.day": day,
"title": title
}, {
w: 1
}, function (err) {
mongodb.close();
if (err) {
return callback(err);
}
callback(null);
});
});
});
});
};
注意:我們使用了 $pull 來刪除數(shù)組中的特定項,關于 $pull 的詳細使用請查閱 《MongoDB 權威指南》。
至此,我們給博客添加了轉(zhuǎn)載功能和轉(zhuǎn)載統(tǒng)計。