diff --git a/extend.php b/extend.php index 1706fd2..0b0d4fc 100644 --- a/extend.php +++ b/extend.php @@ -11,6 +11,7 @@ namespace ACPL\FlarumLSCache; +use Flarum\Api\Serializer\UserSerializer; use ACPL\FlarumLSCache\Api\Controller\{LSCacheCsrfResponseController, PurgeLSCacheController}; use ACPL\FlarumLSCache\Command\LSCachePurgeCommand; use ACPL\FlarumLSCache\Compatibility\{ @@ -57,6 +58,13 @@ ->default('acpl-lscache.status_codes_cache', "404 3600\n403 3600\n500 120"), (new Extend\Event())->listen(Saved::class, Listener\UpdateSettingsListener::class), + // Permissions + (new Extend\ApiSerializer(UserSerializer::class)) + ->attribute( + 'canPurgeLSCache', + fn (UserSerializer $serializer) => $serializer->getActor()->can('lscache.purge'), + ), + // Vary cookie (new Extend\Middleware('forum'))->insertAfter(CheckCsrfToken::class, VaryCookieMiddleware::class), (new Extend\Middleware('admin'))->insertAfter(CheckCsrfToken::class, VaryCookieMiddleware::class), diff --git a/js/src/@types/shims.d.ts b/js/src/@types/shims.d.ts new file mode 100644 index 0000000..99b2ee4 --- /dev/null +++ b/js/src/@types/shims.d.ts @@ -0,0 +1,7 @@ +import 'flarum/common/models/User'; + +declare module 'flarum/common/models/User' { + export default interface User { + canPurgeLSCache(): boolean; + } +} diff --git a/js/src/admin/addPurgeLSCacheButton.tsx b/js/src/admin/addPurgeLSCacheButton.tsx index a382abd..78bf785 100644 --- a/js/src/admin/addPurgeLSCacheButton.tsx +++ b/js/src/admin/addPurgeLSCacheButton.tsx @@ -2,22 +2,10 @@ import app from 'flarum/admin/app'; import { extend } from 'flarum/common/extend'; import StatusWidget from 'flarum/admin/components/StatusWidget'; import Button from 'flarum/common/components/Button'; -import type ItemList from 'flarum/common/utils/ItemList'; -import type { Children } from 'mithril'; - -function handleClearLSCache() { - app - .request({ - url: `${app.forum.attribute('apiUrl')}/lscache-purge`, - method: 'GET', - }) - .then(() => { - app.alerts.show({ type: 'success' }, app.translator.trans('acpl-lscache.admin.purge_all_success')); - }); -} +import purgeLSCache from '../common/purgeLSCache'; export default () => { - extend(StatusWidget.prototype, 'toolsItems', (items: ItemList) => { - items.add('clearLSCache', ); + extend(StatusWidget.prototype, 'toolsItems', (items) => { + items.add('clearLSCache', ); }); }; diff --git a/js/src/admin/index.tsx b/js/src/admin/index.tsx index 854d684..2bcdfe9 100644 --- a/js/src/admin/index.tsx +++ b/js/src/admin/index.tsx @@ -3,6 +3,8 @@ import Link from 'flarum/common/components/Link'; import addPurgeLSCacheButton from './addPurgeLSCacheButton'; app.initializers.add('acpl-lscache', () => { + addPurgeLSCacheButton(); + app.extensionData .for('acpl-lscache') .registerSetting({ @@ -79,7 +81,13 @@ app.initializers.add('acpl-lscache', () => { label: app.translator.trans('acpl-lscache.admin.status_codes_cache_label'), help: app.translator.trans('acpl-lscache.admin.status_codes_cache_help'), type: 'textarea', - }); - - addPurgeLSCacheButton(); + }) + .registerPermission( + { + icon: 'fas fa-broom', + label: app.translator.trans('acpl-lscache.admin.permissions.purge'), + permission: 'lscache.purge', + }, + 'moderate' + ); }); diff --git a/js/src/common/purgeLSCache.ts b/js/src/common/purgeLSCache.ts new file mode 100644 index 0000000..c697d37 --- /dev/null +++ b/js/src/common/purgeLSCache.ts @@ -0,0 +1,24 @@ +import app from 'flarum/common/app'; + +export default async function purgeLSCache(tags?: string[], paths?: string[]) { + const queryParams: Record = {}; + + if (tags?.length) { + queryParams.tags = tags; + } + + if (paths?.length) { + queryParams.paths = paths; + } + + await app.request({ + url: `${app.forum.attribute('apiUrl')}/lscache-purge`, + method: 'GET', + params: queryParams, + }); + + app.alerts.show( + { type: 'success' }, + app.translator.trans(!tags?.length && !paths?.length ? 'acpl-lscache.lib.purge_all_success' : 'acpl-lscache.lib.purge_success') + ); +} diff --git a/js/src/forum/extend.ts b/js/src/forum/extend.ts new file mode 100644 index 0000000..5cd3212 --- /dev/null +++ b/js/src/forum/extend.ts @@ -0,0 +1,4 @@ +import Extend from 'flarum/common/extenders'; +import User from 'flarum/common/models/User'; + +export default [new Extend.Model(User).attribute('canPurgeLSCache')]; diff --git a/js/src/forum/extendDiscussionControls.tsx b/js/src/forum/extendDiscussionControls.tsx new file mode 100644 index 0000000..dc5c05b --- /dev/null +++ b/js/src/forum/extendDiscussionControls.tsx @@ -0,0 +1,23 @@ +import app from 'flarum/forum/app'; +import { extend } from 'flarum/common/extend'; +import DiscussionControls from 'flarum/forum/utils/DiscussionControls'; +import Button from 'flarum/common/components/Button'; +import purgeLSCache from '../common/purgeLSCache'; + +export default function extendDiscussionControls() { + extend(DiscussionControls, 'moderationControls', (items, discussion) => { + const discussionId = discussion.id(); + const { user } = app.session; + + if (!discussionId || !user || !user.canPurgeLSCache()) { + return; + } + + items.add( + 'acpl-lscache-purge', + + ); + }); +} diff --git a/js/src/forum/index.ts b/js/src/forum/index.ts index 21c1627..254f04b 100644 --- a/js/src/forum/index.ts +++ b/js/src/forum/index.ts @@ -2,8 +2,12 @@ import app from 'flarum/forum/app'; import { extend } from 'flarum/common/extend'; import Modal from 'flarum/common/components/Modal'; import updateCSRF from './utils/updateCSRF'; +import extendDiscussionControls from './extendDiscussionControls'; + +export { default as extend } from './extend'; app.initializers.add('acpl-lscache', () => { // We extend each modal to also include those added by external extensions extend(Modal.prototype, 'oninit', updateCSRF); + extendDiscussionControls(); }); diff --git a/locale/en.yml b/locale/en.yml index 62f7105..badc48f 100644 --- a/locale/en.yml +++ b/locale/en.yml @@ -25,5 +25,18 @@ acpl-lscache: status_codes_cache_label: "Default HTTP Status Code Page TTL" status_codes_cache_help: "Specify an HTTP status code and the number of seconds to cache that page, separated by a space. One per line." - purge_all: "Purge All LSCache" - purge_all_success: "LiteSpeed Web Server has been notified to purge all LSCache entries" + purge_all: => acpl-lscache.ref.purge_cache + + permissions: + purge: => acpl-lscache.ref.purge_cache + + forum: + purge: + discussion: => acpl-lscache.ref.purge_cache + + lib: + purge_success: "Notified LiteSpeed Server to purge LSCache entries" + purge_all_success: "Notified LiteSpeed Server to purge all LSCache entries" + + ref: + purge_cache: "Purge LSCache" diff --git a/src/Api/Controller/PurgeLSCacheController.php b/src/Api/Controller/PurgeLSCacheController.php index 9f4d751..293a69c 100644 --- a/src/Api/Controller/PurgeLSCacheController.php +++ b/src/Api/Controller/PurgeLSCacheController.php @@ -27,11 +27,11 @@ public function handle(ServerRequestInterface $request): ResponseInterface { $canPurge = false; - if (RequestUtil::getActor($request)->isAdmin()) { + if (RequestUtil::getActor($request)->can('lscache.purge')) { $canPurge = true; } - //If a command is used, use the temporary key because the user is not logged in + // If a command is used, use the temporary key because the user is not logged in if (! $canPurge) { $key = $this->settings->get('acpl-lscache.purgeKey'); $reqKey = $request->getHeaderLine('LSCachePurgeKey');