feat: add federatedDescription property to a category.

The federated description will be appended to the category description when queried remotely.
The default string is translatable, and tells users that it identifies as a forum category and that topics can be created within by mentioning the category directly.

closes #13127
This commit is contained in:
Julian Lam
2025-03-06 11:44:50 -05:00
parent 8ca1d6e6cc
commit dfabadbeab
4 changed files with 21 additions and 5 deletions

View File

@@ -10,6 +10,9 @@
"handle": "Category Handle", "handle": "Category Handle",
"handle.help": "Your category handle is used as a representation of this category across other networks, similar to a username. A category handle must not match an existing username or user group.", "handle.help": "Your category handle is used as a representation of this category across other networks, similar to a username. A category handle must not match an existing username or user group.",
"description": "Category Description", "description": "Category Description",
"federatedDescription": "Federated Description",
"federatedDescription.help": "This text will be appended to the category description when queried by other websites/apps.",
"federatedDescription.default": "This is a forum category containing topical discussion. You can start new discussions by mentioning this category.",
"bg-color": "Background Colour", "bg-color": "Background Colour",
"text-color": "Text Colour", "text-color": "Text Colour",
"bg-image-size": "Background Image Size", "bg-image-size": "Background Image Size",

View File

@@ -413,10 +413,11 @@ Mocks.actors.user = async (uid) => {
}; };
Mocks.actors.category = async (cid) => { Mocks.actors.category = async (cid) => {
const { let {
name, handle: preferredUsername, slug, name, handle: preferredUsername, slug,
descriptionParsed: summary, backgroundImage, descriptionParsed: summary, federatedDescription, backgroundImage,
} = await categories.getCategoryData(cid); } = await categories.getCategoryFields(cid,
['name', 'handle', 'slug', 'description', 'descriptionParsed', 'federatedDescription', 'backgroundImage']);
const publicKey = await activitypub.getPublicKey('cid', cid); const publicKey = await activitypub.getPublicKey('cid', cid);
let icon; let icon;
@@ -437,6 +438,9 @@ Mocks.actors.category = async (cid) => {
}; };
} }
// Append federated desc.
const fallback = await translator.translate('[[admin/manage/categories:federatedDescription.default]]');
summary += `<hr /><p dir="auto">${federatedDescription || fallback}</p>\n`;
return { return {
'@context': [ '@context': [

View File

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

View File

@@ -36,6 +36,16 @@
<textarea id="cid-{category.cid}-description" data-name="description" class="form-control category_description description" rows="4" />{category.description}</textarea> <textarea id="cid-{category.cid}-description" data-name="description" class="form-control category_description description" rows="4" />{category.description}</textarea>
</div> </div>
<div class="mb-3">
<label class="form-label" for="cid-{category.cid}-federatedDescription">
[[admin/manage/categories:federatedDescription]]
</label>
<textarea id="cid-{category.cid}-federatedDescription" data-name="federatedDescription" class="form-control" rows="2" placeholder="[[admin/manage/categories:federatedDescription.default]]" />{category.federatedDescription}</textarea>
<p class="form-text">
[[admin/manage/categories:federatedDescription.help]]
</p>
</div>
<div class="mb-3 d-flex justify-content-between align-items-center gap-2"> <div class="mb-3 d-flex justify-content-between align-items-center gap-2">
<label class="form-label" for="cid-{category.cid}-parentCid">[[admin/manage/categories:parent-category]]</label> <label class="form-label" for="cid-{category.cid}-parentCid">[[admin/manage/categories:parent-category]]</label>
<div id="parent-category-selector"> <div id="parent-category-selector">