mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-20 23:40:38 +01:00
closes #6216
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
"description": "Select tags via clicking and/or dragging, use shift to select multiple.",
|
||||
"create": "Create Tag",
|
||||
"modify": "Modify Tags",
|
||||
"rename": "Rename Tags",
|
||||
"delete": "Delete Selected Tags",
|
||||
"search": "Search for tags...",
|
||||
"settings": "Click <a href=\"%1\">here</a> to visit the tag settings page.",
|
||||
|
||||
@@ -15,6 +15,7 @@ define('admin/manage/tags', [
|
||||
handleCreate();
|
||||
handleSearch();
|
||||
handleModify();
|
||||
handleRename();
|
||||
handleDeleteSelected();
|
||||
};
|
||||
|
||||
@@ -103,15 +104,25 @@ define('admin/manage/tags', [
|
||||
var modal = $('.bootbox');
|
||||
var bgColor = modal.find('[data-name="bgColor"]').val();
|
||||
var color = modal.find('[data-name="color"]').val();
|
||||
|
||||
var data = [];
|
||||
tagsToModify.each(function (idx, tag) {
|
||||
tag = $(tag);
|
||||
data.push({
|
||||
value: tag.attr('data-tag'),
|
||||
color: modal.find('[data-name="color"]').val(),
|
||||
bgColor: modal.find('[data-name="bgColor"]').val(),
|
||||
});
|
||||
|
||||
tag.find('[data-name="bgColor"]').val(bgColor);
|
||||
tag.find('[data-name="color"]').val(color);
|
||||
tag.find('.tag-item').css('background-color', bgColor).css('color', color);
|
||||
});
|
||||
|
||||
save(tag);
|
||||
socket.emit('admin.tags.update', data, function (err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
app.alertSuccess('[[admin/manage/tags:alerts.update-success]]');
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -122,6 +133,46 @@ define('admin/manage/tags', [
|
||||
});
|
||||
}
|
||||
|
||||
function handleRename() {
|
||||
$('#rename').on('click', function () {
|
||||
var tagsToModify = $('.tag-row.ui-selected');
|
||||
if (!tagsToModify.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var firstTag = $(tagsToModify[0]);
|
||||
var title = tagsToModify.length > 1 ? '[[admin/manage/tags:alerts.editing-multiple]]' : '[[admin/manage/tags:alerts.editing-x, ' + firstTag.find('.tag-item').attr('data-tag') + ']]';
|
||||
|
||||
var modal = bootbox.dialog({
|
||||
title: title,
|
||||
message: $('.rename-modal').html(),
|
||||
buttons: {
|
||||
success: {
|
||||
label: 'Save',
|
||||
className: 'btn-primary save',
|
||||
callback: function () {
|
||||
var data = [];
|
||||
tagsToModify.each(function (idx, tag) {
|
||||
tag = $(tag);
|
||||
data.push({
|
||||
value: tag.attr('data-tag'),
|
||||
newName: modal.find('[data-name="value"]').val(),
|
||||
});
|
||||
});
|
||||
|
||||
socket.emit('admin.tags.rename', data, function (err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
app.alertSuccess('[[admin/manage/tags:alerts.update-success]]');
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleDeleteSelected() {
|
||||
$('#deleteSelected').on('click', function () {
|
||||
var tagsToDelete = $('.tag-row.ui-selected');
|
||||
@@ -158,21 +209,5 @@ define('admin/manage/tags', [
|
||||
modal.find('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker);
|
||||
}
|
||||
|
||||
function save(tag) {
|
||||
var data = {
|
||||
tag: tag.attr('data-tag'),
|
||||
bgColor: tag.find('[data-name="bgColor"]').val(),
|
||||
color: tag.find('[data-name="color"]').val(),
|
||||
};
|
||||
|
||||
socket.emit('admin.tags.update', data, function (err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
app.alertSuccess('[[admin/manage/tags:alerts.update-success]]');
|
||||
});
|
||||
}
|
||||
|
||||
return Tags;
|
||||
});
|
||||
|
||||
@@ -140,7 +140,7 @@ module.exports = function (Posts) {
|
||||
db.setObject('topic:' + tid, results.topic, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.updateTags(tid, data.tags, next);
|
||||
topics.updateTopicTags(tid, data.tags, next);
|
||||
},
|
||||
function (next) {
|
||||
topics.getTopicTagsObjects(tid, next);
|
||||
|
||||
@@ -13,11 +13,19 @@ Tags.create = function (socket, data, callback) {
|
||||
};
|
||||
|
||||
Tags.update = function (socket, data, callback) {
|
||||
if (!data) {
|
||||
if (!Array.isArray(data)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
topics.updateTag(data.tag, data, callback);
|
||||
topics.updateTags(data, callback);
|
||||
};
|
||||
|
||||
Tags.rename = function (socket, data, callback) {
|
||||
if (!Array.isArray(data)) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
topics.renameTags(data, callback);
|
||||
};
|
||||
|
||||
Tags.deleteTags = function (socket, data, callback) {
|
||||
|
||||
@@ -9,7 +9,7 @@ var meta = require('../meta');
|
||||
var _ = require('lodash');
|
||||
var plugins = require('../plugins');
|
||||
var utils = require('../utils');
|
||||
|
||||
var batch = require('../batch');
|
||||
|
||||
module.exports = function (Topics) {
|
||||
Topics.createTags = function (tags, tid, timestamp, callback) {
|
||||
@@ -96,13 +96,61 @@ module.exports = function (Topics) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
Topics.updateTag = function (tag, data, callback) {
|
||||
if (!tag) {
|
||||
return setImmediate(callback, new Error('[[error:invalid-tag]]'));
|
||||
}
|
||||
db.setObject('tag:' + tag, data, callback);
|
||||
Topics.updateTags = function (data, callback) {
|
||||
async.eachSeries(data, function (tagData, next) {
|
||||
db.setObject('tag:' + tagData.value, {
|
||||
color: tagData.color,
|
||||
bgColor: tagData.bgColor,
|
||||
}, next);
|
||||
}, callback);
|
||||
};
|
||||
|
||||
Topics.renameTags = function (data, callback) {
|
||||
async.eachSeries(data, function (tagData, next) {
|
||||
renameTag(tagData.value, tagData.newName, next);
|
||||
}, callback);
|
||||
};
|
||||
|
||||
function renameTag(tag, newTagName, callback) {
|
||||
if (!newTagName || tag === newTagName) {
|
||||
return setImmediate(callback);
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.createEmptyTag(newTagName, next);
|
||||
},
|
||||
function (next) {
|
||||
batch.processSortedSet('tag:' + tag + ':topics', function (tids, next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.sortedSetScores('tag:' + tag + ':topics', tids, next);
|
||||
},
|
||||
function (scores, next) {
|
||||
db.sortedSetAdd('tag:' + newTagName + ':topics', scores, tids, next);
|
||||
},
|
||||
function (next) {
|
||||
var keys = tids.map(function (tid) {
|
||||
return 'topic:' + tid + ':tags';
|
||||
});
|
||||
|
||||
async.series([
|
||||
async.apply(db.sortedSetRemove, 'tag:' + tag + ':topics', tids),
|
||||
async.apply(db.setsRemove, keys, tag),
|
||||
async.apply(db.setsAdd, keys, newTagName),
|
||||
], next);
|
||||
},
|
||||
], next);
|
||||
}, next);
|
||||
},
|
||||
function (next) {
|
||||
Topics.deleteTag(tag, next);
|
||||
},
|
||||
function (next) {
|
||||
updateTagCount(newTagName, next);
|
||||
},
|
||||
], callback);
|
||||
}
|
||||
|
||||
function updateTagCount(tag, callback) {
|
||||
callback = callback || function () {};
|
||||
async.waterfall([
|
||||
@@ -148,7 +196,9 @@ module.exports = function (Topics) {
|
||||
return 'tag:' + tag;
|
||||
}), next);
|
||||
},
|
||||
], callback);
|
||||
], function (err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
function removeTagsFromTopics(tags, callback) {
|
||||
@@ -266,7 +316,7 @@ module.exports = function (Topics) {
|
||||
], callback);
|
||||
};
|
||||
|
||||
Topics.updateTags = function (tid, tags, callback) {
|
||||
Topics.updateTopicTags = function (tid, tags, callback) {
|
||||
callback = callback || function () {};
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<p>[[admin/manage/tags:description]]</p>
|
||||
<button class="btn btn-primary btn-block" id="create">[[admin/manage/tags:create]]</button>
|
||||
<button class="btn btn-primary btn-block" id="modify">[[admin/manage/tags:modify]]</button>
|
||||
<button class="btn btn-primary btn-block" id="rename">[[admin/manage/tags:rename]]</button>
|
||||
<button class="btn btn-warning btn-block" id="deleteSelected">[[admin/manage/tags:delete]]</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,4 +75,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rename-modal hidden">
|
||||
<div class="form-group">
|
||||
<label for="value">[[admin/manage/tags:name]]</label>
|
||||
<input id="value" data-name="value" value="{tags.value}" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1350,22 +1350,22 @@ describe('Topic\'s', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if data.tag is invalid', function (done) {
|
||||
it('should error if data is not an array', function (done) {
|
||||
socketAdmin.tags.update({ uid: adminUid }, {
|
||||
bgColor: '#ff0000',
|
||||
color: '#00ff00',
|
||||
}, function (err) {
|
||||
assert.equal(err.message, '[[error:invalid-tag]]');
|
||||
assert.equal(err.message, '[[error:invalid-data]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update tag', function (done) {
|
||||
socketAdmin.tags.update({ uid: adminUid }, {
|
||||
tag: 'emptytag',
|
||||
socketAdmin.tags.update({ uid: adminUid }, [{
|
||||
value: 'emptytag',
|
||||
bgColor: '#ff0000',
|
||||
color: '#00ff00',
|
||||
}, function (err) {
|
||||
}], function (err) {
|
||||
assert.ifError(err);
|
||||
db.getObject('tag:emptytag', function (err, data) {
|
||||
assert.ifError(err);
|
||||
@@ -1376,6 +1376,35 @@ describe('Topic\'s', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should rename tags', function (done) {
|
||||
async.parallel({
|
||||
topic1: function (next) {
|
||||
topics.post({ uid: adminUid, tags: ['plugins'], title: 'topic tagged with plugins', content: 'topic 1 content', cid: topic.categoryId }, next);
|
||||
},
|
||||
topic2: function (next) {
|
||||
topics.post({ uid: adminUid, tags: ['plugin'], title: 'topic tagged with plugin', content: 'topic 2 content', cid: topic.categoryId }, next);
|
||||
},
|
||||
}, function (err, result) {
|
||||
assert.ifError(err);
|
||||
socketAdmin.tags.rename({ uid: adminUid }, [{
|
||||
value: 'plugin',
|
||||
newName: 'plugins',
|
||||
}], function (err) {
|
||||
assert.ifError(err);
|
||||
topics.getTagTids('plugins', 0, -1, function (err, tids) {
|
||||
assert.ifError(err);
|
||||
assert.equal(tids.length, 2);
|
||||
topics.getTopicTags(result.topic2.topicData.tid, function (err, tags) {
|
||||
assert.ifError(err);
|
||||
assert.equal(tags.length, 1);
|
||||
assert.equal(tags[0], 'plugins');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return related topics', function (done) {
|
||||
var meta = require('../src/meta');
|
||||
meta.config.maximumRelatedTopics = 2;
|
||||
|
||||
Reference in New Issue
Block a user