feat: ability to nickname remote categories, closes #13677

This commit is contained in:
Julian Lam
2025-09-24 11:25:20 -04:00
parent 175dc20906
commit bd80b77a7a
6 changed files with 58 additions and 7 deletions

View File

@@ -4,6 +4,7 @@
"add-local-category": "Add Local category",
"add-remote-category": "Add Remote category",
"remove": "Remove",
"rename": "Rename",
"jump-to": "Jump to...",
"settings": "Category Settings",
"edit-category": "Edit Category",
@@ -111,6 +112,9 @@
"alert.create": "Create a Category",
"alert.add": "Add a Category",
"alert.add-help": "Remote categories can be added to the categories listing by specifying their handle.<br /><br /><strong>Note</strong> — The remote category may not reflect all topics published unless at least one local user tracks/watches it.",
"alert.rename": "Rename a Remote Category",
"alert.rename-help": "Please enter the a new name for this category. Leave blank to restore original name.",
"alert.confirm-remove": "Do you really want to remove this category? You can add it back at any time.",
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
"alert.purge-success": "Category purged!",
"alert.copy-success": "Settings Copied!",

View File

@@ -81,7 +81,21 @@ define('admin/manage/categories', [
});
});
$('.categories').on('click', 'a[data-action="remove"]', Categories.removeCategory);
$('.categories').on('click', 'a[data-action]', function () {
const action = this.getAttribute('data-action');
switch (action) {
case 'remove': {
Categories.remove.call(this);
break;
}
case 'rename': {
Categories.rename.call(this);
break;
}
}
});
$('#toggle-collapse-all').on('click', function () {
const $this = $(this);
@@ -181,9 +195,24 @@ define('admin/manage/categories', [
});
};
Categories.removeCategory = function () {
const cid = this.getAttribute('data-cid');
api.del(`/api/admin/manage/categories/${encodeURIComponent(cid)}`).then(ajaxify.refresh);
Categories.remove = function () {
bootbox.confirm('[[admin/manage/categories:alert.confirm-remove]]', (ok) => {
if (ok) {
const cid = this.getAttribute('data-cid');
api.del(`/api/admin/manage/categories/${encodeURIComponent(cid)}`).then(ajaxify.refresh);
}
});
};
Categories.rename = function () {
bootbox.prompt({
title: '[[admin/manage/categories:alert.rename]]',
message: '<p class="mb-3">[[admin/manage/categories:alert.rename-help]]</p>',
callback: (name) => {
const cid = this.getAttribute('data-cid');
api.post(`/api/admin/manage/categories/${encodeURIComponent(cid)}/name`, { name }).then(ajaxify.refresh);
},
});
};
Categories.create = function (payload) {

View File

@@ -117,7 +117,7 @@ function modifyCategory(category, fields) {
db.parseIntFields(category, intFields, fields);
const escapeFields = ['name', 'description', 'federatedDescription', 'color', 'bgColor', 'backgroundImage', 'imageClass', 'class', 'link'];
const escapeFields = ['name', 'nickname', 'description', 'federatedDescription', 'color', 'bgColor', 'backgroundImage', 'imageClass', 'class', 'link'];
escapeFields.forEach((field) => {
if (category.hasOwnProperty(field)) {
category[field] = validator.escape(String(category[field] || ''));
@@ -139,4 +139,8 @@ function modifyCategory(category, fields) {
if (category.description) {
category.descriptionParsed = category.descriptionParsed || category.description;
}
if (category.nickname) {
category.name = category.nickname;
}
}

View File

@@ -65,9 +65,9 @@ categoriesController.getAll = async function (req, res) {
}
const fields = [
'cid', 'name', 'icon', 'parentCid', 'disabled', 'link',
'cid', 'name', 'nickname', 'icon', 'parentCid', 'disabled', 'link',
'order', 'color', 'bgColor', 'backgroundImage', 'imageClass',
'subCategoriesPerPage', 'description',
'subCategoriesPerPage', 'description', 'descriptionParsed',
];
let categoriesData = await categories.getCategoriesFields(cids, fields);
({ categories: categoriesData } = await plugins.hooks.fire('filter:admin.categories.get', { categories: categoriesData, fields: fields }));
@@ -213,6 +213,17 @@ categoriesController.addRemote = async function (req, res) {
res.sendStatus(200);
};
categoriesController.renameRemote = async (req, res) => {
if (utils.isNumber(req.params.cid)) {
return helpers.formatApiResponse(400, res);
}
const { name } = req.body;
await categories.setCategoryField(req.params.cid, 'nickname', name);
res.sendStatus(200);
};
categoriesController.removeRemote = async function (req, res) {
if (utils.isNumber(req.params.cid)) {
return helpers.formatApiResponse(400, res);

View File

@@ -82,6 +82,7 @@ function apiRoutes(router, name, middleware, controllers) {
router.get(`/api/${name}/analytics`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.dashboard.getAnalytics));
router.get(`/api/${name}/advanced/cache/dump`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.cache.dump));
router.post(`/api/${name}/manage/categories`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.categories.addRemote));
router.post(`/api/${name}/manage/categories/:cid/name`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.categories.renameRemote));
router.delete(`/api/${name}/manage/categories/:cid`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.categories.removeRemote));
const multer = require('multer');

View File

@@ -43,6 +43,8 @@
<li><a class="dropdown-item rounded-1" href="./categories/{categories.cid}/analytics" role="menuitem">[[admin/manage/categories:analytics]]</a></li>
<li><a class="dropdown-item rounded-1" href="{config.relative_path}/admin/manage/privileges/{categories.cid}" role="menuitem">[[admin/manage/categories:privileges]]</a></li>
<li><a class="dropdown-item rounded-1" href="./categories/{categories.cid}/federation" role="menuitem">[[admin/manage/categories:federation]]</a></li>
{{{ else }}}
<li><a class="dropdown-item rounded-1" href="#" data-cid="{./cid}" data-action="rename" role="menuitem">[[admin/manage/categories:rename]]</a></li>
{{{ end }}}
<li><a href="#" class="set-order dropdown-item rounded-1" data-cid="{categories.cid}" data-order="{categories.order}" role="menuitem">[[admin/manage/categories:set-order]]</a></li>
<li class="dropdown-divider"></li>