Update for composer branch

This commit is contained in:
Toby Zerner 2015-10-11 18:51:25 +10:30
parent a1e01938ef
commit 5f9d45c4ab
43 changed files with 1277 additions and 517 deletions

View File

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

View File

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

View File

@ -1,10 +1,34 @@
{
"name": "flarum/flags",
"description": "Allow users to flag posts for moderator review.",
"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/flags"
},
"require": {
"flarum/core": "^0.1.0-beta.3"
},
"autoload": {
"psr-4": {
"Flarum\\Flags\\": "src/"
}
},
"scripts": {
"style": "phpcs --standard=PSR2 -np src"
"extra": {
"flarum-extension": {
"title": "Flags",
"icon": {
"name": "flag",
"backgroundColor": "#e92693",
"color": "#fff"
}
}
}
}

View File

@ -1,25 +0,0 @@
{
"name": "flags",
"title": "Flags",
"description": "Allow users to flag posts for moderator review.",
"keywords": [],
"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/flags",
"issues": "https://github.com/flarum/core/issues"
},
"icon": {
"name": "flag",
"backgroundColor": "#e92693",
"color": "#fff"
}
}

View File

@ -1,3 +0,0 @@
bower_components
node_modules
dist

View File

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

View File

@ -0,0 +1,34 @@
System.register('flarum/flags/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('flarum-flags', function () {
extend(PermissionGrid.prototype, 'moderateItems', function (items) {
items.add('viewFlags', {
icon: 'flag',
label: 'View flagged posts',
permission: 'discussion.viewFlags'
}, 65);
});
extend(PermissionGrid.prototype, 'replyItems', function (items) {
items.add('flagPosts', {
icon: 'flag',
label: 'Flag posts',
permission: 'discussion.flagPosts'
}, 70);
});
});
}
};
});

View File

@ -2,7 +2,7 @@ import { extend } from 'flarum/extend';
import app from 'flarum/app';
import PermissionGrid from 'flarum/components/PermissionGrid';
app.initializers.add('flags', () => {
app.initializers.add('flarum-flags', () => {
extend(PermissionGrid.prototype, 'moderateItems', items => {
items.add('viewFlags', {
icon: 'flag',

View File

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

View File

@ -0,0 +1,646 @@
System.register('flarum/flags/addFlagControl', ['flarum/extend', 'flarum/app', 'flarum/utils/PostControls', 'flarum/components/Button', 'flarum/flags/components/FlagPostModal'], function (_export) {
'use strict';
var extend, app, PostControls, Button, FlagPostModal;
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
}, function (_flarumApp) {
app = _flarumApp['default'];
}, function (_flarumUtilsPostControls) {
PostControls = _flarumUtilsPostControls['default'];
}, function (_flarumComponentsButton) {
Button = _flarumComponentsButton['default'];
}, function (_flarumFlagsComponentsFlagPostModal) {
FlagPostModal = _flarumFlagsComponentsFlagPostModal['default'];
}],
execute: function () {
_export('default', function () {
extend(PostControls, 'userControls', function (items, post) {
if (post.isHidden() || post.contentType() !== 'comment' || !post.canFlag() || post.user() === app.session.user) return;
items.add('flag', m(
Button,
{ icon: 'flag', onclick: function () {
return app.modal.show(new FlagPostModal({ post: post }));
} },
'Flag'
));
});
});
}
};
});;System.register('flarum/flags/addFlagsDropdown', ['flarum/extend', 'flarum/app', 'flarum/components/HeaderSecondary', 'flarum/flags/components/FlagsDropdown'], function (_export) {
'use strict';
var extend, app, HeaderSecondary, FlagsDropdown;
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
}, function (_flarumApp) {
app = _flarumApp['default'];
}, function (_flarumComponentsHeaderSecondary) {
HeaderSecondary = _flarumComponentsHeaderSecondary['default'];
}, function (_flarumFlagsComponentsFlagsDropdown) {
FlagsDropdown = _flarumFlagsComponentsFlagsDropdown['default'];
}],
execute: function () {
_export('default', function () {
extend(HeaderSecondary.prototype, 'items', function (items) {
if (app.forum.attribute('canViewFlags')) {
items.add('flags', m(FlagsDropdown, null), 15);
}
});
});
}
};
});;System.register('flarum/flags/addFlagsToPosts', ['flarum/extend', 'flarum/app', 'flarum/components/CommentPost', 'flarum/components/Button', 'flarum/helpers/punctuate', 'flarum/helpers/username', 'flarum/utils/ItemList', 'flarum/utils/PostControls'], function (_export) {
'use strict';
var extend, app, CommentPost, Button, punctuate, username, ItemList, PostControls;
return {
setters: [function (_flarumExtend) {
extend = _flarumExtend.extend;
}, function (_flarumApp) {
app = _flarumApp['default'];
}, function (_flarumComponentsCommentPost) {
CommentPost = _flarumComponentsCommentPost['default'];
}, function (_flarumComponentsButton) {
Button = _flarumComponentsButton['default'];
}, function (_flarumHelpersPunctuate) {
punctuate = _flarumHelpersPunctuate['default'];
}, function (_flarumHelpersUsername) {
username = _flarumHelpersUsername['default'];
}, function (_flarumUtilsItemList) {
ItemList = _flarumUtilsItemList['default'];
}, function (_flarumUtilsPostControls) {
PostControls = _flarumUtilsPostControls['default'];
}],
execute: function () {
_export('default', function () {
extend(CommentPost.prototype, 'attrs', function (attrs) {
if (this.props.post.flags().length) {
attrs.className += ' Post--flagged';
}
});
CommentPost.prototype.dismissFlag = function (data) {
var post = this.props.post;
delete post.data.relationships.flags;
this.subtree.invalidate();
if (app.cache.flags) {
app.cache.flags.some(function (flag, i) {
if (flag.post() === post) {
app.cache.flags.splice(i, 1);
if (app.cache.flagIndex === post) {
var next = app.cache.flags[i];
if (!next) next = app.cache.flags[0];
if (next) {
var nextPost = next.post();
app.cache.flagIndex = nextPost;
m.route(app.route.post(nextPost));
}
}
return true;
}
});
}
return app.request({
url: app.forum.attribute('apiUrl') + post.apiEndpoint() + '/flags',
method: 'DELETE',
data: data
});
};
CommentPost.prototype.flagActionItems = function () {
var _this = this;
var items = new ItemList();
var controls = PostControls.destructiveControls(this.props.post);
Object.keys(controls).forEach(function (k) {
var props = controls[k].content.props;
props.className = 'Button';
extend(props, 'onclick', function () {
return _this.dismissFlag();
});
});
items.merge(controls);
items.add('dismiss', m(Button, { className: 'Button Button--icon Button--link', icon: 'times', onclick: this.dismissFlag.bind(this), title: 'Dismiss Flag' }), -100);
return items;
};
extend(CommentPost.prototype, 'content', function (vdom) {
var _this2 = this;
var post = this.props.post;
var flags = post.flags();
if (!flags.length) return;
if (post.isHidden()) this.revealContent = true;
vdom.unshift(m(
'div',
{ className: 'Post-flagged' },
m(
'div',
{ className: 'Post-flagged-flags' },
flags.map(function (flag) {
return m(
'div',
{ className: 'Post-flagged-flag' },
_this2.flagReason(flag)
);
})
),
m(
'div',
{ className: 'Post-flagged-actions' },
this.flagActionItems().toArray()
)
));
});
CommentPost.prototype.flagReason = function (flag) {
if (flag.type() === 'user') {
var user = flag.user();
var reason = flag.reason();
var detail = flag.reasonDetail();
return [app.trans(reason ? 'flarum-flags.forum.flagged_by_with_reason' : 'flarum-flags.forum.flagged_by', { user: user, reason: reason }), detail ? m(
'span',
{ className: 'Post-flagged-detail' },
detail
) : ''];
}
};
});
}
};
});;System.register('flarum/flags/main', ['flarum/app', 'flarum/Model', 'flarum/flags/models/Flag', 'flarum/flags/components/FlagsPage', 'flarum/flags/addFlagControl', 'flarum/flags/addFlagsDropdown', 'flarum/flags/addFlagsToPosts'], function (_export) {
'use strict';
var app, Model, Flag, FlagsPage, addFlagControl, addFlagsDropdown, addFlagsToPosts;
return {
setters: [function (_flarumApp) {
app = _flarumApp['default'];
}, function (_flarumModel) {
Model = _flarumModel['default'];
}, function (_flarumFlagsModelsFlag) {
Flag = _flarumFlagsModelsFlag['default'];
}, function (_flarumFlagsComponentsFlagsPage) {
FlagsPage = _flarumFlagsComponentsFlagsPage['default'];
}, function (_flarumFlagsAddFlagControl) {
addFlagControl = _flarumFlagsAddFlagControl['default'];
}, function (_flarumFlagsAddFlagsDropdown) {
addFlagsDropdown = _flarumFlagsAddFlagsDropdown['default'];
}, function (_flarumFlagsAddFlagsToPosts) {
addFlagsToPosts = _flarumFlagsAddFlagsToPosts['default'];
}],
execute: function () {
app.initializers.add('flarum-flags', function () {
app.store.models.posts.prototype.flags = Model.hasMany('flags');
app.store.models.posts.prototype.canFlag = Model.attribute('canFlag');
app.store.models.flags = Flag;
app.routes.flags = { path: '/flags', component: m(FlagsPage, null) };
addFlagControl();
addFlagsDropdown();
addFlagsToPosts();
});
}
};
});;System.register('flarum/flags/components/FlagList', ['flarum/Component', 'flarum/components/LoadingIndicator', 'flarum/helpers/avatar', 'flarum/helpers/username', 'flarum/helpers/icon', 'flarum/helpers/humanTime'], function (_export) {
'use strict';
var Component, LoadingIndicator, avatar, username, icon, humanTime, FlagList;
return {
setters: [function (_flarumComponent) {
Component = _flarumComponent['default'];
}, function (_flarumComponentsLoadingIndicator) {
LoadingIndicator = _flarumComponentsLoadingIndicator['default'];
}, function (_flarumHelpersAvatar) {
avatar = _flarumHelpersAvatar['default'];
}, function (_flarumHelpersUsername) {
username = _flarumHelpersUsername['default'];
}, function (_flarumHelpersIcon) {
icon = _flarumHelpersIcon['default'];
}, function (_flarumHelpersHumanTime) {
humanTime = _flarumHelpersHumanTime['default'];
}],
execute: function () {
FlagList = (function (_Component) {
babelHelpers.inherits(FlagList, _Component);
function FlagList() {
babelHelpers.classCallCheck(this, FlagList);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
babelHelpers.get(Object.getPrototypeOf(FlagList.prototype), 'constructor', this).apply(this, args);
/**
* Whether or not the notifications are loading.
*
* @type {Boolean}
*/
this.loading = false;
}
babelHelpers.createClass(FlagList, [{
key: 'view',
value: function view() {
var flags = app.cache.flags || [];
return m(
'div',
{ className: 'NotificationList FlagList' },
m(
'div',
{ className: 'NotificationList-header' },
m(
'h4',
{ className: 'App-titleControl App-titleControl--text' },
'Flagged Posts'
)
),
m(
'div',
{ className: 'NotificationList-content' },
m(
'ul',
{ className: 'NotificationGroup-content' },
flags.length ? flags.map(function (flag) {
var post = flag.post();
return m(
'li',
null,
m(
'a',
{ href: app.route.post(post), className: 'Notification Flag', config: function (element, isInitialized) {
m.route.apply(this, arguments);
if (!isInitialized) $(element).on('click', function () {
return app.cache.flagIndex = post;
});
} },
avatar(post.user()),
icon('flag', { className: 'Notification-icon' }),
m(
'span',
{ className: 'Notification-content' },
username(post.user()),
' in ',
m(
'em',
null,
post.discussion().title()
)
),
humanTime(flag.time()),
m(
'div',
{ className: 'Notification-excerpt' },
post.contentPlain()
)
)
);
}) : !this.loading ? m(
'div',
{ className: 'NotificationList-empty' },
app.trans('flarum-flags.forum.no_flags')
) : LoadingIndicator.component({ className: 'LoadingIndicator--block' })
)
)
);
}
/**
* Load flags into the application's cache if they haven't already
* been loaded.
*/
}, {
key: 'load',
value: function load() {
var _this = this;
if (app.cache.flags && !app.forum.attribute('unreadFlagsCount')) {
return;
}
this.loading = true;
m.redraw();
app.store.find('flags').then(function (flags) {
app.forum.pushAttributes({ unreadFlagsCount: 0 });
app.cache.flags = flags.sort(function (a, b) {
return b.time() - a.time();
});
_this.loading = false;
m.redraw();
});
}
}]);
return FlagList;
})(Component);
_export('default', FlagList);
}
};
});;System.register('flarum/flags/components/FlagPostModal', ['flarum/components/Modal', 'flarum/components/Button'], function (_export) {
'use strict';
var Modal, Button, FlagPostModal;
return {
setters: [function (_flarumComponentsModal) {
Modal = _flarumComponentsModal['default'];
}, function (_flarumComponentsButton) {
Button = _flarumComponentsButton['default'];
}],
execute: function () {
FlagPostModal = (function (_Modal) {
babelHelpers.inherits(FlagPostModal, _Modal);
function FlagPostModal() {
babelHelpers.classCallCheck(this, FlagPostModal);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
babelHelpers.get(Object.getPrototypeOf(FlagPostModal.prototype), 'constructor', this).apply(this, args);
this.reason = m.prop('');
this.reasonDetail = m.prop('');
}
babelHelpers.createClass(FlagPostModal, [{
key: 'className',
value: function className() {
return 'FlagPostModal Modal--small';
}
}, {
key: 'title',
value: function title() {
return 'Flag Post';
}
}, {
key: 'content',
value: function content() {
return m(
'div',
{ className: 'Modal-body' },
m(
'div',
{ className: 'Form' },
m(
'div',
{ className: 'Form-group' },
m(
'label',
null,
'Choose a Reason'
),
m(
'div',
null,
m(
'label',
{ className: 'checkbox' },
m('input', { type: 'radio', name: 'reason', checked: this.reason() === 'off_topic', value: 'off_topic', onclick: m.withAttr('value', this.reason) }),
'Off-topic'
),
m(
'label',
{ className: 'checkbox' },
m('input', { type: 'radio', name: 'reason', checked: this.reason() === 'inappropriate', value: 'inappropriate', onclick: m.withAttr('value', this.reason) }),
'Inappropriate'
),
m(
'label',
{ className: 'checkbox' },
m('input', { type: 'radio', name: 'reason', checked: this.reason() === 'spam', value: 'spam', onclick: m.withAttr('value', this.reason) }),
'Spam'
),
m(
'label',
{ className: 'checkbox' },
m('input', { type: 'radio', name: 'reason', checked: this.reason() === 'other', value: 'other', onclick: m.withAttr('value', this.reason) }),
'Other',
this.reason() === 'other' ? m('textarea', { className: 'FormControl', value: this.reasonDetail(), oninput: m.withAttr('value', this.reasonDetail) }) : ''
)
)
),
m(
'div',
{ className: 'Form-group' },
m(
Button,
{
className: 'Button Button--primary',
type: 'submit',
loading: this.loading,
disabled: !this.reason() },
'Flag Post'
)
)
)
);
}
}, {
key: 'onsubmit',
value: function onsubmit(e) {
var _this = this;
e.preventDefault();
this.loading = true;
app.store.createRecord('flags').save({
reason: this.reason() === 'other' ? null : this.reason(),
reasonDetail: this.reasonDetail(),
relationships: {
user: app.session.user,
post: this.props.post
}
}).then(function () {
return _this.hide();
}, function () {
_this.loading = false;
m.redraw();
});
}
}]);
return FlagPostModal;
})(Modal);
_export('default', FlagPostModal);
}
};
});;System.register('flarum/flags/components/FlagsDropdown', ['flarum/components/NotificationsDropdown', 'flarum/flags/components/FlagList'], function (_export) {
'use strict';
var NotificationsDropdown, FlagList, FlagsDropdown;
return {
setters: [function (_flarumComponentsNotificationsDropdown) {
NotificationsDropdown = _flarumComponentsNotificationsDropdown['default'];
}, function (_flarumFlagsComponentsFlagList) {
FlagList = _flarumFlagsComponentsFlagList['default'];
}],
execute: function () {
FlagsDropdown = (function (_NotificationsDropdown) {
babelHelpers.inherits(FlagsDropdown, _NotificationsDropdown);
babelHelpers.createClass(FlagsDropdown, null, [{
key: 'initProps',
value: function initProps(props) {
props.label = props.label || 'Flagged Posts';
props.icon = props.icon || 'flag';
babelHelpers.get(Object.getPrototypeOf(FlagsDropdown), 'initProps', this).call(this, props);
}
}]);
function FlagsDropdown() {
babelHelpers.classCallCheck(this, FlagsDropdown);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
babelHelpers.get(Object.getPrototypeOf(FlagsDropdown.prototype), 'constructor', this).apply(this, args);
this.list = new FlagList();
}
babelHelpers.createClass(FlagsDropdown, [{
key: 'goToRoute',
value: function goToRoute() {
m.route(app.route('flags'));
}
}, {
key: 'getUnreadCount',
value: function getUnreadCount() {
return app.forum.attribute('unreadFlagsCount');
}
}, {
key: 'getNewCount',
value: function getNewCount() {
return app.forum.attribute('newFlagsCount');
}
}]);
return FlagsDropdown;
})(NotificationsDropdown);
_export('default', FlagsDropdown);
}
};
});;System.register('flarum/flags/components/FlagsPage', ['flarum/components/Page', 'flarum/flags/components/FlagList'], function (_export) {
/**
* The `FlagsPage` component shows the flags list. It is only
* used on mobile devices where the flags dropdown is within the drawer.
*/
'use strict';
var Page, FlagList, FlagsPage;
return {
setters: [function (_flarumComponentsPage) {
Page = _flarumComponentsPage['default'];
}, function (_flarumFlagsComponentsFlagList) {
FlagList = _flarumFlagsComponentsFlagList['default'];
}],
execute: function () {
FlagsPage = (function (_Page) {
babelHelpers.inherits(FlagsPage, _Page);
function FlagsPage() {
babelHelpers.classCallCheck(this, FlagsPage);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
babelHelpers.get(Object.getPrototypeOf(FlagsPage.prototype), 'constructor', this).apply(this, args);
app.history.push('flags');
this.list = new FlagList();
this.list.load();
this.bodyClass = 'App--flags';
}
babelHelpers.createClass(FlagsPage, [{
key: 'view',
value: function view() {
return m(
'div',
{ className: 'FlagsPage' },
this.list.render()
);
}
}]);
return FlagsPage;
})(Page);
_export('default', FlagsPage);
}
};
});;System.register('flarum/flags/models/Flag', ['flarum/Model', 'flarum/utils/mixin'], function (_export) {
'use strict';
var Model, mixin, Flag;
return {
setters: [function (_flarumModel) {
Model = _flarumModel['default'];
}, function (_flarumUtilsMixin) {
mixin = _flarumUtilsMixin['default'];
}],
execute: function () {
Flag = (function (_Model) {
babelHelpers.inherits(Flag, _Model);
function Flag() {
babelHelpers.classCallCheck(this, Flag);
babelHelpers.get(Object.getPrototypeOf(Flag.prototype), 'constructor', this).apply(this, arguments);
}
return Flag;
})(Model);
babelHelpers._extends(Flag.prototype, {
type: Model.attribute('type'),
reason: Model.attribute('reason'),
reasonDetail: Model.attribute('reasonDetail'),
time: Model.attribute('time', Model.transformDate),
post: Model.hasOne('post'),
user: Model.hasOne('user')
});
_export('default', Flag);
}
};
});

View File

@ -3,7 +3,7 @@ import app from 'flarum/app';
import PostControls from 'flarum/utils/PostControls';
import Button from 'flarum/components/Button';
import FlagPostModal from 'flags/components/FlagPostModal';
import FlagPostModal from 'flarum/flags/components/FlagPostModal';
export default function() {
extend(PostControls, 'userControls', function(items, post) {

View File

@ -1,7 +1,7 @@
import { extend } from 'flarum/extend';
import app from 'flarum/app';
import HeaderSecondary from 'flarum/components/HeaderSecondary';
import FlagsDropdown from 'flags/components/FlagsDropdown';
import FlagsDropdown from 'flarum/flags/components/FlagsDropdown';
export default function() {
extend(HeaderSecondary.prototype, 'items', function(items) {

View File

@ -101,7 +101,7 @@ export default function() {
const detail = flag.reasonDetail();
return [
app.trans(reason ? 'flags.flagged_by_with_reason' : 'flags.flagged_by', {user, reason}),
app.trans(reason ? 'flarum-flags.forum.flagged_by_with_reason' : 'flarum-flags.forum.flagged_by', {user, reason}),
detail ? <span className="Post-flagged-detail">{detail}</span> : ''
];
}

View File

@ -52,7 +52,7 @@ export default class FlagList extends Component {
);
})
: !this.loading
? <div className="NotificationList-empty">{app.trans('flags.no_flags')}</div>
? <div className="NotificationList-empty">{app.trans('flarum-flags.forum.no_flags')}</div>
: LoadingIndicator.component({className: 'LoadingIndicator--block'})}
</ul>
</div>

View File

@ -1,6 +1,6 @@
import NotificationsDropdown from 'flarum/components/NotificationsDropdown';
import FlagList from 'flags/components/FlagList';
import FlagList from 'flarum/flags/components/FlagList';
export default class FlagsDropdown extends NotificationsDropdown {
static initProps(props) {
@ -23,4 +23,8 @@ export default class FlagsDropdown extends NotificationsDropdown {
getUnreadCount() {
return app.forum.attribute('unreadFlagsCount');
}
getNewCount() {
return app.forum.attribute('newFlagsCount');
}
}

View File

@ -1,6 +1,6 @@
import Page from 'flarum/components/Page';
import FlagList from 'flags/components/FlagList';
import FlagList from 'flarum/flags/components/FlagList';
/**
* The `FlagsPage` component shows the flags list. It is only

View File

@ -1,13 +1,13 @@
import app from 'flarum/app';
import Model from 'flarum/Model';
import Flag from 'flags/models/Flag';
import FlagsPage from 'flags/components/FlagsPage';
import addFlagControl from 'flags/addFlagControl';
import addFlagsDropdown from 'flags/addFlagsDropdown';
import addFlagsToPosts from 'flags/addFlagsToPosts';
import Flag from 'flarum/flags/models/Flag';
import FlagsPage from 'flarum/flags/components/FlagsPage';
import addFlagControl from 'flarum/flags/addFlagControl';
import addFlagsDropdown from 'flarum/flags/addFlagsDropdown';
import addFlagsToPosts from 'flarum/flags/addFlagsToPosts';
app.initializers.add('flags', () => {
app.initializers.add('flarum-flags', () => {
app.store.models.posts.prototype.flags = Model.hasMany('flags');
app.store.models.posts.prototype.canFlag = Model.attribute('canFlag');

View File

@ -1,7 +1,9 @@
import Model from 'flarum/Model';
import mixin from 'flarum/utils/mixin';
export default class Flag extends mixin(Model, {
class Flag extends Model {}
Object.assign(Flag.prototype, {
type: Model.attribute('type'),
reason: Model.attribute('reason'),
reasonDetail: Model.attribute('reasonDetail'),
@ -9,4 +11,6 @@ export default class Flag extends mixin(Model, {
post: Model.hasOne('post'),
user: Model.hasOne('user')
}) {}
});
export default Flag;

View File

@ -1,8 +0,0 @@
flags:
reason_off_topic: Off-topic
reason_spam: Spam
reason_inappropriate: Inappropriate
reason_other: Other
flagged_by: "{username} flagged"
flagged_by_with_reason: "{username} flagged as {reason}"
no_flags: No Flags

View File

@ -8,12 +8,12 @@
* file that was distributed with this source code.
*/
namespace Flarum\Migrations\Flags;
namespace Flarum\Flags\Migration;
use Flarum\Database\AbstractMigration;
use Illuminate\Database\Schema\Blueprint;
use Flarum\Migrations\Migration;
class AddFlagsReadTimeToUsersTable extends Migration
class AddFlagsReadTimeToUsersTable extends AbstractMigration
{
public function up()
{

View File

@ -8,12 +8,12 @@
* file that was distributed with this source code.
*/
namespace Flarum\Migrations\Flags;
namespace Flarum\Flags\Migration;
use Flarum\Database\AbstractMigration;
use Illuminate\Database\Schema\Blueprint;
use Flarum\Migrations\Migration;
class CreateFlagsTable extends Migration
class CreateFlagsTable extends AbstractMigration
{
public function up()
{

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

@ -0,0 +1,57 @@
<?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\Flags\Api\Controller;
use Flarum\Api\Controller\AbstractCreateController;
use Flarum\Flags\Api\Serializer\FlagSerializer;
use Flarum\Flags\Command\CreateFlag;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class CreateFlagController extends AbstractCreateController
{
/**
* {@inheritdoc}
*/
public $serializer = FlagSerializer::class;
/**
* {@inheritdoc}
*/
public $include = [
'post',
'post.flags'
];
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new CreateFlag($request->getAttribute('actor'), array_get($request->getParsedBody(), 'data', []))
);
}
}

View File

@ -8,14 +8,14 @@
* file that was distributed with this source code.
*/
namespace Flarum\Flags\Api;
namespace Flarum\Flags\Api\Controller;
use Flarum\Flags\Commands\DeleteFlags;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Flarum\Api\Controller\AbstractDeleteController;
use Flarum\Flags\Command\DeleteFlags;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
class DeleteAction extends BaseDeleteAction
class DeleteFlagsController extends AbstractDeleteController
{
/**
* @var Dispatcher
@ -31,14 +31,12 @@ class DeleteAction extends BaseDeleteAction
}
/**
* Delete flags for a post.
*
* @param Request $request
* {@inheritdoc}
*/
protected function delete(Request $request)
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeleteFlags($request->get('id'), $request->actor, $request->all())
new DeleteFlags(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'), $request->getParsedBody())
);
}
}

View File

@ -0,0 +1,52 @@
<?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\Flags\Api\Controller;
use Flarum\Api\Controller\AbstractCollectionController;
use Flarum\Flags\Api\Serializer\FlagSerializer;
use Flarum\Flags\Flag;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListFlagsController extends AbstractCollectionController
{
/**
* {@inheritdoc}
*/
public $serializer = FlagSerializer::class;
/**
* {@inheritdoc}
*/
public $include = [
'user',
'post',
'post.user',
'post.discussion'
];
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$actor->flags_read_time = time();
$actor->save();
return Flag::whereVisibleTo($actor)
->with($this->extractInclude($request))
->latest('flags.time')
->groupBy('post_id')
->get();
}
}

View File

@ -1,58 +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\Flags\Api;
use Flarum\Flags\Commands\CreateFlag;
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Flags\Api\FlagSerializer';
/**
* @inheritdoc
*/
public $include = [
'post' => true,
'post.flags' => true
];
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Create a flag according to input from the API request.
*
* @param JsonApiRequest $request
* @return \Flarum\Flags\Flag
*/
protected function create(JsonApiRequest $request)
{
return $this->bus->dispatch(
new CreateFlag($request->actor, $request->get('data'))
);
}
}

View File

@ -1,53 +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\Flags\Api;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Flags\Flag;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Flags\Api\FlagSerializer';
/**
* @inheritdoc
*/
public $include = [
'user' => true,
'post' => true,
'post.user' => true,
'post.discussion' => true
];
/**
* @inheritdoc
*/
public $link = [];
protected function data(JsonApiRequest $request, Document $document)
{
$actor = $request->actor;
$actor->flags_read_time = time();
$actor->save();
return Flag::whereVisibleTo($actor)
->with($request->include)
->latest('flags.time')
->groupBy('post_id')
->get();
}
}

View File

@ -8,14 +8,22 @@
* file that was distributed with this source code.
*/
namespace Flarum\Flags\Api;
namespace Flarum\Flags\Api\Serializer;
use Flarum\Api\Serializers\Serializer;
use Flarum\Api\Serializer\AbstractSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Api\Serializer\UserBasicSerializer;
class FlagSerializer extends Serializer
class FlagSerializer extends AbstractSerializer
{
/**
* {@inheritdoc}
*/
protected $type = 'flags';
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($flag)
{
return [
@ -25,13 +33,19 @@ class FlagSerializer extends Serializer
];
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function post()
{
return $this->hasOne('Flarum\Api\Serializers\PostSerializer');
return $this->hasOne(PostSerializer::class);
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function user()
{
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
return $this->hasOne(UserBasicSerializer::class);
}
}

View File

@ -8,9 +8,9 @@
* file that was distributed with this source code.
*/
namespace Flarum\Flags\Commands;
namespace Flarum\Flags\Command;
use Flarum\Core\Users\User;
use Flarum\Core\User;
class CreateFlag
{

View File

@ -8,17 +8,26 @@
* file that was distributed with this source code.
*/
namespace Flarum\Flags\Commands;
namespace Flarum\Flags\Command;
use Flarum\Core\Access\AssertPermissionTrait;
use Flarum\Flags\Flag;
use Flarum\Core\Posts\PostRepository;
use Flarum\Core\Posts\CommentPost;
use Exception;
use Flarum\Core\Repository\PostRepository;
use Flarum\Core\Post\CommentPost;
use Tobscure\JsonApi\Exception\InvalidParameterException;
class CreateFlagHandler
{
private $posts;
use AssertPermissionTrait;
/**
* @var PostRepository
*/
protected $posts;
/**
* @param PostRepository $posts
*/
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
@ -27,6 +36,7 @@ class CreateFlagHandler
/**
* @param CreateFlag $command
* @return Flag
* @throws InvalidParameterException
*/
public function handle(CreateFlag $command)
{
@ -37,11 +47,10 @@ class CreateFlagHandler
$post = $this->posts->findOrFail($postId, $actor);
if (! ($post instanceof CommentPost)) {
// TODO: throw 400(?) error
throw new Exception;
throw new InvalidParameterException;
}
$post->assertCan($actor, 'flag');
$this->assertCan($actor, 'flag', $post);
Flag::unguard();

View File

@ -8,10 +8,9 @@
* file that was distributed with this source code.
*/
namespace Flarum\Flags\Commands;
namespace Flarum\Flags\Command;
use Flarum\Flags\Flag;
use Flarum\Core\Users\User;
use Flarum\Core\User;
class DeleteFlags
{

View File

@ -0,0 +1,61 @@
<?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\Flags\Command;
use Flarum\Core\Access\AssertPermissionTrait;
use Flarum\Core\Repository\PostRepository;
use Flarum\Flags\Event\FlagsWillBeDeleted;
use Flarum\Flags\Flag;
use Illuminate\Contracts\Events\Dispatcher;
class DeleteFlagsHandler
{
use AssertPermissionTrait;
/**
* @var PostRepository
*/
protected $posts;
/**
* @var Dispatcher
*/
protected $events;
/**
* @param PostRepository $posts
* @param Dispatcher $events
*/
public function __construct(PostRepository $posts, Dispatcher $events)
{
$this->posts = $posts;
$this->events = $events;
}
/**
* @param DeleteFlags $command
* @return Flag
*/
public function handle(DeleteFlags $command)
{
$actor = $command->actor;
$post = $this->posts->findOrFail($command->postId, $actor);
$this->assertCan($actor, 'viewFlags', $post->discussion);
$this->events->fire(new FlagsWillBeDeleted($post, $actor, $command->data));
$post->flags()->delete();
return $post;
}
}

View File

@ -1,45 +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\Flags\Commands;
use Flarum\Flags\Flag;
use Flarum\Core\Posts\PostRepository;
use Flarum\Flags\Events\FlagsWillBeDeleted;
class DeleteFlagsHandler
{
protected $posts;
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
}
/**
* @param DeleteFlag $command
* @return Flag
* @throws \Flarum\Core\Exceptions\PermissionDeniedException
*/
public function handle(DeleteFlags $command)
{
$actor = $command->actor;
$post = $this->posts->findOrFail($command->postId, $actor);
$post->discussion->assertCan($actor, 'viewFlags');
event(new FlagsWillBeDeleted($post, $actor, $command->data));
$post->flags()->delete();
return $post;
}
}

View File

@ -9,10 +9,10 @@
* file that was distributed with this source code.
*/
namespace Flarum\Flags\Events;
namespace Flarum\Flags\Event;
use Flarum\Core\Posts\Post;
use Flarum\Core\Users\User;
use Flarum\Core\Post;
use Flarum\Core\User;
class FlagsWillBeDeleted
{

View File

@ -1,25 +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\Flags;
use Flarum\Support\Extension as BaseExtension;
use Illuminate\Events\Dispatcher;
use Flarum\Core\Posts\Post;
class Extension extends BaseExtension
{
public function listen(Dispatcher $events)
{
$events->subscribe('Flarum\Flags\Listeners\AddClientAssets');
$events->subscribe('Flarum\Flags\Listeners\AddApiAttributes');
$events->subscribe('Flarum\Flags\Listeners\AddModelRelationship');
}
}

View File

@ -10,24 +10,38 @@
namespace Flarum\Flags;
use Flarum\Core\Model;
use Flarum\Core\Support\VisibleScope;
use Flarum\Core\Post;
use Flarum\Core\Support\ScopeVisibilityTrait;
use Flarum\Core\User;
use Flarum\Database\AbstractModel;
class Flag extends Model
class Flag extends AbstractModel
{
use VisibleScope;
use ScopeVisibilityTrait;
/**
* {@inheritdoc}
*/
protected $table = 'flags';
/**
* {@inheritdoc}
*/
protected $dates = ['time'];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function post()
{
return $this->belongsTo('Flarum\Core\Posts\Post');
return $this->belongsTo(Post::class);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('Flarum\Core\Users\User');
return $this->belongsTo(User::class);
}
}

View File

@ -0,0 +1,48 @@
<?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\Flags\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/flags/main');
$event->addTranslations('flarum-flags.forum');
}
if ($event->isAdmin()) {
$event->addAssets([
__DIR__.'/../../js/admin/dist/extension.js'
]);
$event->addBootstrapper('flarum/flags/main');
$event->addTranslations('flarum-flags.admin');
}
}
}

View File

@ -0,0 +1,78 @@
<?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\Flags\Listener;
use Flarum\Api\Serializer\ForumSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Core\User;
use Flarum\Event\ConfigureApiRoutes;
use Flarum\Event\ConfigureModelDates;
use Flarum\Event\PrepareApiAttributes;
use Flarum\Flags\Api\Controller;
use Flarum\Flags\Flag;
use Illuminate\Contracts\Events\Dispatcher;
class AddFlagsApi
{
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(ConfigureModelDates::class, [$this, 'configureModelDates']);
$events->listen(PrepareApiAttributes::class, [$this, 'prepareApiAttributes']);
$events->listen(ConfigureApiRoutes::class, [$this, 'configureApiRoutes']);
}
/**
* @param ConfigureModelDates $event
*/
public function configureModelDates(ConfigureModelDates $event)
{
if ($event->isModel(User::class)) {
$event->dates[] = 'flags_read_time';
}
}
/**
* @param PrepareApiAttributes $event
*/
public function prepareApiAttributes(PrepareApiAttributes $event)
{
if ($event->isSerializer(ForumSerializer::class)) {
$event->attributes['canViewFlags'] = $event->actor->hasPermissionLike('discussion.viewFlags');
if ($event->attributes['canViewFlags']) {
$query = Flag::whereVisibleTo($event->actor);
if ($time = $event->actor->flags_read_time) {
$query->where('flags.time', '>', $time);
}
$event->attributes['unreadFlagsCount'] = $query->distinct('flags.post_id')->count();
}
}
if ($event->isSerializer(PostSerializer::class)) {
$event->attributes['canFlag'] = $event->actor->can('flag', $event->model);
}
}
/**
* @param ConfigureApiRoutes $event
*/
public function configureApiRoutes(ConfigureApiRoutes $event)
{
$event->get('/flags', 'flags.index', Controller\ListFlagsController::class);
$event->post('/flags', 'flags.create', Controller\CreateFlagController::class);
$event->delete('/posts/{id}/flags', 'flags.delete', Controller\DeleteFlagsController::class);
}
}

View File

@ -0,0 +1,137 @@
<?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\Flags\Listener;
use Flarum\Api\Controller;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Core\Post;
use Flarum\Event\ConfigureApiController;
use Flarum\Event\GetApiRelationship;
use Flarum\Event\GetModelRelationship;
use Flarum\Event\PostWasDeleted;
use Flarum\Event\PrepareApiData;
use Flarum\Flags\Api\Controller\CreateFlagController;
use Flarum\Flags\Api\Serializer\FlagSerializer;
use Flarum\Flags\Flag;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Collection;
class AddPostFlagsRelationship
{
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(GetModelRelationship::class, [$this, 'getModelRelationship']);
$events->listen(PostWasDeleted::class, [$this, 'postWasDeleted']);
$events->listen(GetApiRelationship::class, [$this, 'getApiRelationship']);
$events->listen(ConfigureApiController::class, [$this, 'includeFlagsRelationship']);
$events->listen(PrepareApiData::class, [$this, 'prepareApiData']);
}
/**
* @param GetModelRelationship $event
* @return \Illuminate\Database\Eloquent\Relations\HasMany|null
*/
public function getModelRelationship(GetModelRelationship $event)
{
if ($event->isRelationship(Post::class, 'flags')) {
return $event->model->hasMany(Flag::class, 'post_id');
}
}
/**
* @param PostWasDeleted $event
*/
public function postWasDeleted(PostWasDeleted $event)
{
$event->post->flags()->delete();
}
/**
* @param GetApiRelationship $event
* @return \Flarum\Api\Relationship\HasManyBuilder|null
*/
public function getApiRelationship(GetApiRelationship $event)
{
if ($event->isRelationship(PostSerializer::class, 'flags')) {
return $event->serializer->hasMany(FlagSerializer::class, 'flags');
}
}
/**
* @param ConfigureApiController $event
*/
public function includeFlagsRelationship(ConfigureApiController $event)
{
if ($event->isController(Controller\ShowDiscussionController::class)) {
$event->addInclude([
'posts.flags',
'posts.flags.user'
]);
}
if ($event->isController(Controller\ListPostsController::class)
|| $event->isController(Controller\ShowPostController::class)) {
$event->addInclude([
'flags',
'flags.user'
]);
}
}
/**
* @param PrepareApiData $event
*/
public function prepareApiData(PrepareApiData $event)
{
// For any API action that allows the 'flags' relationship to be
// included, we need to preload this relationship onto the data (Post
// models) so that we can selectively expose only the flags that the
// user has permission to view.
if ($event->isController(Controller\ShowDiscussionController::class)) {
$posts = $event->data->posts;
}
if ($event->isController(Controller\ListPostsController::class)) {
$posts = $event->data->all();
}
if ($event->isController(Controller\ShowPostController::class)) {
$posts = [$event->data];
}
if ($event->isController(CreateFlagController::class)) {
$posts = [$event->data->post];
}
if (isset($posts)) {
$actor = $event->request->getAttribute('actor');
$postsWithPermission = [];
foreach ($posts as $post) {
$post->setRelation('flags', null);
if ($actor->can('viewFlags', $post->discussion)) {
$postsWithPermission[] = $post;
}
}
if (count($postsWithPermission)) {
(new Collection($postsWithPermission))
->load('flags', 'flags.user');
}
}
}
}

View File

@ -1,129 +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\Flags\Listeners;
use Flarum\Events\ApiRelationship;
use Flarum\Events\WillSerializeData;
use Flarum\Events\BuildApiAction;
use Flarum\Events\ApiAttributes;
use Flarum\Events\RegisterApiRoutes;
use Flarum\Api\Serializers\PostSerializer;
use Flarum\Api\Serializers\ForumSerializer;
use Flarum\Api\Actions\Posts;
use Flarum\Api\Actions\Discussions;
use Flarum\Flags\Flag;
use Flarum\Flags\Api\CreateAction as FlagsCreateAction;
use Illuminate\Database\Eloquent\Collection;
class AddApiAttributes
{
public function subscribe($events)
{
$events->listen(ApiRelationship::class, [$this, 'addFlagsRelationship']);
$events->listen(WillSerializeData::class, [$this, 'loadFlagsRelationship']);
$events->listen(BuildApiAction::class, [$this, 'includeFlagsRelationship']);
$events->listen(ApiAttributes::class, [$this, 'addAttributes']);
$events->listen(RegisterApiRoutes::class, [$this, 'addRoutes']);
}
public function loadFlagsRelationship(WillSerializeData $event)
{
// For any API action that allows the 'flags' relationship to be
// included, we need to preload this relationship onto the data (Post
// models) so that we can selectively expose only the flags that the
// user has permission to view.
if ($event->action instanceof Discussions\ShowAction) {
$discussion = $event->data;
$posts = $discussion->posts->all();
}
if ($event->action instanceof Posts\IndexAction) {
$posts = $event->data->all();
}
if ($event->action instanceof Posts\ShowAction) {
$posts = [$event->data];
}
if ($event->action instanceof FlagsCreateAction) {
$flag = $event->data;
$posts = [$flag->post];
}
if (isset($posts)) {
$actor = $event->request->actor;
$postsWithPermission = [];
foreach ($posts as $post) {
$post->setRelation('flags', null);
if ($post->discussion->can($actor, 'viewFlags')) {
$postsWithPermission[] = $post;
}
}
if (count($postsWithPermission)) {
(new Collection($postsWithPermission))
->load('flags', 'flags.user');
}
}
}
public function addFlagsRelationship(ApiRelationship $event)
{
if ($event->serializer instanceof PostSerializer &&
$event->relationship === 'flags') {
return $event->serializer->hasMany('Flarum\Flags\Api\FlagSerializer', 'flags');
}
}
public function includeFlagsRelationship(BuildApiAction $event)
{
if ($event->action instanceof Discussions\ShowAction) {
$event->addInclude('posts.flags');
$event->addInclude('posts.flags.user');
}
if ($event->action instanceof Posts\IndexAction ||
$event->action instanceof Posts\ShowAction) {
$event->addInclude('flags');
$event->addInclude('flags.user');
}
}
public function addAttributes(ApiAttributes $event)
{
if ($event->serializer instanceof ForumSerializer) {
$event->attributes['canViewFlags'] = $event->actor->hasPermissionLike('discussion.viewFlags');
if ($event->attributes['canViewFlags']) {
$query = Flag::whereVisibleTo($event->actor);
if ($time = $event->actor->flags_read_time) {
$query->where('flags.time', '>', $time);
}
$event->attributes['unreadFlagsCount'] = $query->distinct('flags.post_id')->count();
}
}
if ($event->serializer instanceof PostSerializer) {
$event->attributes['canFlag'] = $event->model->can($event->actor, 'flag');
}
}
public function addRoutes(RegisterApiRoutes $event)
{
$event->get('/flags', 'flags.index', 'Flarum\Flags\Api\IndexAction');
$event->post('/flags', 'flags.create', 'Flarum\Flags\Api\CreateAction');
$event->delete('/posts/{id}/flags', 'flags.delete', 'Flarum\Flags\Api\DeleteAction');
}
}

View File

@ -1,60 +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\Flags\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('flags/main');
$event->forumTranslations([
'flags.reason_off_topic',
'flags.reason_spam',
'flags.reason_inappropriate',
'flags.reason_other',
'flags.flagged_by',
'flags.flagged_by_with_reason',
'flags.no_flags'
]);
$event->adminAssets([
__DIR__.'/../../js/admin/dist/extension.js',
__DIR__.'/../../less/admin/extension.less'
]);
$event->adminBootstrapper('flags/main');
$event->adminTranslations([
// 'flag.hello_world'
]);
}
}

View File

@ -1,47 +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\Flags\Listeners;
use Flarum\Events\ModelRelationship;
use Flarum\Events\ModelDates;
use Flarum\Events\PostWasDeleted;
use Flarum\Core\Posts\Post;
use Flarum\Core\Users\User;
use Flarum\Flags\Flag;
class AddModelRelationship
{
public function subscribe($events)
{
$events->listen(ModelRelationship::class, [$this, 'addFlagsRelationship']);
$events->listen(ModelDates::class, [$this, 'modelDates']);
$events->listen(PostWasDeleted::class, [$this, 'deleteFlags']);
}
public function addFlagsRelationship(ModelRelationship $event)
{
if ($event->model instanceof Post && $event->relationship === 'flags') {
return $event->model->hasMany('Flarum\Flags\Flag', 'post_id');
}
}
public function modelDates(ModelDates $event)
{
if ($event->model instanceof User) {
$event->dates[] = 'flags_read_time';
}
}
public function deleteFlags(PostWasDeleted $event)
{
$event->post->flags()->delete();
}
}