{"action":"created","comment":{"url":"https://api.github.com/repos/NodeBB/NodeBB/pulls/comments/32388428","id":32388428,"diff_hunk":"@@ -1,369 +1,373 @@\n-\"use strict\";\r\n-\r\n-var async = require('async'),\r\n-\tvalidator = require('validator'),\r\n-\r\n-\t_ = require('underscore'),\r\n-\tdb = require('./database'),\r\n-\tposts = require('./posts'),\r\n-\tutils = require('../public/src/utils'),\r\n-\tplugins = require('./plugins'),\r\n-\tuser = require('./user'),\r\n-\tcategories = require('./categories'),\r\n-\tprivileges = require('./privileges');\r\n-\r\n-(function(Topics) {\r\n-\r\n-\trequire('./topics/create')(Topics);\r\n-\trequire('./topics/delete')(Topics);\r\n-\trequire('./topics/unread')(Topics);\r\n-\trequire('./topics/recent')(Topics);\r\n-\trequire('./topics/popular')(Topics);\r\n-\trequire('./topics/user')(Topics);\r\n-\trequire('./topics/fork')(Topics);\r\n-\trequire('./topics/posts')(Topics);\r\n-\trequire('./topics/follow')(Topics);\r\n-\trequire('./topics/tags')(Topics);\r\n-\trequire('./topics/teaser')(Topics);\r\n-\trequire('./topics/suggested')(Topics);\r\n-\r\n-\tTopics.exists = function(tid, callback) {\r\n-\t\tdb.isSortedSetMember('topics:tid', tid, callback);\r\n-\t};\r\n-\r\n-\tTopics.getTopicData = function(tid, callback) {\r\n-\t\tdb.getObject('topic:' + tid, function(err, topic) {\r\n-\t\t\tif (err || !topic) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\t\t\tmodifyTopic(topic, callback);\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.getTopicsData = function(tids, callback) {\r\n-\t\tvar keys = [];\r\n-\r\n-\t\tfor (var i=0; i<tids.length; ++i) {\r\n-\t\t\tkeys.push('topic:' + tids[i]);\r\n-\t\t}\r\n-\r\n-\t\tdb.getObjects(keys, function(err, topics) {\r\n-\t\t\tif (err) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\t\t\tasync.map(topics, modifyTopic, callback);\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tfunction modifyTopic(topic, callback) {\r\n-\t\tif (!topic) {\r\n-\t\t\treturn callback(null, topic);\r\n-\t\t}\r\n-\t\ttopic.title = validator.escape(topic.title);\r\n-\t\ttopic.relativeTime = utils.toISOString(topic.timestamp);\r\n-\t\ttopic.lastposttimeISO = utils.toISOString(topic.lastposttime);\r\n-\t\tcallback(null, topic);\r\n-\t}\r\n-\r\n-\tTopics.getPageCount = function(tid, uid, callback) {\r\n-\t\tTopics.getTopicField(tid, 'postcount', function(err, postCount) {\r\n-\t\t\tif (err) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\t\t\tif (!parseInt(postCount, 10)) {\r\n-\t\t\t\treturn callback(null, 1);\r\n-\t\t\t}\r\n-\t\t\tuser.getSettings(uid, function(err, settings) {\r\n-\t\t\t\tif (err) {\r\n-\t\t\t\t\treturn callback(err);\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\tcallback(null, Math.ceil((parseInt(postCount, 10) - 1) / settings.postsPerPage));\r\n-\t\t\t});\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.getTidPage = function(tid, uid, callback) {\r\n-\t\tif(!tid) {\r\n-\t\t\treturn callback(new Error('[[error:invalid-tid]]'));\r\n-\t\t}\r\n-\r\n-\t\tasync.parallel({\r\n-\t\t\tindex: function(next) {\r\n-\t\t\t\tcategories.getTopicIndex(tid, next);\r\n-\t\t\t},\r\n-\t\t\tsettings: function(next) {\r\n-\t\t\t\tuser.getSettings(uid, next);\r\n-\t\t\t}\r\n-\t\t}, function(err, results) {\r\n-\t\t\tif(err) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\t\t\tcallback(null, Math.ceil((results.index + 1) / results.settings.topicsPerPage));\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.getCategoryData = function(tid, callback) {\r\n-\t\tTopics.getTopicField(tid, 'cid', function(err, cid) {\r\n-\t\t\tif (err) {\r\n-\t\t\t\tcallback(err);\r\n-\t\t\t}\r\n-\r\n-\t\t\tcategories.getCategoryData(cid, callback);\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.getTopicsFromSet = function(set, uid, start, stop, callback) {\r\n-\t\tasync.waterfall([\r\n-\t\t\tfunction(next) {\r\n-\t\t\t\tdb.getSortedSetRevRange(set, start, stop, next);\r\n-\t\t\t},\r\n-\t\t\tfunction(tids, next) {\r\n-\t\t\t\tTopics.getTopics(tids, uid, next);\r\n-\t\t\t},\r\n-\t\t\tfunction(topics, next) {\r\n-\t\t\t\tnext(null, {topics: topics, nextStart: stop + 1});\r\n-\t\t\t}\r\n-\t\t], callback);\r\n-\t};\r\n-\r\n-\tTopics.getTopics = function(tids, uid, callback) {\r\n-\t\tasync.waterfall([\r\n-\t\t\tfunction(next) {\r\n-\t\t\t\tprivileges.topics.filterTids('read', tids, uid, next);\r\n-\t\t\t},\r\n-\t\t\tfunction(tids, next) {\r\n-\t\t\t\tTopics.getTopicsByTids(tids, uid, next);\r\n-\t\t\t}\r\n-\t\t], callback);\r\n-\t};\r\n-\r\n-\tTopics.getTopicsByTids = function(tids, uid, callback) {\r\n-\t\tif (!Array.isArray(tids) || !tids.length) {\r\n-\t\t\treturn callback(null, []);\r\n-\t\t}\r\n-\r\n-\t\tTopics.getTopicsData(tids, function(err, topics) {\r\n-\t\t\tfunction mapFilter(array, field) {\r\n-\t\t\t\treturn array.map(function(topic) {\r\n-\t\t\t\t\treturn topic && topic[field] && topic[field].toString();\r\n-\t\t\t\t}).filter(function(value, index, array) {\r\n-\t\t\t\t\treturn utils.isNumber(value) && array.indexOf(value) === index;\r\n-\t\t\t\t});\r\n-\t\t\t}\r\n-\r\n-\t\t\tif (err) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\r\n-\t\t\tvar uids = mapFilter(topics, 'uid');\r\n-\t\t\tvar cids = mapFilter(topics, 'cid');\r\n-\r\n-\t\t\tasync.parallel({\r\n-\t\t\t\tteasers: function(next) {\r\n-\t\t\t\t\tTopics.getTeasers(topics, next);\r\n-\t\t\t\t},\r\n-\t\t\t\tusers: function(next) {\r\n-\t\t\t\t\tuser.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], next);\r\n-\t\t\t\t},\r\n-\t\t\t\tcategories: function(next) {\r\n-\t\t\t\t\tcategories.getMultipleCategoryFields(cids, ['cid', 'name', 'slug', 'icon', 'bgColor', 'color', 'disabled'], next);\r\n-\t\t\t\t},\r\n-\t\t\t\thasRead: function(next) {\r\n-\t\t\t\t\tTopics.hasReadTopics(tids, uid, next);\r\n-\t\t\t\t},\r\n-\t\t\t\ttags: function(next) {\r\n-\t\t\t\t\tTopics.getTopicsTagsObjects(tids, next);\r\n-\t\t\t\t}\r\n-\t\t\t}, function(err, results) {\r\n-\t\t\t\tif (err) {\r\n-\t\t\t\t\treturn callback(err);\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\tvar users = _.object(uids, results.users);\r\n-\t\t\t\tvar categories = _.object(cids, results.categories);\r\n-\r\n-\t\t\t\tfor (var i=0; i<topics.length; ++i) {\r\n-\t\t\t\t\tif (topics[i]) {\r\n-\t\t\t\t\t\ttopics[i].category = categories[topics[i].cid];\r\n-\t\t\t\t\t\ttopics[i].user = users[topics[i].uid];\r\n-\t\t\t\t\t\ttopics[i].teaser = results.teasers[i];\r\n-\t\t\t\t\t\ttopics[i].tags = results.tags[i];\r\n-\r\n-\t\t\t\t\t\ttopics[i].isOwner = parseInt(topics[i].uid, 10) === parseInt(uid, 10);\r\n-\t\t\t\t\t\ttopics[i].pinned = parseInt(topics[i].pinned, 10) === 1;\r\n-\t\t\t\t\t\ttopics[i].locked = parseInt(topics[i].locked, 10) === 1;\r\n-\t\t\t\t\t\ttopics[i].deleted = parseInt(topics[i].deleted, 10) === 1;\r\n-\t\t\t\t\t\ttopics[i].unread = !results.hasRead[i];\r\n-\t\t\t\t\t\ttopics[i].unreplied = parseInt(topics[i].postcount, 10) <= 1;\r\n-\t\t\t\t\t}\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\ttopics = topics.filter(function(topic) {\r\n-\t\t\t\t\treturn topic &&\ttopic.category && !topic.category.disabled;\r\n-\t\t\t\t});\r\n-\r\n-\t\t\t\tplugins.fireHook('filter:topics.get', {topics: topics, uid: uid}, function(err, topicData) {\r\n-\t\t\t\t\tcallback(err, topicData.topics);\r\n-\t\t\t\t});\r\n-\t\t\t});\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.getTopicWithPosts = function(tid, set, uid, start, stop, reverse, callback) {\r\n-\t\tTopics.getTopicData(tid, function(err, topicData) {\r\n-\t\t\tif (err || !topicData) {\r\n-\t\t\t\treturn callback(err || new Error('[[error:no-topic]]'));\r\n-\t\t\t}\r\n-\r\n-\t\t\tasync.parallel({\r\n-\t\t\t\tposts: async.apply(getMainPostAndReplies, topicData, set, uid, start, stop, reverse),\r\n-\t\t\t\tcategory: async.apply(Topics.getCategoryData, tid),\r\n-\t\t\t\tthreadTools: async.apply(plugins.fireHook, 'filter:topic.thread_tools', {topic: topicData, uid: uid, tools: []}),\r\n-\t\t\t\ttags: async.apply(Topics.getTopicTagsObjects, tid),\r\n-\t\t\t\tisFollowing: async.apply(Topics.isFollowing, [tid], uid)\r\n-\t\t\t}, function(err, results) {\r\n-\t\t\t\tif (err) {\r\n-\t\t\t\t\treturn callback(err);\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\ttopicData.posts = results.posts;\r\n-\t\t\t\ttopicData.category = results.category;\r\n-\t\t\t\ttopicData.thread_tools = results.threadTools.tools;\r\n-\t\t\t\ttopicData.tags = results.tags;\r\n-\t\t\t\ttopicData.isFollowing = results.isFollowing[0];\r\n-\r\n-\t\t\t\ttopicData.unreplied = parseInt(topicData.postcount, 10) === 1;\r\n-\t\t\t\ttopicData.deleted = parseInt(topicData.deleted, 10) === 1;\r\n-\t\t\t\ttopicData.locked = parseInt(topicData.locked, 10) === 1;\r\n-\t\t\t\ttopicData.pinned = parseInt(topicData.pinned, 10) === 1;\r\n-\r\n-\t\t\t\tplugins.fireHook('filter:topic.get', {topic: topicData, uid: uid}, function(err, data) {\r\n-\t\t\t\t\tcallback(err, data ? data.topic : null);\r\n-\t\t\t\t});\r\n-\t\t\t});\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tfunction getMainPostAndReplies(topic, set, uid, start, stop, reverse, callback) {\r\n-\t\tasync.waterfall([\r\n-\t\t\tfunction(next) {\r\n-\t\t\t\tposts.getPidsFromSet(set, start, stop, reverse, next);\r\n-\t\t\t},\r\n-\t\t\tfunction(pids, next) {\r\n-\t\t\t\tif ((!Array.isArray(pids) || !pids.length) && !topic.mainPid) {\r\n-\t\t\t\t\treturn callback(null, []);\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\tif (topic.mainPid) {\r\n-\t\t\t\t\tpids.unshift(topic.mainPid);\r\n-\t\t\t\t}\r\n-\t\t\t\tposts.getPostsByPids(pids, uid, next);\r\n-\t\t\t},\r\n-\t\t\tfunction(posts, next) {\r\n-\t\t\t\tif (!posts.length) {\r\n-\t\t\t\t\treturn next(null, []);\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\tif (topic.mainPid) {\r\n-\t\t\t\t\tposts[0].index = 0;\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\tvar indices = Topics.calculatePostIndices(start, stop, topic.postcount, reverse);\r\n-\t\t\t\tfor (var i=1; i<posts.length; ++i) {\r\n-\t\t\t\t\tif (posts[i]) {\r\n-\t\t\t\t\t\tposts[i].index = indices[i - 1];\r\n-\t\t\t\t\t}\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\tTopics.addPostData(posts, uid, callback);\r\n-\t\t\t}\r\n-\t\t]);\r\n-\t}\r\n-\r\n-\tTopics.getMainPost = function(tid, uid, callback) {\r\n-\t\tTopics.getMainPosts([tid], uid, function(err, mainPosts) {\r\n-\t\t\tcallback(err, Array.isArray(mainPosts) && mainPosts.length ? mainPosts[0] : null);\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.getMainPids = function(tids, callback) {\r\n-\t\tif (!Array.isArray(tids) || !tids.length) {\r\n-\t\t\treturn callback(null, []);\r\n-\t\t}\r\n-\r\n-\t\tTopics.getTopicsFields(tids, ['mainPid'], function(err, topicData) {\r\n-\t\t\tif (err) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\r\n-\t\t\tvar mainPids = topicData.map(function(topic) {\r\n-\t\t\t\treturn topic && topic.mainPid;\r\n-\t\t\t});\r\n-\t\t\tcallback(null, mainPids);\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.getMainPosts = function(tids, uid, callback) {\r\n-\t\tTopics.getMainPids(tids, function(err, mainPids) {\r\n-\t\t\tif (err) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\t\t\tgetMainPosts(mainPids, uid, callback);\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tfunction getMainPosts(mainPids, uid, callback) {\r\n-\t\tposts.getPostsByPids(mainPids, uid, function(err, postData) {\r\n-\t\t\tif (err) {\r\n-\t\t\t\treturn callback(err);\r\n-\t\t\t}\r\n-\t\t\tpostData.forEach(function(post) {\r\n-\t\t\t\tif (post) {\r\n-\t\t\t\t\tpost.index = 0;\r\n-\t\t\t\t}\r\n-\t\t\t});\r\n-\t\t\tTopics.addPostData(postData, uid, callback);\r\n-\t\t});\r\n-\t}\r\n-\r\n-\tTopics.getTopicField = function(tid, field, callback) {\r\n-\t\tdb.getObjectField('topic:' + tid, field, callback);\r\n-\t};\r\n-\r\n-\tTopics.getTopicFields = function(tid, fields, callback) {\r\n-\t\tdb.getObjectFields('topic:' + tid, fields, callback);\r\n-\t};\r\n-\r\n-\tTopics.getTopicsFields = function(tids, fields, callback) {\r\n-\t\tif (!Array.isArray(tids) || !tids.length) {\r\n-\t\t\treturn callback(null, []);\r\n-\t\t}\r\n-\t\tvar keys = tids.map(function(tid) {\r\n-\t\t\treturn 'topic:' + tid;\r\n-\t\t});\r\n-\t\tdb.getObjectsFields(keys, fields, callback);\r\n-\t};\r\n-\r\n-\tTopics.setTopicField = function(tid, field, value, callback) {\r\n-\t\tdb.setObjectField('topic:' + tid, field, value, callback);\r\n-\t};\r\n-\r\n-\tTopics.isLocked = function(tid, callback) {\r\n-\t\tTopics.getTopicField(tid, 'locked', function(err, locked) {\r\n-\t\t\tcallback(err, parseInt(locked, 10) === 1);\r\n-\t\t});\r\n-\t};\r\n-\r\n-\tTopics.search = function(tid, term, callback) {\r\n-\t\tif (plugins.hasListeners('filter:topic.search')) {\r\n-\t\t\tplugins.fireHook('filter:topic.search', {\r\n-\t\t\t\ttid: tid,\r\n-\t\t\t\tterm: term\r\n-\t\t\t}, callback);\r\n-\t\t} else {\r\n-\t\t\tcallback(new Error('no-plugins-available'), []);\r\n-\t\t}\r\n-\t};\r\n-\r\n-}(exports));\r\n+\"use strict\";\n+\n+var async = require('async'),\n+\tvalidator = require('validator'),\n+\n+\t_ = require('underscore'),\n+\tdb = require('./database'),\n+\tposts = require('./posts'),\n+\tutils = require('../public/src/utils'),\n+\tplugins = require('./plugins'),\n+\tuser = require('./user'),\n+\tcategories = require('./categories'),\n+\tprivileges = require('./privileges');\n+\n+(function(Topics) {\n+\n+\trequire('./topics/create')(Topics);\n+\trequire('./topics/delete')(Topics);\n+\trequire('./topics/unread')(Topics);\n+\trequire('./topics/recent')(Topics);\n+\trequire('./topics/popular')(Topics);\n+\trequire('./topics/user')(Topics);\n+\trequire('./topics/fork')(Topics);\n+\trequire('./topics/posts')(Topics);\n+\trequire('./topics/follow')(Topics);\n+\trequire('./topics/tags')(Topics);\n+\trequire('./topics/teaser')(Topics);\n+\trequire('./topics/suggested')(Topics);\n+\n+\tTopics.exists = function(tid, callback) {\n+\t\tdb.isSortedSetMember('topics:tid', tid, callback);\n+\t};\n+\n+\tTopics.getTopicData = function(tid, callback) {\n+\t\tdb.getObject('topic:' + tid, function(err, topic) {\n+\t\t\tif (err || !topic) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\t\t\tmodifyTopic(topic, callback);\n+\t\t});\n+\t};\n+\n+\tTopics.getTopicsData = function(tids, callback) {\n+\t\tvar keys = [];\n+\n+\t\tfor (var i=0; i<tids.length; ++i) {\n+\t\t\tkeys.push('topic:' + tids[i]);\n+\t\t}\n+\n+\t\tdb.getObjects(keys, function(err, topics) {\n+\t\t\tif (err) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\t\t\tasync.map(topics, modifyTopic, callback);\n+\t\t});\n+\t};\n+\n+\tfunction modifyTopic(topic, callback) {\n+\t\tif (!topic) {\n+\t\t\treturn callback(null, topic);\n+\t\t}\n+\t\ttopic.title = validator.escape(topic.title);\n+\t\ttopic.relativeTime = utils.toISOString(topic.timestamp);\n+\t\ttopic.lastposttimeISO = utils.toISOString(topic.lastposttime);\n+\t\tcallback(null, topic);\n+\t}\n+\n+\tTopics.getPageCount = function(tid, uid, callback) {\n+\t\tTopics.getTopicField(tid, 'postcount', function(err, postCount) {\n+\t\t\tif (err) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\t\t\tif (!parseInt(postCount, 10)) {\n+\t\t\t\treturn callback(null, 1);\n+\t\t\t}\n+\t\t\tuser.getSettings(uid, function(err, settings) {\n+\t\t\t\tif (err) {\n+\t\t\t\t\treturn callback(err);\n+\t\t\t\t}\n+\n+\t\t\t\tcallback(null, Math.ceil((parseInt(postCount, 10) - 1) / settings.postsPerPage));\n+\t\t\t});\n+\t\t});\n+\t};\n+\n+\tTopics.getTidPage = function(tid, uid, callback) {\n+\t\tif(!tid) {\n+\t\t\treturn callback(new Error('[[error:invalid-tid]]'));\n+\t\t}\n+\n+\t\tasync.parallel({\n+\t\t\tindex: function(next) {\n+\t\t\t\tcategories.getTopicIndex(tid, next);\n+\t\t\t},\n+\t\t\tsettings: function(next) {\n+\t\t\t\tuser.getSettings(uid, next);\n+\t\t\t}\n+\t\t}, function(err, results) {\n+\t\t\tif(err) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\t\t\tcallback(null, Math.ceil((results.index + 1) / results.settings.topicsPerPage));\n+\t\t});\n+\t};\n+\n+\tTopics.getCategoryData = function(tid, callback) {\n+\t\tTopics.getTopicField(tid, 'cid', function(err, cid) {\n+\t\t\tif (err) {\n+\t\t\t\tcallback(err);\n+\t\t\t}\n+\n+\t\t\tcategories.getCategoryData(cid, callback);\n+\t\t});\n+\t};\n+\n+\tTopics.getTopicsFromSet = function(set, uid, start, stop, callback) {\n+\t\tasync.waterfall([\n+\t\t\tfunction(next) {\n+\t\t\t\tdb.getSortedSetRevRange(set, start, stop, next);\n+\t\t\t},\n+\t\t\tfunction(tids, next) {\n+\t\t\t\tTopics.getTopics(tids, uid, next);\n+\t\t\t},\n+\t\t\tfunction(topics, next) {\n+\t\t\t\tnext(null, {topics: topics, nextStart: stop + 1});\n+\t\t\t}\n+\t\t], callback);\n+\t};\n+\n+\tTopics.getTopics = function(tids, uid, callback) {\n+\t\tasync.waterfall([\n+\t\t\tfunction(next) {\n+\t\t\t\tprivileges.topics.filterTids('read', tids, uid, next);\n+\t\t\t},\n+\t\t\tfunction(tids, next) {\n+\t\t\t\tTopics.getTopicsByTids(tids, uid, next);\n+\t\t\t}\n+\t\t], callback);\n+\t};\n+\n+\tTopics.getTopicsByTids = function(tids, uid, callback) {\n+\t\tif (!Array.isArray(tids) || !tids.length) {\n+\t\t\treturn callback(null, []);\n+\t\t}\n+\n+\t\tTopics.getTopicsData(tids, function(err, topics) {\n+\t\t\tfunction mapFilter(array, field) {\n+\t\t\t\treturn array.map(function(topic) {\n+\t\t\t\t\treturn topic && topic[field] && topic[field].toString();\n+\t\t\t\t}).filter(function(value, index, array) {\n+\t\t\t\t\treturn utils.isNumber(value) && array.indexOf(value) === index;\n+\t\t\t\t});\n+\t\t\t}\n+\n+\t\t\tif (err) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\n+\t\t\tvar uids = mapFilter(topics, 'uid');\n+\t\t\tvar cids = mapFilter(topics, 'cid');\n+\n+\t\t\tasync.parallel({\n+\t\t\t\tteasers: function(next) {\n+\t\t\t\t\tTopics.getTeasers(topics, next);\n+\t\t\t\t},\n+\t\t\t\tusers: function(next) {\n+\t\t\t\t\tuser.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], next);\n+\t\t\t\t},\n+\t\t\t\tcategories: function(next) {\n+\t\t\t\t\tcategories.getMultipleCategoryFields(cids, ['cid', 'name', 'slug', 'icon', 'bgColor', 'color', 'disabled'], next);\n+\t\t\t\t},\n+\t\t\t\thasRead: function(next) {\n+\t\t\t\t\tTopics.hasReadTopics(tids, uid, next);\n+\t\t\t\t},\n+\t\t\t\ttags: function(next) {\n+\t\t\t\t\tTopics.getTopicsTagsObjects(tids, next);\n+\t\t\t\t}\n+\t\t\t}, function(err, results) {\n+\t\t\t\tif (err) {\n+\t\t\t\t\treturn callback(err);\n+\t\t\t\t}\n+\n+\t\t\t\tvar users = _.object(uids, results.users);\n+\t\t\t\tvar categories = _.object(cids, results.categories);\n+\n+\t\t\t\tfor (var i=0; i<topics.length; ++i) {\n+\t\t\t\t\tif (topics[i]) {\n+\t\t\t\t\t\ttopics[i].category = categories[topics[i].cid];\n+\t\t\t\t\t\ttopics[i].user = users[topics[i].uid];\n+\t\t\t\t\t\ttopics[i].teaser = results.teasers[i];\n+\t\t\t\t\t\ttopics[i].tags = results.tags[i];\n+\n+\t\t\t\t\t\ttopics[i].isOwner = parseInt(topics[i].uid, 10) === parseInt(uid, 10);\n+\t\t\t\t\t\ttopics[i].pinned = parseInt(topics[i].pinned, 10) === 1;\n+\t\t\t\t\t\ttopics[i].locked = parseInt(topics[i].locked, 10) === 1;\n+\t\t\t\t\t\ttopics[i].deleted = parseInt(topics[i].deleted, 10) === 1;\n+\t\t\t\t\t\ttopics[i].unread = !results.hasRead[i];\n+\t\t\t\t\t\ttopics[i].unreplied = parseInt(topics[i].postcount, 10) <= 1;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\ttopics = topics.filter(function(topic) {\n+\t\t\t\t\treturn topic &&\ttopic.category && !topic.category.disabled;\n+\t\t\t\t});\n+\n+\t\t\t\tplugins.fireHook('filter:topics.get', {topics: topics, uid: uid}, function(err, topicData) {\n+\t\t\t\t\tcallback(err, topicData.topics);\n+\t\t\t\t});\n+\t\t\t});\n+\t\t});\n+\t};\n+\n+\tTopics.getTopicWithPosts = function(tid, set, uid, start, stop, reverse, callback) {\n+\t\tTopics.getTopicData(tid, function(err, topicData) {\n+\t\t\tif (err || !topicData) {\n+\t\t\t\treturn callback(err || new Error('[[error:no-topic]]'));\n+\t\t\t}\n+\n+\t\t\tasync.parallel({\n+\t\t\t\tposts: async.apply(getMainPostAndReplies, topicData, set, uid, start, stop, reverse),\n+\t\t\t\tcategory: async.apply(Topics.getCategoryData, tid),\n+\t\t\t\tthreadTools: async.apply(plugins.fireHook, 'filter:topic.thread_tools', {topic: topicData, uid: uid, tools: []}),\n+\t\t\t\ttags: async.apply(Topics.getTopicTagsObjects, tid),\n+\t\t\t\tisFollowing: async.apply(Topics.isFollowing, [tid], uid)\n+\t\t\t}, function(err, results) {\n+\t\t\t\tif (err) {\n+\t\t\t\t\treturn callback(err);\n+\t\t\t\t}\n+\n+\t\t\t\ttopicData.posts = results.posts;\n+\t\t\t\ttopicData.category = results.category;\n+\t\t\t\ttopicData.thread_tools = results.threadTools.tools;\n+\t\t\t\ttopicData.tags = results.tags;\n+\t\t\t\ttopicData.isFollowing = results.isFollowing[0];\n+\n+\t\t\t\ttopicData.unreplied = parseInt(topicData.postcount, 10) === 1;\n+\t\t\t\ttopicData.deleted = parseInt(topicData.deleted, 10) === 1;\n+\t\t\t\ttopicData.locked = parseInt(topicData.locked, 10) === 1;\n+\t\t\t\ttopicData.pinned = parseInt(topicData.pinned, 10) === 1;\n+\n+\t\t\t\tplugins.fireHook('filter:topic.get', {topic: topicData, uid: uid}, function(err, data) {\n+\t\t\t\t\tcallback(err, data ? data.topic : null);\n+\t\t\t\t});\n+\t\t\t});\n+\t\t});\n+\t};\n+\n+\tfunction getMainPostAndReplies(topic, set, uid, start, stop, reverse, callback) {\n+\t\tasync.waterfall([\n+\t\t\tfunction(next) {\n+\t\t\t\tposts.getPidsFromSet(set, start, stop, reverse, next);\n+\t\t\t},\n+\t\t\tfunction(pids, next) {\n+\t\t\t\tif ((!Array.isArray(pids) || !pids.length) && !topic.mainPid) {\n+\t\t\t\t\treturn callback(null, []);\n+\t\t\t\t}\n+\n+\t\t\t\tif (topic.mainPid) {\n+\t\t\t\t\tpids.unshift(topic.mainPid);\n+\t\t\t\t}\n+\t\t\t\tposts.getPostsByPids(pids, uid, next);\n+\t\t\t},\n+\t\t\tfunction(posts, next) {\n+\t\t\t\tif (!posts.length) {\n+\t\t\t\t\treturn next(null, []);\n+\t\t\t\t}\n+\n+\t\t\t\tif (topic.mainPid) {\n+\t\t\t\t\tposts[0].index = 0;\n+\t\t\t\t}\n+\n+\t\t\t\tvar indices = Topics.calculatePostIndices(start, stop, topic.postcount, reverse);\n+\t\t\t\tfor (var i=1; i<posts.length; ++i) {\n+\t\t\t\t\tif (posts[i]) {\n+\t\t\t\t\t\tposts[i].index = indices[i - 1];\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tTopics.addPostData(posts, uid, callback);\n+\t\t\t}\n+\t\t]);\n+\t}\n+\n+\tTopics.getMainPost = function(tid, uid, callback) {\n+\t\tTopics.getMainPosts([tid], uid, function(err, mainPosts) {\n+\t\t\tcallback(err, Array.isArray(mainPosts) && mainPosts.length ? mainPosts[0] : null);\n+\t\t});\n+\t};\n+\n+\tTopics.getMainPids = function(tids, callback) {\n+\t\tif (!Array.isArray(tids) || !tids.length) {\n+\t\t\treturn callback(null, []);\n+\t\t}\n+\n+\t\tTopics.getTopicsFields(tids, ['mainPid'], function(err, topicData) {\n+\t\t\tif (err) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\n+\t\t\tvar mainPids = topicData.map(function(topic) {\n+\t\t\t\treturn topic && topic.mainPid;\n+\t\t\t});\n+\t\t\tcallback(null, mainPids);\n+\t\t});\n+\t};\n+\n+\tTopics.getMainPosts = function(tids, uid, callback) {\n+\t\tTopics.getMainPids(tids, function(err, mainPids) {\n+\t\t\tif (err) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\t\t\tgetMainPosts(mainPids, uid, callback);\n+\t\t});\n+\t};\n+\n+\tfunction getMainPosts(mainPids, uid, callback) {\n+\t\tposts.getPostsByPids(mainPids, uid, function(err, postData) {\n+\t\t\tif (err) {\n+\t\t\t\treturn callback(err);\n+\t\t\t}\n+\t\t\tpostData.forEach(function(post) {\n+\t\t\t\tif (post) {\n+\t\t\t\t\tpost.index = 0;\n+\t\t\t\t}\n+\t\t\t});\n+\t\t\tTopics.addPostData(postData, uid, callback);\n+\t\t});\n+\t}\n+","path":"src/topics.js","position":698,"original_position":698,"commit_id":"ca88ea7f8ca7734820dfa3975223743aad6f2b23","original_commit_id":"ca88ea7f8ca7734820dfa3975223743aad6f2b23","user":{"login":"bdharrington7","id":4553795,"avatar_url":"https://avatars.githubusercontent.com/u/4553795?v=3","gravatar_id":"","url":"https://api.github.com/users/bdharrington7","html_url":"https://github.com/bdharrington7","followers_url":"https://api.github.com/users/bdharrington7/followers","following_url":"https://api.github.com/users/bdharrington7/following{/other_user}","gists_url":"https://api.github.com/users/bdharrington7/gists{/gist_id}","starred_url":"https://api.github.com/users/bdharrington7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bdharrington7/subscriptions","organizations_url":"https://api.github.com/users/bdharrington7/orgs","repos_url":"https://api.github.com/users/bdharrington7/repos","events_url":"https://api.github.com/users/bdharrington7/events{/privacy}","received_events_url":"https://api.github.com/users/bdharrington7/received_events","type":"User","site_admin":false},"body":"The function I had to add is below","created_at":"2015-06-15T02:12:34Z","updated_at":"2015-06-15T02:12:34Z","html_url":"https://github.com/NodeBB/NodeBB/pull/3234#discussion_r32388428","pull_request_url":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234","_links":{"self":{"href":"https://api.github.com/repos/NodeBB/NodeBB/pulls/comments/32388428"},"html":{"href":"https://github.com/NodeBB/NodeBB/pull/3234#discussion_r32388428"},"pull_request":{"href":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234"}}},"pull_request":{"url":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234","id":37653744,"html_url":"https://github.com/NodeBB/NodeBB/pull/3234","diff_url":"https://github.com/NodeBB/NodeBB/pull/3234.diff","patch_url":"https://github.com/NodeBB/NodeBB/pull/3234.patch","issue_url":"https://api.github.com/repos/NodeBB/NodeBB/issues/3234","number":3234,"state":"open","locked":false,"title":"Saving user bookmarks in db","user":{"login":"bdharrington7","id":4553795,"avatar_url":"https://avatars.githubusercontent.com/u/4553795?v=3","gravatar_id":"","url":"https://api.github.com/users/bdharrington7","html_url":"https://github.com/bdharrington7","followers_url":"https://api.github.com/users/bdharrington7/followers","following_url":"https://api.github.com/users/bdharrington7/following{/other_user}","gists_url":"https://api.github.com/users/bdharrington7/gists{/gist_id}","starred_url":"https://api.github.com/users/bdharrington7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bdharrington7/subscriptions","organizations_url":"https://api.github.com/users/bdharrington7/orgs","repos_url":"https://api.github.com/users/bdharrington7/repos","events_url":"https://api.github.com/users/bdharrington7/events{/privacy}","received_events_url":"https://api.github.com/users/bdharrington7/received_events","type":"User","site_admin":false},"body":"Hey NodeBB team, \r\n\r\nI've started figuring ways to save a users bookmark in a thread to \r\n1. Save the user's place across devices\r\n2. Save the user's place in case of a browser clear\r\n\r\nThis is only half of it: saving the bookmark in the database, but I just wanted to run this by you before a full pull request for any suggestions ","created_at":"2015-06-15T02:10:25Z","updated_at":"2015-06-15T02:12:34Z","closed_at":null,"merged_at":null,"merge_commit_sha":"129fe1d8bb7df6335035775fff6c44556172847e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234/commits","review_comments_url":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234/comments","review_comment_url":"https://api.github.com/repos/NodeBB/NodeBB/pulls/comments{/number}","comments_url":"https://api.github.com/repos/NodeBB/NodeBB/issues/3234/comments","statuses_url":"https://api.github.com/repos/NodeBB/NodeBB/statuses/ca88ea7f8ca7734820dfa3975223743aad6f2b23","head":{"label":"bdharrington7:bookmark","ref":"bookmark","sha":"ca88ea7f8ca7734820dfa3975223743aad6f2b23","user":{"login":"bdharrington7","id":4553795,"avatar_url":"https://avatars.githubusercontent.com/u/4553795?v=3","gravatar_id":"","url":"https://api.github.com/users/bdharrington7","html_url":"https://github.com/bdharrington7","followers_url":"https://api.github.com/users/bdharrington7/followers","following_url":"https://api.github.com/users/bdharrington7/following{/other_user}","gists_url":"https://api.github.com/users/bdharrington7/gists{/gist_id}","starred_url":"https://api.github.com/users/bdharrington7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bdharrington7/subscriptions","organizations_url":"https://api.github.com/users/bdharrington7/orgs","repos_url":"https://api.github.com/users/bdharrington7/repos","events_url":"https://api.github.com/users/bdharrington7/events{/privacy}","received_events_url":"https://api.github.com/users/bdharrington7/received_events","type":"User","site_admin":false},"repo":{"id":27753451,"name":"NodeBB","full_name":"bdharrington7/NodeBB","owner":{"login":"bdharrington7","id":4553795,"avatar_url":"https://avatars.githubusercontent.com/u/4553795?v=3","gravatar_id":"","url":"https://api.github.com/users/bdharrington7","html_url":"https://github.com/bdharrington7","followers_url":"https://api.github.com/users/bdharrington7/followers","following_url":"https://api.github.com/users/bdharrington7/following{/other_user}","gists_url":"https://api.github.com/users/bdharrington7/gists{/gist_id}","starred_url":"https://api.github.com/users/bdharrington7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bdharrington7/subscriptions","organizations_url":"https://api.github.com/users/bdharrington7/orgs","repos_url":"https://api.github.com/users/bdharrington7/repos","events_url":"https://api.github.com/users/bdharrington7/events{/privacy}","received_events_url":"https://api.github.com/users/bdharrington7/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bdharrington7/NodeBB","description":"Node.js based forum software built for the modern web","fork":true,"url":"https://api.github.com/repos/bdharrington7/NodeBB","forks_url":"https://api.github.com/repos/bdharrington7/NodeBB/forks","keys_url":"https://api.github.com/repos/bdharrington7/NodeBB/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bdharrington7/NodeBB/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bdharrington7/NodeBB/teams","hooks_url":"https://api.github.com/repos/bdharrington7/NodeBB/hooks","issue_events_url":"https://api.github.com/repos/bdharrington7/NodeBB/issues/events{/number}","events_url":"https://api.github.com/repos/bdharrington7/NodeBB/events","assignees_url":"https://api.github.com/repos/bdharrington7/NodeBB/assignees{/user}","branches_url":"https://api.github.com/repos/bdharrington7/NodeBB/branches{/branch}","tags_url":"https://api.github.com/repos/bdharrington7/NodeBB/tags","blobs_url":"https://api.github.com/repos/bdharrington7/NodeBB/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bdharrington7/NodeBB/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bdharrington7/NodeBB/git/refs{/sha}","trees_url":"https://api.github.com/repos/bdharrington7/NodeBB/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bdharrington7/NodeBB/statuses/{sha}","languages_url":"https://api.github.com/repos/bdharrington7/NodeBB/languages","stargazers_url":"https://api.github.com/repos/bdharrington7/NodeBB/stargazers","contributors_url":"https://api.github.com/repos/bdharrington7/NodeBB/contributors","subscribers_url":"https://api.github.com/repos/bdharrington7/NodeBB/subscribers","subscription_url":"https://api.github.com/repos/bdharrington7/NodeBB/subscription","commits_url":"https://api.github.com/repos/bdharrington7/NodeBB/commits{/sha}","git_commits_url":"https://api.github.com/repos/bdharrington7/NodeBB/git/commits{/sha}","comments_url":"https://api.github.com/repos/bdharrington7/NodeBB/comments{/number}","issue_comment_url":"https://api.github.com/repos/bdharrington7/NodeBB/issues/comments{/number}","contents_url":"https://api.github.com/repos/bdharrington7/NodeBB/contents/{+path}","compare_url":"https://api.github.com/repos/bdharrington7/NodeBB/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bdharrington7/NodeBB/merges","archive_url":"https://api.github.com/repos/bdharrington7/NodeBB/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bdharrington7/NodeBB/downloads","issues_url":"https://api.github.com/repos/bdharrington7/NodeBB/issues{/number}","pulls_url":"https://api.github.com/repos/bdharrington7/NodeBB/pulls{/number}","milestones_url":"https://api.github.com/repos/bdharrington7/NodeBB/milestones{/number}","notifications_url":"https://api.github.com/repos/bdharrington7/NodeBB/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bdharrington7/NodeBB/labels{/name}","releases_url":"https://api.github.com/repos/bdharrington7/NodeBB/releases{/id}","created_at":"2014-12-09T06:44:11Z","updated_at":"2015-04-29T04:05:56Z","pushed_at":"2015-06-15T02:06:28Z","git_url":"git://github.com/bdharrington7/NodeBB.git","ssh_url":"git@github.com:bdharrington7/NodeBB.git","clone_url":"https://github.com/bdharrington7/NodeBB.git","svn_url":"https://github.com/bdharrington7/NodeBB","homepage":"http://www.nodebb.org","size":24198,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"NodeBB:master","ref":"master","sha":"29c9b2e69fc04d0d7029e94d3dfc8f2319defdbd","user":{"login":"NodeBB","id":4449608,"avatar_url":"https://avatars.githubusercontent.com/u/4449608?v=3","gravatar_id":"","url":"https://api.github.com/users/NodeBB","html_url":"https://github.com/NodeBB","followers_url":"https://api.github.com/users/NodeBB/followers","following_url":"https://api.github.com/users/NodeBB/following{/other_user}","gists_url":"https://api.github.com/users/NodeBB/gists{/gist_id}","starred_url":"https://api.github.com/users/NodeBB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NodeBB/subscriptions","organizations_url":"https://api.github.com/users/NodeBB/orgs","repos_url":"https://api.github.com/users/NodeBB/repos","events_url":"https://api.github.com/users/NodeBB/events{/privacy}","received_events_url":"https://api.github.com/users/NodeBB/received_events","type":"Organization","site_admin":false},"repo":{"id":9603889,"name":"NodeBB","full_name":"NodeBB/NodeBB","owner":{"login":"NodeBB","id":4449608,"avatar_url":"https://avatars.githubusercontent.com/u/4449608?v=3","gravatar_id":"","url":"https://api.github.com/users/NodeBB","html_url":"https://github.com/NodeBB","followers_url":"https://api.github.com/users/NodeBB/followers","following_url":"https://api.github.com/users/NodeBB/following{/other_user}","gists_url":"https://api.github.com/users/NodeBB/gists{/gist_id}","starred_url":"https://api.github.com/users/NodeBB/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NodeBB/subscriptions","organizations_url":"https://api.github.com/users/NodeBB/orgs","repos_url":"https://api.github.com/users/NodeBB/repos","events_url":"https://api.github.com/users/NodeBB/events{/privacy}","received_events_url":"https://api.github.com/users/NodeBB/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/NodeBB/NodeBB","description":"Node.js based forum software built for the modern web","fork":false,"url":"https://api.github.com/repos/NodeBB/NodeBB","forks_url":"https://api.github.com/repos/NodeBB/NodeBB/forks","keys_url":"https://api.github.com/repos/NodeBB/NodeBB/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NodeBB/NodeBB/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NodeBB/NodeBB/teams","hooks_url":"https://api.github.com/repos/NodeBB/NodeBB/hooks","issue_events_url":"https://api.github.com/repos/NodeBB/NodeBB/issues/events{/number}","events_url":"https://api.github.com/repos/NodeBB/NodeBB/events","assignees_url":"https://api.github.com/repos/NodeBB/NodeBB/assignees{/user}","branches_url":"https://api.github.com/repos/NodeBB/NodeBB/branches{/branch}","tags_url":"https://api.github.com/repos/NodeBB/NodeBB/tags","blobs_url":"https://api.github.com/repos/NodeBB/NodeBB/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NodeBB/NodeBB/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NodeBB/NodeBB/git/refs{/sha}","trees_url":"https://api.github.com/repos/NodeBB/NodeBB/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NodeBB/NodeBB/statuses/{sha}","languages_url":"https://api.github.com/repos/NodeBB/NodeBB/languages","stargazers_url":"https://api.github.com/repos/NodeBB/NodeBB/stargazers","contributors_url":"https://api.github.com/repos/NodeBB/NodeBB/contributors","subscribers_url":"https://api.github.com/repos/NodeBB/NodeBB/subscribers","subscription_url":"https://api.github.com/repos/NodeBB/NodeBB/subscription","commits_url":"https://api.github.com/repos/NodeBB/NodeBB/commits{/sha}","git_commits_url":"https://api.github.com/repos/NodeBB/NodeBB/git/commits{/sha}","comments_url":"https://api.github.com/repos/NodeBB/NodeBB/comments{/number}","issue_comment_url":"https://api.github.com/repos/NodeBB/NodeBB/issues/comments{/number}","contents_url":"https://api.github.com/repos/NodeBB/NodeBB/contents/{+path}","compare_url":"https://api.github.com/repos/NodeBB/NodeBB/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NodeBB/NodeBB/merges","archive_url":"https://api.github.com/repos/NodeBB/NodeBB/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NodeBB/NodeBB/downloads","issues_url":"https://api.github.com/repos/NodeBB/NodeBB/issues{/number}","pulls_url":"https://api.github.com/repos/NodeBB/NodeBB/pulls{/number}","milestones_url":"https://api.github.com/repos/NodeBB/NodeBB/milestones{/number}","notifications_url":"https://api.github.com/repos/NodeBB/NodeBB/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NodeBB/NodeBB/labels{/name}","releases_url":"https://api.github.com/repos/NodeBB/NodeBB/releases{/id}","created_at":"2013-04-22T16:49:40Z","updated_at":"2015-06-14T20:19:55Z","pushed_at":"2015-06-15T02:10:26Z","git_url":"git://github.com/NodeBB/NodeBB.git","ssh_url":"git@github.com:NodeBB/NodeBB.git","clone_url":"https://github.com/NodeBB/NodeBB.git","svn_url":"https://github.com/NodeBB/NodeBB","homepage":"http://www.nodebb.org","size":55503,"stargazers_count":4337,"watchers_count":4337,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":799,"mirror_url":null,"open_issues_count":144,"forks":799,"open_issues":144,"watchers":4337,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234"},"html":{"href":"https://github.com/NodeBB/NodeBB/pull/3234"},"issue":{"href":"https://api.github.com/repos/NodeBB/NodeBB/issues/3234"},"comments":{"href":"https://api.github.com/repos/NodeBB/NodeBB/issues/3234/comments"},"review_comments":{"href":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234/comments"},"review_comment":{"href":"https://api.github.com/repos/NodeBB/NodeBB/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/NodeBB/NodeBB/pulls/3234/commits"},"statuses":{"href":"https://api.github.com/repos/NodeBB/NodeBB/statuses/ca88ea7f8ca7734820dfa3975223743aad6f2b23"}}}}