diff --git a/public/language/en-GB/admin/menu.json b/public/language/en-GB/admin/menu.json index 913c74f475..319e4d3af2 100644 --- a/public/language/en-GB/admin/menu.json +++ b/public/language/en-GB/admin/menu.json @@ -48,6 +48,13 @@ "settings.page-title": "%1 Settings", + "section-federation": "Federation", + "federation/general": "General", + "federation/rules": "Categorization", + "federation/relays": "Relays", + "federation/pruning": "Content Pruning", + "federation/safety": "Trust & Safety", + "section-appearance": "Appearance", "appearance/themes": "Themes", "appearance/skins": "Skins", diff --git a/public/src/admin/federation/relays.js b/public/src/admin/federation/relays.js new file mode 100644 index 0000000000..190720318d --- /dev/null +++ b/public/src/admin/federation/relays.js @@ -0,0 +1,70 @@ +'use strict'; + +import { post, del } from 'api'; +import { error } from 'alerts'; +import { render } from 'benchpress'; + +export function init() { + setupRelays(); +}; + +function setupRelays() { + const relaysEl = document.getElementById('relays'); + if (relaysEl) { + relaysEl.addEventListener('click', (e) => { + const subselector = e.target.closest('[data-action]'); + if (subselector) { + const action = subselector.getAttribute('data-action'); + switch (action) { + case 'relays.add': { + throwModal(); + break; + } + + case 'relays.remove': { + const url = subselector.closest('tr').getAttribute('data-url'); + del(`/admin/activitypub/relays/${encodeURIComponent(url)}`, {}).then(async (data) => { + const html = await app.parseAndTranslate('admin/settings/activitypub', 'relays', { relays: data }); + const tbodyEl = document.querySelector('#relays tbody'); + if (tbodyEl) { + $(tbodyEl).html(html); + } + }).catch(error); + } + } + } + }); + } +} + +function throwModal() { + render('admin/partials/activitypub/relays', {}).then(function (html) { + const submit = function () { + const formEl = modal.find('form').get(0); + const payload = Object.fromEntries(new FormData(formEl)); + + post('/admin/activitypub/relays', payload).then(async (data) => { + const html = await app.parseAndTranslate('admin/settings/activitypub', 'relays', { relays: data }); + const tbodyEl = document.querySelector('#relays tbody'); + if (tbodyEl) { + $(tbodyEl).html(html); + } + }).catch(error); + }; + const modal = bootbox.dialog({ + title: '[[admin/settings/activitypub:relays.add]]', + message: html, + buttons: { + save: { + label: '[[global:save]]', + className: 'btn-primary', + callback: submit, + }, + }, + }); + + modal.on('shown.bs.modal', function () { + modal.find('input').focus(); + }); + }); +} \ No newline at end of file diff --git a/public/src/admin/federation/rules.js b/public/src/admin/federation/rules.js new file mode 100644 index 0000000000..fca956e567 --- /dev/null +++ b/public/src/admin/federation/rules.js @@ -0,0 +1,122 @@ +'use strict'; + +import { post, del, put } from 'api'; +import { error } from 'alerts'; +import { render } from 'benchpress'; +import { translate } from 'translator'; +import * as categorySelector from 'categorySelector'; + +export function init() { + setupRules(); +}; + +function setupRules() { + const rulesEl = document.getElementById('rules'); + if (!rulesEl) { + return; + } + + rulesEl.addEventListener('click', (e) => { + const subselector = e.target.closest('[data-action]'); + if (subselector) { + const action = subselector.getAttribute('data-action'); + switch (action) { + case 'rules.add': { + throwModal(); + break; + } + + case 'rules.delete': { + const rid = subselector.closest('tr').getAttribute('data-rid'); + del(`/admin/activitypub/rules/${rid}`, {}).then(async (data) => { + const html = await render('admin/settings/activitypub', { rules: data }, 'rules'); + const tbodyEl = document.querySelector('#rules tbody'); + if (tbodyEl) { + tbodyEl.innerHTML = html; + } + }).catch(error); + } + } + } + }); + + const tbodyEl = $(rulesEl).find('tbody'); + tbodyEl.sortable({ + handle: '.drag-handle', + helper: fixWidthHelper, + placeholder: 'ui-state-highlight', + axis: 'y', + update: function () { + const rids = []; + tbodyEl.find('tr').each(function () { + rids.push($(this).data('rid')); + }); + + put('/admin/activitypub/rules/order', { rids }).catch(error); + }, + }); + + function fixWidthHelper(e, ui) { + ui.children().each(function () { + $(this).width($(this).width()); + }); + return ui; + } +} + +function throwModal() { + render('admin/partials/activitypub/rules', {}).then(function (html) { + const submit = function () { + const formEl = modal.find('form').get(0); + const payload = Object.fromEntries(new FormData(formEl)); + + post('/admin/activitypub/rules', payload).then(async (data) => { + const html = await render('admin/settings/activitypub', { rules: data }, 'rules'); + const tbodyEl = document.querySelector('#rules tbody'); + if (tbodyEl) { + tbodyEl.innerHTML = html; + } + }).catch(error); + }; + const modal = bootbox.dialog({ + title: '[[admin/settings/activitypub:rules.add]]', + message: html, + buttons: { + save: { + label: '[[global:save]]', + className: 'btn-primary', + callback: submit, + }, + }, + }); + + modal.on('shown.bs.modal', function () { + modal.find('input').focus(); + }); + + + // help text + const updateHelp = async (key, el) => { + const text = await translate(`[[admin/settings/activitypub:rules.help-${key}]]`); + el.innerHTML = text; + }; + const helpTextEl = modal.get(0).querySelector('#help-text'); + const typeEl = modal.get(0).querySelector('#type'); + updateHelp(modal.get(0).querySelector('#type option').value, helpTextEl); + if (typeEl && helpTextEl) { + typeEl.addEventListener('change', function () { + updateHelp(this.value, helpTextEl); + }); + } + + // category switcher + categorySelector.init(modal.find('[component="category-selector"]'), { + onSelect: function (selectedCategory) { + modal.find('[name="cid"]').val(selectedCategory.cid); + }, + cacheList: false, + showLinks: true, + template: 'admin/partials/category/selector-dropdown-right', + }); + }); +} \ No newline at end of file diff --git a/public/src/admin/settings/activitypub.js b/public/src/admin/settings/activitypub.js deleted file mode 100644 index 9351f6e6c9..0000000000 --- a/public/src/admin/settings/activitypub.js +++ /dev/null @@ -1,193 +0,0 @@ -'use strict'; - -define('admin/settings/activitypub', [ - 'benchpress', - 'bootbox', - 'categorySelector', - 'api', - 'alerts', - 'translator', - - 'jquery-ui/widgets/sortable', -], function (Benchpress, bootbox, categorySelector, api, alerts, translator) { - const ActivityPub = {}; - - ActivityPub.init = function () { - setupRules(); - setupRelays(); - }; - - ActivityPub.throwRulesModal = function () { - Benchpress.render('admin/partials/activitypub/rules', {}).then(function (html) { - const submit = function () { - const formEl = modal.find('form').get(0); - const payload = Object.fromEntries(new FormData(formEl)); - - api.post('/admin/activitypub/rules', payload).then(async (data) => { - const html = await Benchpress.render('admin/settings/activitypub', { rules: data }, 'rules'); - const tbodyEl = document.querySelector('#rules tbody'); - if (tbodyEl) { - tbodyEl.innerHTML = html; - } - }).catch(alerts.error); - }; - const modal = bootbox.dialog({ - title: '[[admin/settings/activitypub:rules.add]]', - message: html, - buttons: { - save: { - label: '[[global:save]]', - className: 'btn-primary', - callback: submit, - }, - }, - }); - - modal.on('shown.bs.modal', function () { - modal.find('input').focus(); - }); - - - // help text - const updateHelp = async (key, el) => { - const text = await translator.translate(`[[admin/settings/activitypub:rules.help-${key}]]`); - el.innerHTML = text; - }; - const helpTextEl = modal.get(0).querySelector('#help-text'); - const typeEl = modal.get(0).querySelector('#type'); - updateHelp(modal.get(0).querySelector('#type option').value, helpTextEl); - if (typeEl && helpTextEl) { - typeEl.addEventListener('change', function () { - updateHelp(this.value, helpTextEl); - }); - } - - // category switcher - categorySelector.init(modal.find('[component="category-selector"]'), { - onSelect: function (selectedCategory) { - modal.find('[name="cid"]').val(selectedCategory.cid); - }, - cacheList: false, - showLinks: true, - template: 'admin/partials/category/selector-dropdown-right', - }); - }); - }; - - ActivityPub.throwRelaysModal = function () { - Benchpress.render('admin/partials/activitypub/relays', {}).then(function (html) { - const submit = function () { - const formEl = modal.find('form').get(0); - const payload = Object.fromEntries(new FormData(formEl)); - - api.post('/admin/activitypub/relays', payload).then(async (data) => { - const html = await app.parseAndTranslate('admin/settings/activitypub', 'relays', { relays: data }); - const tbodyEl = document.querySelector('#relays tbody'); - if (tbodyEl) { - $(tbodyEl).html(html); - } - }).catch(alerts.error); - }; - const modal = bootbox.dialog({ - title: '[[admin/settings/activitypub:relays.add]]', - message: html, - buttons: { - save: { - label: '[[global:save]]', - className: 'btn-primary', - callback: submit, - }, - }, - }); - - modal.on('shown.bs.modal', function () { - modal.find('input').focus(); - }); - }); - }; - - function setupRules() { - const rulesEl = document.getElementById('rules'); - if (!rulesEl) { - return; - } - - rulesEl.addEventListener('click', (e) => { - const subselector = e.target.closest('[data-action]'); - if (subselector) { - const action = subselector.getAttribute('data-action'); - switch (action) { - case 'rules.add': { - ActivityPub.throwRulesModal(); - break; - } - - case 'rules.delete': { - const rid = subselector.closest('tr').getAttribute('data-rid'); - api.del(`/admin/activitypub/rules/${rid}`, {}).then(async (data) => { - const html = await Benchpress.render('admin/settings/activitypub', { rules: data }, 'rules'); - const tbodyEl = document.querySelector('#rules tbody'); - if (tbodyEl) { - tbodyEl.innerHTML = html; - } - }).catch(alerts.error); - } - } - } - }); - - const tbodyEl = $(rulesEl).find('tbody'); - tbodyEl.sortable({ - handle: '.drag-handle', - helper: fixWidthHelper, - placeholder: 'ui-state-highlight', - axis: 'y', - update: function () { - const rids = []; - tbodyEl.find('tr').each(function () { - rids.push($(this).data('rid')); - }); - - api.put('/admin/activitypub/rules/order', { rids }).catch(alerts.error); - }, - }); - - function fixWidthHelper(e, ui) { - ui.children().each(function () { - $(this).width($(this).width()); - }); - return ui; - } - } - - function setupRelays() { - const relaysEl = document.getElementById('relays'); - if (relaysEl) { - relaysEl.addEventListener('click', (e) => { - const subselector = e.target.closest('[data-action]'); - if (subselector) { - const action = subselector.getAttribute('data-action'); - switch (action) { - case 'relays.add': { - ActivityPub.throwRelaysModal(); - break; - } - - case 'relays.remove': { - const url = subselector.closest('tr').getAttribute('data-url'); - api.del(`/admin/activitypub/relays/${encodeURIComponent(url)}`, {}).then(async (data) => { - const html = await app.parseAndTranslate('admin/settings/activitypub', 'relays', { relays: data }); - const tbodyEl = document.querySelector('#relays tbody'); - if (tbodyEl) { - $(tbodyEl).html(html); - } - }).catch(alerts.error); - } - } - } - }); - } - } - - return ActivityPub; -}); diff --git a/src/controllers/admin.js b/src/controllers/admin.js index b167f606e6..bdb8ca6ba1 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -14,6 +14,7 @@ const adminController = { groups: require('./admin/groups'), digest: require('./admin/digest'), appearance: require('./admin/appearance'), + federation: require('./admin/federation'), extend: { widgets: require('./admin/widgets'), rewards: require('./admin/rewards'), diff --git a/src/controllers/admin/federation.js b/src/controllers/admin/federation.js new file mode 100644 index 0000000000..4d6cd104a4 --- /dev/null +++ b/src/controllers/admin/federation.js @@ -0,0 +1,43 @@ +'use strict'; + +const activitypub = require('../../activitypub'); + +const federationController = module.exports; + +federationController.general = function (req, res) { + res.render(`admin/federation/general`, { + title: '[[admin/menu:federation/general]]', + }); +}; + +federationController.rules = async function (req, res) { + const rules = await activitypub.rules.list(); + + res.render(`admin/federation/rules`, { + title: '[[admin/menu:federation/rules]]', + rules, + hideSave: true, + }); +}; + +federationController.relays = async function (req, res) { + const relays = await activitypub.relays.list(); + + res.render(`admin/federation/relays`, { + title: '[[admin/menu:federation/relays]]', + relays, + hideSave: true, + }); +}; + +federationController.pruning = function (req, res) { + res.render(`admin/federation/pruning`, { + title: '[[admin/menu:federation/pruning]]', + }); +}; + +federationController.safety = function (req, res) { + res.render(`admin/federation/safety`, { + title: '[[admin/menu:federation/safety]]', + }); +}; diff --git a/src/routes/admin.js b/src/routes/admin.js index 96cc92c62d..bca37af2ce 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -52,6 +52,12 @@ module.exports = function (app, name, middleware, controllers) { helpers.setupAdminPageRoute(app, `/${name}/settings/web-crawler`, middlewares, controllers.admin.settings.webCrawler); helpers.setupAdminPageRoute(app, `/${name}/settings/advanced`, middlewares, controllers.admin.settings.advanced); + helpers.setupAdminPageRoute(app, `/${name}/federation/general`, middlewares, controllers.admin.federation.general); + helpers.setupAdminPageRoute(app, `/${name}/federation/rules`, middlewares, controllers.admin.federation.rules); + helpers.setupAdminPageRoute(app, `/${name}/federation/relays`, middlewares, controllers.admin.federation.relays); + helpers.setupAdminPageRoute(app, `/${name}/federation/pruning`, middlewares, controllers.admin.federation.pruning); + helpers.setupAdminPageRoute(app, `/${name}/federation/safety`, middlewares, controllers.admin.federation.safety); + helpers.setupAdminPageRoute(app, `/${name}/appearance/themes`, middlewares, controllers.admin.appearance.themes); helpers.setupAdminPageRoute(app, `/${name}/appearance/skins`, middlewares, controllers.admin.appearance.skins); helpers.setupAdminPageRoute(app, `/${name}/appearance/customise`, middlewares, controllers.admin.appearance.customise); diff --git a/src/views/admin/federation/general.tpl b/src/views/admin/federation/general.tpl new file mode 100644 index 0000000000..ea10732c1e --- /dev/null +++ b/src/views/admin/federation/general.tpl @@ -0,0 +1,49 @@ +
+ + +
+
+ +

[[admin/settings/activitypub:intro-lead]]

+

[[admin/settings/activitypub:intro-body]]

+ +
+ +
+
[[admin/settings/activitypub:general]]
+
+
+ + +

[[admin/settings/activitypub:enabled-help]]

+
+
+ + +

[[admin/settings/activitypub:allowLoopback-help]]

+
+
+
+ +
+
[[admin/settings/activitypub:probe]]
+
+
+ + +

[[admin/settings/activitypub:probe-enabled-help]]

+
+
+ + +
+ [[admin/settings/activitypub:probe-timeout-help]] +
+
+
+
+
+ + +
+
diff --git a/src/views/admin/federation/pruning.tpl b/src/views/admin/federation/pruning.tpl new file mode 100644 index 0000000000..86d6f6768a --- /dev/null +++ b/src/views/admin/federation/pruning.tpl @@ -0,0 +1,28 @@ +
+ + +
+
+
+
+
+ + +
+ [[admin/settings/activitypub:content-pruning-help]] +
+
+
+ + +
+ [[admin/settings/activitypub:user-pruning-help]] +
+
+
+
+
+ + +
+
diff --git a/src/views/admin/federation/relays.tpl b/src/views/admin/federation/relays.tpl new file mode 100644 index 0000000000..e38a3fd1b4 --- /dev/null +++ b/src/views/admin/federation/relays.tpl @@ -0,0 +1,39 @@ +
+ + +
+
+
+

[[admin/settings/activitypub:relays.intro]]

+

[[admin/settings/activitypub:relays.warning]]

+
+ + + + + + + + {{{ each relays }}} + + + + + + {{{ end }}} + + + + + + +
[[admin/settings/activitypub:relays.relay]][[admin/settings/activitypub:relays.state]]
{./url}{./label}
+ +
+
+
+
+ + +
+
diff --git a/src/views/admin/federation/rules.tpl b/src/views/admin/federation/rules.tpl new file mode 100644 index 0000000000..344eb1ec23 --- /dev/null +++ b/src/views/admin/federation/rules.tpl @@ -0,0 +1,45 @@ +
+ + +
+
+
+

[[admin/settings/activitypub:rules-intro]]

+ +
+ + + + + + + + + + {{{ each rules }}} + + + + + + + + {{{ end }}} + + + + + + +
[[admin/settings/activitypub:rules.type]][[admin/settings/activitypub:rules.value]][[admin/settings/activitypub:rules.cid]]
+ + {./type}{./value}{./cid}
+ +
+
+
+
+ + +
+
diff --git a/src/views/admin/federation/safety.tpl b/src/views/admin/federation/safety.tpl new file mode 100644 index 0000000000..9c92085732 --- /dev/null +++ b/src/views/admin/federation/safety.tpl @@ -0,0 +1,26 @@ +
+ + +
+
+
+
[[admin/settings/activitypub:server-filtering]]
+
+
+

[[admin/settings/activitypub:server.filter-help]]

+

[[admin/settings/activitypub:server.filter-help-hostname]]

+

[[admin/settings/activitypub:count, {instanceCount}]]

+ + +
+
+ + +
+
+
+
+ + +
+
diff --git a/src/views/admin/partials/navigation.tpl b/src/views/admin/partials/navigation.tpl index 091bc2d60a..a2a2a08fa4 100644 --- a/src/views/admin/partials/navigation.tpl +++ b/src/views/admin/partials/navigation.tpl @@ -63,8 +63,8 @@ {{{ end }}} - {{{ if user.privileges.admin:settings }}} +
+ +
+ + +
+ +
+
+ +
+ {{{ end }}} \ No newline at end of file diff --git a/src/views/admin/settings/activitypub.tpl b/src/views/admin/settings/activitypub.tpl deleted file mode 100644 index c9de5f37b6..0000000000 --- a/src/views/admin/settings/activitypub.tpl +++ /dev/null @@ -1,153 +0,0 @@ -
- - -
-
- -

[[admin/settings/activitypub:intro-lead]]

-

[[admin/settings/activitypub:intro-body]]

- -
- -
-
[[admin/settings/activitypub:general]]
-
-
- - -

[[admin/settings/activitypub:enabled-help]]

-
-
- - -

[[admin/settings/activitypub:allowLoopback-help]]

-
-
-
- -
-
[[admin/settings/activitypub:probe]]
-
-
- - -

[[admin/settings/activitypub:probe-enabled-help]]

-
-
- - -
- [[admin/settings/activitypub:probe-timeout-help]] -
-
-
-
- -
-
[[admin/settings/activitypub:rules]]
-
-

[[admin/settings/activitypub:rules-intro]]

- - - - - - - - - - {{{ each rules }}} - - - - - - - - {{{ end }}} - - - - - - -
[[admin/settings/activitypub:rules.type]][[admin/settings/activitypub:rules.value]][[admin/settings/activitypub:rules.cid]]
- - {./type}{./value}{./cid}
- -
-
-
- -
-
[[admin/settings/activitypub:relays]]
-

[[admin/settings/activitypub:relays.intro]]

-

[[admin/settings/activitypub:relays.warning]]

-
- - - - - - - - {{{ each relays }}} - - - - - - {{{ end }}} - - - - - - -
[[admin/settings/activitypub:relays.relay]][[admin/settings/activitypub:relays.state]]
{./url}{./label}
- -
-
-
- -
-
[[admin/settings/activitypub:pruning]]
-
-
- - -
- [[admin/settings/activitypub:content-pruning-help]] -
-
-
- - -
- [[admin/settings/activitypub:user-pruning-help]] -
-
-
-
- -
-
[[admin/settings/activitypub:server-filtering]]
-
-
-

[[admin/settings/activitypub:server.filter-help]]

-

[[admin/settings/activitypub:server.filter-help-hostname]]

-

[[admin/settings/activitypub:count, {instanceCount}]]

- - -
-
- - -
-
-
-
- - -
-