From 1ad057d4d7cdbe77db8229d94a804813b47fb81f Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Mon, 20 Jul 2015 18:14:42 +0930 Subject: [PATCH 1/2] Update for evented API --- extensions/sticky/bootstrap.php | 6 +- extensions/sticky/js/Gulpfile.js | 2 +- extensions/sticky/js/bootstrap.js | 81 ------------------- extensions/sticky/js/src/addStickyBadge.js | 15 ++++ extensions/sticky/js/src/addStickyControl.js | 26 ++++++ extensions/sticky/js/src/addStickyExcerpt.js | 24 ++++++ .../DiscussionStickiedNotification.js | 15 ++++ .../src/components/DiscussionStickiedPost.js | 13 +++ .../discussion-stickied-notification.js | 14 ---- .../components/discussion-stickied-post.js | 9 --- extensions/sticky/js/src/main.js | 29 +++++++ extensions/sticky/less/extension.less | 26 ++++++ extensions/sticky/less/sticky.less | 27 ------- extensions/sticky/locale/en.yml | 8 ++ .../src/Events/DiscussionWasStickied.php | 12 +-- .../src/Events/DiscussionWasUnstickied.php | 12 +-- extensions/sticky/src/Extension.php | 16 ++++ .../sticky/src/{ => Gambits}/StickyGambit.php | 12 +-- .../Handlers/DiscussionStickiedNotifier.php | 58 ------------- .../sticky/src/Listeners/AddApiAttributes.php | 30 +++++++ .../sticky/src/Listeners/AddClientAssets.php | 39 +++++++++ .../Listeners/NotifyDiscussionStickied.php | 71 ++++++++++++++++ .../PersistData.php} | 20 ++--- .../PinStickiedDiscussionsToTop.php} | 28 ++++--- .../DiscussionStickiedBlueprint.php} | 18 ++--- .../{ => Posts}/DiscussionStickiedPost.php | 38 +++++---- .../sticky/src/StickyServiceProvider.php | 41 ---------- 27 files changed, 387 insertions(+), 303 deletions(-) delete mode 100644 extensions/sticky/js/bootstrap.js create mode 100644 extensions/sticky/js/src/addStickyBadge.js create mode 100644 extensions/sticky/js/src/addStickyControl.js create mode 100644 extensions/sticky/js/src/addStickyExcerpt.js create mode 100644 extensions/sticky/js/src/components/DiscussionStickiedNotification.js create mode 100644 extensions/sticky/js/src/components/DiscussionStickiedPost.js delete mode 100644 extensions/sticky/js/src/components/discussion-stickied-notification.js delete mode 100644 extensions/sticky/js/src/components/discussion-stickied-post.js create mode 100644 extensions/sticky/js/src/main.js create mode 100644 extensions/sticky/less/extension.less delete mode 100644 extensions/sticky/less/sticky.less create mode 100644 extensions/sticky/locale/en.yml create mode 100644 extensions/sticky/src/Extension.php rename extensions/sticky/src/{ => Gambits}/StickyGambit.php (56%) delete mode 100755 extensions/sticky/src/Handlers/DiscussionStickiedNotifier.php create mode 100755 extensions/sticky/src/Listeners/AddApiAttributes.php create mode 100755 extensions/sticky/src/Listeners/AddClientAssets.php create mode 100755 extensions/sticky/src/Listeners/NotifyDiscussionStickied.php rename extensions/sticky/src/{Handlers/StickySaver.php => Listeners/PersistData.php} (54%) rename extensions/sticky/src/{Handlers/StickySearchModifier.php => Listeners/PinStickiedDiscussionsToTop.php} (55%) rename extensions/sticky/src/{DiscussionStickiedNotification.php => Notifications/DiscussionStickiedBlueprint.php} (73%) rename extensions/sticky/src/{ => Posts}/DiscussionStickiedPost.php (56%) delete mode 100644 extensions/sticky/src/StickyServiceProvider.php diff --git a/extensions/sticky/bootstrap.php b/extensions/sticky/bootstrap.php index f09cac86c..0afa317f8 100644 --- a/extensions/sticky/bootstrap.php +++ b/extensions/sticky/bootstrap.php @@ -1,9 +1,5 @@ app->register('Flarum\Sticky\StickyServiceProvider'); +return 'Flarum\Sticky\Extension'; diff --git a/extensions/sticky/js/Gulpfile.js b/extensions/sticky/js/Gulpfile.js index fae866679..f015c1647 100644 --- a/extensions/sticky/js/Gulpfile.js +++ b/extensions/sticky/js/Gulpfile.js @@ -1,5 +1,5 @@ var gulp = require('flarum-gulp'); gulp({ - modulePrefix: 'flarum-sticky' + modulePrefix: 'sticky' }); diff --git a/extensions/sticky/js/bootstrap.js b/extensions/sticky/js/bootstrap.js deleted file mode 100644 index f5baf754d..000000000 --- a/extensions/sticky/js/bootstrap.js +++ /dev/null @@ -1,81 +0,0 @@ -import { extend } from 'flarum/extension-utils'; -import Model from 'flarum/model'; -import Discussion from 'flarum/models/discussion'; -import DiscussionPage from 'flarum/components/discussion-page'; -import DiscussionList from 'flarum/components/discussion-list'; -import DiscussionListItem from 'flarum/components/discussion-list-item'; -import Badge from 'flarum/components/badge'; -import ActionButton from 'flarum/components/action-button'; -import SettingsPage from 'flarum/components/settings-page'; -import icon from 'flarum/helpers/icon'; -import truncate from 'flarum/utils/truncate'; -import app from 'flarum/app'; - -import DiscussionStickiedPost from 'flarum-sticky/components/discussion-stickied-post'; -import DiscussionStickiedNotification from 'flarum-sticky/components/discussion-stickied-notification'; - -app.initializers.add('sticky', function() { - - // Register components. - app.postComponentRegistry['discussionStickied'] = DiscussionStickiedPost; - app.notificationComponentRegistry['discussionStickied'] = DiscussionStickiedNotification; - - Discussion.prototype.isSticky = Model.prop('isSticky'); - Discussion.prototype.canSticky = Model.prop('canSticky'); - - // Add a sticky badge to discussions. - extend(Discussion.prototype, 'badges', function(badges) { - if (this.isSticky()) { - badges.add('sticky', Badge.component({ - label: 'Sticky', - icon: 'thumb-tack', - className: 'badge-sticky', - }), {last: true}); - } - }); - - function toggleSticky() { - this.save({isSticky: !this.isSticky()}).then(discussion => { - if (app.current instanceof DiscussionPage) { - app.current.stream.sync(); - } - m.redraw(); - }); - } - - // Add a sticky control to discussions. - extend(Discussion.prototype, 'moderationControls', function(items) { - if (this.canSticky()) { - items.add('sticky', ActionButton.component({ - label: this.isSticky() ? 'Unsticky' : 'Sticky', - icon: 'thumb-tack', - onclick: toggleSticky.bind(this) - })); - } - }); - - // Add a notification preference. - extend(SettingsPage.prototype, 'notificationTypes', function(items) { - items.add('discussionStickied', { - name: 'discussionStickied', - label: [icon('thumb-tack'), ' Someone stickies a discussion I started'] - }, {after: 'discussionRenamed'}); - }); - - extend(DiscussionList.prototype, 'params', function(params) { - params.include.push('startPost'); - }); - - extend(DiscussionListItem.prototype, 'infoItems', function(items) { - var discussion = this.props.discussion; - - if (discussion.isSticky()) { - var startPost = discussion.startPost(); - if (startPost) { - var excerpt = m('span', truncate(startPost.contentPlain(), 200)); - excerpt.wrapperClass = 'discussion-excerpt'; - items.add('excerpt', excerpt, {last: true}); - } - } - }); -}); diff --git a/extensions/sticky/js/src/addStickyBadge.js b/extensions/sticky/js/src/addStickyBadge.js new file mode 100644 index 000000000..1566c3434 --- /dev/null +++ b/extensions/sticky/js/src/addStickyBadge.js @@ -0,0 +1,15 @@ +import { extend } from 'flarum/extend'; +import Discussion from 'flarum/models/Discussion'; +import Badge from 'flarum/components/Badge'; + +export default function addStickyBadge() { + extend(Discussion.prototype, 'badges', function(badges) { + if (this.isSticky()) { + badges.add('sticky', Badge.component({ + type: 'sticky', + label: app.trans('sticky.stickied'), + icon: 'thumb-tack' + }), 10); + } + }); +} diff --git a/extensions/sticky/js/src/addStickyControl.js b/extensions/sticky/js/src/addStickyControl.js new file mode 100644 index 000000000..e7651fd23 --- /dev/null +++ b/extensions/sticky/js/src/addStickyControl.js @@ -0,0 +1,26 @@ +import { extend } from 'flarum/extend'; +import DiscussionControls from 'flarum/utils/DiscussionControls'; +import DiscussionPage from 'flarum/components/DiscussionPage'; +import Button from 'flarum/components/Button'; + +export default function addStickyControl() { + extend(DiscussionControls, 'moderationControls', function(items, discussion) { + if (discussion.canSticky()) { + items.add('sticky', Button.component({ + children: app.trans(discussion.isSticky() ? 'sticky.unsticky' : 'sticky.sticky'), + icon: 'thumb-tack', + onclick: this.stickyAction.bind(discussion) + })); + } + }); + + DiscussionControls.stickyAction = function() { + this.save({isSticky: !this.isSticky()}).then(() => { + if (app.current instanceof DiscussionPage) { + app.current.stream.update(); + } + + m.redraw(); + }); + }; +} diff --git a/extensions/sticky/js/src/addStickyExcerpt.js b/extensions/sticky/js/src/addStickyExcerpt.js new file mode 100644 index 000000000..932f748d5 --- /dev/null +++ b/extensions/sticky/js/src/addStickyExcerpt.js @@ -0,0 +1,24 @@ +import { extend } from 'flarum/extend'; +import DiscussionList from 'flarum/components/DiscussionList'; +import DiscussionListItem from 'flarum/components/DiscussionListItem'; +import { truncate } from 'flarum/utils/string'; + +export default function addStickyControl() { + extend(DiscussionList.prototype, 'requestParams', function(params) { + params.include.push('startPost'); + }); + + extend(DiscussionListItem.prototype, 'infoItems', function(items) { + const discussion = this.props.discussion; + + if (discussion.isSticky()) { + const startPost = discussion.startPost(); + + if (startPost) { + const excerpt = {truncate(startPost.contentPlain(), 200)}; + + items.add('excerpt', excerpt, 100); + } + } + }); +} diff --git a/extensions/sticky/js/src/components/DiscussionStickiedNotification.js b/extensions/sticky/js/src/components/DiscussionStickiedNotification.js new file mode 100644 index 000000000..b1dc64217 --- /dev/null +++ b/extensions/sticky/js/src/components/DiscussionStickiedNotification.js @@ -0,0 +1,15 @@ +import Notification from 'flarum/components/Notification'; + +export default class DiscussionStickiedNotification extends Notification { + icon() { + return 'thumb-tack'; + } + + href() { + return app.route.discussion(notification.subject(), notification.content().postNumber); + } + + content() { + return app.trans('sticky.discussion_stickied_notification', {user: this.props.notification.sender()}); + } +} diff --git a/extensions/sticky/js/src/components/DiscussionStickiedPost.js b/extensions/sticky/js/src/components/DiscussionStickiedPost.js new file mode 100644 index 000000000..5b3408e69 --- /dev/null +++ b/extensions/sticky/js/src/components/DiscussionStickiedPost.js @@ -0,0 +1,13 @@ +import EventPost from 'flarum/components/EventPost'; + +export default class DiscussionStickiedPost extends EventPost { + icon() { + return 'thumb-tack'; + } + + descriptionKey() { + return this.props.post.content().sticky + ? 'sticky.discussion_stickied_post' + : 'sticky.discussion_unstickied_post'; + } +} diff --git a/extensions/sticky/js/src/components/discussion-stickied-notification.js b/extensions/sticky/js/src/components/discussion-stickied-notification.js deleted file mode 100644 index 81f636e19..000000000 --- a/extensions/sticky/js/src/components/discussion-stickied-notification.js +++ /dev/null @@ -1,14 +0,0 @@ -import Notification from 'flarum/components/notification'; -import username from 'flarum/helpers/username'; - -export default class DiscussionStickiedNotification extends Notification { - view() { - var notification = this.props.notification; - - return super.view({ - href: app.route.discussion(notification.subject(), notification.content().postNumber), - icon: 'thumb-tack', - content: [username(notification.sender()), ' stickied'] - }); - } -} diff --git a/extensions/sticky/js/src/components/discussion-stickied-post.js b/extensions/sticky/js/src/components/discussion-stickied-post.js deleted file mode 100644 index 3d8e786a4..000000000 --- a/extensions/sticky/js/src/components/discussion-stickied-post.js +++ /dev/null @@ -1,9 +0,0 @@ -import EventPost from 'flarum/components/event-post'; - -export default class DiscussionStickiedPost extends EventPost { - view() { - var post = this.props.post; - - return super.view('thumb-tack', [post.content().sticky ? 'stickied' : 'unstickied', ' the discussion.']); - } -} diff --git a/extensions/sticky/js/src/main.js b/extensions/sticky/js/src/main.js new file mode 100644 index 000000000..655a9b09d --- /dev/null +++ b/extensions/sticky/js/src/main.js @@ -0,0 +1,29 @@ +import { extend, notificationType } from 'flarum/extend'; +import app from 'flarum/app'; +import Model from 'flarum/Model'; +import Discussion from 'flarum/models/Discussion'; +import NotificationGrid from 'flarum/components/NotificationGrid'; + +import DiscussionStickiedPost from 'sticky/components/DiscussionStickiedPost'; +import DiscussionStickiedNotification from 'sticky/components/DiscussionStickiedNotification'; +import addStickyBadge from 'sticky/addStickyBadge'; +import addStickyControl from 'sticky/addStickyControl'; +import addStickyExcerpt from 'sticky/addStickyExcerpt'; + +app.postComponents.discussionStickied = DiscussionStickiedPost; +app.notificationComponents.discussionStickied = DiscussionStickiedNotification; + +Discussion.prototype.isSticky = Model.attribute('isSticky'); +Discussion.prototype.canSticky = Model.attribute('canSticky'); + +addStickyBadge(); +addStickyControl(); +addStickyExcerpt(); + +extend(NotificationGrid.prototype, 'notificationTypes', function(items) { + items.add('discussionStickied', { + name: 'discussionStickied', + icon: 'thumb-tack', + label: app.trans('sticky.notify_discussion_stickied') + }); +}); diff --git a/extensions/sticky/less/extension.less b/extensions/sticky/less/extension.less new file mode 100644 index 000000000..046da8ed5 --- /dev/null +++ b/extensions/sticky/less/extension.less @@ -0,0 +1,26 @@ +.Badge--sticky { + background: #d13e32; +} +.DiscussionStickiedPost { + & .EventPost-icon, + & .EventPost-info, + & .EventPost-info a { + color: #d13e32; + } +} +.DiscussionListItem-info .item-excerpt { + margin-top: 8px; + margin-right: 20px; + white-space: normal; + font-size: 12px; + line-height: 1.5em; + color: @muted-more-color; + display: block; + + .DiscussionPage-list & { + display: none; + } + @media @phone { + display: none; + } +} diff --git a/extensions/sticky/less/sticky.less b/extensions/sticky/less/sticky.less deleted file mode 100644 index cc7456d96..000000000 --- a/extensions/sticky/less/sticky.less +++ /dev/null @@ -1,27 +0,0 @@ -.badge-sticky { - background: #d13e32; -} -.discussion-stickied-post { - & .post-icon, & .event-post-info, & .event-post-info a { - color: #d13e32; - } -} -.discussion-excerpt { - margin-top: 8px; - margin-right: 20px; - white-space: normal; - font-size: 12px; - line-height: 1.5em; - color: @fl-body-muted-more-color; - - .discussion-summary .info > li& { - display: block; - - .paned & { - display: none; - } - @media @phone { - display: none; - } - } -} diff --git a/extensions/sticky/locale/en.yml b/extensions/sticky/locale/en.yml new file mode 100644 index 000000000..b871de0d5 --- /dev/null +++ b/extensions/sticky/locale/en.yml @@ -0,0 +1,8 @@ +sticky: + discussion_stickied_notification: "{username} stickied" + discussion_stickied_post: "{username} stickied the discussion." + discussion_unstickied_post: "{username} unstickied the discussion." + notify_discussion_stickied: Someone stickies a discussion I started + stickied: Sticky + sticky: Sticky + unsticky: Unsticky diff --git a/extensions/sticky/src/Events/DiscussionWasStickied.php b/extensions/sticky/src/Events/DiscussionWasStickied.php index 31432f4aa..f1747a406 100644 --- a/extensions/sticky/src/Events/DiscussionWasStickied.php +++ b/extensions/sticky/src/Events/DiscussionWasStickied.php @@ -1,23 +1,23 @@ subscribe('Flarum\Sticky\Listeners\AddClientAssets'); + $events->subscribe('Flarum\Sticky\Listeners\AddApiAttributes'); + $events->subscribe('Flarum\Sticky\Listeners\PersistData'); + $events->subscribe('Flarum\Sticky\Listeners\PinStickiedDiscussionsToTop'); + $events->subscribe('Flarum\Sticky\Listeners\NotifyDiscussionStickied'); + } +} diff --git a/extensions/sticky/src/StickyGambit.php b/extensions/sticky/src/Gambits/StickyGambit.php similarity index 56% rename from extensions/sticky/src/StickyGambit.php rename to extensions/sticky/src/Gambits/StickyGambit.php index 7dd2262d5..2915ad1b9 100644 --- a/extensions/sticky/src/StickyGambit.php +++ b/extensions/sticky/src/Gambits/StickyGambit.php @@ -1,9 +1,9 @@ -getQuery()->where('is_sticky', ! $negate); + $search->getQuery()->where('is_sticky', ! $negate); } } diff --git a/extensions/sticky/src/Handlers/DiscussionStickiedNotifier.php b/extensions/sticky/src/Handlers/DiscussionStickiedNotifier.php deleted file mode 100755 index c36c5a97a..000000000 --- a/extensions/sticky/src/Handlers/DiscussionStickiedNotifier.php +++ /dev/null @@ -1,58 +0,0 @@ -notifications = $notifications; - } - - /** - * Register the listeners for the subscriber. - * - * @param \Illuminate\Contracts\Events\Dispatcher $events - */ - public function subscribe(Dispatcher $events) - { - $events->listen('Flarum\Sticky\Events\DiscussionWasStickied', __CLASS__.'@whenDiscussionWasStickied'); - $events->listen('Flarum\Sticky\Events\DiscussionWasUnstickied', __CLASS__.'@whenDiscussionWasUnstickied'); - } - - public function whenDiscussionWasStickied(DiscussionWasStickied $event) - { - $this->stickyChanged($event->discussion, $event->user, true); - } - - public function whenDiscussionWasUnstickied(DiscussionWasUnstickied $event) - { - $this->stickyChanged($event->discussion, $event->user, false); - } - - protected function stickyChanged(Discussion $discussion, User $user, $isSticky) - { - $post = DiscussionStickiedPost::reply( - $discussion->id, - $user->id, - $isSticky - ); - - $post = $discussion->addPost($post); - - if ($discussion->start_user_id !== $user->id) { - $notification = new DiscussionStickiedNotification($post); - - $this->notifications->sync($notification, $post->exists ? [$discussion->startUser] : []); - } - } -} diff --git a/extensions/sticky/src/Listeners/AddApiAttributes.php b/extensions/sticky/src/Listeners/AddApiAttributes.php new file mode 100755 index 000000000..c3597275c --- /dev/null +++ b/extensions/sticky/src/Listeners/AddApiAttributes.php @@ -0,0 +1,30 @@ +listen(ApiAttributes::class, __CLASS__.'@addAttributes'); + $events->listen(BuildApiAction::class, __CLASS__.'@includeStartPost'); + } + + public function addAttributes(ApiAttributes $event) + { + if ($event->serializer instanceof DiscussionSerializer) { + $event->attributes['isSticky'] = (bool) $event->model->is_sticky; + $event->attributes['canSticky'] = (bool) $event->model->can($event->actor, 'sticky'); + } + } + public function includeStartPost(BuildApiAction $event) + { + if ($event->action instanceof DiscussionsIndexAction) { + $event->addInclude('startPost'); + } + } +} diff --git a/extensions/sticky/src/Listeners/AddClientAssets.php b/extensions/sticky/src/Listeners/AddClientAssets.php new file mode 100755 index 000000000..0d5d8d3bb --- /dev/null +++ b/extensions/sticky/src/Listeners/AddClientAssets.php @@ -0,0 +1,39 @@ +listen(RegisterLocales::class, __CLASS__.'@addLocale'); + $events->listen(BuildClientView::class, __CLASS__.'@addAssets'); + } + + public function addLocale(RegisterLocales $event) + { + $event->addTranslations('en', __DIR__.'/../../locale/en.yml'); + } + + public function addAssets(BuildClientView $event) + { + $event->forumAssets([ + __DIR__.'/../../js/dist/extension.js', + __DIR__.'/../../less/extension.less' + ]); + + $event->forumBootstrapper('sticky/main'); + + $event->forumTranslations([ + 'sticky.discussion_stickied_notification', + 'sticky.discussion_stickied_post', + 'sticky.discussion_unstickied_post', + 'sticky.notify_discussion_stickied', + 'sticky.stickied', + 'sticky.sticky', + 'sticky.unsticky' + ]); + } +} diff --git a/extensions/sticky/src/Listeners/NotifyDiscussionStickied.php b/extensions/sticky/src/Listeners/NotifyDiscussionStickied.php new file mode 100755 index 000000000..15eb094ab --- /dev/null +++ b/extensions/sticky/src/Listeners/NotifyDiscussionStickied.php @@ -0,0 +1,71 @@ +notifications = $notifications; + } + + public function subscribe(Dispatcher $events) + { + $events->listen(RegisterPostTypes::class, __CLASS__.'@registerPostType'); + $events->listen(RegisterNotificationTypes::class, __CLASS__.'@registerNotificationType'); + $events->listen(DiscussionWasStickied::class, __CLASS__.'@whenDiscussionWasStickied'); + $events->listen(DiscussionWasUnstickied::class, __CLASS__.'@whenDiscussionWasUnstickied'); + } + + public function registerPostType(RegisterPostTypes $event) + { + $event->register('Flarum\Sticky\Posts\DiscussionStickiedPost'); + } + + public function registerNotificationType(RegisterNotificationTypes $event) + { + $event->register( + 'Flarum\Sticky\Notifications\DiscussionStickiedBlueprint', + 'Flarum\Api\Serializers\DiscussionBasicSerializer', + ['alert'] + ); + } + + public function whenDiscussionWasStickied(DiscussionWasStickied $event) + { + $this->stickyChanged($event->discussion, $event->user, true); + } + + public function whenDiscussionWasUnstickied(DiscussionWasUnstickied $event) + { + $this->stickyChanged($event->discussion, $event->user, false); + } + + protected function stickyChanged(Discussion $discussion, User $user, $isSticky) + { + $post = DiscussionStickiedPost::reply( + $discussion->id, + $user->id, + $isSticky + ); + + $post = $discussion->mergePost($post); + + if ($discussion->start_user_id !== $user->id) { + $notification = new DiscussionStickiedBlueprint($post); + + $this->notifications->sync($notification, $post->exists ? [$discussion->startUser] : []); + } + } +} diff --git a/extensions/sticky/src/Handlers/StickySaver.php b/extensions/sticky/src/Listeners/PersistData.php similarity index 54% rename from extensions/sticky/src/Handlers/StickySaver.php rename to extensions/sticky/src/Listeners/PersistData.php index d01da961c..c434bd6b6 100755 --- a/extensions/sticky/src/Handlers/StickySaver.php +++ b/extensions/sticky/src/Listeners/PersistData.php @@ -1,24 +1,24 @@ -listen('Flarum\Core\Events\DiscussionWillBeSaved', __CLASS__.'@whenDiscussionWillBeSaved'); + $events->listen(DiscussionWillBeSaved::class, __CLASS__.'@whenDiscussionWillBeSaved'); } public function whenDiscussionWillBeSaved(DiscussionWillBeSaved $event) { - if (isset($event->command->data['isSticky'])) { - $isSticky = (bool) $event->command->data['isSticky']; + if (isset($event->data['attributes']['isSticky'])) { + $isSticky = (bool) $event->data['attributes']['isSticky']; $discussion = $event->discussion; - $user = $event->command->user; + $actor = $event->actor; - $discussion->assertCan($user, 'sticky'); + $discussion->assertCan($actor, 'sticky'); if ((bool) $discussion->is_sticky === $isSticky) { return; @@ -28,8 +28,8 @@ class StickySaver $discussion->raise( $discussion->is_sticky - ? new DiscussionWasStickied($discussion, $user) - : new DiscussionWasUnstickied($discussion, $user) + ? new DiscussionWasStickied($discussion, $actor) + : new DiscussionWasUnstickied($discussion, $actor) ); } } diff --git a/extensions/sticky/src/Handlers/StickySearchModifier.php b/extensions/sticky/src/Listeners/PinStickiedDiscussionsToTop.php similarity index 55% rename from extensions/sticky/src/Handlers/StickySearchModifier.php rename to extensions/sticky/src/Listeners/PinStickiedDiscussionsToTop.php index 021a17cc2..ce3e236a6 100755 --- a/extensions/sticky/src/Handlers/StickySearchModifier.php +++ b/extensions/sticky/src/Listeners/PinStickiedDiscussionsToTop.php @@ -1,25 +1,33 @@ -listen('Flarum\Core\Events\DiscussionSearchWillBePerformed', __CLASS__.'@reorderSearch'); + $events->listen(RegisterDiscussionGambits::class, __CLASS__.'@registerStickyGambit'); + $events->listen(DiscussionSearchWillBePerformed::class, __CLASS__.'@reorderSearch'); + } + + public function registerStickyGambit(RegisterDiscussionGambits $event) + { + $event->gambits->add('Flarum\Sticky\Gambits\StickyGambit'); } public function reorderSearch(DiscussionSearchWillBePerformed $event) { if ($event->criteria->sort === null) { - $query = $event->searcher->getQuery(); + $query = $event->search->getQuery(); - if (!is_array($query->orders)) { + if (! is_array($query->orders)) { $query->orders = []; } - foreach ($event->searcher->getActiveGambits() as $gambit) { + foreach ($event->search->getActiveGambits() as $gambit) { if ($gambit instanceof TagGambit) { array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']); return; @@ -29,7 +37,7 @@ class StickySearchModifier $query->leftJoin('users_discussions', function ($join) use ($event) { $join->on('users_discussions.discussion_id', '=', 'discussions.id') ->where('discussions.is_sticky', '=', true) - ->where('users_discussions.user_id', '=', $event->criteria->user->id); + ->where('users_discussions.user_id', '=', $event->search->getActor()->id); }); // might be quicker to do a subquery in the order clause than a join? array_unshift( diff --git a/extensions/sticky/src/DiscussionStickiedNotification.php b/extensions/sticky/src/Notifications/DiscussionStickiedBlueprint.php similarity index 73% rename from extensions/sticky/src/DiscussionStickiedNotification.php rename to extensions/sticky/src/Notifications/DiscussionStickiedBlueprint.php index b9ff17b0b..3fa44c4c5 100644 --- a/extensions/sticky/src/DiscussionStickiedNotification.php +++ b/extensions/sticky/src/Notifications/DiscussionStickiedBlueprint.php @@ -1,8 +1,8 @@ -post = $post; } - public function getSubject() - { - return $this->post->discussion; - } - public function getSender() { return $this->post->user; } + public function getSubject() + { + return $this->post->discussion; + } + public function getData() { return ['postNumber' => (int) $this->post->number]; @@ -33,6 +33,6 @@ class DiscussionStickiedNotification extends NotificationAbstract public static function getSubjectModel() { - return 'Flarum\Core\Models\Discussion'; + return 'Flarum\Core\Discussions\Discussion'; } } diff --git a/extensions/sticky/src/DiscussionStickiedPost.php b/extensions/sticky/src/Posts/DiscussionStickiedPost.php similarity index 56% rename from extensions/sticky/src/DiscussionStickiedPost.php rename to extensions/sticky/src/Posts/DiscussionStickiedPost.php index 5d94bd947..9af84f4c2 100755 --- a/extensions/sticky/src/DiscussionStickiedPost.php +++ b/extensions/sticky/src/Posts/DiscussionStickiedPost.php @@ -1,35 +1,33 @@ -user_id === $previous->user_id) { + // If the previous post is another 'discussion stickied' post, and it's + // by the same user, then we can merge this post into it. If we find + // that we've in fact reverted the sticky status, delete it. Otherwise, + // update its content. + if ($previous instanceof static && $this->user_id === $previous->user_id) { if ($previous->content['sticky'] != $this->content['sticky']) { - return; + $previous->delete(); + } else { + $previous->content = $this->content; + + $previous->save(); } - $previous->content = $this->content; return $previous; } + $this->save(); + return $this; } diff --git a/extensions/sticky/src/StickyServiceProvider.php b/extensions/sticky/src/StickyServiceProvider.php deleted file mode 100644 index ca2fa685e..000000000 --- a/extensions/sticky/src/StickyServiceProvider.php +++ /dev/null @@ -1,41 +0,0 @@ -extend( - new Extend\EventSubscriber([ - 'Flarum\Sticky\Handlers\StickySaver', - 'Flarum\Sticky\Handlers\StickySearchModifier', - 'Flarum\Sticky\Handlers\DiscussionStickiedNotifier' - ]), - - (new Extend\ForumClient()) - ->assets([ - __DIR__.'/../js/dist/extension.js', - __DIR__.'/../less/sticky.less' - ]), - - new Extend\PostType('Flarum\Sticky\DiscussionStickiedPost'), - - (new Extend\ApiSerializer('Flarum\Api\Serializers\DiscussionSerializer')) - ->attributes(function (&$attributes, $model, $user) { - $attributes['isSticky'] = (bool) $model->is_sticky; - $attributes['canSticky'] = (bool) $model->can($user, 'sticky'); - }), - - // include discussion start posts by default - (new Extend\ApiAction('Flarum\Api\Actions\Discussions\IndexAction')) - ->addInclude('startPost'), - - new Extend\DiscussionGambit('Flarum\Sticky\StickyGambit'), - - (new Extend\NotificationType('Flarum\Sticky\DiscussionStickiedNotification', 'Flarum\Api\Serializers\DiscussionBasicSerializer')) - ->enableByDefault('alert') - ); - } -} From 4f4bc435757e8dc7ef30f029a2353068a05de71d Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Mon, 20 Jul 2015 18:14:52 +0930 Subject: [PATCH 2/2] Update meta --- extensions/sticky/.editorconfig | 32 ++++++ extensions/sticky/.eslintignore | 5 + extensions/sticky/.eslintrc | 171 ++++++++++++++++++++++++++++++++ extensions/sticky/flarum.json | 10 +- 4 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 extensions/sticky/.editorconfig create mode 100644 extensions/sticky/.eslintignore create mode 100644 extensions/sticky/.eslintrc diff --git a/extensions/sticky/.editorconfig b/extensions/sticky/.editorconfig new file mode 100644 index 000000000..5612a5e74 --- /dev/null +++ b/extensions/sticky/.editorconfig @@ -0,0 +1,32 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.js] +indent_style = space +indent_size = 2 + +[*.{css,less}] +indent_style = space +indent_size = 2 + +[*.html] +indent_style = space +indent_size = 2 + +[*.{diff,md}] +trim_trailing_whitespace = false + +[*.php] +indent_style = space +indent_size = 4 diff --git a/extensions/sticky/.eslintignore b/extensions/sticky/.eslintignore new file mode 100644 index 000000000..86b7c8854 --- /dev/null +++ b/extensions/sticky/.eslintignore @@ -0,0 +1,5 @@ +**/bower_components/**/* +**/node_modules/**/* +vendor/**/* +**/Gulpfile.js +**/dist/**/* diff --git a/extensions/sticky/.eslintrc b/extensions/sticky/.eslintrc new file mode 100644 index 000000000..9cebc759d --- /dev/null +++ b/extensions/sticky/.eslintrc @@ -0,0 +1,171 @@ +{ + "parser": "babel-eslint", // https://github.com/babel/babel-eslint + "env": { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments + "browser": true // browser global variables + }, + "ecmaFeatures": { + "arrowFunctions": true, + "blockBindings": true, + "classes": true, + "defaultParams": true, + "destructuring": true, + "forOf": true, + "generators": false, + "modules": true, + "objectLiteralComputedProperties": true, + "objectLiteralDuplicateProperties": false, + "objectLiteralShorthandMethods": true, + "objectLiteralShorthandProperties": true, + "spread": true, + "superInFunctions": true, + "templateStrings": true, + "jsx": true + }, + "globals": { + "m": true, + "app": true, + "$": true, + "moment": true + }, + "rules": { +/** + * Strict mode + */ + // babel inserts "use strict"; for us + "strict": [2, "never"], // http://eslint.org/docs/rules/strict + +/** + * ES6 + */ + "no-var": 2, // http://eslint.org/docs/rules/no-var + "prefer-const": 2, // http://eslint.org/docs/rules/prefer-const + +/** + * Variables + */ + "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow + "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names + "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars + "vars": "local", + "args": "after-used" + }], + "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define + +/** + * Possible errors + */ + "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle + "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign + "no-console": 1, // http://eslint.org/docs/rules/no-console + "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger + "no-alert": 1, // http://eslint.org/docs/rules/no-alert + "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition + "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys + "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case + "no-empty": 2, // http://eslint.org/docs/rules/no-empty + "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign + "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast + "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi + "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign + "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations + "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp + "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace + "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls + "no-reserved-keys": 2, // http://eslint.org/docs/rules/no-reserved-keys + "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays + "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable + "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan + "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var + +/** + * Best practices + */ + "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return + "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly + "default-case": 2, // http://eslint.org/docs/rules/default-case + "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation + "allowKeywords": true + }], + "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq + "no-caller": 2, // http://eslint.org/docs/rules/no-caller + "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return + "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null + "no-eval": 2, // http://eslint.org/docs/rules/no-eval + "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native + "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind + "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough + "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal + "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval + "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks + "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func + "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str + "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign + "no-new": 2, // http://eslint.org/docs/rules/no-new + "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func + "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers + "no-octal": 2, // http://eslint.org/docs/rules/no-octal + "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape + "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign + "no-proto": 2, // http://eslint.org/docs/rules/no-proto + "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare + "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign + "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare + "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences + "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal + "no-with": 2, // http://eslint.org/docs/rules/no-with + "radix": 2, // http://eslint.org/docs/rules/radix + "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top + "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife + "yoda": 2, // http://eslint.org/docs/rules/yoda + +/** + * Style + */ + "indent": [2, 2], // http://eslint.org/docs/rules/indent + "brace-style": [2, // http://eslint.org/docs/rules/brace-style + "1tbs", { + "allowSingleLine": true + }], + "quotes": [ + 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes + ], + "camelcase": [2, { // http://eslint.org/docs/rules/camelcase + "properties": "never" + }], + "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing + "before": false, + "after": true + }], + "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style + "eol-last": 2, // http://eslint.org/docs/rules/eol-last + "func-names": 1, // http://eslint.org/docs/rules/func-names + "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing + "beforeColon": false, + "afterColon": true + }], + "new-cap": [2, { // http://eslint.org/docs/rules/new-cap + "newIsCap": true + }], + "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines + "max": 2 + }], + "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object + "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func + "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces + "no-wrap-func": 2, // http://eslint.org/docs/rules/no-wrap-func + "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle + "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var + "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks + "semi": [2, "always"], // http://eslint.org/docs/rules/semi + "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing + "before": false, + "after": true + }], + "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords + "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks + "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren + "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops + "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case + "spaced-line-comment": 2, // http://eslint.org/docs/rules/spaced-line-comment + } +} diff --git a/extensions/sticky/flarum.json b/extensions/sticky/flarum.json index db93e353e..78880df55 100644 --- a/extensions/sticky/flarum.json +++ b/extensions/sticky/flarum.json @@ -1,10 +1,8 @@ { - "name": "flarum-sticky", + "name": "sticky", "title": "Sticky", "description": "Pin discussions to the top of the list.", - "tags": [ - "discussions" - ], + "keywords": ["discussions"], "version": "0.1.0", "author": { "name": "Toby Zerner", @@ -16,8 +14,8 @@ "php": ">=5.4.0", "flarum": ">0.1.0" }, - "links": { - "github": "https://github.com/flarum/sticky", + "support": { + "source": "https://github.com/flarum/sticky", "issues": "https://github.com/flarum/sticky/issues" } }