1
0
mirror of https://github.com/flarum/core.git synced 2025-08-02 22:47:33 +02:00

Update for composer branch

Also remove sticky notification
This commit is contained in:
Toby Zerner
2015-10-11 11:43:13 +10:30
parent 0d7a7682be
commit 37d7874705
33 changed files with 637 additions and 448 deletions

View File

@@ -2,3 +2,5 @@
composer.phar
.DS_Store
Thumbs.db
node_modules
bower_components

View File

@@ -9,6 +9,13 @@
* file that was distributed with this source code.
*/
require __DIR__.'/vendor/autoload.php';
use Flarum\Sticky\Listener;
use Illuminate\Contracts\Events\Dispatcher;
return 'Flarum\Sticky\Extension';
return function (Dispatcher $events) {
$events->subscribe(Listener\AddApiAttributes::class);
$events->subscribe(Listener\AddClientAssets::class);
$events->subscribe(Listener\CreatePostWhenDiscussionIsStickied::class);
$events->subscribe(Listener\PinStickiedDiscussionsToTop::class);
$events->subscribe(Listener\SaveStickyToDatabase::class);
};

View File

@@ -1,10 +1,34 @@
{
"name": "flarum/sticky",
"description": "Pin discussions to the top of the list.",
"type": "flarum-extension",
"license": "MIT",
"authors": [
{
"name": "Toby Zerner",
"email": "toby.zerner@gmail.com"
}
],
"support": {
"issues": "https://github.com/flarum/core/issues",
"source": "https://github.com/flarum/sticky"
},
"require": {
"flarum/core": "^0.1.0-beta.3"
},
"autoload": {
"psr-4": {
"Flarum\\Sticky\\": "src/"
}
},
"scripts": {
"style": "phpcs --standard=PSR2 -np src"
"extra": {
"flarum-extension": {
"title": "Sticky",
"icon": {
"name": "thumb-tack",
"backgroundColor": "#D13E32",
"color": "#fff"
}
}
}
}

View File

@@ -1,25 +0,0 @@
{
"name": "sticky",
"title": "Sticky",
"description": "Pin discussions to the top of the list.",
"keywords": ["moderation"],
"version": "0.1.0-beta.2",
"author": {
"name": "Toby Zerner",
"email": "toby@flarum.org",
"homepage": "http://tobyzerner.com"
},
"license": "MIT",
"require": {
"flarum": ">=0.1.0-beta.2"
},
"support": {
"source": "https://github.com/flarum/sticky",
"issues": "https://github.com/flarum/core/issues"
},
"icon": {
"name": "thumb-tack",
"backgroundColor": "#D13E32",
"color": "#fff"
}
}

View File

@@ -1,4 +0,0 @@
bower_components
node_modules
mithril.js
dist

View File

@@ -2,6 +2,6 @@ var gulp = require('flarum-gulp');
gulp({
modules: {
'sticky': 'src/**/*.js'
'flarum/sticky': 'src/**/*.js'
}
});

View File

@@ -0,0 +1,26 @@
System.register('flarum/sticky/main', ['flarum/extend', 'flarum/app', 'flarum/components/PermissionGrid'], function (_export) {
'use strict';
var extend, app, PermissionGrid;
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
}, function (_flarumApp) {
app = _flarumApp['default'];
}, function (_flarumComponentsPermissionGrid) {
PermissionGrid = _flarumComponentsPermissionGrid['default'];
}],
execute: function () {
app.initializers.add('sticky', function () {
extend(PermissionGrid.prototype, 'moderateItems', function (items) {
items.add('sticky', {
icon: 'thumb-tack',
label: 'Sticky discussions',
permission: 'discussion.sticky'
}, 95);
});
});
}
};
});

View File

@@ -2,6 +2,6 @@ var gulp = require('flarum-gulp');
gulp({
modules: {
'sticky': 'src/**/*.js'
'flarum/sticky': 'src/**/*.js'
}
});

View File

@@ -0,0 +1,224 @@
System.register('flarum/sticky/addStickyBadge', ['flarum/extend', 'flarum/models/Discussion', 'flarum/components/Badge'], function (_export) {
'use strict';
var extend, Discussion, Badge;
_export('default', addStickyBadge);
function addStickyBadge() {
extend(Discussion.prototype, 'badges', function (badges) {
if (this.isSticky()) {
badges.add('sticky', Badge.component({
type: 'sticky',
label: app.trans('flarum-sticky.forum.stickied'),
icon: 'thumb-tack'
}), 10);
}
});
}
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
}, function (_flarumModelsDiscussion) {
Discussion = _flarumModelsDiscussion['default'];
}, function (_flarumComponentsBadge) {
Badge = _flarumComponentsBadge['default'];
}],
execute: function () {}
};
});;System.register('flarum/sticky/addStickyControl', ['flarum/extend', 'flarum/utils/DiscussionControls', 'flarum/components/DiscussionPage', 'flarum/components/Button'], function (_export) {
'use strict';
var extend, DiscussionControls, DiscussionPage, Button;
_export('default', addStickyControl);
function addStickyControl() {
extend(DiscussionControls, 'moderationControls', function (items, discussion) {
if (discussion.canSticky()) {
items.add('sticky', Button.component({
children: app.trans(discussion.isSticky() ? 'flarum-sticky.forum.unsticky' : 'flarum-sticky.forum.sticky'),
icon: 'thumb-tack',
onclick: this.stickyAction.bind(discussion)
}));
}
});
DiscussionControls.stickyAction = function () {
this.save({ isSticky: !this.isSticky() }).then(function () {
if (app.current instanceof DiscussionPage) {
app.current.stream.update();
}
m.redraw();
});
};
}
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
}, function (_flarumUtilsDiscussionControls) {
DiscussionControls = _flarumUtilsDiscussionControls['default'];
}, function (_flarumComponentsDiscussionPage) {
DiscussionPage = _flarumComponentsDiscussionPage['default'];
}, function (_flarumComponentsButton) {
Button = _flarumComponentsButton['default'];
}],
execute: function () {}
};
});;System.register('flarum/sticky/addStickyExcerpt', ['flarum/extend', 'flarum/components/DiscussionList', 'flarum/components/DiscussionListItem', 'flarum/utils/string'], function (_export) {
'use strict';
var extend, DiscussionList, DiscussionListItem, truncate;
_export('default', addStickyControl);
function addStickyControl() {
extend(DiscussionList.prototype, 'requestParams', 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',
null,
truncate(startPost.contentPlain(), 200)
);
items.add('excerpt', excerpt, -100);
}
}
});
}
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
}, function (_flarumComponentsDiscussionList) {
DiscussionList = _flarumComponentsDiscussionList['default'];
}, function (_flarumComponentsDiscussionListItem) {
DiscussionListItem = _flarumComponentsDiscussionListItem['default'];
}, function (_flarumUtilsString) {
truncate = _flarumUtilsString.truncate;
}],
execute: function () {}
};
});;System.register('flarum/sticky/main', ['flarum/extend', 'flarum/app', 'flarum/Model', 'flarum/models/Discussion', 'flarum/sticky/components/DiscussionStickiedPost', 'flarum/sticky/addStickyBadge', 'flarum/sticky/addStickyControl', 'flarum/sticky/addStickyExcerpt'], function (_export) {
'use strict';
var extend, notificationType, app, Model, Discussion, DiscussionStickiedPost, addStickyBadge, addStickyControl, addStickyExcerpt;
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
notificationType = _flarumExtend.notificationType;
}, function (_flarumApp) {
app = _flarumApp['default'];
}, function (_flarumModel) {
Model = _flarumModel['default'];
}, function (_flarumModelsDiscussion) {
Discussion = _flarumModelsDiscussion['default'];
}, function (_flarumStickyComponentsDiscussionStickiedPost) {
DiscussionStickiedPost = _flarumStickyComponentsDiscussionStickiedPost['default'];
}, function (_flarumStickyAddStickyBadge) {
addStickyBadge = _flarumStickyAddStickyBadge['default'];
}, function (_flarumStickyAddStickyControl) {
addStickyControl = _flarumStickyAddStickyControl['default'];
}, function (_flarumStickyAddStickyExcerpt) {
addStickyExcerpt = _flarumStickyAddStickyExcerpt['default'];
}],
execute: function () {
app.postComponents.discussionStickied = DiscussionStickiedPost;
Discussion.prototype.isSticky = Model.attribute('isSticky');
Discussion.prototype.canSticky = Model.attribute('canSticky');
addStickyBadge();
addStickyControl();
addStickyExcerpt();
}
};
});;System.register('flarum/sticky/components/DiscussionStickiedNotification', ['flarum/components/Notification'], function (_export) {
'use strict';
var Notification, DiscussionStickiedNotification;
return {
setters: [function (_flarumComponentsNotification) {
Notification = _flarumComponentsNotification['default'];
}],
execute: function () {
DiscussionStickiedNotification = (function (_Notification) {
babelHelpers.inherits(DiscussionStickiedNotification, _Notification);
function DiscussionStickiedNotification() {
babelHelpers.classCallCheck(this, DiscussionStickiedNotification);
babelHelpers.get(Object.getPrototypeOf(DiscussionStickiedNotification.prototype), 'constructor', this).apply(this, arguments);
}
babelHelpers.createClass(DiscussionStickiedNotification, [{
key: 'icon',
value: function icon() {
return 'thumb-tack';
}
}, {
key: 'href',
value: function href() {
var notification = this.props.notification;
return app.route.discussion(notification.subject(), notification.content().postNumber);
}
}, {
key: 'content',
value: function content() {
return app.trans('flarum-sticky.forum.discussion_stickied_notification', { user: this.props.notification.sender() });
}
}]);
return DiscussionStickiedNotification;
})(Notification);
_export('default', DiscussionStickiedNotification);
}
};
});;System.register('flarum/sticky/components/DiscussionStickiedPost', ['flarum/components/EventPost'], function (_export) {
'use strict';
var EventPost, DiscussionStickiedPost;
return {
setters: [function (_flarumComponentsEventPost) {
EventPost = _flarumComponentsEventPost['default'];
}],
execute: function () {
DiscussionStickiedPost = (function (_EventPost) {
babelHelpers.inherits(DiscussionStickiedPost, _EventPost);
function DiscussionStickiedPost() {
babelHelpers.classCallCheck(this, DiscussionStickiedPost);
babelHelpers.get(Object.getPrototypeOf(DiscussionStickiedPost.prototype), 'constructor', this).apply(this, arguments);
}
babelHelpers.createClass(DiscussionStickiedPost, [{
key: 'icon',
value: function icon() {
return 'thumb-tack';
}
}, {
key: 'descriptionKey',
value: function descriptionKey() {
return this.props.post.content().sticky ? 'flarum-sticky.forum.discussion_stickied_post' : 'flarum-sticky.forum.discussion_unstickied_post';
}
}]);
return DiscussionStickiedPost;
})(EventPost);
_export('default', DiscussionStickiedPost);
}
};
});

View File

@@ -7,7 +7,7 @@ export default function addStickyBadge() {
if (this.isSticky()) {
badges.add('sticky', Badge.component({
type: 'sticky',
label: app.trans('sticky.stickied'),
label: app.trans('flarum-sticky.forum.stickied'),
icon: 'thumb-tack'
}), 10);
}

View File

@@ -7,7 +7,7 @@ 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'),
children: app.trans(discussion.isSticky() ? 'flarum-sticky.forum.unsticky' : 'flarum-sticky.forum.sticky'),
icon: 'thumb-tack',
onclick: this.stickyAction.bind(discussion)
}));

View File

@@ -12,6 +12,6 @@ export default class DiscussionStickiedNotification extends Notification {
}
content() {
return app.trans('sticky.discussion_stickied_notification', {user: this.props.notification.sender()});
return app.trans('flarum-sticky.forum.discussion_stickied_notification', {user: this.props.notification.sender()});
}
}

View File

@@ -7,7 +7,7 @@ export default class DiscussionStickiedPost extends EventPost {
descriptionKey() {
return this.props.post.content().sticky
? 'sticky.discussion_stickied_post'
: 'sticky.discussion_unstickied_post';
? 'flarum-sticky.forum.discussion_stickied_post'
: 'flarum-sticky.forum.discussion_unstickied_post';
}
}

View File

@@ -2,28 +2,17 @@ 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';
import DiscussionStickiedPost from 'flarum/sticky/components/DiscussionStickiedPost';
import addStickyBadge from 'flarum/sticky/addStickyBadge';
import addStickyControl from 'flarum/sticky/addStickyControl';
import addStickyExcerpt from 'flarum/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')
});
});
addStickyExcerpt();

View File

@@ -1,8 +0,0 @@
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

View File

@@ -8,18 +8,13 @@
* file that was distributed with this source code.
*/
namespace Flarum\Migrations\Sticky;
namespace Flarum\Sticky\Migration;
use Flarum\Database\AbstractMigration;
use Illuminate\Database\Schema\Blueprint;
use Flarum\Migrations\Migration;
class AddStickyToDiscussions extends Migration
class AddStickyToDiscussions extends AbstractMigration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$this->schema->table('discussions', function (Blueprint $table) {
@@ -27,11 +22,6 @@ class AddStickyToDiscussions extends Migration
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$this->schema->table('discussions', function (Blueprint $table) {

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# This script compiles the extension so that it can be used in a Flarum
# installation. It should be run from the root directory of the extension.
base=$PWD
cd "${base}/js"
if [ -f bower.json ]; then
bower install
fi
for app in forum admin; do
cd "${base}/js"
if [ -d $app ]; then
cd $app
if [ -f bower.json ]; then
bower install
fi
npm install
gulp --production
fi
done

View File

@@ -8,10 +8,10 @@
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Events;
namespace Flarum\Sticky\Event;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;
use Flarum\Core\Discussion;
use Flarum\Core\User;
class DiscussionWasStickied
{

View File

@@ -8,10 +8,10 @@
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Events;
namespace Flarum\Sticky\Event;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;
use Flarum\Core\Discussion;
use Flarum\Core\User;
class DiscussionWasUnstickied
{

View File

@@ -1,26 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky;
use Flarum\Support\Extension as BaseExtension;
use Illuminate\Events\Dispatcher;
class Extension extends BaseExtension
{
public function listen(Dispatcher $events)
{
$events->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');
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Gambit;
use Flarum\Core\Search\AbstractSearch;
use Flarum\Core\Search\AbstractRegexGambit;
class StickyGambit extends AbstractRegexGambit
{
/**
* {@inheritdoc}
*/
protected $pattern = 'is:sticky';
/**
* {@inheritdoc}
*/
protected function conditions(AbstractSearch $search, array $matches, $negate)
{
$search->getQuery()->where('is_sticky', ! $negate);
}
}

View File

@@ -1,37 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Gambits;
use Flarum\Core\Search\Search;
use Flarum\Core\Search\RegexGambit;
class StickyGambit extends RegexGambit
{
/**
* The gambit's regex pattern.
*
* @var string
*/
protected $pattern = 'is:sticky';
/**
* Apply conditions to the searcher, given matches from the gambit's
* regex.
*
* @param array $matches The matches from the gambit's regex.
* @param \Flarum\Core\Search\SearcherInterface $searcher
* @return void
*/
protected function conditions(Search $search, array $matches, $negate)
{
$search->getQuery()->where('is_sticky', ! $negate);
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listener;
use Flarum\Api\Controller\ListDiscussionsController;
use Flarum\Api\Serializer\DiscussionSerializer;
use Flarum\Event\ConfigureApiController;
use Flarum\Event\PrepareApiAttributes;
use Illuminate\Contracts\Events\Dispatcher;
class AddApiAttributes
{
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(PrepareApiAttributes::class, [$this, 'prepareApiAttributes']);
$events->listen(ConfigureApiController::class, [$this, 'includeStartPost']);
}
/**
* @param PrepareApiAttributes $event
*/
public function prepareApiAttributes(PrepareApiAttributes $event)
{
if ($event->isSerializer(DiscussionSerializer::class)) {
$event->attributes['isSticky'] = (bool) $event->model->is_sticky;
$event->attributes['canSticky'] = (bool) $event->actor->can('sticky', $event->model);
}
}
/**
* @param ConfigureApiController $event
*/
public function includeStartPost(ConfigureApiController $event)
{
if ($event->isController(ListDiscussionsController::class)) {
$event->addInclude('startPost');
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listener;
use Flarum\Event\ConfigureClientView;
use Illuminate\Contracts\Events\Dispatcher;
class AddClientAssets
{
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(ConfigureClientView::class, [$this, 'addAssets']);
}
/**
* @param ConfigureClientView $event
*/
public function addAssets(ConfigureClientView $event)
{
if ($event->isForum()) {
$event->addAssets([
__DIR__.'/../../js/forum/dist/extension.js',
__DIR__.'/../../less/forum/extension.less'
]);
$event->addBootstrapper('flarum/sticky/main');
$event->addTranslations('flarum-sticky.forum');
}
if ($event->isAdmin()) {
$event->addAssets([
__DIR__.'/../../js/admin/dist/extension.js'
]);
$event->addBootstrapper('flarum/sticky/main');
}
}
}

View File

@@ -0,0 +1,72 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listener;
use Flarum\Core\Discussion;
use Flarum\Core\User;
use Flarum\Event\ConfigurePostTypes;
use Flarum\Sticky\Event\DiscussionWasStickied;
use Flarum\Sticky\Event\DiscussionWasUnstickied;
use Flarum\Sticky\Post\DiscussionStickiedPost;
use Illuminate\Contracts\Events\Dispatcher;
class CreatePostWhenDiscussionIsStickied
{
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(ConfigurePostTypes::class, [$this, 'configurePostTypes']);
$events->listen(DiscussionWasStickied::class, [$this, 'whenDiscussionWasStickied']);
$events->listen(DiscussionWasUnstickied::class, [$this, 'whenDiscussionWasUnstickied']);
}
/**
* @param ConfigurePostTypes $event
*/
public function configurePostTypes(ConfigurePostTypes $event)
{
$event->add(DiscussionStickiedPost::class);
}
/**
* @param DiscussionWasStickied $event
*/
public function whenDiscussionWasStickied(DiscussionWasStickied $event)
{
$this->stickyChanged($event->discussion, $event->user, true);
}
/**
* @param DiscussionWasUnstickied $event
*/
public function whenDiscussionWasUnstickied(DiscussionWasUnstickied $event)
{
$this->stickyChanged($event->discussion, $event->user, false);
}
/**
* @param Discussion $discussion
* @param User $user
* @param bool $isSticky
*/
protected function stickyChanged(Discussion $discussion, User $user, $isSticky)
{
$post = DiscussionStickiedPost::reply(
$discussion->id,
$user->id,
$isSticky
);
$discussion->mergePost($post);
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listener;
use Flarum\Event\ConfigureDiscussionGambits;
use Flarum\Event\DiscussionSearchWillBePerformed;
use Flarum\Sticky\Gambit\StickyGambit;
use Flarum\Tags\Gambit\TagGambit;
use Illuminate\Contracts\Events\Dispatcher;
class PinStickiedDiscussionsToTop
{
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(ConfigureDiscussionGambits::class, [$this, 'addStickyGambit']);
$events->listen(DiscussionSearchWillBePerformed::class, [$this, 'reorderSearch']);
}
/**
* @param ConfigureDiscussionGambits $event
*/
public function addStickyGambit(ConfigureDiscussionGambits $event)
{
$event->gambits->add(StickyGambit::class);
}
/**
* @param DiscussionSearchWillBePerformed $event
*/
public function reorderSearch(DiscussionSearchWillBePerformed $event)
{
if ($event->criteria->sort === null) {
$search = $event->search;
$query = $search->getQuery();
if (! is_array($query->orders)) {
$query->orders = [];
}
foreach ($search->getActiveGambits() as $gambit) {
if ($gambit instanceof TagGambit) {
array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']);
return;
}
}
$query->leftJoin('users_discussions', function ($join) use ($search) {
$join->on('users_discussions.discussion_id', '=', 'discussions.id')
->where('discussions.is_sticky', '=', true)
->where('users_discussions.user_id', '=', $search->getActor()->id);
});
// TODO: Might be quicker to do a subquery in the order clause than a join?
$grammar = $query->getGrammar();
$readNumber = $grammar->wrap('users_discussions.read_number');
$lastPostNumber = $grammar->wrap('discussions.last_post_number');
array_unshift($query->orders, [
'type' => 'raw',
'sql' => "(is_sticky AND ($readNumber IS NULL OR $lastPostNumber > $readNumber)) desc"
]);
}
}
}

View File

@@ -8,19 +8,29 @@
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listeners;
namespace Flarum\Sticky\Listener;
use Flarum\Sticky\Events\DiscussionWasStickied;
use Flarum\Sticky\Events\DiscussionWasUnstickied;
use Flarum\Events\DiscussionWillBeSaved;
use Flarum\Core\Access\AssertPermissionTrait;
use Flarum\Sticky\Event\DiscussionWasStickied;
use Flarum\Sticky\Event\DiscussionWasUnstickied;
use Flarum\Event\DiscussionWillBeSaved;
use Illuminate\Contracts\Events\Dispatcher;
class PersistData
class SaveStickyToDatabase
{
public function subscribe($events)
use AssertPermissionTrait;
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(DiscussionWillBeSaved::class, [$this, 'whenDiscussionWillBeSaved']);
}
/**
* @param DiscussionWillBeSaved $event
*/
public function whenDiscussionWillBeSaved(DiscussionWillBeSaved $event)
{
if (isset($event->data['attributes']['isSticky'])) {
@@ -28,7 +38,7 @@ class PersistData
$discussion = $event->discussion;
$actor = $event->actor;
$discussion->assertCan($actor, 'sticky');
$this->assertCan($actor, 'sticky', $discussion);
if ((bool) $discussion->is_sticky === $isSticky) {
return;

View File

@@ -1,41 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listeners;
use Flarum\Events\ApiAttributes;
use Flarum\Events\BuildApiAction;
use Illuminate\Contracts\Events\Dispatcher;
use Flarum\Api\Serializers\DiscussionSerializer;
use Flarum\Api\Actions\Discussions\IndexAction as DiscussionsIndexAction;
class AddApiAttributes
{
public function subscribe(Dispatcher $events)
{
$events->listen(ApiAttributes::class, [$this, 'addAttributes']);
$events->listen(BuildApiAction::class, [$this, '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');
}
}
}

View File

@@ -1,55 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listeners;
use Flarum\Events\RegisterLocales;
use Flarum\Events\BuildClientView;
use Illuminate\Contracts\Events\Dispatcher;
class AddClientAssets
{
public function subscribe(Dispatcher $events)
{
$events->listen(RegisterLocales::class, [$this, 'addLocale']);
$events->listen(BuildClientView::class, [$this, 'addAssets']);
}
public function addLocale(RegisterLocales $event)
{
$event->addTranslations('en', __DIR__.'/../../locale/en.yml');
}
public function addAssets(BuildClientView $event)
{
$event->forumAssets([
__DIR__.'/../../js/forum/dist/extension.js',
__DIR__.'/../../less/forum/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'
]);
$event->adminAssets([
__DIR__.'/../../js/admin/dist/extension.js'
]);
$event->adminBootstrapper('sticky/main');
}
}

View File

@@ -1,81 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listeners;
use Flarum\Events\RegisterPostTypes;
use Flarum\Events\RegisterNotificationTypes;
use Flarum\Sticky\Posts\DiscussionStickiedPost;
use Flarum\Sticky\Notifications\DiscussionStickiedBlueprint;
use Flarum\Sticky\Events\DiscussionWasStickied;
use Flarum\Sticky\Events\DiscussionWasUnstickied;
use Flarum\Core\Notifications\NotificationSyncer;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;
use Illuminate\Contracts\Events\Dispatcher;
class NotifyDiscussionStickied
{
protected $notifications;
public function __construct(NotificationSyncer $notifications)
{
$this->notifications = $notifications;
}
public function subscribe(Dispatcher $events)
{
$events->listen(RegisterPostTypes::class, [$this, 'registerPostType']);
$events->listen(RegisterNotificationTypes::class, [$this, 'registerNotificationType']);
$events->listen(DiscussionWasStickied::class, [$this, 'whenDiscussionWasStickied']);
$events->listen(DiscussionWasUnstickied::class, [$this, '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] : []);
}
}
}

View File

@@ -1,64 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Listeners;
use Flarum\Events\RegisterDiscussionGambits;
use Flarum\Events\DiscussionSearchWillBePerformed;
use Flarum\Tags\Gambits\TagGambit;
use Illuminate\Contracts\Events\Dispatcher;
class PinStickiedDiscussionsToTop
{
public function subscribe(Dispatcher $events)
{
$events->listen(RegisterDiscussionGambits::class, [$this, 'registerStickyGambit']);
$events->listen(DiscussionSearchWillBePerformed::class, [$this, '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->search->getQuery();
if (! is_array($query->orders)) {
$query->orders = [];
}
foreach ($event->search->getActiveGambits() as $gambit) {
if ($gambit instanceof TagGambit) {
array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']);
return;
}
}
$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->search->getActor()->id);
});
// might be quicker to do a subquery in the order clause than a join?
$prefix = app('Illuminate\Database\ConnectionInterface')->getTablePrefix();
array_unshift(
$query->orders,
[
'type' => 'raw',
'sql' => "(is_sticky AND ({$prefix}users_discussions.read_number IS NULL OR {$prefix}discussions.last_post_number > {$prefix}users_discussions.read_number)) desc"
]
);
}
}
}

View File

@@ -1,49 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Notifications;
use Flarum\Sticky\Posts\DiscussionStickiedPost;
use Flarum\Core\Notifications\Blueprint;
class DiscussionStickiedBlueprint implements Blueprint
{
protected $post;
public function __construct(DiscussionStickiedPost $post)
{
$this->post = $post;
}
public function getSender()
{
return $this->post->user;
}
public function getSubject()
{
return $this->post->discussion;
}
public function getData()
{
return ['postNumber' => (int) $this->post->number];
}
public static function getType()
{
return 'discussionStickied';
}
public static function getSubjectModel()
{
return 'Flarum\Core\Discussions\Discussion';
}
}

View File

@@ -8,16 +8,22 @@
* file that was distributed with this source code.
*/
namespace Flarum\Sticky\Posts;
namespace Flarum\Sticky\Post;
use Flarum\Core\Posts\Post;
use Flarum\Core\Posts\EventPost;
use Flarum\Core\Posts\MergeablePost;
use Flarum\Core\Post;
use Flarum\Core\Post\AbstractEventPost;
use Flarum\Core\Post\MergeableInterface;
class DiscussionStickiedPost extends EventPost implements MergeablePost
class DiscussionStickiedPost extends AbstractEventPost implements MergeableInterface
{
/**
* {@inheritdoc}
*/
public static $type = 'discussionStickied';
/**
* {@inheritdoc}
*/
public function saveAfter(Post $previous)
{
// If the previous post is another 'discussion stickied' post, and it's