Skip to content
This repository was archived by the owner on Feb 5, 2024. It is now read-only.

Commit c27332f

Browse files
committed
Merge pull request crowi#47 from crowi/feature-comment
Comment feature
2 parents 297cbc8 + 8991030 commit c27332f

19 files changed

+689
-131
lines changed

lib/form/comment.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
3+
var form = require('express-form')
4+
, field = form.field;
5+
6+
module.exports = form(
7+
field('commentForm.page_id').trim().required(),
8+
field('commentForm.revision_id').trim().required(),
9+
field('commentForm.comment').trim().required(),
10+
field('commentForm.comment_position').trim().toInt()
11+
);

lib/form/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ exports.login = require('./login');
22
exports.register = require('./register');
33
exports.invited = require('./invited');
44
exports.revision = require('./revision');
5+
exports.comment = require('./comment');
56
exports.me = {
67
user: require('./me/user'),
78
password: require('./me/password'),

lib/models/comment.js

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
module.exports = function(crowi) {
2+
var debug = require('debug')('crowi:models:comment')
3+
, mongoose = require('mongoose')
4+
, ObjectId = mongoose.Schema.Types.ObjectId
5+
, USER_PUBLIC_FIELDS = '_id fbId image googleId name username email status createdAt' // TODO: どこか別の場所へ...
6+
, commentSchema
7+
;
8+
9+
commentSchema = new mongoose.Schema({
10+
page: { type: ObjectId, ref: 'Page', index: true },
11+
creator: { type: ObjectId, ref: 'User', index: true },
12+
revision: { type: ObjectId, ref: 'Revision', index: true },
13+
comment: { type: String, required: true },
14+
commentPosition: { type: Number, default: -1 },
15+
createdAt: { type: Date, default: Date.now }
16+
});
17+
18+
commentSchema.statics.create = function(pageId, creatorId, revisionId, comment, position) {
19+
var Comment = this,
20+
commentPosition = position || -1;
21+
22+
23+
return new Promise(function(resolve, reject) {
24+
var newComment = new Comment();
25+
26+
newComment.page = pageId;
27+
newComment.creator = creatorId;
28+
newComment.revision = revisionId;
29+
newComment.comment = comment;
30+
newComment.commentPosition = position;
31+
32+
newComment.save(function(err, data) {
33+
if (err) {
34+
debug('Error on saving comment.', err);
35+
return reject(err);
36+
}
37+
debug('Comment saved.', data);
38+
return resolve(data);
39+
});
40+
});
41+
};
42+
43+
commentSchema.statics.getCommentsByPageId = function(id) {
44+
var self = this;
45+
46+
return new Promise(function(resolve, reject) {
47+
self
48+
.find({page: id})
49+
.sort({'createdAt': -1})
50+
.populate('creator', USER_PUBLIC_FIELDS)
51+
.exec(function(err, data) {
52+
if (err) {
53+
return reject(err);
54+
}
55+
56+
if (data.length < 1) {
57+
return resolve([]);
58+
}
59+
60+
debug('Comment loaded', data);
61+
return resolve(data);
62+
});
63+
});
64+
};
65+
66+
commentSchema.statics.getCommentsByRevisionId = function(id) {
67+
var self = this;
68+
69+
return new Promise(function(resolve, reject) {
70+
self
71+
.find({revision: id})
72+
.sort({'createdAt': -1})
73+
.populate('creator', USER_PUBLIC_FIELDS)
74+
.exec(function(err, data) {
75+
if (err) {
76+
return reject(err);
77+
}
78+
79+
if (data.length < 1) {
80+
return resolve([]);
81+
}
82+
83+
debug('Comment loaded', data);
84+
return resolve(data);
85+
});
86+
});
87+
};
88+
89+
commentSchema.statics.countCommentByPageId = function(page) {
90+
var self = this;
91+
92+
return new Promise(function(resolve, reject) {
93+
self.count({page: page}, function(err, data) {
94+
if (err) {
95+
return reject(err);
96+
}
97+
98+
return resolve(data);
99+
});
100+
});
101+
};
102+
103+
/**
104+
* post save hook
105+
*/
106+
commentSchema.post('save', function(savedComment) {
107+
var Page = crowi.model('Page')
108+
, Comment = crowi.model('Comment')
109+
;
110+
111+
Comment.countCommentByPageId(savedComment.page)
112+
.then(function(count) {
113+
return Page.updateCommentCount(savedComment.page, count);
114+
}).then(function(page) {
115+
debug('CommentCount Updated', page);
116+
}).catch(function() {
117+
});
118+
});
119+
120+
return mongoose.model('Comment', commentSchema);
121+
};

lib/models/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
'use strict';
2+
13
module.exports = {
24
Page: require('./page'),
35
User: require('./user'),
46
Revision: require('./revision'),
57
Bookmark: require('./bookmark'),
8+
Comment: require('./comment'),
69
Attachment: require('./attachment'),
710
};

lib/models/page.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports = function(crowi) {
77
, GRANT_SPECIFIED = 3
88
, GRANT_OWNER = 4
99
, PAGE_GRANT_ERROR = 1
10+
, USER_PUBLIC_FIELDS = '_id fbId image googleId name username email status createdAt' // TODO: どこか別の場所へ...
1011
, pageSchema;
1112

1213
function populatePageData(pageData, revisionId, callback) {
@@ -20,7 +21,7 @@ module.exports = function(crowi) {
2021
pageData.seenUsersCount = pageData.seenUsers.length || 0;
2122

2223
pageData.populate([
23-
{path: 'creator', model: 'User'},
24+
{path: 'creator', model: 'User', select: USER_PUBLIC_FIELDS},
2425
{path: 'revision', model: 'Revision'},
2526
{path: 'liker', options: { limit: 11 }},
2627
{path: 'seenUsers', options: { limit: 11 }},
@@ -38,6 +39,7 @@ module.exports = function(crowi) {
3839
creator: { type: ObjectId, ref: 'User', index: true },
3940
liker: [{ type: ObjectId, ref: 'User', index: true }],
4041
seenUsers: [{ type: ObjectId, ref: 'User', index: true }],
42+
commentCount: { type: Number, default: 0 },
4143
createdAt: { type: Date, default: Date.now },
4244
updatedAt: Date
4345
});
@@ -163,6 +165,22 @@ module.exports = function(crowi) {
163165
});
164166
};
165167

168+
pageSchema.statics.updateCommentCount = function (page, num)
169+
{
170+
var self = this;
171+
172+
return new Promise(function(resolve, reject) {
173+
self.update({_id: page}, {commentCount: num}, {}, function(err, data) {
174+
if (err) {
175+
debug('Update commentCount Error', err);
176+
return reject(err);
177+
}
178+
179+
return resolve(data);
180+
});
181+
});
182+
};
183+
166184
pageSchema.statics.getGrantLabels = function() {
167185
var grantLabels = {};
168186
grantLabels[GRANT_PUBLIC] = '公開';

lib/models/user.js

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = function(crowi) {
1111
, STATUS_SUSPENDED = 3
1212
, STATUS_DELETED = 4
1313
, STATUS_INVITED = 5
14+
, USER_PUBLIC_FIELDS = '_id fbId image googleId name username email status createdAt' // TODO: どこか別の場所へ...
1415

1516
, PAGE_ITEMS = 20
1617

@@ -534,6 +535,7 @@ module.exports = function(crowi) {
534535
userSchema.statics.STATUS_SUSPENDED = STATUS_SUSPENDED;
535536
userSchema.statics.STATUS_DELETED = STATUS_DELETED;
536537
userSchema.statics.STATUS_INVITED = STATUS_INVITED;
538+
userSchema.statics.USER_PUBLIC_FIELDS = USER_PUBLIC_FIELDS;
537539

538540
return mongoose.model('User', userSchema);
539541
};

lib/routes/comment.js

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
module.exports = function(crowi, app) {
2+
'use strict';
3+
4+
var debug = require('debug')('crowi:routs:comment')
5+
, Comment = crowi.model('Comment')
6+
, User = crowi.model('User')
7+
, Page = crowi.model('Page')
8+
, ApiResponse = require('../util/apiResponse')
9+
, actions = {}
10+
, api = {};
11+
12+
actions.api = api;
13+
14+
/**
15+
* @api {get} /comments.get Get comments of the page of the revision
16+
* @apiName GetComments
17+
* @apiGroup Comment
18+
*
19+
* @apiParam {String} page_id Page Id.
20+
* @apiParam {String} revision_id Revision Id.
21+
*/
22+
api.get = function(req, res){
23+
var pageId = req.query.page_id;
24+
var revisionId = req.query.revision_id;
25+
26+
if (revisionId) {
27+
return Comment.getCommentsByRevisionId(revisionId)
28+
.then(function(comments) {
29+
res.json(ApiResponse.success({comments}));
30+
}).catch(function(err) {
31+
res.json(ApiResponse.error(err));
32+
});
33+
}
34+
35+
return Comment.getCommentsByPageId(pageId)
36+
.then(function(comments) {
37+
res.json(ApiResponse.success({comments}));
38+
}).catch(function(err) {
39+
res.json(ApiResponse.error(err));
40+
});
41+
};
42+
43+
/**
44+
* @api {post} /comments.post Post comment for the page
45+
* @apiName PostComment
46+
* @apiGroup Comment
47+
*
48+
* @apiParam {String} page_id Page Id.
49+
* @apiParam {String} revision_id Revision Id.
50+
* @apiParam {String} comment Comment body
51+
* @apiParam {Number} comment_position=-1 Line number of the comment
52+
*/
53+
api.post = function(req, res){
54+
var form = req.form.commentForm;
55+
56+
if (!req.form.isValid) {
57+
return res.json(ApiResponse.error('Invalid comment.'));
58+
}
59+
60+
var pageId = form.page_id;
61+
var revisionId = form.revision_id;
62+
var comment = form.comment;
63+
var position = form.comment_position || -1;
64+
65+
return Comment.create(pageId, req.user._id, revisionId, comment, position)
66+
.then(function(createdComment) {
67+
createdComment.creator = req.user;
68+
return res.json(ApiResponse.success({comment: createdComment}));
69+
}).catch(function(err) {
70+
return res.json(ApiResponse.error(err));
71+
});
72+
};
73+
74+
return actions;
75+
};

lib/routes/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = function(crowi, app) {
99
, installer = require('./installer')(crowi, app)
1010
, user = require('./user')(crowi, app)
1111
, attachment= require('./attachment')(crowi, app)
12+
, comment= require('./comment')(crowi, app)
1213
, loginRequired = middleware.loginRequired
1314
, accessTokenParser = middleware.accessTokenParser
1415
;
@@ -77,6 +78,8 @@ module.exports = function(crowi, app) {
7778

7879
// HTTP RPC Styled API (に徐々に移行していいこうと思う)
7980
app.get('/_api/pages.get' , accessTokenParser(crowi, app) , loginRequired(crowi, app) , page.api.get);
81+
app.get('/_api/comments.get' , accessTokenParser(crowi, app) , loginRequired(crowi, app) , comment.api.get);
82+
app.post('/_api/comments.post' , form.comment, accessTokenParser(crowi, app) , loginRequired(crowi, app) , comment.api.post);
8083
//app.get('/_api/revision/:id' , user.useUserData() , revision.api.get);
8184
//app.get('/_api/r/:revisionId' , user.useUserData() , page.api.get);
8285

lib/routes/page.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module.exports = function(crowi, app) {
66
, User = crowi.model('User')
77
, Revision = crowi.model('Revision')
88
, Bookmark = crowi.model('Bookmark')
9+
, ApiResponse = require('../util/apiResponse')
910
, actions = {};
1011

1112
function getPathFromRequest(req) {
@@ -217,16 +218,10 @@ module.exports = function(crowi, app) {
217218
Page.findPage(pagePath, req.user, revision, options, function(err, pageData) {
218219
var result = {};
219220
if (err) {
220-
result = {
221-
ok: false,
222-
message: err.toString()
223-
};
221+
result = ApiResponse.error(err);
224222
}
225223
if (pageData) {
226-
result = {
227-
ok: true,
228-
page: pageData
229-
};
224+
result = ApiResponse.success(pageData);
230225
}
231226

232227
return res.json(result);

lib/util/apiResponse.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
function ApiResponse () {
4+
};
5+
6+
ApiResponse.error = function (err) {
7+
var result = {};
8+
9+
result = {
10+
ok: false
11+
};
12+
13+
if (typeof err == Error) {
14+
result.error = err.toString();
15+
} else {
16+
result.error = err;
17+
}
18+
19+
return result;
20+
};
21+
22+
ApiResponse.success = function (data) {
23+
var result = data;
24+
25+
result.ok = true;
26+
return result;
27+
};
28+
29+
module.exports = ApiResponse;

lib/views/layout/layout.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
<link rel="stylesheet" href="/css/crowi{% if env == 'production' %}.min{% endif %}.css">
1515
<script src="/js/crowi{% if env == 'production' %}.min{% endif %}.js"></script>
16-
<link href='//fonts.googleapis.com/css?family=Maven+Pro:400,700' rel='stylesheet' type='text/css'>
16+
<link href='//fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
1717
</head>
1818
{% endblock %}
1919

0 commit comments

Comments
 (0)