From d411ed5c28490f6d24372e2c5d0d04f711a19ec5 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sun, 17 Aug 2014 19:26:24 -0400 Subject: [PATCH] closes #1995 --- public/src/forum/admin/tags.js | 60 +++++++++++++++++++++++++++++ src/controllers/admin.js | 12 +++++- src/routes/admin.js | 3 ++ src/socket.io/admin.js | 1 + src/socket.io/admin/tags.js | 15 ++++++++ src/topics/tags.js | 70 +++++++++++++++++++++++++--------- 6 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 public/src/forum/admin/tags.js create mode 100644 src/socket.io/admin/tags.js diff --git a/public/src/forum/admin/tags.js b/public/src/forum/admin/tags.js new file mode 100644 index 0000000000..e6468c7f21 --- /dev/null +++ b/public/src/forum/admin/tags.js @@ -0,0 +1,60 @@ +"use strict"; +/*global define, socket, app, admin*/ + +define('forum/admin/tags', [], function() { + var Tags = {}; + + Tags.init = function() { + handleColorPickers(); + + $('.tag-list').on('click', '.save', function() { + save($(this)); + }); + + $('#tag-search').on('input propertychange', function() { + $('.tag-list').children().each(function() { + var $this = $(this); + $this.toggleClass('hide', $this.attr('data-tag').indexOf($('#tag-search').val()) === -1); + }); + }); + }; + + function handleColorPickers() { + function enableColorPicker(idx, inputEl) { + var $inputEl = $(inputEl), + previewEl = $inputEl.parents('.tag-row').find('.tag-item'); + + admin.enableColorPicker($inputEl, function(hsb, hex) { + if ($inputEl.attr('data-name') === 'bgColor') { + previewEl.css('background-color', '#' + hex); + } else if ($inputEl.attr('data-name') === 'color') { + previewEl.css('color', '#' + hex); + } + }); + } + + + $('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); + } + + function save(saveBtn) { + var tagRow = saveBtn.parents('.tag-row'); + + var data = { + tag: tagRow.attr('data-tag'), + bgColor : tagRow.find('[data-name="bgColor"]').val(), + color : tagRow.find('[data-name="color"]').val() + }; + + socket.emit('admin.tags.update', data, function(err) { + if (err) { + return app.alertError(err.message); + } + app.alertSuccess('Tag Updated!'); + }); + } + + + + return Tags; +}); \ No newline at end of file diff --git a/src/controllers/admin.js b/src/controllers/admin.js index cb81fab3d3..afbadadcc8 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -16,9 +16,9 @@ var async = require('async'), validator = require('validator'); - var adminController = { categories: {}, + tags: {}, topics: {}, groups: {}, themes: {}, @@ -126,6 +126,16 @@ function filterAndRenderCategories(req, res, next, active) { }); } +adminController.tags.get = function(req, res, next) { + topics.getTags(0, -1, function(err, tags) { + if (err) { + return next(err); + } + + res.render('admin/tags', {tags: tags}); + }); +}; + adminController.database.get = function(req, res, next) { db.info(function (err, data) { res.render('admin/database', data); diff --git a/src/routes/admin.js b/src/routes/admin.js index 8b6b06468c..77dc3cc31a 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -48,6 +48,9 @@ function forumRoutes(app, middleware, controllers) { app.get('/admin/categories/disabled', middleware.admin.buildHeader, controllers.admin.categories.disabled); app.get('/api/admin/categories/disabled', controllers.admin.categories.disabled); + + app.get('/admin/tags', middleware.admin.buildHeader, controllers.admin.tags.get); + app.get('/api/admin/tags', controllers.admin.tags.get); } function apiRoutes(app, middleware, controllers) { diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 44bb228ade..bc8140a2a1 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -19,6 +19,7 @@ var groups = require('../groups'), user: require('./admin/user'), categories: require('./admin/categories'), groups: require('./admin/groups'), + tags: require('./admin/tags'), themes: {}, plugins: {}, widgets: {}, diff --git a/src/socket.io/admin/tags.js b/src/socket.io/admin/tags.js new file mode 100644 index 0000000000..08ceca1a9f --- /dev/null +++ b/src/socket.io/admin/tags.js @@ -0,0 +1,15 @@ +"use strict"; + +var topics = require('../../topics'), + Tags = {}; + +Tags.update = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + + topics.updateTag(data.tag, data, callback); +}; + + +module.exports = Tags; \ No newline at end of file diff --git a/src/topics/tags.js b/src/topics/tags.js index 9feb917677..861a8974a6 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -57,6 +57,10 @@ module.exports = function(Topics) { return tag; }; + Topics.updateTag = function(tag, data, callback) { + db.setObject('tag:' + tag, data, callback); + }; + function updateTagCount(tag, callback) { callback = callback || function() {}; Topics.getTagTopicCount(tag, function(err, count) { @@ -80,9 +84,33 @@ module.exports = function(Topics) { }; Topics.getTags = function(start, end, callback) { - db.getSortedSetRevRangeWithScores('tags:topic:count', start, end, callback); + db.getSortedSetRevRangeWithScores('tags:topic:count', start, end, function(err, tags) { + if (err) { + return callback(err); + } + + addTagData(tags, callback); + }); }; + function addTagData(tags, callback) { + var keys = tags.map(function(tag) { + return 'tag:' + tag.value; + }); + + db.getObjects(keys, function(err, tagData) { + if (err) { + return callback(err); + } + + tags.forEach(function(tag, index) { + tag.color = tagData[index] ? tagData[index].color : ''; + tag.bgColor = tagData[index] ? tagData[index].bgColor : ''; + }); + callback(null, tags); + }); + } + Topics.getTopicTags = function(tid, callback) { db.getSetMembers('topic:' + tid + ':tags', callback); }; @@ -98,40 +126,46 @@ module.exports = function(Topics) { return 'topic:' + tid + ':tags'; }); - db.getSetsMembers(sets, function(err, members) { + db.getSetsMembers(sets, function(err, topicTags) { if (err) { return callback(err); } - var uniqueTags = _.uniq(_.flatten(members)); + var uniqueTopicTags = _.uniq(_.flatten(topicTags)); + + var tags = uniqueTopicTags.map(function(tag) { + return {value: tag}; + }); - db.sortedSetScores('tags:topic:count', uniqueTags, function(err, data) { + async.parallel({ + tagData: function(next) { + addTagData(tags, next); + }, + counts: function(next) { + db.sortedSetScores('tags:topic:count', uniqueTopicTags, next); + } + }, function(err, results) { if (err) { return callback(err); } - var tagCounts = _.object(uniqueTags, data); + results.tagData.forEach(function(tag, index) { + tag.score = results.counts[index] ? results.counts[index] : 0; + }); + + var tagData = _.object(uniqueTopicTags, results.tagData); - members.forEach(function(tags, index) { + topicTags.forEach(function(tags, index) { if (Array.isArray(tags)) { - members[index] = mapToObject(tags, tagCounts); + topicTags[index] = tags.map(function(tag) {return tagData[tag];}); } }); - callback(null, members); + + callback(null, topicTags); }); }); }; - function mapToObject(tags, tagCounts) { - if (!tags) { - return tags; - } - - return tags.map(function(tag) { - return {name: tag, score: tagCounts ? tagCounts[tag] : 0}; - }); - } - Topics.updateTags = function(tid, tags, callback) { callback = callback || function() {}; Topics.getTopicField(tid, 'timestamp', function(err, timestamp) {