fix: ordering nested categories

if a category is nested beyond one level, the cache for the categories needs to be cleared all the way to the root.
This commit is contained in:
Barış Soner Uşaklı
2026-03-01 11:46:47 -05:00
parent 643991ab03
commit a1b77fa033
5 changed files with 28 additions and 31 deletions

View File

@@ -79,7 +79,11 @@ async function xhr(options) {
if (!res.ok) {
if (response) {
throw new Error(isJSON ? response.status.message : response);
const jsonError = isJSON && (response.status?.message || response.error || '');
throw new Error(isJSON && jsonError ?
jsonError :
response
);
}
throw new Error(res.statusText);
}

View File

@@ -101,7 +101,7 @@ module.exports = function (Categories) {
await privileges.categories.give(result.guestPrivileges, category.cid, ['guests', 'spiders']);
cache.del('categories:cid');
await clearParentCategoryCache(parentCid);
await Categories.clearParentCategoryCache(parentCid);
if (data.cloneFromCid && parseInt(data.cloneFromCid, 10)) {
category = await Categories.copySettingsFrom(data.cloneFromCid, category.cid, !data.parentCid);
@@ -115,8 +115,8 @@ module.exports = function (Categories) {
return category;
};
async function clearParentCategoryCache(parentCid) {
while (parseInt(parentCid, 10) >= 0) {
Categories.clearParentCategoryCache = async function (parentCid) {
while (parentCid || parseInt(parentCid, 10) === 0) {
cache.del([
`cid:${parentCid}:children`,
`cid:${parentCid}:children:all`,
@@ -129,7 +129,7 @@ module.exports = function (Categories) {
// eslint-disable-next-line no-await-in-loop
parentCid = await Categories.getCategoryField(parentCid, 'parentCid');
}
}
};
async function duplicateCategoriesChildren(parentCid, cid, uid) {
let children = await Categories.getChildren([cid], uid);
@@ -188,17 +188,13 @@ module.exports = function (Categories) {
throw new Error('[[error:invalid-cid]]');
}
const oldParent = parseInt(destination.parentCid, 10) || 0;
const newParent = parseInt(source.parentCid, 10) || 0;
if (copyParent && newParent !== parseInt(toCid, 10)) {
const oldParent = String(destination.parentCid || 0);
const newParent = String(source.parentCid || 0);
if (copyParent && newParent !== String(toCid)) {
await db.sortedSetRemove(`cid:${oldParent}:children`, toCid);
await db.sortedSetAdd(`cid:${newParent}:children`, source.order, toCid);
cache.del([
`cid:${oldParent}:children`,
`cid:${oldParent}:children:all`,
`cid:${newParent}:children`,
`cid:${newParent}:children:all`,
]);
await Categories.clearParentCategoryCache(oldParent);
await Categories.clearParentCategoryCache(newParent);
}
destination.description = source.description;

View File

@@ -79,12 +79,13 @@ module.exports = function (Categories) {
cache.del([
'categories:cid',
'cid:0:children',
`cid:${parentCid}:children`,
`cid:${parentCid}:children:all`,
`cid:${cid}:children`,
`cid:${cid}:children:all`,
'cid:0:children:all',
`cid:${cid}:tag:whitelist`,
]);
await Promise.all([
Categories.clearParentCategoryCache(parentCid),
Categories.clearParentCategoryCache(cid),
]);
}
async function deleteTags(cid) {

View File

@@ -85,12 +85,9 @@ module.exports = function (Categories) {
db.sortedSetAdd(`cid:${newParent}:children`, categoryData.order, cid),
db.setObjectField(`${utils.isNumber(cid) ? 'category' : 'categoryRemote'}:${cid}`, 'parentCid', newParent),
]);
cache.del([
`cid:${oldParent}:children`,
`cid:${newParent}:children`,
`cid:${oldParent}:children:all`,
`cid:${newParent}:children:all`,
await Promise.all([
Categories.clearParentCategoryCache(oldParent),
Categories.clearParentCategoryCache(newParent),
]);
}
@@ -134,11 +131,9 @@ module.exports = function (Categories) {
await db.setObjectBulk(
childrenCids.map((cid, index) => [`${utils.isNumber(cid) ? 'category' : 'categoryRemote'}:${cid}`, { order: index + 1 }])
);
await Categories.clearParentCategoryCache(parentCid);
cache.del([
'categories:cid',
`cid:${parentCid}:children`,
`cid:${parentCid}:children:all`,
]);
}

View File

@@ -202,13 +202,14 @@ categoriesController.addRemote = async function (req, res) {
return res.sendStatus(404);
}
const score = await db.sortedSetCard('cid:0:children');
const order = score + 1; // order is 1-based lol
const lastItem = await db.getSortedSetRevRangeWithScores('cid:0:children', 0, 0);
const order = lastItem.length ? lastItem[0].score + 1 : 1;
await Promise.all([
db.sortedSetAdd('cid:0:children', order, id),
categories.setCategoryField(id, 'order', order),
]);
cache.del('cid:0:children');
cache.del('cid:0:children:all');
res.sendStatus(200);
};
@@ -231,7 +232,7 @@ categoriesController.removeRemote = async function (req, res) {
const parentCid = await categories.getCategoryField(req.params.cid, 'parentCid');
await db.sortedSetRemove(`cid:${parentCid || 0}:children`, req.params.cid);
cache.del(`cid:${parentCid || 0}:children`);
await categories.clearParentCategoryCache(parentCid || 0);
await categories.setCategoryField(req.params.cid, 'parentCid', 0);
res.sendStatus(200);
};