mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-03-06 04:21:17 +01:00
closes #1556
added tag input box to composer when creating a topic added new routes for viewing tags 'tags' and 'tags/:tagname' respectively post_bar.tpl shows the tags of the topic can edit the main post to remove or add new tags added a new menu item to header to go to the tags page
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
"header.admin": "Admin",
|
||||
"header.recent": "Recent",
|
||||
"header.unread": "Unread",
|
||||
"header.tags": "Tags",
|
||||
"header.popular": "Popular",
|
||||
"header.users": "Users",
|
||||
"header.chats": "Chats",
|
||||
|
||||
5
public/language/en_GB/tags.json
Normal file
5
public/language/en_GB/tags.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"no_tag_topics": "There are no topics with this tag.",
|
||||
"tags": "Tags",
|
||||
"enter_tags_here": "Enter tags here..."
|
||||
}
|
||||
41
public/src/forum/tag.js
Normal file
41
public/src/forum/tag.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, app, socket */
|
||||
|
||||
define(['forum/recent', 'forum/infinitescroll'], function(recent, infinitescroll) {
|
||||
var Tag = {};
|
||||
|
||||
Tag.init = function() {
|
||||
app.enterRoom('tags');
|
||||
|
||||
if ($('body').height() <= $(window).height() && $('#topics-container').children().length >= 20) {
|
||||
$('#load-more-btn').show();
|
||||
}
|
||||
|
||||
$('#load-more-btn').on('click', function() {
|
||||
loadMoreTopics();
|
||||
});
|
||||
|
||||
infinitescroll.init(loadMoreTopics);
|
||||
|
||||
function loadMoreTopics(direction) {
|
||||
if(direction < 0 || !$('#topics-container').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
infinitescroll.loadMore('topics.loadMoreFromSet', {
|
||||
set: 'tag:' + ajaxify.variables.get('tag') + ':topics',
|
||||
after: $('#topics-container').attr('data-nextstart')
|
||||
}, function(data) {
|
||||
if (data.topics && data.topics.length) {
|
||||
recent.onTopicsLoaded('tag', data.topics, false);
|
||||
$('#topics-container').attr('data-nextstart', data.nextStart);
|
||||
} else {
|
||||
$('#load-more-btn').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return Tag;
|
||||
});
|
||||
@@ -3,8 +3,7 @@
|
||||
/* globals define, app, socket */
|
||||
|
||||
define(['forum/recent', 'topicSelect', 'forum/infinitescroll'], function(recent, topicSelect, infinitescroll) {
|
||||
var Unread = {},
|
||||
loadingMoreTopics = false;
|
||||
var Unread = {};
|
||||
|
||||
$(window).on('action:ajaxify.start', function(ev, data) {
|
||||
if(data.url.indexOf('unread') !== 0) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* globals define, socket, app, config, ajaxify, utils, translator, templates, bootbox */
|
||||
|
||||
define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting', 'composer/drafts'], function(taskbar, controls, uploads, formatting, drafts) {
|
||||
define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting', 'composer/drafts', 'composer/tags'], function(taskbar, controls, uploads, formatting, drafts, tags) {
|
||||
var composer = {
|
||||
active: undefined,
|
||||
posts: {}
|
||||
@@ -152,7 +152,8 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
body: threadData.body,
|
||||
modified: false,
|
||||
isMain: !threadData.index,
|
||||
topic_thumb: threadData.topic_thumb
|
||||
topic_thumb: threadData.topic_thumb,
|
||||
tags: threadData.tags
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -176,8 +177,10 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
|
||||
function createNewComposer(post_uuid) {
|
||||
var allowTopicsThumbnail = config.allowTopicsThumbnail && composer.posts[post_uuid].isMain && (config.hasImageUploadPlugin || config.allowFileUploads);
|
||||
var isTopic = composer.posts[post_uuid] ? !!composer.posts[post_uuid].cid : false;
|
||||
var isMain = composer.posts[post_uuid] ? !!composer.posts[post_uuid].isMain : false;
|
||||
|
||||
templates.parse('composer', {allowTopicsThumbnail: allowTopicsThumbnail}, function(composerTemplate) {
|
||||
templates.parse('composer', {allowTopicsThumbnail: allowTopicsThumbnail, showTags: isTopic || isMain}, function(composerTemplate) {
|
||||
translator.translate(composerTemplate, function(composerTemplate) {
|
||||
composerTemplate = $(composerTemplate);
|
||||
|
||||
@@ -185,10 +188,12 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
|
||||
$(document.body).append(composerTemplate);
|
||||
|
||||
activateReposition(post_uuid);
|
||||
|
||||
var postContainer = $(composerTemplate[0]);
|
||||
|
||||
tags.init(postContainer, composer.posts[post_uuid]);
|
||||
|
||||
activateReposition(post_uuid);
|
||||
|
||||
if(config.allowFileUploads || config.hasImageUploadPlugin) {
|
||||
uploads.initialize(post_uuid);
|
||||
}
|
||||
@@ -266,6 +271,8 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
});
|
||||
|
||||
formatting.addComposerButtons();
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -321,15 +328,16 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
if (resizeActive) {
|
||||
var position = (e.clientY + resizeOffset);
|
||||
var newHeight = $(window).height() - position;
|
||||
var paddingBottom = parseInt(postContainer.css('padding-bottom'), 10);
|
||||
|
||||
if(newHeight > $(window).height() - $('#header-menu').height() - 20) {
|
||||
newHeight = $(window).height() - $('#header-menu').height() - 20;
|
||||
} else if (newHeight < paddingBottom) {
|
||||
newHeight = paddingBottom;
|
||||
} else if (newHeight < 100) {
|
||||
newHeight = 100;
|
||||
}
|
||||
|
||||
postContainer.css('height', newHeight);
|
||||
$('body').css({'margin-bottom': newHeight});
|
||||
resizeTabContent(postContainer);
|
||||
resizeSavePosition(newHeight);
|
||||
}
|
||||
e.preventDefault();
|
||||
@@ -395,6 +403,19 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
$('body').css({'margin-bottom': postContainer.css('height')});
|
||||
|
||||
focusElements(post_uuid);
|
||||
resizeTabContent(postContainer);
|
||||
}
|
||||
|
||||
function resizeTabContent(postContainer) {
|
||||
var h1 = postContainer.find('.title').outerHeight(true);
|
||||
var h2 = postContainer.find('.tags-container').outerHeight(true);
|
||||
var h3 = postContainer.find('.formatting-bar').outerHeight(true);
|
||||
var h4 = postContainer.find('.nav-tabs').outerHeight(true);
|
||||
var h5 = postContainer.find('.instructions').outerHeight(true);
|
||||
var h6 = postContainer.find('.topic-thumb-container').outerHeight(true);
|
||||
var h7 = $('.taskbar').height();
|
||||
var total = h1 + h2 + h3 + h4 + h5 + h6 + h7;
|
||||
postContainer.find('.tab-content').css('height', postContainer.height() - total);
|
||||
}
|
||||
|
||||
function focusElements(post_uuid) {
|
||||
@@ -441,7 +462,8 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
title: titleEl.val(),
|
||||
content: bodyEl.val(),
|
||||
topic_thumb: thumbEl.val() || '',
|
||||
category_id: postData.cid
|
||||
category_id: postData.cid,
|
||||
tags: tags.getTags(post_uuid)
|
||||
}, function(err, topic) {
|
||||
done(err);
|
||||
if (!err) {
|
||||
@@ -459,7 +481,8 @@ define(['taskbar', 'composer/controls', 'composer/uploads', 'composer/formatting
|
||||
pid: postData.pid,
|
||||
content: bodyEl.val(),
|
||||
title: titleEl.val(),
|
||||
topic_thumb: thumbEl.val() || ''
|
||||
topic_thumb: thumbEl.val() || '',
|
||||
tags: tags.getTags(post_uuid)
|
||||
}, done);
|
||||
}
|
||||
|
||||
|
||||
60
public/src/modules/composer/tags.js
Normal file
60
public/src/modules/composer/tags.js
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
/*globals define*/
|
||||
|
||||
define(function() {
|
||||
var tags = {};
|
||||
|
||||
tags.init = function(postContainer, postData) {
|
||||
var tagEl = postContainer.find('.tags');
|
||||
if (!tagEl.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
tagEl.tagsinput();
|
||||
addTags(postData.tags, tagEl);
|
||||
|
||||
var input = postContainer.find('.bootstrap-tagsinput input');
|
||||
input.autocomplete({
|
||||
delay: 100,
|
||||
source: function(request, response) {
|
||||
socket.emit('topics.searchTags', request.term, function(err, tags) {
|
||||
if (err) {
|
||||
return app.alertError(err.message)
|
||||
}
|
||||
if (tags) {
|
||||
response(tags);
|
||||
$('.ui-autocomplete a').attr('href', '#');
|
||||
}
|
||||
});
|
||||
},
|
||||
select: function(event, ui) {
|
||||
// when autocomplete is selected from the dropdown simulate a enter key down to turn it into a tag
|
||||
// http://stackoverflow.com/a/3276819/583363
|
||||
var e = jQuery.Event('keydown');
|
||||
e.which = 13;
|
||||
e.keyCode = 13;
|
||||
setTimeout(function() {
|
||||
input.trigger(e);
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
input.attr('tabIndex', tagEl.attr('tabIndex'));
|
||||
};
|
||||
|
||||
function addTags(tags, tagEl) {
|
||||
if (tags && tags.length) {
|
||||
for(var i=0; i<tags.length; ++i) {
|
||||
tagEl.tagsinput('add', tags[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags.getTags = function(post_uuid) {
|
||||
return $('#cmp-uuid-' + post_uuid + ' .tags').tagsinput('items');
|
||||
};
|
||||
|
||||
return tags;
|
||||
});
|
||||
45
public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css
vendored
Normal file
45
public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
.bootstrap-tagsinput {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
vertical-align: middle;
|
||||
border-radius: 4px;
|
||||
max-width: 100%;
|
||||
line-height: 22px;
|
||||
}
|
||||
.bootstrap-tagsinput input {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: auto !important;
|
||||
max-width: inherit;
|
||||
}
|
||||
.bootstrap-tagsinput input:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.bootstrap-tagsinput .tag {
|
||||
margin-right: 2px;
|
||||
color: white;
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"] {
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"]:after {
|
||||
content: "x";
|
||||
padding: 0px 2px;
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"]:hover {
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
7
public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js
vendored
Normal file
7
public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -10,7 +10,7 @@ var categoriesController = {},
|
||||
topics = require('./../topics');
|
||||
|
||||
categoriesController.recent = function(req, res, next) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
var uid = req.user ? req.user.uid : 0;
|
||||
topics.getLatestTopics(uid, 0, 19, req.params.term, function (err, data) {
|
||||
if(err) {
|
||||
return next(err);
|
||||
@@ -21,7 +21,7 @@ categoriesController.recent = function(req, res, next) {
|
||||
};
|
||||
|
||||
categoriesController.popular = function(req, res, next) {
|
||||
var uid = (req.user) ? req.user.uid : 0;
|
||||
var uid = req.user ? req.user.uid : 0;
|
||||
var set = 'topics:' + req.params.set;
|
||||
if(!req.params.set) {
|
||||
set = 'topics:posts';
|
||||
@@ -37,7 +37,7 @@ categoriesController.popular = function(req, res, next) {
|
||||
};
|
||||
|
||||
categoriesController.unread = function(req, res, next) {
|
||||
var uid = req.user.uid;
|
||||
var uid = req.user ? req.user.uid : 0;
|
||||
|
||||
topics.getUnreadTopics(uid, 0, 20, function (err, data) {
|
||||
if(err) {
|
||||
@@ -49,7 +49,7 @@ categoriesController.unread = function(req, res, next) {
|
||||
};
|
||||
|
||||
categoriesController.unreadTotal = function(req, res, next) {
|
||||
var uid = req.user.uid;
|
||||
var uid = req.user ? req.user.uid : 0;
|
||||
|
||||
topics.getTotalUnread(uid, function (err, data) {
|
||||
if(err) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
var topicsController = require('./topics'),
|
||||
categoriesController = require('./categories'),
|
||||
tagsController = require('./tags'),
|
||||
usersController = require('./users'),
|
||||
accountsController = require('./accounts'),
|
||||
staticController = require('./static'),
|
||||
@@ -24,6 +25,7 @@ var topicsController = require('./topics'),
|
||||
var Controllers = {
|
||||
topics: topicsController,
|
||||
categories: categoriesController,
|
||||
tags: tagsController,
|
||||
users: usersController,
|
||||
accounts: accountsController,
|
||||
static: staticController,
|
||||
|
||||
53
src/controllers/tags.js
Normal file
53
src/controllers/tags.js
Normal file
@@ -0,0 +1,53 @@
|
||||
"use strict";
|
||||
|
||||
var tagsController = {},
|
||||
async = require('async'),
|
||||
topics = require('./../topics');
|
||||
|
||||
tagsController.getTag = function(req, res, next) {
|
||||
var tag = req.params.tag;
|
||||
var uid = req.user ? req.user.uid : 0;
|
||||
|
||||
topics.getTagTids(tag, 0, 19, function(err, tids) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
topics.getTopics('tag:' + tag + ':topics', uid, tids, function(err, data) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
data.tag = tag;
|
||||
res.render('tag', data);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
tagsController.getTags = function(req, res, next) {
|
||||
topics.getTagsObjects(function(err, tags) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
async.map(tags, function(tag, next) {
|
||||
topics.getTagTopicCount(tag.name, function(err, count) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
tag.topicCount = count;
|
||||
next(null, tag);
|
||||
});
|
||||
}, function(err, tags) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
tags = tags.sort(function(a, b) {
|
||||
return parseInt(b.topicCount, 10) - parseInt(a.topicCount, 10);
|
||||
});
|
||||
res.render('tags', {tags: tags});
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
module.exports = tagsController;
|
||||
@@ -235,6 +235,7 @@ var fs = require('fs'),
|
||||
'vendor/jquery/js/jquery.form.min.js',
|
||||
'vendor/jquery/serializeObject/jquery.ba-serializeobject.min.js',
|
||||
'vendor/bootstrap/js/bootstrap.min.js',
|
||||
'vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js',
|
||||
'vendor/requirejs/require.js',
|
||||
'vendor/bootbox/bootbox.min.js',
|
||||
'vendor/tinycon/tinycon.js',
|
||||
@@ -354,6 +355,7 @@ var fs = require('fs'),
|
||||
}
|
||||
|
||||
source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui-1.10.4.custom.min.css";';
|
||||
source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";';
|
||||
|
||||
var parser = new (less.Parser)({
|
||||
paths: paths
|
||||
|
||||
@@ -61,6 +61,8 @@ var winston = require('winston'),
|
||||
|
||||
topics.setTopicField(tid, 'thumb', options.topic_thumb);
|
||||
|
||||
topics.updateTags(tid, options.tags);
|
||||
|
||||
plugins.fireHook('action:topic.edit', tid);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,15 @@ function topicRoutes(app, middleware, controllers) {
|
||||
app.get('/api/topic/:topic_id/:slug?', controllers.topics.get);
|
||||
}
|
||||
|
||||
function tagRoutes(app, middleware, controllers) {
|
||||
|
||||
app.get('/tags/:tag', middleware.buildHeader, controllers.tags.getTag);
|
||||
app.get('/api/tags/:tag', controllers.tags.getTag);
|
||||
|
||||
app.get('/tags', middleware.buildHeader, controllers.tags.getTags);
|
||||
app.get('/api/tags', controllers.tags.getTags);
|
||||
}
|
||||
|
||||
function categoryRoutes(app, middleware, controllers) {
|
||||
app.get('/popular/:set?', middleware.buildHeader, controllers.categories.popular);
|
||||
app.get('/api/popular/:set?', controllers.categories.popular);
|
||||
@@ -152,6 +161,7 @@ module.exports = function(app, middleware) {
|
||||
mainRoutes(app, middleware, controllers);
|
||||
staticRoutes(app, middleware, controllers);
|
||||
topicRoutes(app, middleware, controllers);
|
||||
tagRoutes(app, middleware, controllers);
|
||||
categoryRoutes(app, middleware, controllers);
|
||||
accountRoutes(app, middleware, controllers);
|
||||
userRoutes(app, middleware, controllers);
|
||||
|
||||
@@ -49,7 +49,7 @@ var stopTracking = function(replyObj) {
|
||||
};
|
||||
|
||||
SocketModules.composer.push = function(socket, pid, callback) {
|
||||
posts.getPostFields(pid, ['content'], function(err, postData) {
|
||||
posts.getPostFields(pid, ['content', 'tid'], function(err, postData) {
|
||||
if(err || (!postData && !postData.content)) {
|
||||
return callback(err || new Error('[[error:invalid-pid]]'));
|
||||
}
|
||||
@@ -58,6 +58,9 @@ SocketModules.composer.push = function(socket, pid, callback) {
|
||||
topic: function(next) {
|
||||
topics.getTopicDataByPid(pid, next);
|
||||
},
|
||||
tags: function(next) {
|
||||
topics.getTopicTags(postData.tid, next);
|
||||
},
|
||||
index: function(next) {
|
||||
posts.getPidIndex(pid, next);
|
||||
}
|
||||
@@ -71,6 +74,7 @@ SocketModules.composer.push = function(socket, pid, callback) {
|
||||
body: postData.content,
|
||||
title: results.topic.title,
|
||||
topic_thumb: results.topic.thumb,
|
||||
tags: results.tags,
|
||||
index: results.index
|
||||
});
|
||||
});
|
||||
|
||||
@@ -161,7 +161,7 @@ SocketPosts.edit = function(socket, data, callback) {
|
||||
return callback(new Error('[[error:content-too-short, ' + meta.config.minimumPostLength + ']]'));
|
||||
}
|
||||
|
||||
postTools.edit(socket.uid, data.pid, data.title, data.content, {topic_thumb: data.topic_thumb}, function(err, results) {
|
||||
postTools.edit(socket.uid, data.pid, data.title, data.content, {topic_thumb: data.topic_thumb, tags: data.tags}, function(err, results) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ SocketTopics.post = function(socket, data, callback) {
|
||||
content: data.content,
|
||||
cid: data.category_id,
|
||||
thumb: data.topic_thumb,
|
||||
tags: data.tags,
|
||||
req: websockets.reqFromSocket(socket)
|
||||
}, function(err, result) {
|
||||
if(err) {
|
||||
@@ -376,4 +377,8 @@ SocketTopics.getTidIndex = function(socket, tid, callback) {
|
||||
categories.getTopicIndex(tid, callback);
|
||||
};
|
||||
|
||||
SocketTopics.searchTags = function(socket, query, callback) {
|
||||
topics.searchTags(query, callback);
|
||||
};
|
||||
|
||||
module.exports = SocketTopics;
|
||||
|
||||
@@ -20,6 +20,7 @@ var async = require('async'),
|
||||
require('./topics/fork')(Topics);
|
||||
require('./topics/posts')(Topics);
|
||||
require('./topics/follow')(Topics);
|
||||
require('./topics/tags')(Topics);
|
||||
|
||||
Topics.getTopicData = function(tid, callback) {
|
||||
Topics.getTopicsData([tid], function(err, topics) {
|
||||
@@ -275,6 +276,9 @@ var async = require('async'),
|
||||
},
|
||||
threadTools: function(next) {
|
||||
plugins.fireHook('filter:topic.thread_tools', [], next);
|
||||
},
|
||||
tags: function(next) {
|
||||
Topics.getTopicTagsObjects(tid, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
@@ -283,6 +287,7 @@ var async = require('async'),
|
||||
|
||||
topicData.category = results.category;
|
||||
topicData.posts = results.posts;
|
||||
topicData.tags = results.tags;
|
||||
topicData.thread_tools = results.threadTools;
|
||||
topicData.pageCount = results.pageCount;
|
||||
topicData.unreplied = parseInt(topicData.postcount, 10) === 1;
|
||||
|
||||
@@ -60,6 +60,8 @@ module.exports = function(Topics) {
|
||||
db.incrObjectField('category:' + cid, 'topic_count');
|
||||
db.incrObjectField('global', 'topicCount');
|
||||
|
||||
Topics.createTags(data.tags, tid, timestamp);
|
||||
|
||||
callback(null, tid);
|
||||
});
|
||||
});
|
||||
@@ -111,7 +113,7 @@ module.exports = function(Topics) {
|
||||
user.isReadyToPost(uid, next);
|
||||
},
|
||||
function(next) {
|
||||
Topics.create({uid: uid, title: title, cid: cid, thumb: data.thumb}, next);
|
||||
Topics.create({uid: uid, title: title, cid: cid, thumb: data.thumb, tags: data.tags}, next);
|
||||
},
|
||||
function(tid, next) {
|
||||
Topics.reply({uid:uid, tid:tid, content:content, req: data.req}, next);
|
||||
|
||||
115
src/topics/tags.js
Normal file
115
src/topics/tags.js
Normal file
@@ -0,0 +1,115 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var async = require('async'),
|
||||
db = require('../database');
|
||||
|
||||
module.exports = function(Topics) {
|
||||
|
||||
Topics.createTags = function(tags, tid, timestamp) {
|
||||
if(Array.isArray(tags)) {
|
||||
for (var i=0; i<tags.length; ++i) {
|
||||
tags[i] = tags[i].trim().toLowerCase();
|
||||
db.sortedSetAdd('tag:' + tags[i] + ':topics', timestamp, tid);
|
||||
db.setAdd('topic:' + tid + ':tags', tags[i]);
|
||||
db.setAdd('tags', tags[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Topics.getTagTids = function(tag, start, end, callback) {
|
||||
db.getSortedSetRevRange('tag:' + tag + ':topics', start, end, callback);
|
||||
};
|
||||
|
||||
Topics.getTagTopicCount = function(tag, callback) {
|
||||
db.sortedSetCard('tag:' + tag + ':topics', callback);
|
||||
};
|
||||
|
||||
Topics.getTags = function(callback) {
|
||||
db.getSetMembers('tags', callback);
|
||||
};
|
||||
|
||||
//returns tags as objects cuz templates.js cant do arrays yet >_>
|
||||
Topics.getTagsObjects = function(callback) {
|
||||
Topics.getTags(function(err, tags) {
|
||||
callback(err, mapToObject(tags));
|
||||
});
|
||||
};
|
||||
|
||||
Topics.getTopicTags = function(tid, callback) {
|
||||
db.getSetMembers('topic:' + tid + ':tags', callback);
|
||||
};
|
||||
|
||||
//returns tags as objects cuz templates.js cant do arrays yet >_>
|
||||
Topics.getTopicTagsObjects = function(tid, callback) {
|
||||
Topics.getTopicTags(tid, function(err, tags) {
|
||||
callback(err, mapToObject(tags));
|
||||
});
|
||||
};
|
||||
|
||||
function mapToObject(tags) {
|
||||
if (!tags) {
|
||||
return tags;
|
||||
}
|
||||
return tags.map(function(tag) {
|
||||
return {name: tag};
|
||||
});
|
||||
}
|
||||
|
||||
Topics.updateTags = function(tid, tags) {
|
||||
async.parallel({
|
||||
timestamp: function(next) {
|
||||
Topics.getTopicField(tid, 'timestamp', next);
|
||||
},
|
||||
currentTags: function(next) {
|
||||
Topics.getTopicTags(tid, next);
|
||||
}
|
||||
}, function(err, results) {
|
||||
removeTopicTags(tid, results.currentTags, function(err) {
|
||||
if (!err) {
|
||||
Topics.createTags(tags, tid, results.timestamp);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function removeTopicTags(tid, tags, callback) {
|
||||
async.parallel([
|
||||
function(next) {
|
||||
db.delete('topic:' + tid + ':tags', next);
|
||||
},
|
||||
function(next) {
|
||||
async.each(tags, function(tag, next) {
|
||||
db.sortedSetRemove('tag:' + tag + ':topics', tid, next);
|
||||
}, next);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
Topics.searchTags = function(query, callback) {
|
||||
if (!query || query.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
db.getSetMembers('tags', function(err, tags) {
|
||||
if (err) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
query = query.toLowerCase();
|
||||
var matches = [];
|
||||
for(var i=0; i<tags.length; ++i) {
|
||||
if (tags[i].toLowerCase().indexOf(query) === 0) {
|
||||
matches.push(tags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
matches = matches.slice(0, 10).sort(function(a, b) {
|
||||
return a > b;
|
||||
});
|
||||
|
||||
callback(null, matches);
|
||||
});
|
||||
};
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user