jsduck 3.10.0 → 3.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,366 +0,0 @@
1
-
2
- /**
3
- * JSDuck authentication / commenting server side element. Requires Node.js + MongoDB.
4
- *
5
- * Authentication assumes a vBulletin forum database, but could easily be adapted (see ForumUser.js)
6
- *
7
- * Expects a config file, config.js, that looks like this:
8
- *
9
- * exports.db = {
10
- * user: 'forumUsername',
11
- * password: 'forumPassword',
12
- * host: 'forumHost',
13
- * dbName: 'forumDb'
14
- * };
15
- *
16
- * exports.sessionSecret = 'random string for session cookie encryption';
17
- *
18
- * exports.mongoDb = 'mongodb://mongoHost:port/comments';
19
- *
20
- */
21
-
22
- var config = require('./config');
23
- require('./database');
24
-
25
- var mysql = require('mysql'),
26
- client = mysql.createClient({
27
- host: config.db.host,
28
- user: config.db.user,
29
- password: config.db.password,
30
- database: config.db.dbName
31
- }),
32
- express = require('express'),
33
- MongoStore = require('connect-mongo'),
34
- _ = require('underscore'),
35
- ForumUser = require('./ForumUser').ForumUser,
36
- forumUser = new ForumUser(client),
37
- util = require('./util'),
38
- crypto = require('crypto'),
39
- mongoose = require('mongoose');
40
-
41
- var app = express();
42
-
43
- app.configure(function() {
44
-
45
- // Headers for Cross Origin Resource Sharing (CORS)
46
- app.use(function (req, res, next) {
47
- res.setHeader('Access-Control-Allow-Origin', '*');
48
- res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
49
- res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
50
- next();
51
- });
52
-
53
- app.use(express.cookieParser(config.sessionSecret));
54
-
55
- // Hack to set session cookie if session ID is set as a URL param.
56
- // This is because not all browsers support sending cookies via CORS
57
- app.use(function(req, res, next) {
58
- if (req.query.sid && req.query.sid != 'null') {
59
- var sid = req.query.sid.replace(/ /g, '+');
60
- req.sessionID = sid;
61
- req.signedCookies = req.signedCookies || {};
62
- req.signedCookies['sencha_docs'] = sid;
63
- }
64
- next();
65
- });
66
-
67
- // Use MongoDB for session storage
68
- app.use(express.session({
69
- secret: config.sessionSecret,
70
- key: 'sencha_docs',
71
- store: new MongoStore({
72
- url: exports.mongoDb + "/sessions"
73
- })
74
- }));
75
-
76
- app.use(function(req, res, next) {
77
- // IE doesn't get content-type, so default to form encoded.
78
- if (!req.headers['content-type']) {
79
- req.headers['content-type'] = 'application/x-www-form-urlencoded';
80
- }
81
- next();
82
- });
83
-
84
- app.use(express.bodyParser());
85
- app.use(express.methodOverride());
86
-
87
- app.enable('jsonp callback');
88
- });
89
-
90
- app.configure('development', function(){
91
- app.use(express.logger('dev'));
92
- app.use(express.errorHandler());
93
- });
94
-
95
- /**
96
- * Authentication
97
- */
98
-
99
- app.get('/auth/session', function(req, res) {
100
- var result = req.session && req.session.user && forumUser.clientUser(req.session.user);
101
- res.json(result || false);
102
- });
103
-
104
- app.post('/auth/login', function(req, res){
105
-
106
- forumUser.login(req.body.username, req.body.password, function(err, result) {
107
-
108
- if (err) {
109
- res.json({ success: false, reason: err });
110
- return;
111
- }
112
-
113
- req.session = req.session || {};
114
- req.session.user = result;
115
-
116
- var response = _.extend(forumUser.clientUser(result), {
117
- sessionID: req.sessionID,
118
- success: true
119
- });
120
-
121
- res.json(response);
122
- });
123
- });
124
-
125
- // Remove session
126
- app.post('/auth/logout', function(req, res){
127
- req.session.user = null;
128
- res.json({ success: true });
129
- });
130
-
131
- /**
132
- * Handles comment unsubscription requests.
133
- */
134
- app.get('/auth/unsubscribe/:subscriptionId', function(req, res) {
135
-
136
- Subscription.findOne({ _id: req.params.subscriptionId }, function(err, subscription) {
137
- if (err) throw(err);
138
-
139
- if (subscription) {
140
- if (req.query.all == 'true') {
141
- Subscription.remove({ userId: subscription.userId }, function(err) {
142
- res.send("You have been unsubscribed from all threads.");
143
- });
144
- } else {
145
- Subscription.remove({ _id: req.params.subscriptionId }, function(err) {
146
- res.send("You have been unsubscribed from that thread.");
147
- });
148
- }
149
- } else {
150
- res.send("You are already unsubscribed.");
151
- }
152
- });
153
- });
154
-
155
-
156
- /**
157
- * Commenting
158
- */
159
-
160
- /**
161
- * Returns a list of comments for a particular target (eg class, guide, video)
162
- */
163
- app.get('/auth/:sdk/:version/comments', util.getCommentReads, function(req, res) {
164
-
165
- if (!req.query.startkey) {
166
- res.json({error: 'Invalid request'});
167
- return;
168
- }
169
-
170
- Comment.find({
171
- target: JSON.parse(req.query.startkey),
172
- deleted: { '$ne': true },
173
- sdk: req.params.sdk,
174
- version: req.params.version
175
- }).sort('createdAt', 1).run(function(err, comments){
176
- res.json(util.scoreComments(comments, req));
177
- });
178
- });
179
-
180
- /**
181
- * Returns n most recent comments.
182
- * Takes two parameters: offset and limit.
183
- *
184
- * The last comment object returned will contain `total_rows`,
185
- * `offset` and `limit` fields. I'd say it's a hack, but at least
186
- * it works for now.
187
- */
188
- app.get('/auth/:sdk/:version/comments_recent', util.getCommentReads, function(req, res) {
189
- var offset = parseInt(req.query.offset, 10) || 0;
190
- var limit = parseInt(req.query.limit, 10) || 100;
191
- var filter = {
192
- deleted: { '$ne': true },
193
- sdk: req.params.sdk,
194
- version: req.params.version
195
- };
196
-
197
- if (req.query.hideRead && req.commentMeta.reads.length > 0) {
198
- filter._id = { $nin: req.commentMeta.reads };
199
- }
200
-
201
- Comment.find(filter).sort('createdAt', -1).skip(offset).limit(limit).run(function(err, comments) {
202
- comments = util.scoreComments(comments, req);
203
- // Count all comments, store count to last comment
204
- Comment.count(filter).run(function(err, count) {
205
- var last = comments[comments.length-1];
206
- last.total_rows = count;
207
- last.offset = offset;
208
- last.limit = limit;
209
- res.json(comments);
210
- });
211
- });
212
- });
213
-
214
- /**
215
- * Returns number of comments for each class/member,
216
- * and a list of classes/members into which the user has subscribed.
217
- */
218
- app.get('/auth/:sdk/:version/comments_meta', util.getCommentCounts, util.getCommentSubscriptions, function(req, res) {
219
- res.send({ comments: req.commentCounts, subscriptions: req.commentSubscriptions || [] });
220
- });
221
-
222
- /**
223
- * Returns an individual comment (used when editing a comment)
224
- */
225
- app.get('/auth/:sdk/:version/comments/:commentId', util.findComment, function(req, res) {
226
- res.json({ success: true, content: req.comment.content });
227
- });
228
-
229
- /**
230
- * Creates a new comment
231
- */
232
- app.post('/auth/:sdk/:version/comments', util.requireLoggedInUser, function(req, res) {
233
-
234
- var target = JSON.parse(req.body.target);
235
-
236
- if (target.length === 2) {
237
- target.push('');
238
- }
239
-
240
- var comment = new Comment({
241
- author: req.session.user.username,
242
- userId: req.session.user.userid,
243
- content: req.body.comment,
244
- action: req.body.action,
245
- rating: Number(req.body.rating),
246
- contentHtml: util.markdown(req.body.comment),
247
- downVotes: [],
248
- upVotes: [],
249
- createdAt: new Date,
250
- target: target,
251
- emailHash: crypto.createHash('md5').update(req.session.user.email).digest("hex"),
252
- sdk: req.params.sdk,
253
- version: req.params.version,
254
- moderator: req.session.user.moderator,
255
- title: req.body.title,
256
- url: req.body.url
257
- });
258
-
259
- comment.save(function(err, response) {
260
- res.json({ success: true, id: response._id, action: req.body.action });
261
-
262
- util.sendEmailUpdates(comment);
263
- });
264
- });
265
-
266
- /**
267
- * Updates an existing comment (for voting or updating contents)
268
- */
269
- app.post('/auth/:sdk/:version/comments/:commentId', util.requireLoggedInUser, util.findComment, function(req, res) {
270
-
271
- var voteDirection,
272
- comment = req.comment;
273
-
274
- if (req.body.vote) {
275
- util.vote(req, res, comment);
276
- } else {
277
- util.requireOwner(req, res, function() {
278
- comment.content = req.body.content;
279
- comment.contentHtml = util.markdown(req.body.content);
280
-
281
- comment.updates = comment.updates || [];
282
- comment.updates.push({
283
- updatedAt: String(new Date()),
284
- author: req.session.user.username
285
- });
286
-
287
- comment.save(function(err, response) {
288
- res.json({ success: true, content: comment.contentHtml });
289
- });
290
- });
291
- }
292
- });
293
-
294
- /**
295
- * Deletes a comment
296
- */
297
- app.post('/auth/:sdk/:version/comments/:commentId/delete', util.requireLoggedInUser, util.findComment, util.requireOwner, function(req, res) {
298
- req.comment.deleted = true;
299
- req.comment.save(function(err, response) {
300
- res.send({ success: true });
301
- });
302
- });
303
-
304
- /**
305
- * Restores deleted comment
306
- */
307
- app.post('/auth/:sdk/:version/comments/:commentId/undo_delete', util.requireLoggedInUser, util.findComment, util.requireOwner, util.getCommentReads, function(req, res) {
308
- req.comment.deleted = false;
309
- req.comment.save(function(err, response) {
310
- res.send({ success: true, comment: util.scoreComments([req.comment], req)[0] });
311
- });
312
- });
313
-
314
- /**
315
- * Marks a comment 'read'
316
- */
317
- app.post('/auth/:sdk/:version/comments/:commentId/read', util.requireLoggedInUser, util.findCommentMeta, function(req, res) {
318
- req.commentMeta.metaType = 'read';
319
- req.commentMeta.save(function(err, response) {
320
- res.send({ success: true });
321
- });
322
- });
323
-
324
- /**
325
- * Get email subscriptions
326
- */
327
- app.get('/auth/:sdk/:version/subscriptions', util.getCommentSubscriptions, function(req, res) {
328
- res.json({ subscriptions: req.commentMeta.subscriptions });
329
- });
330
-
331
- /**
332
- * Subscibe / unsubscribe to a comment thread
333
- */
334
- app.post('/auth/:sdk/:version/subscribe', util.requireLoggedInUser, function(req, res) {
335
-
336
- var subscriptionBody = {
337
- sdk: req.params.sdk,
338
- version: req.params.version,
339
- target: JSON.parse(req.body.target),
340
- userId: req.session.user.userid
341
- };
342
-
343
- Subscription.findOne(subscriptionBody, function(err, subscription) {
344
-
345
- if (subscription && req.body.subscribed == 'false') {
346
-
347
- subscription.remove(function(err, ok) {
348
- res.send({ success: true });
349
- });
350
-
351
- } else if (!subscription && req.body.subscribed == 'true') {
352
-
353
- subscription = new Subscription(subscriptionBody);
354
- subscription.email = req.session.user.email;
355
-
356
- subscription.save(function(err, ok) {
357
- res.send({ success: true });
358
- });
359
- }
360
- });
361
- });
362
-
363
- var port = 3000;
364
- app.listen(port);
365
- console.log("Server started at port "+port+"...");
366
-
@@ -1,53 +0,0 @@
1
-
2
- /**
3
- * Defines comment schema and connects to database
4
- */
5
-
6
- var mongoose = require('mongoose'),
7
- config = require('./config');
8
-
9
- Comment = mongoose.model('Comment', new mongoose.Schema({
10
- sdk: String,
11
- version: String,
12
-
13
- action: String,
14
- author: String,
15
- userId: Number,
16
- content: String,
17
- contentHtml: String,
18
- createdAt: Date,
19
- downVotes: Array,
20
- emailHash: String,
21
- rating: Number,
22
- target: Array,
23
- upVotes: Array,
24
- deleted: Boolean,
25
- updates: Array,
26
- mod: Boolean,
27
- title: String,
28
- url: String
29
- }));
30
-
31
- Subscription = mongoose.model('Subscription', new mongoose.Schema({
32
- sdk: String,
33
- version: String,
34
-
35
- createdAt: Date,
36
- userId: Number,
37
- email: String,
38
- target: Array
39
- }));
40
-
41
- Meta = mongoose.model('Meta', new mongoose.Schema({
42
- sdk: String,
43
- version: String,
44
-
45
- createdAt: Date,
46
- userId: Number,
47
- commentId: String,
48
- metaType: String
49
- }));
50
-
51
- mongoose.connect(config.mongoDb, function(err, ok) {
52
- console.log("Connected to DB")
53
- });
@@ -1,19 +0,0 @@
1
- {
2
- "name": "jsduck_comments",
3
- "version": "0.5.0",
4
- "description": "Commenting backend for JSDuck Documentation",
5
- "author": "Nick Poudlen <nick@sencha.com>",
6
- "dependencies": {
7
- "express": "git://github.com/visionmedia/express.git",
8
- "express-namespace": "",
9
- "connect": "",
10
- "connect-mongo": "",
11
- "marked": "",
12
- "mongoose": "",
13
- "mysql": "",
14
- "sanitizer": "",
15
- "step": "",
16
- "underscore": "",
17
- "nodemailer": ""
18
- }
19
- }
@@ -1,396 +0,0 @@
1
-
2
- var marked = require('marked'),
3
- _ = require('underscore'),
4
- sanitizer = require('sanitizer'),
5
- nodemailer = require("nodemailer"),
6
- mongoose = require('mongoose');
7
-
8
- /**
9
- * Converts Markdown-formatted comment text into HTML.
10
- *
11
- * @param {String} content Markdown-formatted text
12
- * @return {String} HTML
13
- */
14
- exports.markdown = function(content) {
15
- var markdowned;
16
- try {
17
- markdowned = marked(content);
18
- } catch(e) {
19
- markdowned = content;
20
- }
21
-
22
- // Strip dangerous markup, but allow links to all URL-s
23
- var sanitized_output = sanitizer.sanitize(markdowned, function(str) {
24
- return str;
25
- });
26
-
27
- // IE does not support &apos;
28
- return sanitized_output.replace(/&apos;/g, '&#39;');
29
- };
30
-
31
- /**
32
- * Calculates up/down scores for each comment.
33
- *
34
- * Marks if the current user has already voted on the comment.
35
- * Ensures createdAt timestamp is a string.
36
- *
37
- * @param {Object[]} comments
38
- * @param {Object} req Containing username data
39
- * @return {Object[]}
40
- */
41
- exports.scoreComments = function(comments, req) {
42
- return _.map(comments, function(comment) {
43
- comment = _.extend(comment._doc, {
44
- score: comment.upVotes.length - comment.downVotes.length,
45
- createdAt: String(comment.createdAt)
46
- });
47
-
48
- if (req.commentMeta.reads.length > 0) {
49
- comment.read = _.include(req.commentMeta.reads, ""+comment._id);
50
- }
51
-
52
- if (req.session.user) {
53
- comment.upVote = _.contains(comment.upVotes, req.session.user.username);
54
- comment.downVote = _.contains(comment.downVotes, req.session.user.username);
55
- }
56
-
57
- return comment;
58
- });
59
- };
60
-
61
- /**
62
- * Performs voting on comment.
63
- *
64
- * @param {Object} req The request object.
65
- * @param {Object} res The response object where voting result is written.
66
- * @param {Comment} comment The comment to vote on.
67
- */
68
- exports.vote = function(req, res, comment) {
69
- var voteDirection;
70
- var username = req.session.user.username;
71
-
72
- if (username == comment.author) {
73
-
74
- // Ignore votes from the author
75
- res.json({success: false, reason: 'You cannot vote on your own content'});
76
- return;
77
-
78
- } else if (req.body.vote == 'up' && !_.include(comment.upVotes, username)) {
79
-
80
- var voted = _.include(comment.downVotes, username);
81
-
82
- comment.downVotes = _.reject(comment.downVotes, function(v) {
83
- return v == username;
84
- });
85
-
86
- if (!voted) {
87
- voteDirection = 'up';
88
- comment.upVotes.push(username);
89
- }
90
- } else if (req.body.vote == 'down' && !_.include(comment.downVotes, username)) {
91
-
92
- var voted = _.include(comment.upVotes, username);
93
-
94
- comment.upVotes = _.reject(comment.upVotes, function(v) {
95
- return v == username;
96
- });
97
-
98
- if (!voted) {
99
- voteDirection = 'down';
100
- comment.downVotes.push(username);
101
- }
102
- }
103
-
104
- comment.save(function(err, response) {
105
- res.json({
106
- success: true,
107
- direction: voteDirection,
108
- total: (comment.upVotes.length - comment.downVotes.length)
109
- });
110
- });
111
- };
112
-
113
- /**
114
- * Ensures that user is logged in.
115
- *
116
- * @param {Object} req
117
- * @param {Object} res
118
- * @param {Function} next
119
- */
120
- exports.requireLoggedInUser = function(req, res, next) {
121
- if (!req.session || !req.session.user) {
122
- res.json({success: false, reason: 'Forbidden'}, 403);
123
- } else {
124
- next();
125
- }
126
- };
127
-
128
- /**
129
- * Looks up comment by ID.
130
- *
131
- * Stores it into `req.comment`.
132
- *
133
- * @param {Object} req
134
- * @param {Object} res
135
- * @param {Function} next
136
- */
137
- exports.findComment = function(req, res, next) {
138
- if (req.params.commentId) {
139
- Comment.findById(req.params.commentId, function(err, comment) {
140
- req.comment = comment;
141
- next();
142
- });
143
- } else {
144
- res.json({success: false, reason: 'No such comment'});
145
- }
146
- };
147
-
148
- /**
149
- * Looks up comment meta by comment ID.
150
- *
151
- * Stores it into `req.commentMeta`.
152
- *
153
- * @param {Object} req
154
- * @param {Object} res
155
- * @param {Function} next
156
- */
157
- exports.findCommentMeta = function(req, res, next) {
158
- if (req.params.commentId) {
159
-
160
- var userCommentMeta = {
161
- userId: req.session.user.userid,
162
- commentId: req.params.commentId
163
- };
164
-
165
- Meta.findOne(userCommentMeta, function(err, commentMeta) {
166
- req.commentMeta = commentMeta || new Meta(userCommentMeta);
167
- next();
168
- });
169
- } else {
170
- res.json({success: false, reason: 'No such comment'});
171
- }
172
- };
173
-
174
- // True if the user is moderator
175
- function isModerator(user) {
176
- return _.include(user.membergroupids, 7);
177
- }
178
-
179
- // True if the user is author of the comment
180
- function isAuthor(user, comment) {
181
- return user.username === comment.author;
182
- }
183
-
184
- /**
185
- * Ensures that user is allowed to modify/delete the comment,
186
- * that is, he is the owner of the comment or a moderator.
187
- *
188
- * @param {Object} req
189
- * @param {Object} res
190
- * @param {Function} next
191
- */
192
- exports.requireOwner = function(req, res, next) {
193
- if (isModerator(req.session.user) || isAuthor(req.session.user, req.comment)) {
194
- next();
195
- }
196
- else {
197
- res.json({ success: false, reason: 'Forbidden' }, 403);
198
- }
199
- };
200
-
201
- /**
202
- * Sends e-mail updates when comment is posted to a thread that has
203
- * subscribers.
204
- *
205
- * @param {Comment} comment
206
- */
207
- exports.sendEmailUpdates = function(comment) {
208
- var mailTransport = nodemailer.createTransport("SMTP",{
209
- host: 'localhost',
210
- port: 25
211
- });
212
-
213
- var sendSubscriptionEmail = function(emails) {
214
- var email = emails.shift();
215
-
216
- if (email) {
217
- nodemailer.sendMail(email, function(err){
218
- if (err){
219
- console.log(err);
220
- } else{
221
- console.log("Sent email to " + email.to);
222
- sendSubscriptionEmail(emails);
223
- }
224
- });
225
- } else {
226
- console.log("Finished sending emails");
227
- mailTransport.close();
228
- }
229
- };
230
-
231
- var subscriptionBody = {
232
- sdk: comment.sdk,
233
- version: comment.version,
234
- target: comment.target
235
- };
236
-
237
- var emails = [];
238
-
239
- Subscription.find(subscriptionBody, function(err, subscriptions) {
240
- _.each(subscriptions, function(subscription) {
241
- var mailOptions = {
242
- transport: mailTransport,
243
- from: "Sencha Documentation <no-reply@sencha.com>",
244
- to: subscription.email,
245
- subject: "Comment on '" + comment.title + "'",
246
- text: [
247
- "A comment by " + comment.author + " on '" + comment.title + "' was posted on the Sencha Documentation:\n",
248
- comment.content + "\n",
249
- "--",
250
- "Original thread: " + comment.url,
251
- "Unsubscribe from this thread: http://projects.sencha.com/auth/unsubscribe/" + subscription._id,
252
- "Unsubscribe from all threads: http://projects.sencha.com/auth/unsubscribe/" + subscription._id + '?all=true'
253
- ].join("\n")
254
- };
255
-
256
- if (Number(comment.userId) != Number(subscription.userId)) {
257
- emails.push(mailOptions);
258
- }
259
- });
260
-
261
- if (emails.length) {
262
- sendSubscriptionEmail(emails);
263
- } else {
264
- console.log("No emails to send");
265
- }
266
- });
267
- };
268
-
269
- /**
270
- * Retrieves comment counts for each target.
271
- *
272
- * Stores into `req.commentCounts` field an array like this:
273
- *
274
- * [
275
- * {"_id": "class__Ext__", "value": 3},
276
- * {"_id": "class__Ext__method-define", "value": 1},
277
- * {"_id": "class__Ext.Panel__cfg-title", "value": 8}
278
- * ]
279
- *
280
- * @param {Object} req
281
- * @param {Object} res
282
- * @param {Function} next
283
- */
284
- exports.getCommentCounts = function(req, res, next) {
285
- // Map each comment into: ("type__Class__member", 1)
286
- var map = function() {
287
- if (this.target) {
288
- emit(this.target.slice(0,3).join('__'), 1);
289
- } else {
290
- return;
291
- }
292
- };
293
-
294
- // Sum comment counts for each target
295
- var reduce = function(key, values) {
296
- var total = 0;
297
-
298
- for (var i = 0; i < values.length; i++) {
299
- total += values[i];
300
- }
301
-
302
- return total;
303
- };
304
-
305
- mongoose.connection.db.executeDbCommand({
306
- mapreduce: 'comments',
307
- map: map.toString(),
308
- reduce: reduce.toString(),
309
- out: 'commentCounts',
310
- query: {
311
- deleted: { '$ne': true },
312
- sdk: req.params.sdk,
313
- version: req.params.version
314
- }
315
- }, function(err, dbres) {
316
- mongoose.connection.db.collection('commentCounts', function(err, collection) {
317
- collection.find({}).toArray(function(err, comments) {
318
- req.commentCounts = comments;
319
- next();
320
- });
321
- });
322
- });
323
- };
324
-
325
- /**
326
- * Retrieves list of commenting targets into which the current user
327
- * has subscribed for e-mail updates.
328
- *
329
- * Stores them into `req.commentMeta.subscriptions` field as array:
330
- *
331
- * [
332
- * ["class", "Ext", ""],
333
- * ["class", "Ext", "method-define"],
334
- * ["class", "Ext.Panel", "cfg-title"]
335
- * ]
336
- *
337
- * @param {Object} req
338
- * @param {Object} res
339
- * @param {Function} next
340
- */
341
- exports.getCommentSubscriptions = function(req, res, next) {
342
-
343
- req.commentMeta = req.commentMeta || {};
344
- req.commentMeta.subscriptions = req.commentMeta.subscriptions || [];
345
-
346
- if (req.session.user) {
347
- Subscription.find({
348
- sdk: req.params.sdk,
349
- version: req.params.version,
350
- userId: req.session.user.userid
351
- }, function(err, subscriptions) {
352
- req.commentMeta.subscriptions = _.map(subscriptions, function(subscription) {
353
- return subscription.target;
354
- });
355
- next();
356
- });
357
- } else {
358
- next();
359
- }
360
- };
361
-
362
- /**
363
- * Retrieves list of comments marked 'read' by the current user.
364
- *
365
- * Stores them into `req.commentMeta.reads` field as array:
366
- *
367
- * [
368
- * 'abc123',
369
- * 'abc456',
370
- * 'abc789'
371
- * ]
372
- *
373
- * @param {Object} req
374
- * @param {Object} res
375
- * @param {Function} next
376
- */
377
- exports.getCommentReads = function(req, res, next) {
378
-
379
- req.commentMeta = req.commentMeta || {};
380
- req.commentMeta.reads = req.commentMeta.reads || [];
381
-
382
- if (req.session.user && isModerator(req.session.user)) {
383
- Meta.find({
384
- userId: req.session.user.userid
385
- }, function(err, commentMeta) {
386
- req.commentMeta.reads = _.map(commentMeta, function(commentMeta) {
387
- return commentMeta.commentId;
388
- });
389
- next();
390
- });
391
- } else {
392
- next();
393
- }
394
- };
395
-
396
-