mirror of
https://github.com/flarum/framework.git
synced 2025-04-01 21:55:16 +08:00
Initial commit
This commit is contained in:
commit
cb6347ef6a
19
extensions/flags/.editorconfig
Normal file
19
extensions/flags/.editorconfig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
[*.{diff,md}]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.php]
|
||||||
|
indent_size = 4
|
5
extensions/flags/.eslintignore
Normal file
5
extensions/flags/.eslintignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
**/bower_components/**/*
|
||||||
|
**/node_modules/**/*
|
||||||
|
vendor/**/*
|
||||||
|
**/Gulpfile.js
|
||||||
|
**/dist/**/*
|
175
extensions/flags/.eslintrc
Normal file
175
extensions/flags/.eslintrc
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"react/jsx-uses-vars": 1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
"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/flags/.gitignore
vendored
Normal file
4
extensions/flags/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/vendor
|
||||||
|
composer.phar
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
26
extensions/flags/.php_cs
Executable file
26
extensions/flags/.php_cs
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$header = <<<EOF
|
||||||
|
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.
|
||||||
|
EOF;
|
||||||
|
|
||||||
|
Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header);
|
||||||
|
|
||||||
|
$finder = Symfony\CS\Finder\DefaultFinder::create()
|
||||||
|
->exclude('js')
|
||||||
|
->exclude('less')
|
||||||
|
->in(__DIR__);
|
||||||
|
|
||||||
|
return Symfony\CS\Config\Config::create()
|
||||||
|
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
|
||||||
|
->fixers([
|
||||||
|
'short_array_syntax',
|
||||||
|
'header_comment',
|
||||||
|
'-psr0'
|
||||||
|
])
|
||||||
|
->finder($finder);
|
23
extensions/flags/.travis.yml
Normal file
23
extensions/flags/.travis.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
language: php
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.5
|
||||||
|
- 5.6
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- php: hhvm
|
||||||
|
fast_finish: true
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- curl -s http://getcomposer.org/installer | php
|
||||||
|
- php composer.phar install
|
||||||
|
|
||||||
|
script:
|
||||||
|
- php composer.phar style
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
on_failure: change
|
||||||
|
|
||||||
|
sudo: false
|
21
extensions/flags/LICENSE
Normal file
21
extensions/flags/LICENSE
Normal 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.
|
14
extensions/flags/bootstrap.php
Normal file
14
extensions/flags/bootstrap.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
return 'Flarum\Reports\Extension';
|
10
extensions/flags/composer.json
Normal file
10
extensions/flags/composer.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Flarum\\Reports\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"style": "phpcs --standard=PSR2 -np src"
|
||||||
|
}
|
||||||
|
}
|
25
extensions/flags/flarum.json
Normal file
25
extensions/flags/flarum.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "reports",
|
||||||
|
"title": "Reports",
|
||||||
|
"description": "Allow users to report 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/reports",
|
||||||
|
"issues": "https://github.com/flarum/core/issues"
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"name": "flag",
|
||||||
|
"backgroundColor": "#e92693",
|
||||||
|
"color": "#fff"
|
||||||
|
}
|
||||||
|
}
|
3
extensions/flags/js/.gitignore
vendored
Normal file
3
extensions/flags/js/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
bower_components
|
||||||
|
node_modules
|
||||||
|
dist
|
7
extensions/flags/js/admin/Gulpfile.js
Normal file
7
extensions/flags/js/admin/Gulpfile.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
var gulp = require('flarum-gulp');
|
||||||
|
|
||||||
|
gulp({
|
||||||
|
modules: {
|
||||||
|
'reports': 'src/**/*.js'
|
||||||
|
}
|
||||||
|
});
|
7
extensions/flags/js/admin/package.json
Normal file
7
extensions/flags/js/admin/package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "^3.8.11",
|
||||||
|
"flarum-gulp": "^0.1.0"
|
||||||
|
}
|
||||||
|
}
|
19
extensions/flags/js/admin/src/main.js
Normal file
19
extensions/flags/js/admin/src/main.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { extend } from 'flarum/extend';
|
||||||
|
import app from 'flarum/app';
|
||||||
|
import PermissionGrid from 'flarum/components/PermissionGrid';
|
||||||
|
|
||||||
|
app.initializers.add('reports', () => {
|
||||||
|
extend(PermissionGrid.prototype, 'moderateItems', items => {
|
||||||
|
items.add('viewReports', {
|
||||||
|
label: 'View reported posts',
|
||||||
|
permission: 'discussion.viewReports'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
extend(PermissionGrid.prototype, 'replyItems', items => {
|
||||||
|
items.add('reportPosts', {
|
||||||
|
label: 'Report posts',
|
||||||
|
permission: 'discussion.reportPosts'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
7
extensions/flags/js/forum/Gulpfile.js
Normal file
7
extensions/flags/js/forum/Gulpfile.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
var gulp = require('flarum-gulp');
|
||||||
|
|
||||||
|
gulp({
|
||||||
|
modules: {
|
||||||
|
'reports': 'src/**/*.js'
|
||||||
|
}
|
||||||
|
});
|
7
extensions/flags/js/forum/package.json
Normal file
7
extensions/flags/js/forum/package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"gulp": "^3.8.11",
|
||||||
|
"flarum-gulp": "^0.1.0"
|
||||||
|
}
|
||||||
|
}
|
16
extensions/flags/js/forum/src/addReportControl.js
Normal file
16
extensions/flags/js/forum/src/addReportControl.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { extend } from 'flarum/extend';
|
||||||
|
import app from 'flarum/app';
|
||||||
|
import PostControls from 'flarum/utils/PostControls';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
|
||||||
|
import ReportPostModal from 'reports/components/ReportPostModal';
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
extend(PostControls, 'userControls', function(items, post) {
|
||||||
|
if (post.isHidden() || post.contentType() !== 'comment' || !post.canReport() || post.user() === app.session.user) return;
|
||||||
|
|
||||||
|
items.add('report',
|
||||||
|
<Button icon="flag" onclick={() => app.modal.show(new ReportPostModal({post}))}>Report</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
12
extensions/flags/js/forum/src/addReportsDropdown.js
Normal file
12
extensions/flags/js/forum/src/addReportsDropdown.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { extend } from 'flarum/extend';
|
||||||
|
import app from 'flarum/app';
|
||||||
|
import HeaderSecondary from 'flarum/components/HeaderSecondary';
|
||||||
|
import ReportsDropdown from 'reports/components/ReportsDropdown';
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
extend(HeaderSecondary.prototype, 'items', function(items) {
|
||||||
|
if (app.forum.attribute('canViewReports')) {
|
||||||
|
items.add('reports', <ReportsDropdown/>, 15);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
134
extensions/flags/js/forum/src/addReportsToPosts.js
Normal file
134
extensions/flags/js/forum/src/addReportsToPosts.js
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import { extend } from 'flarum/extend';
|
||||||
|
import app from 'flarum/app';
|
||||||
|
import CommentPost from 'flarum/components/CommentPost';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import punctuate from 'flarum/helpers/punctuate';
|
||||||
|
import username from 'flarum/helpers/username';
|
||||||
|
import ItemList from 'flarum/utils/ItemList';
|
||||||
|
import PostControls from 'flarum/utils/PostControls';
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
extend(CommentPost.prototype, 'attrs', function(attrs) {
|
||||||
|
if (this.props.post.reports().length) {
|
||||||
|
attrs.className += ' Post--reported';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CommentPost.prototype.dismissReport = function(data) {
|
||||||
|
const post = this.props.post;
|
||||||
|
|
||||||
|
delete post.data.relationships.reports;
|
||||||
|
|
||||||
|
this.subtree.invalidate();
|
||||||
|
|
||||||
|
if (app.cache.reports) {
|
||||||
|
app.cache.reports.some((report, i) => {
|
||||||
|
if (report.post() === post) {
|
||||||
|
app.cache.reports.splice(i, 1);
|
||||||
|
|
||||||
|
if (app.cache.reportIndex === post) {
|
||||||
|
let next = app.cache.reports[i];
|
||||||
|
|
||||||
|
if (!next) next = app.cache.reports[0];
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
const nextPost = next.post();
|
||||||
|
app.cache.reportIndex = nextPost;
|
||||||
|
m.route(app.route.post(nextPost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.request({
|
||||||
|
url: app.forum.attribute('apiUrl') + post.apiEndpoint() + '/reports',
|
||||||
|
method: 'DELETE',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CommentPost.prototype.reportActionItems = function() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
if (this.props.post.isHidden()) {
|
||||||
|
if (this.props.post.canDelete()) {
|
||||||
|
items.add('delete',
|
||||||
|
<Button className="Button"
|
||||||
|
icon="trash-o"
|
||||||
|
onclick={() => {
|
||||||
|
this.dismissReport().then(() => {
|
||||||
|
PostControls.deleteAction.apply(this.props.post);
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
Delete Forever
|
||||||
|
</Button>,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items.add('hide',
|
||||||
|
<Button className="Button"
|
||||||
|
icon="trash-o"
|
||||||
|
onclick={() => {
|
||||||
|
this.dismissReport().then(() => {
|
||||||
|
PostControls.hideAction.apply(this.props.post);
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
Delete Post
|
||||||
|
</Button>,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.add('dismiss', <Button className="Button Button--icon Button--link" icon="times" onclick={this.dismissReport.bind(this)}>Dismiss Report</Button>, -100);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
|
extend(CommentPost.prototype, 'content', function(vdom) {
|
||||||
|
const post = this.props.post;
|
||||||
|
const reports = post.reports();
|
||||||
|
|
||||||
|
if (!reports.length) return;
|
||||||
|
|
||||||
|
if (post.isHidden()) this.revealContent = true;
|
||||||
|
|
||||||
|
const users = reports.map(report => {
|
||||||
|
const user = report.user();
|
||||||
|
|
||||||
|
return user
|
||||||
|
? <a href={app.route.user(user)} config={m.route}>{username(user)}</a>
|
||||||
|
: report.reporter();
|
||||||
|
});
|
||||||
|
|
||||||
|
const usedReasons = [];
|
||||||
|
const reasons = reports.map(report => report.reason()).filter(reason => {
|
||||||
|
if (reason && usedReasons.indexOf(reason) === -1) {
|
||||||
|
usedReasons.push(reason);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const details = reports.map(report => report.reasonDetail()).filter(detail => detail);
|
||||||
|
|
||||||
|
vdom.unshift(
|
||||||
|
<div className="Post-reported">
|
||||||
|
<div className="Post-reported-summary">
|
||||||
|
{app.trans(reasons.length ? 'reports.reported_by_with_reason' : 'reports.reported_by', {
|
||||||
|
reasons: punctuate(reasons.map(reason => app.trans('reports.reason_' + reason, undefined, reason))),
|
||||||
|
users: punctuate(users)
|
||||||
|
})}
|
||||||
|
{details.map(detail => <div className="Post-reported-detail">{detail}</div>)}
|
||||||
|
</div>
|
||||||
|
<div className="Post-reported-actions">
|
||||||
|
{this.reportActionItems().toArray()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
83
extensions/flags/js/forum/src/components/ReportList.js
Normal file
83
extensions/flags/js/forum/src/components/ReportList.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import Component from 'flarum/Component';
|
||||||
|
import LoadingIndicator from 'flarum/components/LoadingIndicator';
|
||||||
|
import avatar from 'flarum/helpers/avatar';
|
||||||
|
import username from 'flarum/helpers/username';
|
||||||
|
import icon from 'flarum/helpers/icon';
|
||||||
|
import humanTime from 'flarum/helpers/humanTime';
|
||||||
|
|
||||||
|
export default class ReportList extends Component {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the notifications are loading.
|
||||||
|
*
|
||||||
|
* @type {Boolean}
|
||||||
|
*/
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
const reports = app.cache.reports || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="NotificationList ReportList">
|
||||||
|
<div className="NotificationList-header">
|
||||||
|
<h4 className="App-titleControl App-titleControl--text">Reported Posts</h4>
|
||||||
|
</div>
|
||||||
|
<div className="NotificationList-content">
|
||||||
|
<ul className="NotificationGroup-content">
|
||||||
|
{reports.length
|
||||||
|
? reports.map(report => {
|
||||||
|
const post = report.post();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<a href={app.route.post(post)} className="Notification Report" config={function(element, isInitialized) {
|
||||||
|
m.route.apply(this, arguments);
|
||||||
|
|
||||||
|
if (!isInitialized) $(element).on('click', () => app.cache.reportIndex = post);
|
||||||
|
}}>
|
||||||
|
{avatar(post.user())}
|
||||||
|
{icon('flag', {className: 'Notification-icon'})}
|
||||||
|
<span className="Notification-content">
|
||||||
|
{username(post.user())} in <em>{post.discussion().title()}</em>
|
||||||
|
</span>
|
||||||
|
{humanTime(report.time())}
|
||||||
|
<div className="Notification-excerpt">
|
||||||
|
{post.contentPlain()}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: !this.loading
|
||||||
|
? <div className="NotificationList-empty">{app.trans('reports.no_reports')}</div>
|
||||||
|
: LoadingIndicator.component({className: 'LoadingIndicator--block'})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load reports into the application's cache if they haven't already
|
||||||
|
* been loaded.
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
if (app.cache.reports && !app.forum.attribute('unreadReportsCount')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
m.redraw();
|
||||||
|
|
||||||
|
app.store.find('reports').then(reports => {
|
||||||
|
app.forum.pushAttributes({unreadReportsCount: 0});
|
||||||
|
app.cache.reports = reports.sort((a, b) => b.time() - a.time());
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
85
extensions/flags/js/forum/src/components/ReportPostModal.js
Normal file
85
extensions/flags/js/forum/src/components/ReportPostModal.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import Modal from 'flarum/components/Modal';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
|
||||||
|
export default class ReportPostModal extends Modal {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.reason = m.prop('');
|
||||||
|
this.reasonDetail = m.prop('');
|
||||||
|
}
|
||||||
|
|
||||||
|
className() {
|
||||||
|
return 'ReportPostModal Modal--small';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return 'Report Post';
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<div className="Form">
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>Choose a Reason</label>
|
||||||
|
<div>
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="reason" checked={this.reason() === 'off_topic'} value="off_topic" onclick={m.withAttr('value', this.reason)}/>
|
||||||
|
Off-topic
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="reason" checked={this.reason() === 'inappropriate'} value="inappropriate" onclick={m.withAttr('value', this.reason)}/>
|
||||||
|
Inappropriate
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="reason" checked={this.reason() === 'spam'} value="spam" onclick={m.withAttr('value', this.reason)}/>
|
||||||
|
Spam
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="reason" checked={this.reason() === 'other'} value="other" onclick={m.withAttr('value', this.reason)}/>
|
||||||
|
Other
|
||||||
|
{this.reason() === 'other' ? (
|
||||||
|
<textarea className="FormControl" value={this.reasonDetail()} oninput={m.withAttr('value', this.reasonDetail)}></textarea>
|
||||||
|
) : ''}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
{Button.component({
|
||||||
|
children: 'Report Post',
|
||||||
|
className: 'Button Button--primary',
|
||||||
|
loading: this.loading,
|
||||||
|
disabled: !this.reason()
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onsubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
app.store.createRecord('reports').save({
|
||||||
|
reason: this.reason() === 'other' ? null : this.reason(),
|
||||||
|
reasonDetail: this.reasonDetail(),
|
||||||
|
relationships: {
|
||||||
|
user: app.session.user,
|
||||||
|
post: this.props.post
|
||||||
|
}
|
||||||
|
}).then(
|
||||||
|
() => this.hide(),
|
||||||
|
() => {
|
||||||
|
this.loading = false;
|
||||||
|
m.redraw();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
extensions/flags/js/forum/src/components/ReportsDropdown.js
Normal file
26
extensions/flags/js/forum/src/components/ReportsDropdown.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import NotificationsDropdown from 'flarum/components/NotificationsDropdown';
|
||||||
|
|
||||||
|
import ReportList from 'reports/components/ReportList';
|
||||||
|
|
||||||
|
export default class ReportsDropdown extends NotificationsDropdown {
|
||||||
|
static initProps(props) {
|
||||||
|
props.label = props.label || 'Reports';
|
||||||
|
props.icon = props.icon || 'flag';
|
||||||
|
|
||||||
|
super.initProps(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.list = new ReportList();
|
||||||
|
}
|
||||||
|
|
||||||
|
goToRoute() {
|
||||||
|
m.route(app.route('reports'));
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnreadCount() {
|
||||||
|
return app.forum.attribute('unreadReportsCount');
|
||||||
|
}
|
||||||
|
}
|
24
extensions/flags/js/forum/src/components/ReportsPage.js
Normal file
24
extensions/flags/js/forum/src/components/ReportsPage.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
|
||||||
|
import ReportList from 'reports/components/ReportList';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `ReportsPage` component shows the reports list. It is only
|
||||||
|
* used on mobile devices where the reports dropdown is within the drawer.
|
||||||
|
*/
|
||||||
|
export default class ReportsPage extends Page {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
app.history.push('reports');
|
||||||
|
|
||||||
|
this.list = new ReportList();
|
||||||
|
this.list.load();
|
||||||
|
|
||||||
|
this.bodyClass = 'App--reports';
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
return <div className="ReportsPage">{this.list.render()}</div>;
|
||||||
|
}
|
||||||
|
}
|
21
extensions/flags/js/forum/src/main.js
Normal file
21
extensions/flags/js/forum/src/main.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import app from 'flarum/app';
|
||||||
|
import Model from 'flarum/Model';
|
||||||
|
|
||||||
|
import Report from 'reports/models/Report';
|
||||||
|
import ReportsPage from 'reports/components/ReportsPage';
|
||||||
|
import addReportControl from 'reports/addReportControl';
|
||||||
|
import addReportsDropdown from 'reports/addReportsDropdown';
|
||||||
|
import addReportsToPosts from 'reports/addReportsToPosts';
|
||||||
|
|
||||||
|
app.initializers.add('reports', () => {
|
||||||
|
app.store.models.posts.prototype.reports = Model.hasMany('reports');
|
||||||
|
app.store.models.posts.prototype.canReport = Model.attribute('canReport');
|
||||||
|
|
||||||
|
app.store.models.reports = Report;
|
||||||
|
|
||||||
|
app.routes.reports = {path: '/reports', component: <ReportsPage/>};
|
||||||
|
|
||||||
|
addReportControl();
|
||||||
|
addReportsDropdown();
|
||||||
|
addReportsToPosts();
|
||||||
|
});
|
12
extensions/flags/js/forum/src/models/Report.js
Normal file
12
extensions/flags/js/forum/src/models/Report.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import Model from 'flarum/Model';
|
||||||
|
import mixin from 'flarum/utils/mixin';
|
||||||
|
|
||||||
|
export default class Report extends mixin(Model, {
|
||||||
|
reporter: Model.attribute('reporter'),
|
||||||
|
reason: Model.attribute('reason'),
|
||||||
|
reasonDetail: Model.attribute('reasonDetail'),
|
||||||
|
time: Model.attribute('time', Model.transformDate),
|
||||||
|
|
||||||
|
post: Model.hasOne('post'),
|
||||||
|
user: Model.hasOne('user')
|
||||||
|
}) {}
|
0
extensions/flags/less/admin/extension.less
Normal file
0
extensions/flags/less/admin/extension.less
Normal file
59
extensions/flags/less/forum/extension.less
Normal file
59
extensions/flags/less/forum/extension.less
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
.Post--reported {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
border: 2px solid @primary-color;
|
||||||
|
|
||||||
|
.Post-controls {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Post-header .item-reported {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.Post-reported {
|
||||||
|
background: @primary-color;
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: -22px;
|
||||||
|
margin-right: -22px;
|
||||||
|
|
||||||
|
@media @tablet-up {
|
||||||
|
margin-left: -22px - 90px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: @border-radius @border-radius 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
.light-contents(@color: @body-bg; @control-color: @body-bg);
|
||||||
|
|
||||||
|
&, a {
|
||||||
|
color: @body-bg !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Post-reported-summary {
|
||||||
|
@media @tablet-up {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 7px 10px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.Post-reported-detail {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 5px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.Post-reported-actions .Button {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ReportsDropdown .Dropdown-toggle {
|
||||||
|
.Button-label,
|
||||||
|
.Button-caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
8
extensions/flags/locale/en.yml
Normal file
8
extensions/flags/locale/en.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
reports:
|
||||||
|
reason_off_topic: Off-topic
|
||||||
|
reason_spam: Spam
|
||||||
|
reason_inappropriate: Inappropriate
|
||||||
|
reason_other: Other
|
||||||
|
reported_by: "Reported by {users}"
|
||||||
|
reported_by_with_reason: "Reported as {reasons} by {users}"
|
||||||
|
no_reports: No Reports
|
28
extensions/flags/migrations/2015_09_02_000000_add_reports_read_time_to_users_table.php
Normal file
28
extensions/flags/migrations/2015_09_02_000000_add_reports_read_time_to_users_table.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Flarum\Migrations\Migration;
|
||||||
|
|
||||||
|
class AddReportsReadTimeToUsersTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$this->schema->table('users', function (Blueprint $table) {
|
||||||
|
$table->dateTime('reports_read_time')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->schema->drop('reports_read_time');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
<?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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Flarum\Migrations\Migration;
|
||||||
|
|
||||||
|
class CreateReportsTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$this->schema->create('reports', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('post_id')->unsigned();
|
||||||
|
$table->integer('user_id')->unsigned();
|
||||||
|
$table->string('reporter')->nullable();
|
||||||
|
$table->string('reason')->nullable();
|
||||||
|
$table->string('reason_detail')->nullable();
|
||||||
|
$table->dateTime('time');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
$this->schema->drop('reports');
|
||||||
|
}
|
||||||
|
}
|
58
extensions/flags/src/Api/CreateAction.php
Normal file
58
extensions/flags/src/Api/CreateAction.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?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\Reports\Api;
|
||||||
|
|
||||||
|
use Flarum\Reports\Commands\CreateReport;
|
||||||
|
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\Reports\Api\ReportSerializer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public $include = [
|
||||||
|
'post' => true,
|
||||||
|
'post.reports' => true
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Dispatcher $bus
|
||||||
|
*/
|
||||||
|
public function __construct(Dispatcher $bus)
|
||||||
|
{
|
||||||
|
$this->bus = $bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a report according to input from the API request.
|
||||||
|
*
|
||||||
|
* @param JsonApiRequest $request
|
||||||
|
* @return \Flarum\Reports\Report
|
||||||
|
*/
|
||||||
|
protected function create(JsonApiRequest $request)
|
||||||
|
{
|
||||||
|
return $this->bus->dispatch(
|
||||||
|
new CreateReport($request->actor, $request->get('data'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
44
extensions/flags/src/Api/DeleteAction.php
Normal file
44
extensions/flags/src/Api/DeleteAction.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?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\Reports\Api;
|
||||||
|
|
||||||
|
use Flarum\Reports\Commands\DeleteReports;
|
||||||
|
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
|
||||||
|
use Flarum\Api\Request;
|
||||||
|
use Illuminate\Contracts\Bus\Dispatcher;
|
||||||
|
|
||||||
|
class DeleteAction extends BaseDeleteAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Dispatcher
|
||||||
|
*/
|
||||||
|
protected $bus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Dispatcher $bus
|
||||||
|
*/
|
||||||
|
public function __construct(Dispatcher $bus)
|
||||||
|
{
|
||||||
|
$this->bus = $bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete reports for a post.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*/
|
||||||
|
protected function delete(Request $request)
|
||||||
|
{
|
||||||
|
$this->bus->dispatch(
|
||||||
|
new DeleteReports($request->get('id'), $request->actor, $request->all())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
53
extensions/flags/src/Api/IndexAction.php
Normal file
53
extensions/flags/src/Api/IndexAction.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?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\Reports\Api;
|
||||||
|
|
||||||
|
use Flarum\Api\Actions\SerializeCollectionAction;
|
||||||
|
use Flarum\Api\JsonApiRequest;
|
||||||
|
use Flarum\Reports\Report;
|
||||||
|
use Tobscure\JsonApi\Document;
|
||||||
|
|
||||||
|
class IndexAction extends SerializeCollectionAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public $serializer = 'Flarum\Reports\Api\ReportSerializer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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->reports_read_time = time();
|
||||||
|
$actor->save();
|
||||||
|
|
||||||
|
return Report::whereVisibleTo($actor)
|
||||||
|
->with($request->include)
|
||||||
|
->latest('reports.time')
|
||||||
|
->groupBy('post_id')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
37
extensions/flags/src/Api/ReportSerializer.php
Normal file
37
extensions/flags/src/Api/ReportSerializer.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?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\Reports\Api;
|
||||||
|
|
||||||
|
use Flarum\Api\Serializers\Serializer;
|
||||||
|
|
||||||
|
class ReportSerializer extends Serializer
|
||||||
|
{
|
||||||
|
protected $type = 'reports';
|
||||||
|
|
||||||
|
protected function getDefaultAttributes($report)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'reporter' => $report->reporter,
|
||||||
|
'reason' => $report->reason,
|
||||||
|
'reasonDetail' => $report->reason_detail,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function post()
|
||||||
|
{
|
||||||
|
return $this->hasOne('Flarum\Api\Serializers\PostSerializer');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function user()
|
||||||
|
{
|
||||||
|
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||||
|
}
|
||||||
|
}
|
40
extensions/flags/src/Commands/CreateReport.php
Normal file
40
extensions/flags/src/Commands/CreateReport.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?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\Reports\Commands;
|
||||||
|
|
||||||
|
use Flarum\Core\Users\User;
|
||||||
|
|
||||||
|
class CreateReport
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The user performing the action.
|
||||||
|
*
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes of the new report.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $actor The user performing the action.
|
||||||
|
* @param array $data The attributes of the new report.
|
||||||
|
*/
|
||||||
|
public function __construct(User $actor, array $data)
|
||||||
|
{
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
}
|
63
extensions/flags/src/Commands/CreateReportHandler.php
Normal file
63
extensions/flags/src/Commands/CreateReportHandler.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?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\Reports\Commands;
|
||||||
|
|
||||||
|
use Flarum\Reports\Report;
|
||||||
|
use Flarum\Core\Posts\PostRepository;
|
||||||
|
use Flarum\Core\Posts\CommentPost;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class CreateReportHandler
|
||||||
|
{
|
||||||
|
private $posts;
|
||||||
|
|
||||||
|
public function __construct(PostRepository $posts)
|
||||||
|
{
|
||||||
|
$this->posts = $posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CreateReport $command
|
||||||
|
* @return Report
|
||||||
|
*/
|
||||||
|
public function handle(CreateReport $command)
|
||||||
|
{
|
||||||
|
$actor = $command->actor;
|
||||||
|
$data = $command->data;
|
||||||
|
|
||||||
|
$postId = array_get($data, 'relationships.post.data.id');
|
||||||
|
$post = $this->posts->findOrFail($postId, $actor);
|
||||||
|
|
||||||
|
if (! ($post instanceof CommentPost)) {
|
||||||
|
// TODO: throw 400(?) error
|
||||||
|
throw new Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post->assertCan($actor, 'report');
|
||||||
|
|
||||||
|
Report::unguard();
|
||||||
|
|
||||||
|
$report = Report::firstOrNew([
|
||||||
|
'post_id' => $post->id,
|
||||||
|
'user_id' => $actor->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$report->post_id = $post->id;
|
||||||
|
$report->user_id = $actor->id;
|
||||||
|
$report->reason = array_get($data, 'attributes.reason');
|
||||||
|
$report->reason_detail = array_get($data, 'attributes.reasonDetail');
|
||||||
|
$report->time = time();
|
||||||
|
|
||||||
|
$report->save();
|
||||||
|
|
||||||
|
return $report;
|
||||||
|
}
|
||||||
|
}
|
48
extensions/flags/src/Commands/DeleteReports.php
Normal file
48
extensions/flags/src/Commands/DeleteReports.php
Normal 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\Reports\Commands;
|
||||||
|
|
||||||
|
use Flarum\Reports\Report;
|
||||||
|
use Flarum\Core\Users\User;
|
||||||
|
|
||||||
|
class DeleteReports
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The ID of the post to delete reports for.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $postId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user performing the action.
|
||||||
|
*
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $postId The ID of the post to delete reports for.
|
||||||
|
* @param User $actor The user performing the action.
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct($postId, User $actor, array $data = [])
|
||||||
|
{
|
||||||
|
$this->postId = $postId;
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
}
|
45
extensions/flags/src/Commands/DeleteReportsHandler.php
Normal file
45
extensions/flags/src/Commands/DeleteReportsHandler.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?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\Reports\Commands;
|
||||||
|
|
||||||
|
use Flarum\Reports\Report;
|
||||||
|
use Flarum\Core\Posts\PostRepository;
|
||||||
|
use Flarum\Reports\Events\ReportsWillBeDeleted;
|
||||||
|
|
||||||
|
class DeleteReportsHandler
|
||||||
|
{
|
||||||
|
protected $posts;
|
||||||
|
|
||||||
|
public function __construct(PostRepository $posts)
|
||||||
|
{
|
||||||
|
$this->posts = $posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DeleteReport $command
|
||||||
|
* @return Report
|
||||||
|
* @throws \Flarum\Core\Exceptions\PermissionDeniedException
|
||||||
|
*/
|
||||||
|
public function handle(DeleteReports $command)
|
||||||
|
{
|
||||||
|
$actor = $command->actor;
|
||||||
|
|
||||||
|
$post = $this->posts->findOrFail($command->postId, $actor);
|
||||||
|
|
||||||
|
$post->discussion->assertCan($actor, 'viewReports');
|
||||||
|
|
||||||
|
event(new ReportsWillBeDeleted($post, $actor, $command->data));
|
||||||
|
|
||||||
|
$post->reports()->delete();
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
}
|
45
extensions/flags/src/Events/ReportsWillBeDeleted.php
Normal file
45
extensions/flags/src/Events/ReportsWillBeDeleted.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?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\Reports\Events;
|
||||||
|
|
||||||
|
use Flarum\Core\Posts\Post;
|
||||||
|
use Flarum\Core\Users\User;
|
||||||
|
|
||||||
|
class ReportsWillBeDeleted
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Post
|
||||||
|
*/
|
||||||
|
public $post;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Post $post
|
||||||
|
* @param User $actor
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(Post $post, User $actor, array $data = [])
|
||||||
|
{
|
||||||
|
$this->post = $post;
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
}
|
24
extensions/flags/src/Extension.php
Normal file
24
extensions/flags/src/Extension.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?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\Reports;
|
||||||
|
|
||||||
|
use Flarum\Support\Extension as BaseExtension;
|
||||||
|
use Illuminate\Events\Dispatcher;
|
||||||
|
|
||||||
|
class Extension extends BaseExtension
|
||||||
|
{
|
||||||
|
public function listen(Dispatcher $events)
|
||||||
|
{
|
||||||
|
$events->subscribe('Flarum\Reports\Listeners\AddClientAssets');
|
||||||
|
$events->subscribe('Flarum\Reports\Listeners\AddApiAttributes');
|
||||||
|
$events->subscribe('Flarum\Reports\Listeners\AddModelRelationship');
|
||||||
|
}
|
||||||
|
}
|
129
extensions/flags/src/Listeners/AddApiAttributes.php
Executable file
129
extensions/flags/src/Listeners/AddApiAttributes.php
Executable file
@ -0,0 +1,129 @@
|
|||||||
|
<?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\Reports\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\Reports\Report;
|
||||||
|
use Flarum\Reports\Api\CreateAction as ReportsCreateAction;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
|
class AddApiAttributes
|
||||||
|
{
|
||||||
|
public function subscribe($events)
|
||||||
|
{
|
||||||
|
$events->listen(ApiRelationship::class, [$this, 'addReportsRelationship']);
|
||||||
|
$events->listen(WillSerializeData::class, [$this, 'loadReportsRelationship']);
|
||||||
|
$events->listen(BuildApiAction::class, [$this, 'includeReportsRelationship']);
|
||||||
|
$events->listen(ApiAttributes::class, [$this, 'addAttributes']);
|
||||||
|
$events->listen(RegisterApiRoutes::class, [$this, 'addRoutes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadReportsRelationship(WillSerializeData $event)
|
||||||
|
{
|
||||||
|
// For any API action that allows the 'reports' relationship to be
|
||||||
|
// included, we need to preload this relationship onto the data (Post
|
||||||
|
// models) so that we can selectively expose only the reports 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 ReportsCreateAction) {
|
||||||
|
$report = $event->data;
|
||||||
|
$posts = [$report->post];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($posts)) {
|
||||||
|
$actor = $event->request->actor;
|
||||||
|
$postsWithPermission = [];
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$post->setRelation('reports', null);
|
||||||
|
|
||||||
|
if ($post->discussion->can($actor, 'viewReports')) {
|
||||||
|
$postsWithPermission[] = $post;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($postsWithPermission)) {
|
||||||
|
(new Collection($postsWithPermission))
|
||||||
|
->load('reports', 'reports.user');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addReportsRelationship(ApiRelationship $event)
|
||||||
|
{
|
||||||
|
if ($event->serializer instanceof PostSerializer &&
|
||||||
|
$event->relationship === 'reports') {
|
||||||
|
return $event->serializer->hasMany('Flarum\Reports\Api\ReportSerializer', 'reports');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function includeReportsRelationship(BuildApiAction $event)
|
||||||
|
{
|
||||||
|
if ($event->action instanceof Discussions\ShowAction) {
|
||||||
|
$event->addInclude('posts.reports');
|
||||||
|
$event->addInclude('posts.reports.user');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event->action instanceof Posts\IndexAction ||
|
||||||
|
$event->action instanceof Posts\ShowAction) {
|
||||||
|
$event->addInclude('reports');
|
||||||
|
$event->addInclude('reports.user');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addAttributes(ApiAttributes $event)
|
||||||
|
{
|
||||||
|
if ($event->serializer instanceof ForumSerializer) {
|
||||||
|
$event->attributes['canViewReports'] = $event->actor->hasPermissionLike('discussion.viewReports');
|
||||||
|
|
||||||
|
if ($event->attributes['canViewReports']) {
|
||||||
|
$query = Report::whereVisibleTo($event->actor);
|
||||||
|
|
||||||
|
if ($time = $event->actor->reports_read_time) {
|
||||||
|
$query->where('reports.time', '>', $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->attributes['unreadReportsCount'] = $query->distinct('reports.post_id')->count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event->serializer instanceof PostSerializer) {
|
||||||
|
$event->attributes['canReport'] = $event->model->can($event->actor, 'report');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addRoutes(RegisterApiRoutes $event)
|
||||||
|
{
|
||||||
|
$event->get('/reports', 'reports.index', 'Flarum\Reports\Api\IndexAction');
|
||||||
|
$event->post('/reports', 'reports.create', 'Flarum\Reports\Api\CreateAction');
|
||||||
|
$event->delete('/posts/{id}/reports', 'reports.delete', 'Flarum\Reports\Api\DeleteAction');
|
||||||
|
}
|
||||||
|
}
|
60
extensions/flags/src/Listeners/AddClientAssets.php
Normal file
60
extensions/flags/src/Listeners/AddClientAssets.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?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\Reports\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('reports/main');
|
||||||
|
|
||||||
|
$event->forumTranslations([
|
||||||
|
'reports.reason_off_topic',
|
||||||
|
'reports.reason_spam',
|
||||||
|
'reports.reason_inappropriate',
|
||||||
|
'reports.reason_other',
|
||||||
|
'reports.reported_by',
|
||||||
|
'reports.reported_by_with_reason',
|
||||||
|
'reports.no_reports'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$event->adminAssets([
|
||||||
|
__DIR__.'/../../js/admin/dist/extension.js',
|
||||||
|
__DIR__.'/../../less/admin/extension.less'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$event->adminBootstrapper('reports/main');
|
||||||
|
|
||||||
|
$event->adminTranslations([
|
||||||
|
// 'report.hello_world'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
40
extensions/flags/src/Listeners/AddModelRelationship.php
Executable file
40
extensions/flags/src/Listeners/AddModelRelationship.php
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
<?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\Reports\Listeners;
|
||||||
|
|
||||||
|
use Flarum\Events\ModelRelationship;
|
||||||
|
use Flarum\Events\ModelDates;
|
||||||
|
use Flarum\Core\Posts\Post;
|
||||||
|
use Flarum\Core\Users\User;
|
||||||
|
use Flarum\Reports\Report;
|
||||||
|
|
||||||
|
class AddModelRelationship
|
||||||
|
{
|
||||||
|
public function subscribe($events)
|
||||||
|
{
|
||||||
|
$events->listen(ModelRelationship::class, [$this, 'addReportsRelationship']);
|
||||||
|
$events->listen(ModelDates::class, [$this, 'modelDates']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addReportsRelationship(ModelRelationship $event)
|
||||||
|
{
|
||||||
|
if ($event->model instanceof Post && $event->relationship === 'reports') {
|
||||||
|
return $event->model->hasMany('Flarum\Reports\Report', 'post_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modelDates(ModelDates $event)
|
||||||
|
{
|
||||||
|
if ($event->model instanceof User) {
|
||||||
|
$event->dates[] = 'reports_read_time';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
extensions/flags/src/Report.php
Normal file
33
extensions/flags/src/Report.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?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\Reports;
|
||||||
|
|
||||||
|
use Flarum\Core\Model;
|
||||||
|
use Flarum\Core\Support\VisibleScope;
|
||||||
|
|
||||||
|
class Report extends Model
|
||||||
|
{
|
||||||
|
use VisibleScope;
|
||||||
|
|
||||||
|
protected $table = 'reports';
|
||||||
|
|
||||||
|
protected $dates = ['time'];
|
||||||
|
|
||||||
|
public function post()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('Flarum\Core\Posts\Post');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('Flarum\Core\Users\User');
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user