Initial commit

This commit is contained in:
Toby Zerner 2015-07-28 15:35:55 +09:30
commit f6c0f22db0
30 changed files with 810 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,5 @@
**/bower_components/**/*
**/node_modules/**/*
vendor/**/*
**/Gulpfile.js
**/dist/**/*

171
extensions/lock/.eslintrc Normal file
View File

@ -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
}
}

4
extensions/lock/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/vendor
composer.phar
.DS_Store
Thumbs.db

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2015 Toby Zerner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,5 @@
<?php
require __DIR__.'/vendor/autoload.php';
return 'Flarum\Lock\Extension';

View File

@ -0,0 +1,7 @@
{
"autoload": {
"psr-4": {
"Flarum\\Lock\\": "src/"
}
}
}

View File

@ -0,0 +1,21 @@
{
"name": "lock",
"title": "Lock",
"description": "End a discussion and don't let anyone add further replies.",
"keywords": ["discussions"],
"version": "0.1.0",
"author": {
"name": "Toby Zerner",
"email": "toby@flarum.org",
"homepage": "http://tobyzerner.com"
},
"license": "MIT",
"require": {
"php": ">=5.4.0",
"flarum": ">0.1.0"
},
"support": {
"source": "https://github.com/flarum/lock",
"issues": "https://github.com/flarum/lock/issues"
}
}

4
extensions/lock/js/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,7 @@
var gulp = require('flarum-gulp');
gulp({
modules: {
'lock': 'src/**/*.js'
}
});

View File

@ -0,0 +1,7 @@
{
"private": true,
"devDependencies": {
"gulp": "^3.8.11",
"flarum-gulp": "git+https://github.com/flarum/gulp.git"
}
}

View File

@ -0,0 +1,15 @@
import { extend } from 'flarum/extend';
import Discussion from 'flarum/models/Discussion';
import Badge from 'flarum/components/Badge';
export default function addLockBadge() {
extend(Discussion.prototype, 'badges', function(badges) {
if (this.isLocked()) {
badges.add('locked', Badge.component({
type: 'locked',
label: app.trans('lock.locked'),
icon: 'lock'
}));
}
});
}

View File

@ -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 addLockControl() {
extend(DiscussionControls, 'moderationControls', function(items, discussion) {
if (discussion.canSticky()) {
items.add('lock', Button.component({
children: app.trans(discussion.isLocked() ? 'lock.unlock' : 'lock.lock'),
icon: 'lock',
onclick: this.lockAction.bind(discussion)
}));
}
});
DiscussionControls.lockAction = function() {
this.save({isLocked: !this.isLocked()}).then(() => {
if (app.current instanceof DiscussionPage) {
app.current.stream.update();
}
m.redraw();
});
};
}

View File

@ -0,0 +1,17 @@
import Notification from 'flarum/components/Notification';
export default class DiscussionLockedNotification extends Notification {
icon() {
return 'lock';
}
href() {
const notification = this.props.notification;
return app.route.discussion(notification.subject(), notification.content().postNumber);
}
content() {
return app.trans('lock.discussion_locked_notification', {user: this.props.notification.sender()});
}
}

View File

@ -0,0 +1,15 @@
import EventPost from 'flarum/components/EventPost';
export default class DiscussionLockedPost extends EventPost {
icon() {
return this.props.post.content().locked
? 'lock'
: 'unlock';
}
descriptionKey() {
return this.props.post.content().locked
? 'lock.discussion_locked_post'
: 'lock.discussion_unlocked_post';
}
}

View File

@ -0,0 +1,27 @@
import { extend } 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 DiscussionLockedPost from 'lock/components/DiscussionLockedPost';
import DiscussionLockedNotification from 'lock/components/DiscussionLockedNotification';
import addLockBadge from 'lock/addLockBadge';
import addLockControl from 'lock/addLockControl';
app.postComponents.discussionLocked = DiscussionLockedPost;
app.notificationComponents.discussionLocked = DiscussionLockedNotification;
Discussion.prototype.isLocked = Model.attribute('isLocked');
Discussion.prototype.canLock = Model.attribute('canLock');
addLockBadge();
addLockControl();
extend(NotificationGrid.prototype, 'notificationTypes', function(items) {
items.add('discussionLocked', {
name: 'discussionLocked',
icon: 'thumb-tack',
label: app.trans('lock.notify_discussion_locked')
});
});

View File

@ -0,0 +1,10 @@
.Badge--locked {
background: #888;
}
.DiscussionLockedPost {
.EventPost-icon,
.EventPost-info,
.EventPost-info a {
color: #888;
}
}

View File

@ -0,0 +1,8 @@
lock:
discussion_locked_notification: "{username} locked"
discussion_locked_post: "{username} locked the discussion."
discussion_unlocked_post: "{username} unlocked the discussion."
notify_discussion_locked: Someone locks a discussion I started
locked: Locked
lock: Lock
unlock: Unlock

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddLockedToDiscussions extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('discussions', function (Blueprint $table) {
$table->boolean('is_locked')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('discussions', function (Blueprint $table) {
$table->dropColumn('is_locked');
});
}
}

View File

@ -0,0 +1,27 @@
<?php namespace Flarum\Lock\Events;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;
class DiscussionWasLocked
{
/**
* @var Discussion
*/
public $discussion;
/**
* @var User
*/
public $user;
/**
* @param Discussion $discussion
* @param User $user
*/
public function __construct(Discussion $discussion, User $user)
{
$this->discussion = $discussion;
$this->user = $user;
}
}

View File

@ -0,0 +1,27 @@
<?php namespace Flarum\Lock\Events;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;
class DiscussionWasUnlocked
{
/**
* @var Discussion
*/
public $discussion;
/**
* @var User
*/
public $user;
/**
* @param Discussion $discussion
* @param User $user
*/
public function __construct(Discussion $discussion, User $user)
{
$this->discussion = $discussion;
$this->user = $user;
}
}

View File

@ -0,0 +1,16 @@
<?php namespace Flarum\Lock;
use Flarum\Support\Extension as BaseExtension;
use Illuminate\Contracts\Events\Dispatcher;
class Extension extends BaseExtension
{
public function boot(Dispatcher $events)
{
$events->subscribe('Flarum\Lock\Listeners\AddClientAssets');
$events->subscribe('Flarum\Lock\Listeners\AddApiAttributes');
$events->subscribe('Flarum\Lock\Listeners\PersistData');
$events->subscribe('Flarum\Lock\Listeners\NotifyDiscussionLocked');
$events->subscribe('Flarum\Lock\Listeners\ConfigurePermissions');
}
}

View File

@ -0,0 +1,14 @@
<?php namespace Flarum\Lock\Gambits;
use Flarum\Core\Search\Search;
use Flarum\Core\Search\RegexGambit;
class LockGambit extends RegexGambit
{
protected $pattern = 'is:locked';
protected function conditions(Search $search, array $matches, $negate)
{
$search->getQuery()->where('is_locked', ! $negate);
}
}

View File

@ -0,0 +1,21 @@
<?php namespace Flarum\Lock\Listeners;
use Flarum\Events\ApiAttributes;
use Illuminate\Contracts\Events\Dispatcher;
use Flarum\Api\Serializers\DiscussionSerializer;
class AddApiAttributes
{
public function subscribe(Dispatcher $events)
{
$events->listen(ApiAttributes::class, [$this, 'addAttributes']);
}
public function addAttributes(ApiAttributes $event)
{
if ($event->serializer instanceof DiscussionSerializer) {
$event->attributes['isLocked'] = (bool) $event->model->is_locked;
$event->attributes['canLock'] = (bool) $event->model->can($event->actor, 'lock');
}
}
}

View File

@ -0,0 +1,39 @@
<?php namespace Flarum\Lock\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('lock/main');
$event->forumTranslations([
'lock.discussion_locked_notification',
'lock.discussion_locked_post',
'lock.discussion_unlocked_post',
'lock.notify_discussion_locked',
'lock.locked',
'lock.lock',
'lock.unlock'
]);
}
}

View File

@ -0,0 +1,23 @@
<?php namespace Flarum\Lock\Listeners;
use Flarum\Events\ModelAllow;
use Flarum\Core\Discussions\Discussion;
class ConfigurePermissions
{
public function subscribe($events)
{
$events->listen(ModelAllow::class, [$this, 'allowDiscussionPermissions'], 10);
}
public function allowDiscussionPermissions(ModelAllow $event)
{
if ($event->model instanceof Discussion &&
$event->model->is_locked &&
$event->action === 'reply') {
if (! $event->model->can($event->actor, 'lock')) {
return false;
}
}
}
}

View File

@ -0,0 +1,71 @@
<?php namespace Flarum\Lock\Listeners;
use Flarum\Events\RegisterPostTypes;
use Flarum\Events\RegisterNotificationTypes;
use Flarum\Lock\Posts\DiscussionLockedPost;
use Flarum\Lock\Notifications\DiscussionLockedBlueprint;
use Flarum\Lock\Events\DiscussionWasLocked;
use Flarum\Lock\Events\DiscussionWasUnlocked;
use Flarum\Core\Notifications\NotificationSyncer;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;
use Illuminate\Contracts\Events\Dispatcher;
class NotifyDiscussionLocked
{
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(DiscussionWasLocked::class, [$this, 'whenDiscussionWasLocked']);
$events->listen(DiscussionWasUnlocked::class, [$this, 'whenDiscussionWasUnlocked']);
}
public function registerPostType(RegisterPostTypes $event)
{
$event->register('Flarum\Lock\Posts\DiscussionLockedPost');
}
public function registerNotificationType(RegisterNotificationTypes $event)
{
$event->register(
'Flarum\Lock\Notifications\DiscussionLockedBlueprint',
'Flarum\Api\Serializers\DiscussionBasicSerializer',
['alert']
);
}
public function whenDiscussionWasLocked(DiscussionWasLocked $event)
{
$this->stickyChanged($event->discussion, $event->user, true);
}
public function whenDiscussionWasUnlocked(DiscussionWasUnlocked $event)
{
$this->stickyChanged($event->discussion, $event->user, false);
}
protected function stickyChanged(Discussion $discussion, User $user, $isLocked)
{
$post = DiscussionLockedPost::reply(
$discussion->id,
$user->id,
$isLocked
);
$post = $discussion->mergePost($post);
if ($discussion->start_user_id !== $user->id) {
$notification = new DiscussionLockedBlueprint($post);
$this->notifications->sync($notification, $post->exists ? [$discussion->startUser] : []);
}
}
}

View File

@ -0,0 +1,36 @@
<?php namespace Flarum\Lock\Listeners;
use Flarum\Lock\Events\DiscussionWasLocked;
use Flarum\Lock\Events\DiscussionWasUnlocked;
use Flarum\Events\DiscussionWillBeSaved;
class PersistData
{
public function subscribe($events)
{
$events->listen(DiscussionWillBeSaved::class, [$this, 'whenDiscussionWillBeSaved']);
}
public function whenDiscussionWillBeSaved(DiscussionWillBeSaved $event)
{
if (isset($event->data['attributes']['isLocked'])) {
$isLocked = (bool) $event->data['attributes']['isLocked'];
$discussion = $event->discussion;
$actor = $event->actor;
$discussion->assertCan($actor, 'lock');
if ((bool) $discussion->is_locked === $isLocked) {
return;
}
$discussion->is_locked = $isLocked;
$discussion->raise(
$discussion->is_locked
? new DiscussionWasLocked($discussion, $actor)
: new DiscussionWasUnlocked($discussion, $actor)
);
}
}
}

View File

@ -0,0 +1,39 @@
<?php namespace Flarum\Lock\Notifications;
use Flarum\Sticky\Posts\DiscussionLockedPost;
use Flarum\Core\Notifications\Blueprint;
class DiscussionLockedBlueprint implements Blueprint
{
protected $post;
public function __construct(DiscussionLockedPost $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 'discussionLocked';
}
public static function getSubjectModel()
{
return 'Flarum\Core\Discussions\Discussion';
}
}

View File

@ -0,0 +1,64 @@
<?php namespace Flarum\Lock\Posts;
use Flarum\Core\Posts\Post;
use Flarum\Core\Posts\EventPost;
use Flarum\Core\Posts\MergeablePost;
class DiscussionLockedPost extends EventPost implements MergeablePost
{
public static $type = 'discussionLocked';
public function saveAfter(Post $previous)
{
// If the previous post is another 'discussion locked' 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 locked status, delete it. Otherwise,
// update its content.
if ($previous instanceof static && $this->user_id === $previous->user_id) {
if ($previous->content['locked'] != $this->content['locked']) {
$previous->delete();
} else {
$previous->content = $this->content;
$previous->save();
}
return $previous;
}
$this->save();
return $this;
}
/**
* Create a new instance in reply to a discussion.
*
* @param integer $discussionId
* @param integer $userId
* @param boolean $isLocked
* @return static
*/
public static function reply($discussionId, $userId, $isLocked)
{
$post = new static;
$post->content = static::buildContent($isLocked);
$post->time = time();
$post->discussion_id = $discussionId;
$post->user_id = $userId;
return $post;
}
/**
* Build the content attribute.
*
* @param boolean $isLocked Whether or not the discussion is stickied.
* @return array
*/
public static function buildContent($isLocked)
{
return ['locked' => (bool) $isLocked];
}
}