From cbe11bc9209e93b315bb3dcf8291571dce64a83c Mon Sep 17 00:00:00 2001 From: Ian Morland Date: Fri, 24 Nov 2023 12:41:12 +0000 Subject: [PATCH] feat: simplify tag configuration --- .github/workflows/backend.yml | 2 +- composer.json | 2 +- extend.php | 14 ++-- js/src/admin/addQnAToTagsModal.js | 50 ------------ js/src/admin/components/BestAnswerSettings.js | 49 +++++++----- js/src/admin/extend.ts | 3 + js/src/admin/index.ts | 4 +- js/src/common/extend.ts | 8 ++ js/src/forum/extend.ts | 17 ++++ .../extendNotifications.ts | 0 js/src/forum/index.js | 21 ++--- ...000000_create_enabled_tags_setting_key.php | 60 ++++++++++++++ resources/locale/en.yml | 9 ++- .../Controller/FeatureEnableController.php | 80 ------------------- src/Listeners/SaveTagSettings.php | 27 +++++++ 15 files changed, 166 insertions(+), 180 deletions(-) delete mode 100644 js/src/admin/addQnAToTagsModal.js create mode 100644 js/src/admin/extend.ts create mode 100644 js/src/common/extend.ts create mode 100644 js/src/forum/extend.ts rename js/src/forum/{extend => extenders}/extendNotifications.ts (100%) create mode 100644 migrations/2023_11_23_000000_create_enabled_tags_setting_key.php delete mode 100644 src/Api/Controller/FeatureEnableController.php create mode 100644 src/Listeners/SaveTagSettings.php diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index 6fdf293..db59005 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -4,7 +4,7 @@ on: [workflow_dispatch, push, pull_request] jobs: run: - uses: flarum/framework/.github/workflows/REUSABLE_backend.yml@main + uses: flarum/framework/.github/workflows/REUSABLE_backend.yml@1.x with: enable_backend_testing: true enable_phpstan: true diff --git a/composer.json b/composer.json index f0de410..9165f55 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ }, { "name": "IanM", - "homepage": "https://discuss.flarum.org/u/ianm", + "email": "ian@flarum.org", "role": "Developer" } ], diff --git a/extend.php b/extend.php index 579d59b..7a16d0f 100644 --- a/extend.php +++ b/extend.php @@ -17,11 +17,12 @@ use Flarum\Api\Controller\UpdateDiscussionController; use Flarum\Api\Serializer; use Flarum\Discussion\Discussion; -use Flarum\Discussion\Event\Saving; +use Flarum\Discussion\Event\Saving as DiscussionSaving; use Flarum\Discussion\Filter\DiscussionFilterer; use Flarum\Discussion\Search\DiscussionSearcher; use Flarum\Extend; use Flarum\Post\Post; +use Flarum\Settings\Event\Saving as SettingsSaving; use Flarum\Tags\Api\Serializer\TagSerializer; use Flarum\Tags\Event\Creating as TagCreating; use Flarum\Tags\Event\Saving as TagSaving; @@ -40,9 +41,6 @@ new Extend\Locales(__DIR__.'/resources/locale'), - (new Extend\Routes('api')) - ->post('/fof/best-answer/enable', 'fof-best-answer.enable-tags-features', Api\Controller\FeatureEnableController::class), - (new Extend\Model(Discussion::class)) ->belongsTo('bestAnswerPost', Post::class, 'best_answer_post_id') ->belongsTo('bestAnswerUser', User::class, 'best_answer_user_id') @@ -62,11 +60,12 @@ ->cast('best_answer_count', 'int'), (new Extend\Event()) - ->listen(Saving::class, Listeners\SaveBestAnswerToDatabase::class) + ->listen(DiscussionSaving::class, Listeners\SaveBestAnswerToDatabase::class) ->listen(BestAnswerSet::class, Listeners\QueueNotificationJobs::class) ->listen(TagCreating::class, Listeners\TagCreating::class) ->listen(TagSaving::class, Listeners\TagEditing::class) - ->subscribe(Listeners\RecalculateBestAnswerCounts::class), + ->subscribe(Listeners\RecalculateBestAnswerCounts::class) + ->listen(SettingsSaving::class, Listeners\SaveTagSettings::class), (new Extend\Notification()) ->type(Notification\SelectBestAnswerBlueprint::class, Serializer\BasicDiscussionSerializer::class, ['alert', 'email']) @@ -96,7 +95,8 @@ ->serializeToForum('fof-best-answer.show_max_lines', 'fof-best-answer.show_max_lines', 'intVal') ->default('fof-best-answer.schedule_on_one_server', false) ->default('fof-best-answer.stop_overnight', false) - ->default('fof-best-answer.store_log_output', false), + ->default('fof-best-answer.store_log_output', false) + ->default('fof-best-answer.enabled-tags', '[]'), (new Extend\ApiController(ShowDiscussionController::class)) ->addInclude(['bestAnswerPost', 'bestAnswerUser']) diff --git a/js/src/admin/addQnAToTagsModal.js b/js/src/admin/addQnAToTagsModal.js deleted file mode 100644 index 19821ec..0000000 --- a/js/src/admin/addQnAToTagsModal.js +++ /dev/null @@ -1,50 +0,0 @@ -import app from 'flarum/admin/app'; -import EditTagModal from 'flarum/tags/components/EditTagModal'; -import { extend } from 'flarum/common/extend'; -import Stream from 'flarum/utils/Stream'; -import Model from 'flarum/Model'; -import Tag from 'flarum/tags/models/Tag'; - -export default function () { - if (app.initializers.has('flarum-tags')) { - Tag.prototype.isQnA = Model.attribute('isQnA'); - Tag.prototype.reminders = Model.attribute('reminders'); - - extend(EditTagModal.prototype, 'oninit', function () { - this.isQnA = Stream(this.tag.isQnA() || false); - this.reminders = Stream(this.tag.reminders() || false); - }); - - extend(EditTagModal.prototype, 'fields', function (items) { - items.add( - 'qna', -
-
- -
-
, - 10 - ); - - items.add( - 'reminders', -
-
- -
-
- ); - }); - - extend(EditTagModal.prototype, 'submitData', function (data) { - data.isQnA = this.isQnA(); - data.reminders = this.reminders(); - }); - } -} diff --git a/js/src/admin/components/BestAnswerSettings.js b/js/src/admin/components/BestAnswerSettings.js index 08f476f..40017ee 100644 --- a/js/src/admin/components/BestAnswerSettings.js +++ b/js/src/admin/components/BestAnswerSettings.js @@ -41,25 +41,36 @@ export default class BestAnswerSettings extends ExtensionPage {
-

- {app.translator.trans('fof-best-answer.admin.settings.introduction', { - a: , - })} -

-
- - -
+

{app.translator.trans('fof-best-answer.admin.settings.label.tags')}

+

{app.translator.trans('fof-best-answer.admin.settings.tags_info')}

+ {this.buildSettingComponent({ + type: 'flarum-tags.select-tags', + setting: 'fof-best-answer.enabled-tags', + label: app.translator.trans('fof-best-answer.admin.settings.enabled_tags_label'), + help: app.translator.trans('fof-best-answer.admin.settings.enabled_tags_help'), + options: { + requireParentTag: false, + limits: { + max: { + secondary: 0, + }, + }, + }, + })} + {this.buildSettingComponent({ + type: 'flarum-tags.select-tags', + setting: 'fof-best-answer.remind-tags', + label: app.translator.trans('fof-best-answer.admin.settings.remind_tags_label'), + help: app.translator.trans('fof-best-answer.admin.settings.remind_tags_help'), + options: { + requireParentTag: false, + limits: { + max: { + secondary: 0, + }, + }, + }, + })}

diff --git a/js/src/admin/extend.ts b/js/src/admin/extend.ts new file mode 100644 index 0000000..93caee0 --- /dev/null +++ b/js/src/admin/extend.ts @@ -0,0 +1,3 @@ +import commonExtend from '../common/extend'; + +export default [...commonExtend]; diff --git a/js/src/admin/index.ts b/js/src/admin/index.ts index 78e599a..5748b43 100644 --- a/js/src/admin/index.ts +++ b/js/src/admin/index.ts @@ -1,8 +1,9 @@ import app from 'flarum/admin/app'; -import addQnAToTagsModal from './addQnAToTagsModal'; import BestAnswerSettings from './components/BestAnswerSettings'; import addBestAnswerCountSort from '../common/addBestAnswerCountSort'; +export { default as extend } from './extend'; + app.initializers.add( 'fof-best-answer', () => { @@ -26,7 +27,6 @@ app.initializers.add( 'reply' ); - addQnAToTagsModal(); addBestAnswerCountSort(); }, 5 diff --git a/js/src/common/extend.ts b/js/src/common/extend.ts new file mode 100644 index 0000000..533a420 --- /dev/null +++ b/js/src/common/extend.ts @@ -0,0 +1,8 @@ +import Extend from 'flarum/common/extenders'; +import Tag from 'flarum/tags/models/Tag'; + +export default [ + new Extend.Model(Tag) // + .attribute('isQnA') + .attribute('reminders'), +]; diff --git a/js/src/forum/extend.ts b/js/src/forum/extend.ts new file mode 100644 index 0000000..a47d14a --- /dev/null +++ b/js/src/forum/extend.ts @@ -0,0 +1,17 @@ +import Discussion from 'flarum/common/models/Discussion'; +import commonExtend from '../common/extend'; +import Extend from 'flarum/common/extenders'; +import Post from 'flarum/common/models/Post'; +import User from 'flarum/common/models/User'; +import Model from 'flarum/common/Model'; + +export default [ + ...commonExtend, + + new Extend.Model(Discussion) // + .hasOne('bestAnswerPost') + .hasOne('bestAnswerUser') + .attribute('hasBestAnswer') + .attribute('canSelectBestAnswer') + .attribute('bestAnswerSetAt', Model.transformDate), +]; diff --git a/js/src/forum/extend/extendNotifications.ts b/js/src/forum/extenders/extendNotifications.ts similarity index 100% rename from js/src/forum/extend/extendNotifications.ts rename to js/src/forum/extenders/extendNotifications.ts diff --git a/js/src/forum/index.js b/js/src/forum/index.js index 884f00b..5320473 100644 --- a/js/src/forum/index.js +++ b/js/src/forum/index.js @@ -1,8 +1,5 @@ import app from 'flarum/forum/app'; import { extend } from 'flarum/common/extend'; -import Discussion from 'flarum/common/models/Discussion'; -import Tag from 'flarum/tags/models/Tag'; -import Model from 'flarum/common/Model'; import IndexPage from 'flarum/forum/components/IndexPage'; import Dropdown from 'flarum/common/components/Dropdown'; import Button from 'flarum/common/components/Button'; @@ -15,23 +12,15 @@ import addBestAnswerView from './addBestAnswerView'; import addAnsweredBadge from './addAnsweredBadge'; import AwardedBestAnswerNotification from './components/AwardedBestAnswerNotification'; import BestAnswerInDiscussionNotification from './components/BestAnswerInDiscussionNotification'; -import extendNotifications from './extend/extendNotifications'; +import extendNotifications from './extenders/extendNotifications'; import addBestAnswerCountToUsers from './addBestAnswerCountToUsers'; import addBestAnswerCountSort from '../common/addBestAnswerCountSort'; export * from './components'; -app.initializers.add('fof/best-answer', () => { - Discussion.prototype.bestAnswerPost = Model.hasOne('bestAnswerPost'); - Discussion.prototype.bestAnswerUser = Model.hasOne('bestAnswerUser'); - Discussion.prototype.hasBestAnswer = Model.attribute('hasBestAnswer'); - Discussion.prototype.canSelectBestAnswer = Model.attribute('canSelectBestAnswer'); - Discussion.prototype.bestAnswerSetAt = Model.attribute('bestAnswerSetAt', Model.transformDate); - - if (app.initializers.has('flarum-tags')) { - Tag.prototype.isQnA = Model.attribute('isQnA'); - } +export { default as extend } from './extend'; +app.initializers.add('fof/best-answer', () => { app.notificationComponents.selectBestAnswer = SelectBestAnswerNotification; app.notificationComponents.awardedBestAnswer = AwardedBestAnswerNotification; app.notificationComponents.bestAnswerInDiscussion = BestAnswerInDiscussionNotification; @@ -55,7 +44,7 @@ app.initializers.add('fof/best-answer', () => { canStartDiscussion ? 'fof-best-answer.forum.index.ask_question' : 'fof-best-answer.forum.index.cannot_ask_question' ); - items.replace('startDiscussion', cta); + items.setContent('startDiscussion', cta); }); extend(IndexPage.prototype, 'viewItems', function (items) { @@ -134,7 +123,7 @@ app.initializers.add('fof/best-answer', () => { this.attrs.titlePlaceholder = app.translator.trans('fof-best-answer.forum.composer.titlePlaceholder'); - items.replace( + items.setContent( 'discussionTitle',

function (Builder $schema) use ($remindersKey, $qnaKey) { + $reminderTagIds = $schema->getConnection() + ->table('tags') + ->where('qna_reminders', true) + ->pluck('id') + ->map(function ($id) { + return (string) $id; // Convert each ID to string + }); + + $qnaTagIds = $schema->getConnection() + ->table('tags') + ->where('is_qna', true) + ->pluck('id') + ->map(function ($id) { + return (string) $id; // Convert each ID to string + }); + + // Convert the arrays to JSON strings + $reminderTagIdsJson = json_encode($reminderTagIds->all()); + $qnaTagIdsJson = json_encode($qnaTagIds->all()); + + $schema->getConnection() + ->table('settings') + ->insertOrIgnore([ + 'key' => $remindersKey, + 'value' => $reminderTagIdsJson, + ]); + + $schema->getConnection() + ->table('settings') + ->insertOrIgnore([ + 'key' => $qnaKey, + 'value' => $qnaTagIdsJson, + ]); + }, + 'down' => function (Builder $schema) use ($remindersKey, $qnaKey) { + // Delete the settings keys + $schema->getConnection() + ->table('settings') + ->whereIn('key', [$remindersKey, $qnaKey]) + ->delete(); + }, +]; diff --git a/resources/locale/en.yml b/resources/locale/en.yml index 08aff19..6216154 100644 --- a/resources/locale/en.yml +++ b/resources/locale/en.yml @@ -5,14 +5,13 @@ fof-best-answer: best_answer_not_own_discussion: Select Best Answer (not own Discussion) settings: label: + tags: Best Answer Tags general: General reminders: Reminders advanced: Advanced reminders_notice: For reminders to function, you must have set up the Flarum scheduler correctly. allow_select_own_post: Select own post allow_select_own_post_help: Allow a user to select their own post as a best answer to a discussion - enable_all_tags_button: Enable all tags for Best Answers - enable_all_tags_reminder_button: Enable all tags for Best Answer reminders show_max_lines_label: Max lines to show in post preview show_max_lines_help: Set to 0 to disable. If a post is longer than the configured amount of lines, it will be truncated in the post preview with a fade out effect. select_best_answer_reminder_days: Reminder frequency @@ -29,8 +28,10 @@ fof-best-answer: use_alt_ui: Alternative layout use_alt_ui_help: Best answer controls in post footer documentation: Documentation - introduction: | - Enable which tags are to support Best Answers in flarum/tags, and optionally which tag(s) should support reminders. When a tag is Best Answer enabled, assign permissions to each user group that may set answers (own discussion, any discussion). Permissions may be assigned globally (for any Best Answer anabled tag), or on a per tag basis. + enabled_tags_label: Enabled Tags + remind_tags_label: Reminder Tags + tags_info: | + When a tag is Best Answer enabled, assign permissions to each user group that may set answers (own discussion, any discussion). Permissions may be assigned globally (for any Best Answer anabled tag), or on a per tag basis. edit_tag: qna_label: Enable Best Answers to be set in this tag reminders: Send reminders to set a Best Answer for discussions in this tag diff --git a/src/Api/Controller/FeatureEnableController.php b/src/Api/Controller/FeatureEnableController.php deleted file mode 100644 index 653ab2c..0000000 --- a/src/Api/Controller/FeatureEnableController.php +++ /dev/null @@ -1,80 +0,0 @@ -extensions = $extensions; - } - - public function handle(ServerRequestInterface $request): ResponseInterface - { - RequestUtil::getActor($request)->assertAdmin(); - - $body = json_decode($request->getBody()->getContents()); - - if (in_array($body->feature, self::functions)) { - $this->{$body->feature}(); - } - - return new EmptyResponse(); - } - - private function tagsInstalledAndEnabled(): bool - { - return $this->extensions->isEnabled('flarum-tags') && class_exists(Tag::class); - } - - private function enableAllTags(): void - { - if (!$this->tagsInstalledAndEnabled()) { - return; - } - - Tag::chunk(10, function ($tags) { - foreach ($tags as $tag) { - /** @var Tag $tag */ - $tag->is_qna = true; - $tag->save(); - } - }); - } - - private function enableAllReminders(): void - { - if (!$this->tagsInstalledAndEnabled()) { - return; - } - - Tag::chunk(10, function ($tags) { - foreach ($tags as $tag) { - /** @var Tag $tag */ - $tag->qna_reminders = true; - $tag->save(); - } - }); - } -} diff --git a/src/Listeners/SaveTagSettings.php b/src/Listeners/SaveTagSettings.php new file mode 100644 index 0000000..c9885e2 --- /dev/null +++ b/src/Listeners/SaveTagSettings.php @@ -0,0 +1,27 @@ +settings; + + if ($enabledTagIds = Arr::get($settings, 'fof-best-answer.enabled-tags')) { + $enabledTagIds = json_decode($enabledTagIds); + Tag::query()->whereIn('id', $enabledTagIds)->update(['is_qna' => true]); + Tag::query()->whereNotIn('id', $enabledTagIds)->update(['is_qna' => false]); + } + + if ($reminderTagIds = Arr::get($settings, 'fof-best-answer.remind-tags')) { + $enabledReminderIds = json_decode($reminderTagIds); + Tag::query()->whereIn('id', $enabledReminderIds)->update(['qna_reminders' => true]); + Tag::query()->whereNotIn('id', $enabledReminderIds)->update(['qna_reminders' => false]); + } + } +}