Loading comments/api_adapter.js +1 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ module.exports = { score: comment.vote, upVote: comment.vote_dir === 1, downVote: comment.vote_dir === -1, read: comment.read, moderator: comment.moderator, emailHash: crypto.createHash('md5').update(comment.email).digest("hex") }; Loading comments/comments.js +28 −13 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ function Comments(db, domain) { this.domain = domain; this.targets = new Targets(db, domain); this.view = "full_visible_comments AS comments"; this.fields = "*"; this.fields = ["*"]; } Comments.prototype = { Loading @@ -41,8 +41,19 @@ Comments.prototype = { * @param {Number} user_id The ID of the user who's votes to inspect. */ showVoteDirBy: function(user_id) { var sql = "*, (SELECT SUM(value) FROM votes WHERE user_id = ? AND comment_id = comments.id) AS vote_dir"; this.fields = this.db.format(sql, [user_id]); var sql = "(SELECT SUM(value) FROM votes WHERE user_id = ? AND comment_id = comments.id) AS vote_dir"; this.fields.push(this.db.format(sql, [user_id])); }, /** * Includes a `read` field into the comment records returned * by #get* and #find* methods. The `read` field will be 1 * when the user has read the comment, 0 if he hasn't. * @param {Number} user_id The ID of the user who's readings to inspect. */ showReadBy: function(user_id) { var sql = "(SELECT COUNT(*) FROM readings WHERE user_id = ? AND comment_id = comments.id) AS `read`"; this.fields.push(this.db.format(sql, [user_id])); }, /** Loading @@ -55,12 +66,12 @@ Comments.prototype = { */ getById: function(id, callback) { var sql = [ 'SELECT ', this.fields, 'SELECT ', this.fields.join(", "), 'FROM', this.view, 'WHERE domain = ? AND id = ?' ]; this.db.query(sql, [this.domain, id], this.fixVoteDir(function(err, rows) { this.db.query(sql, [this.domain, id], this.fixFields(function(err, rows) { callback(err, rows && rows[0]); })); }, Loading @@ -79,13 +90,13 @@ Comments.prototype = { */ find: function(target, callback) { var sql = [ 'SELECT ', this.fields, 'SELECT ', this.fields.join(", "), 'FROM', this.view, 'WHERE domain = ? AND type = ? AND cls = ? AND member = ?', 'ORDER BY created_at' ]; this.db.query(sql, [this.domain, target.type, target.cls, target.member], this.fixVoteDir(callback)); this.db.query(sql, [this.domain, target.type, target.cls, target.member], this.fixFields(callback)); }, /** Loading @@ -101,14 +112,14 @@ Comments.prototype = { */ findRecent: function(opts, callback) { var sql = [ 'SELECT ', this.fields, 'SELECT ', this.fields.join(", "), 'FROM', this.view, 'WHERE domain = ?', 'ORDER BY created_at DESC', 'LIMIT ? OFFSET ?' ]; this.db.query(sql, [this.domain, opts.limit||100, opts.offset||0], this.fixVoteDir(callback)); this.db.query(sql, [this.domain, opts.limit||100, opts.offset||0], this.fixFields(callback)); }, /** Loading Loading @@ -309,10 +320,11 @@ Comments.prototype = { }.bind(this)); }, // Helper that converts all vote_dir fields into numbers. For some // reason the vote_dir field is a string by default, but we don't // want that. fixVoteDir: function(callback) { // Helper that converts all `vote_dir` and `read` fields into // appropriate type. For some reason the vote_dir field is a // string by default, but we don't want that. The `read` field is // a string, but we really want a boolean instead. fixFields: function(callback) { return function(err, rows) { if (err) { callback(err); Loading @@ -323,6 +335,9 @@ Comments.prototype = { if (r.vote_dir) { r.vote_dir = +r.vote_dir; } if (r.read) { r.read = !!(+r.read); } return r; })); }; Loading comments/comments.spec.js +19 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,25 @@ describe("Comments", function() { }); }); describe("after calling showReadBy(user)", function() { beforeEach(function() { comments.showReadBy(1); }); it("#getById includes read:true when user has read the comment", function(done) { comments.getById(1, function(err, com) { if (err) throw err; expect(com.read).toEqual(true); done(); }); }); it("#getById includes read:false when user hasn't read the comment", function(done) { comments.getById(7, function(err, com) { expect(com.read).toEqual(false); done(); }); }); }); it("#find returns all undeleted comments for a target", function(done) { comments.find({type: "class", cls: "Ext", member: ""}, function(err, rows) { expect(rows.length).toEqual(5); Loading comments/request.js +7 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ Request.prototype = { if (this.isLoggedIn()) { this.db.comments().showVoteDirBy(this.getUserId()); if (this.isModerator()) { this.db.comments().showReadBy(this.getUserId()); } } this.db.comments().find(targetObj, function(err, comments) { Loading Loading @@ -140,6 +143,10 @@ Request.prototype = { return this.req.session && this.req.session.user; }, isModerator: function() { return this.req.session.user.moderator; }, /** * True when logged in user can modify comment with given ID. * Works also for deleted comments. Loading comments/sql/test_data.sql +9 −0 Original line number Diff line number Diff line Loading @@ -180,3 +180,12 @@ INSERT INTO subscriptions SET `user_id` = 3, `target_id` = 1, `created_at` = '20 -- subscribe jack to touch-2 Ext INSERT INTO subscriptions SET `user_id` = 4, `target_id` = 14, `created_at` = '2011-01-01 00:00:00'; -- readings -- renku has read all comments in Ext thread INSERT INTO readings SET `user_id` = 1, `comment_id` = 1, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 2, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 3, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 4, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 5, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 6, `created_at` = '2011-01-01 00:00:00'; Loading
comments/api_adapter.js +1 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ module.exports = { score: comment.vote, upVote: comment.vote_dir === 1, downVote: comment.vote_dir === -1, read: comment.read, moderator: comment.moderator, emailHash: crypto.createHash('md5').update(comment.email).digest("hex") }; Loading
comments/comments.js +28 −13 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ function Comments(db, domain) { this.domain = domain; this.targets = new Targets(db, domain); this.view = "full_visible_comments AS comments"; this.fields = "*"; this.fields = ["*"]; } Comments.prototype = { Loading @@ -41,8 +41,19 @@ Comments.prototype = { * @param {Number} user_id The ID of the user who's votes to inspect. */ showVoteDirBy: function(user_id) { var sql = "*, (SELECT SUM(value) FROM votes WHERE user_id = ? AND comment_id = comments.id) AS vote_dir"; this.fields = this.db.format(sql, [user_id]); var sql = "(SELECT SUM(value) FROM votes WHERE user_id = ? AND comment_id = comments.id) AS vote_dir"; this.fields.push(this.db.format(sql, [user_id])); }, /** * Includes a `read` field into the comment records returned * by #get* and #find* methods. The `read` field will be 1 * when the user has read the comment, 0 if he hasn't. * @param {Number} user_id The ID of the user who's readings to inspect. */ showReadBy: function(user_id) { var sql = "(SELECT COUNT(*) FROM readings WHERE user_id = ? AND comment_id = comments.id) AS `read`"; this.fields.push(this.db.format(sql, [user_id])); }, /** Loading @@ -55,12 +66,12 @@ Comments.prototype = { */ getById: function(id, callback) { var sql = [ 'SELECT ', this.fields, 'SELECT ', this.fields.join(", "), 'FROM', this.view, 'WHERE domain = ? AND id = ?' ]; this.db.query(sql, [this.domain, id], this.fixVoteDir(function(err, rows) { this.db.query(sql, [this.domain, id], this.fixFields(function(err, rows) { callback(err, rows && rows[0]); })); }, Loading @@ -79,13 +90,13 @@ Comments.prototype = { */ find: function(target, callback) { var sql = [ 'SELECT ', this.fields, 'SELECT ', this.fields.join(", "), 'FROM', this.view, 'WHERE domain = ? AND type = ? AND cls = ? AND member = ?', 'ORDER BY created_at' ]; this.db.query(sql, [this.domain, target.type, target.cls, target.member], this.fixVoteDir(callback)); this.db.query(sql, [this.domain, target.type, target.cls, target.member], this.fixFields(callback)); }, /** Loading @@ -101,14 +112,14 @@ Comments.prototype = { */ findRecent: function(opts, callback) { var sql = [ 'SELECT ', this.fields, 'SELECT ', this.fields.join(", "), 'FROM', this.view, 'WHERE domain = ?', 'ORDER BY created_at DESC', 'LIMIT ? OFFSET ?' ]; this.db.query(sql, [this.domain, opts.limit||100, opts.offset||0], this.fixVoteDir(callback)); this.db.query(sql, [this.domain, opts.limit||100, opts.offset||0], this.fixFields(callback)); }, /** Loading Loading @@ -309,10 +320,11 @@ Comments.prototype = { }.bind(this)); }, // Helper that converts all vote_dir fields into numbers. For some // reason the vote_dir field is a string by default, but we don't // want that. fixVoteDir: function(callback) { // Helper that converts all `vote_dir` and `read` fields into // appropriate type. For some reason the vote_dir field is a // string by default, but we don't want that. The `read` field is // a string, but we really want a boolean instead. fixFields: function(callback) { return function(err, rows) { if (err) { callback(err); Loading @@ -323,6 +335,9 @@ Comments.prototype = { if (r.vote_dir) { r.vote_dir = +r.vote_dir; } if (r.read) { r.read = !!(+r.read); } return r; })); }; Loading
comments/comments.spec.js +19 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,25 @@ describe("Comments", function() { }); }); describe("after calling showReadBy(user)", function() { beforeEach(function() { comments.showReadBy(1); }); it("#getById includes read:true when user has read the comment", function(done) { comments.getById(1, function(err, com) { if (err) throw err; expect(com.read).toEqual(true); done(); }); }); it("#getById includes read:false when user hasn't read the comment", function(done) { comments.getById(7, function(err, com) { expect(com.read).toEqual(false); done(); }); }); }); it("#find returns all undeleted comments for a target", function(done) { comments.find({type: "class", cls: "Ext", member: ""}, function(err, rows) { expect(rows.length).toEqual(5); Loading
comments/request.js +7 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ Request.prototype = { if (this.isLoggedIn()) { this.db.comments().showVoteDirBy(this.getUserId()); if (this.isModerator()) { this.db.comments().showReadBy(this.getUserId()); } } this.db.comments().find(targetObj, function(err, comments) { Loading Loading @@ -140,6 +143,10 @@ Request.prototype = { return this.req.session && this.req.session.user; }, isModerator: function() { return this.req.session.user.moderator; }, /** * True when logged in user can modify comment with given ID. * Works also for deleted comments. Loading
comments/sql/test_data.sql +9 −0 Original line number Diff line number Diff line Loading @@ -180,3 +180,12 @@ INSERT INTO subscriptions SET `user_id` = 3, `target_id` = 1, `created_at` = '20 -- subscribe jack to touch-2 Ext INSERT INTO subscriptions SET `user_id` = 4, `target_id` = 14, `created_at` = '2011-01-01 00:00:00'; -- readings -- renku has read all comments in Ext thread INSERT INTO readings SET `user_id` = 1, `comment_id` = 1, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 2, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 3, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 4, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 5, `created_at` = '2011-01-01 00:00:00'; INSERT INTO readings SET `user_id` = 1, `comment_id` = 6, `created_at` = '2011-01-01 00:00:00';