mirror of
https://github.com/flarum/core.git
synced 2025-07-16 14:26:25 +02:00
Merge remote-tracking branch 'extensions_flags/REWRITE'
This commit is contained in:
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,xml,json}]
|
||||||
|
indent_size = 4
|
18
extensions/flags/.gitattributes
vendored
Normal file
18
extensions/flags/.gitattributes
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.gitattributes export-ignore
|
||||||
|
.gitignore export-ignore
|
||||||
|
.gitmodules export-ignore
|
||||||
|
.github export-ignore
|
||||||
|
.travis export-ignore
|
||||||
|
.travis.yml export-ignore
|
||||||
|
.editorconfig export-ignore
|
||||||
|
.styleci.yml export-ignore
|
||||||
|
|
||||||
|
phpunit.xml export-ignore
|
||||||
|
tests export-ignore
|
||||||
|
|
||||||
|
js/dist/* -diff
|
||||||
|
js/dist/* linguist-generated
|
||||||
|
js/dist-typings/* linguist-generated
|
||||||
|
js/yarn.lock -diff
|
||||||
|
|
||||||
|
* text=auto eol=lf
|
15
extensions/flags/.github/workflows/backend.yml
vendored
Normal file
15
extensions/flags/.github/workflows/backend.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
name: Flags PHP
|
||||||
|
|
||||||
|
on: [workflow_dispatch, push, pull_request]
|
||||||
|
|
||||||
|
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
|
||||||
|
# This will break your current script.
|
||||||
|
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run:
|
||||||
|
uses: flarum/.github/.github/workflows/REUSABLE_backend.yml@main
|
||||||
|
with:
|
||||||
|
enable_backend_testing: true
|
||||||
|
|
||||||
|
backend_directory: .
|
21
extensions/flags/.github/workflows/frontend.yml
vendored
Normal file
21
extensions/flags/.github/workflows/frontend.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: Flags JS
|
||||||
|
|
||||||
|
on: [workflow_dispatch, push, pull_request]
|
||||||
|
|
||||||
|
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
|
||||||
|
# This will break your current script.
|
||||||
|
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run:
|
||||||
|
uses: flarum/.github/.github/workflows/REUSABLE_frontend.yml@main
|
||||||
|
with:
|
||||||
|
enable_bundlewatch: false
|
||||||
|
enable_prettier: true
|
||||||
|
enable_typescript: true
|
||||||
|
|
||||||
|
frontend_directory: ./js
|
||||||
|
main_git_branch: master
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
|
12
extensions/flags/.gitignore
vendored
Normal file
12
extensions/flags/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/vendor
|
||||||
|
composer.lock
|
||||||
|
composer.phar
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
tests/.phpunit.result.cache
|
||||||
|
/tests/integration/tmp
|
||||||
|
.vagrant
|
||||||
|
.idea/*
|
||||||
|
.vscode
|
||||||
|
js/coverage-ts
|
14
extensions/flags/.styleci.yml
Normal file
14
extensions/flags/.styleci.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
preset: recommended
|
||||||
|
|
||||||
|
enabled:
|
||||||
|
- logical_not_operators_with_successor_space
|
||||||
|
|
||||||
|
disabled:
|
||||||
|
- align_double_arrow
|
||||||
|
- blank_line_after_opening_tag
|
||||||
|
- multiline_array_trailing_comma
|
||||||
|
- new_with_braces
|
||||||
|
- phpdoc_align
|
||||||
|
- phpdoc_order
|
||||||
|
- phpdoc_separation
|
||||||
|
- phpdoc_types
|
79
extensions/flags/CHANGELOG.md
Normal file
79
extensions/flags/CHANGELOG.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [1.2.0](https://github.com/flarum/flags/compare/v1.1.0...v1.2.0)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Missing translation for the reason flag (https://github.com/flarum/flags/pull/30).
|
||||||
|
- Created flags show user as "deleted" until a page refresh (https://github.com/flarum/flags/pull/42)
|
||||||
|
|
||||||
|
## [1.1.0](https://github.com/flarum/flags/compare/v1.0.0...v1.1.0)
|
||||||
|
|
||||||
|
No changes.
|
||||||
|
|
||||||
|
## [1.0.0](https://github.com/flarum/flags/compare/v0.1.0-beta.16...v1.0.0)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Compatibility with Flarum v1.0.0.
|
||||||
|
- Improvements to performance by eager loading relations (#38)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Incorrectly esolving the deprecated Symfony translator implementation instead of the contract
|
||||||
|
|
||||||
|
## [0.1.0-beta.16](https://github.com/flarum/flags/compare/v0.1.0-beta.15...v0.1.0-beta.16)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Created and Deleting events (https://github.com/flarum/flags/pull/35)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated admin category from moderation to feature (https://github.com/flarum/flags/pull/36)
|
||||||
|
- Moved locale files from translation pack to extension (https://github.com/flarum/flags/pull/32)
|
||||||
|
|
||||||
|
## [0.1.0-beta.15](https://github.com/flarum/flags/compare/v0.1.0-beta.14.1...v0.1.0-beta.15)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated composer.json and admin javascript for new admin area.
|
||||||
|
- Updated to use newest extenders.
|
||||||
|
|
||||||
|
## [0.1.0-beta.14.1](https://github.com/flarum/flags/compare/v0.1.0-beta.14...v0.1.0-beta.14.1)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Flags cache was instantiated prematurely causing incorrect flags count (#31)
|
||||||
|
|
||||||
|
## [0.1.0-beta.14](https://github.com/flarum/flags/compare/v0.1.0-beta.13...v0.1.0-beta.14)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated mithril to version 2
|
||||||
|
- Load language strings correctly on en-/disable
|
||||||
|
- Updated JS dependencies
|
||||||
|
|
||||||
|
## [0.1.0-beta.13](https://github.com/flarum/flags/compare/v0.1.0-beta.12...v0.1.0-beta.13)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated JS dependencies
|
||||||
|
- Stop using deprecated core events, use extenders instead
|
||||||
|
|
||||||
|
## [0.1.0-beta.12](https://github.com/flarum/flags/compare/v0.1.0-beta.10...v0.1.0-beta.12)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Larger flag modal, disallow users to flag their own posts, increase flag message size,
|
||||||
|
allow comment on more reasons, disabled submit on other without comment ([5292e6c](https://github.com/flarum/flags/commit/5292e6cf8a3d4610171f44a6feebb7b31794dd11))
|
||||||
|
|
||||||
|
## [0.1.0-beta.10](https://github.com/flarum/flags/compare/v0.1.0-beta.9...v0.1.0-beta.10)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Guests visiting `api/flags` trigger an exception ([d4680a0](https://github.com/flarum/flags/pull/19/commits/d4680a041afdb286ac85865e5b1f51345a6f9384))
|
||||||
|
|
||||||
|
## [0.1.0-beta.9](https://github.com/flarum/flags/compare/v0.1.0-beta.8.1...v0.1.0-beta.9)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Replace event subscribers (that resolve services too early) with listeners ([2f3417b](https://github.com/flarum/flags/commit/2f3417b863793b918d64c51bcdd65a77e05ffdb9))
|
||||||
|
- Compatibility with Laravel 5.7 ([bd00270](https://github.com/flarum/flags/commit/bd002708c57b5297b1796233d04d18876523ae49))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- API serialization failed when posts for a discussion were not loaded and needed ([9803914](https://github.com/flarum/flags/commit/98039144984eab4e43be7316ecc29fc56959b2c3))
|
||||||
|
|
||||||
|
## [0.1.0-beta.8.1](https://github.com/flarum/flags/compare/v0.1.0-beta.8...v0.1.0-beta.8.1)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix dropping foreign keys in `down` migrations ([e17bd03](https://github.com/flarum/flags/commit/e17bd037b011aac6ef3e38a44ab859a25cd1f763))
|
22
extensions/flags/LICENSE
Normal file
22
extensions/flags/LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2019-2021 Stichting Flarum (Flarum Foundation)
|
||||||
|
Copyright (c) 2014-2019 Toby Zerner (toby.zerner@gmail.com)
|
||||||
|
|
||||||
|
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.
|
80
extensions/flags/composer.json
Normal file
80
extensions/flags/composer.json
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"name": "flarum/flags",
|
||||||
|
"description": "Allow users to flag posts for moderator review.",
|
||||||
|
"type": "flarum-extension",
|
||||||
|
"keywords": [
|
||||||
|
"moderation"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/flarum/core/issues",
|
||||||
|
"source": "https://github.com/flarum/flags",
|
||||||
|
"forum": "https://discuss.flarum.org"
|
||||||
|
},
|
||||||
|
"homepage": "https://flarum.org",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "website",
|
||||||
|
"url": "https://flarum.org/donate/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"flarum/core": "^1.2"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Flarum\\Flags\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.x-dev"
|
||||||
|
},
|
||||||
|
"flarum-extension": {
|
||||||
|
"title": "Flags",
|
||||||
|
"category": "feature",
|
||||||
|
"icon": {
|
||||||
|
"name": "fas fa-flag",
|
||||||
|
"backgroundColor": "#D659B5",
|
||||||
|
"color": "#fff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flarum-cli": {
|
||||||
|
"modules": {
|
||||||
|
"admin": true,
|
||||||
|
"forum": true,
|
||||||
|
"js": true,
|
||||||
|
"jsCommon": false,
|
||||||
|
"css": true,
|
||||||
|
"gitConf": true,
|
||||||
|
"githubActions": true,
|
||||||
|
"prettier": true,
|
||||||
|
"typescript": false,
|
||||||
|
"bundlewatch": false,
|
||||||
|
"backendTesting": true,
|
||||||
|
"editorConfig": true,
|
||||||
|
"styleci": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": [
|
||||||
|
"@test:unit",
|
||||||
|
"@test:integration"
|
||||||
|
],
|
||||||
|
"test:unit": "phpunit -c tests/phpunit.unit.xml",
|
||||||
|
"test:integration": "phpunit -c tests/phpunit.integration.xml",
|
||||||
|
"test:setup": "@php tests/integration/setup.php"
|
||||||
|
},
|
||||||
|
"scripts-descriptions": {
|
||||||
|
"test": "Runs all tests.",
|
||||||
|
"test:unit": "Runs all unit tests.",
|
||||||
|
"test:integration": "Runs all integration tests.",
|
||||||
|
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"flarum/core": "*@dev",
|
||||||
|
"flarum/tags": "*@dev",
|
||||||
|
"flarum/testing": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
86
extensions/flags/extend.php
Normal file
86
extensions/flags/extend.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Api\Controller\AbstractSerializeController;
|
||||||
|
use Flarum\Api\Controller\ListPostsController;
|
||||||
|
use Flarum\Api\Controller\ShowDiscussionController;
|
||||||
|
use Flarum\Api\Controller\ShowPostController;
|
||||||
|
use Flarum\Api\Serializer\CurrentUserSerializer;
|
||||||
|
use Flarum\Api\Serializer\ForumSerializer;
|
||||||
|
use Flarum\Api\Serializer\PostSerializer;
|
||||||
|
use Flarum\Extend;
|
||||||
|
use Flarum\Flags\Access\ScopeFlagVisibility;
|
||||||
|
use Flarum\Flags\AddCanFlagAttribute;
|
||||||
|
use Flarum\Flags\AddFlagsApiAttributes;
|
||||||
|
use Flarum\Flags\AddNewFlagCountAttribute;
|
||||||
|
use Flarum\Flags\Api\Controller\CreateFlagController;
|
||||||
|
use Flarum\Flags\Api\Controller\DeleteFlagsController;
|
||||||
|
use Flarum\Flags\Api\Controller\ListFlagsController;
|
||||||
|
use Flarum\Flags\Api\Serializer\FlagSerializer;
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\Flags\Listener;
|
||||||
|
use Flarum\Flags\PrepareFlagsApiData;
|
||||||
|
use Flarum\Forum\Content\AssertRegistered;
|
||||||
|
use Flarum\Post\Event\Deleted;
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
return [
|
||||||
|
(new Extend\Frontend('forum'))
|
||||||
|
->js(__DIR__.'/js/dist/forum.js')
|
||||||
|
->css(__DIR__.'/less/forum.less')
|
||||||
|
->route('/flags', 'flags', AssertRegistered::class),
|
||||||
|
|
||||||
|
(new Extend\Frontend('admin'))
|
||||||
|
->js(__DIR__.'/js/dist/admin.js'),
|
||||||
|
|
||||||
|
(new Extend\Routes('api'))
|
||||||
|
->get('/flags', 'flags.index', ListFlagsController::class)
|
||||||
|
->post('/flags', 'flags.create', CreateFlagController::class)
|
||||||
|
->delete('/posts/{id}/flags', 'flags.delete', DeleteFlagsController::class),
|
||||||
|
|
||||||
|
(new Extend\Model(User::class))
|
||||||
|
->dateAttribute('read_flags_at'),
|
||||||
|
|
||||||
|
(new Extend\Model(Post::class))
|
||||||
|
->hasMany('flags', Flag::class, 'post_id'),
|
||||||
|
|
||||||
|
(new Extend\ApiSerializer(PostSerializer::class))
|
||||||
|
->hasMany('flags', FlagSerializer::class)
|
||||||
|
->attribute('canFlag', AddCanFlagAttribute::class),
|
||||||
|
|
||||||
|
(new Extend\ApiSerializer(CurrentUserSerializer::class))
|
||||||
|
->attribute('newFlagCount', AddNewFlagCountAttribute::class),
|
||||||
|
|
||||||
|
(new Extend\ApiSerializer(ForumSerializer::class))
|
||||||
|
->attributes(AddFlagsApiAttributes::class),
|
||||||
|
|
||||||
|
(new Extend\ApiController(ShowDiscussionController::class))
|
||||||
|
->addInclude(['posts.flags', 'posts.flags.user']),
|
||||||
|
|
||||||
|
(new Extend\ApiController(ListPostsController::class))
|
||||||
|
->addInclude(['flags', 'flags.user']),
|
||||||
|
|
||||||
|
(new Extend\ApiController(ShowPostController::class))
|
||||||
|
->addInclude(['flags', 'flags.user']),
|
||||||
|
|
||||||
|
(new Extend\ApiController(AbstractSerializeController::class))
|
||||||
|
->prepareDataForSerialization(PrepareFlagsApiData::class),
|
||||||
|
|
||||||
|
(new Extend\Settings())
|
||||||
|
->serializeToForum('guidelinesUrl', 'flarum-flags.guidelines_url'),
|
||||||
|
|
||||||
|
(new Extend\Event())
|
||||||
|
->listen(Deleted::class, Listener\DeleteFlags::class),
|
||||||
|
|
||||||
|
(new Extend\ModelVisibility(Flag::class))
|
||||||
|
->scope(ScopeFlagVisibility::class),
|
||||||
|
|
||||||
|
new Extend\Locales(__DIR__.'/locale'),
|
||||||
|
];
|
9
extensions/flags/js/.gitignore
vendored
Normal file
9
extensions/flags/js/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
node_modules
|
1
extensions/flags/js/admin.js
Normal file
1
extensions/flags/js/admin.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './src/admin';
|
2
extensions/flags/js/dist/admin.js
generated
vendored
Normal file
2
extensions/flags/js/dist/admin.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
module.exports=function(e){var t={};function r(n){if(t[n])return t[n].exports;var a=t[n]={i:n,l:!1,exports:{}};return e[n].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)r.d(n,a,function(t){return e[t]}.bind(null,a));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=24)}({24:function(e,t,r){"use strict";r.r(t);var n=r(5),a=r.n(n);a.a.initializers.add("flarum-flags",(function(){a.a.extensionData.for("flarum-flags").registerSetting({setting:"flarum-flags.guidelines_url",type:"text",label:a.a.translator.trans("flarum-flags.admin.settings.guidelines_url_label")},15).registerSetting({setting:"flarum-flags.can_flag_own",type:"boolean",label:a.a.translator.trans("flarum-flags.admin.settings.flag_own_posts_label")}).registerPermission({icon:"fas fa-flag",label:a.a.translator.trans("flarum-flags.admin.permissions.view_flags_label"),permission:"discussion.viewFlags"},"moderate",65).registerPermission({icon:"fas fa-flag",label:a.a.translator.trans("flarum-flags.admin.permissions.flag_posts_label"),permission:"discussion.flagPosts"},"reply",65)}))},5:function(e,t){e.exports=flarum.core.compat["admin/app"]}});
|
||||||
|
//# sourceMappingURL=admin.js.map
|
1
extensions/flags/js/dist/admin.js.map
generated
vendored
Normal file
1
extensions/flags/js/dist/admin.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
extensions/flags/js/dist/forum.js
generated
vendored
Normal file
2
extensions/flags/js/dist/forum.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
extensions/flags/js/dist/forum.js.map
generated
vendored
Normal file
1
extensions/flags/js/dist/forum.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
extensions/flags/js/forum.js
Normal file
1
extensions/flags/js/forum.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './src/forum';
|
33
extensions/flags/js/package.json
Normal file
33
extensions/flags/js/package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@flarum/flags",
|
||||||
|
"prettier": "@flarum/prettier-config",
|
||||||
|
"dependencies": {
|
||||||
|
"@flarum/prettier-config": "^1.0.0",
|
||||||
|
"flarum-webpack-config": "^1.0.0",
|
||||||
|
"flarum-tsconfig": "^1.0.2",
|
||||||
|
"webpack": "^4.46.0",
|
||||||
|
"webpack-cli": "^4.9.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^2.5.1",
|
||||||
|
"flarum-webpack-config": "^2.0.0",
|
||||||
|
"webpack": "^5.65.0",
|
||||||
|
"webpack-cli": "^4.9.1",
|
||||||
|
"@flarum/prettier-config": "^1.0.0",
|
||||||
|
"flarum-tsconfig": "^1.0.2",
|
||||||
|
"typescript": "^4.5.4",
|
||||||
|
"typescript-coverage-report": "^0.6.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "webpack --mode development --watch",
|
||||||
|
"build": "webpack --mode production",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"format-check": "prettier --check src",
|
||||||
|
"analyze": "cross-env ANALYZER=true yarn build",
|
||||||
|
"clean-typings": "npx rimraf dist-typings && mkdir dist-typings",
|
||||||
|
"build-typings": "npm run clean-typings && cp -r src/@types dist-typings/@types && tsc",
|
||||||
|
"check-typings": "tsc --noEmit --emitDeclarationOnly false",
|
||||||
|
"check-typings-coverage": "typescript-coverage-report"
|
||||||
|
}
|
||||||
|
}
|
38
extensions/flags/js/src/admin/index.js
Normal file
38
extensions/flags/js/src/admin/index.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import app from 'flarum/admin/app';
|
||||||
|
|
||||||
|
app.initializers.add('flarum-flags', () => {
|
||||||
|
app.extensionData
|
||||||
|
.for('flarum-flags')
|
||||||
|
.registerSetting(
|
||||||
|
{
|
||||||
|
setting: 'flarum-flags.guidelines_url',
|
||||||
|
type: 'text',
|
||||||
|
label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label'),
|
||||||
|
},
|
||||||
|
15
|
||||||
|
)
|
||||||
|
.registerSetting({
|
||||||
|
setting: 'flarum-flags.can_flag_own',
|
||||||
|
type: 'boolean',
|
||||||
|
label: app.translator.trans('flarum-flags.admin.settings.flag_own_posts_label'),
|
||||||
|
})
|
||||||
|
.registerPermission(
|
||||||
|
{
|
||||||
|
icon: 'fas fa-flag',
|
||||||
|
label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'),
|
||||||
|
permission: 'discussion.viewFlags',
|
||||||
|
},
|
||||||
|
'moderate',
|
||||||
|
65
|
||||||
|
)
|
||||||
|
|
||||||
|
.registerPermission(
|
||||||
|
{
|
||||||
|
icon: 'fas fa-flag',
|
||||||
|
label: app.translator.trans('flarum-flags.admin.permissions.flag_posts_label'),
|
||||||
|
permission: 'discussion.flagPosts',
|
||||||
|
},
|
||||||
|
'reply',
|
||||||
|
65
|
||||||
|
);
|
||||||
|
});
|
19
extensions/flags/js/src/forum/addFlagControl.js
Normal file
19
extensions/flags/js/src/forum/addFlagControl.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { extend } from 'flarum/common/extend';
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import PostControls from 'flarum/forum/utils/PostControls';
|
||||||
|
import Button from 'flarum/common/components/Button';
|
||||||
|
|
||||||
|
import FlagPostModal from './components/FlagPostModal';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
extend(PostControls, 'userControls', function (items, post) {
|
||||||
|
if (post.isHidden() || post.contentType() !== 'comment' || !post.canFlag()) return;
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'flag',
|
||||||
|
<Button icon="fas fa-flag" onclick={() => app.modal.show(FlagPostModal, { post })}>
|
||||||
|
{app.translator.trans('flarum-flags.forum.post_controls.flag_button')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
12
extensions/flags/js/src/forum/addFlagsDropdown.js
Normal file
12
extensions/flags/js/src/forum/addFlagsDropdown.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { extend } from 'flarum/common/extend';
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import HeaderSecondary from 'flarum/forum/components/HeaderSecondary';
|
||||||
|
import FlagsDropdown from './components/FlagsDropdown';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
extend(HeaderSecondary.prototype, 'items', function (items) {
|
||||||
|
if (app.forum.attribute('canViewFlags')) {
|
||||||
|
items.add('flags', <FlagsDropdown state={app.flags} />, 15);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
115
extensions/flags/js/src/forum/addFlagsToPosts.js
Normal file
115
extensions/flags/js/src/forum/addFlagsToPosts.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import { extend } from 'flarum/common/extend';
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Post from 'flarum/forum/components/Post';
|
||||||
|
import Button from 'flarum/common/components/Button';
|
||||||
|
import ItemList from 'flarum/common/utils/ItemList';
|
||||||
|
import PostControls from 'flarum/forum/utils/PostControls';
|
||||||
|
import humanTime from 'flarum/common/utils/humanTime';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
extend(Post.prototype, 'elementAttrs', function (attrs) {
|
||||||
|
if (this.attrs.post.flags().length) {
|
||||||
|
attrs.className += ' Post--flagged';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Post.prototype.dismissFlag = function (body) {
|
||||||
|
const post = this.attrs.post;
|
||||||
|
|
||||||
|
delete post.data.relationships.flags;
|
||||||
|
|
||||||
|
this.subtree.invalidate();
|
||||||
|
|
||||||
|
if (app.flags.cache) {
|
||||||
|
app.flags.cache.some((flag, i) => {
|
||||||
|
if (flag.post() === post) {
|
||||||
|
app.flags.cache.splice(i, 1);
|
||||||
|
|
||||||
|
if (app.flags.index === post) {
|
||||||
|
let next = app.flags.cache[i];
|
||||||
|
|
||||||
|
if (!next) next = app.flags.cache[0];
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
const nextPost = next.post();
|
||||||
|
app.flags.index = nextPost;
|
||||||
|
m.route.set(app.route.post(nextPost));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.request({
|
||||||
|
url: app.forum.attribute('apiUrl') + post.apiEndpoint() + '/flags',
|
||||||
|
method: 'DELETE',
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Post.prototype.flagActionItems = function () {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
const controls = PostControls.destructiveControls(this.attrs.post);
|
||||||
|
|
||||||
|
Object.keys(controls.items).forEach((k) => {
|
||||||
|
const attrs = controls.get(k).attrs;
|
||||||
|
|
||||||
|
attrs.className = 'Button';
|
||||||
|
|
||||||
|
extend(attrs, 'onclick', () => this.dismissFlag());
|
||||||
|
});
|
||||||
|
|
||||||
|
items.add('controls', <div className="ButtonGroup">{controls.toArray()}</div>);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'dismiss',
|
||||||
|
<Button className="Button" icon="far fa-eye-slash" onclick={this.dismissFlag.bind(this)}>
|
||||||
|
{app.translator.trans('flarum-flags.forum.post.dismiss_flag_button')}
|
||||||
|
</Button>,
|
||||||
|
-100
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
|
extend(Post.prototype, 'content', function (vdom) {
|
||||||
|
const post = this.attrs.post;
|
||||||
|
const flags = post.flags();
|
||||||
|
|
||||||
|
if (!flags.length) return;
|
||||||
|
|
||||||
|
if (post.isHidden()) this.revealContent = true;
|
||||||
|
|
||||||
|
vdom.unshift(
|
||||||
|
<div className="Post-flagged">
|
||||||
|
<div className="Post-flagged-flags">
|
||||||
|
{flags.map((flag) => (
|
||||||
|
<div className="Post-flagged-flag">{this.flagReason(flag)}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="Post-flagged-actions">{this.flagActionItems().toArray()}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Post.prototype.flagReason = function (flag) {
|
||||||
|
if (flag.type() === 'user') {
|
||||||
|
const user = flag.user();
|
||||||
|
const reason = flag.reason() ? app.translator.trans(`flarum-flags.forum.flag_post.reason_${flag.reason()}_label`) : null;
|
||||||
|
const detail = flag.reasonDetail();
|
||||||
|
const time = humanTime(flag.createdAt());
|
||||||
|
|
||||||
|
return [
|
||||||
|
app.translator.trans(reason ? 'flarum-flags.forum.post.flagged_by_with_reason_text' : 'flarum-flags.forum.post.flagged_by_text', {
|
||||||
|
time,
|
||||||
|
user,
|
||||||
|
reason,
|
||||||
|
}),
|
||||||
|
detail ? <span className="Post-flagged-detail">{detail}</span> : '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
19
extensions/flags/js/src/forum/compat.js
Normal file
19
extensions/flags/js/src/forum/compat.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import addFlagsToPosts from './addFlagsToPosts';
|
||||||
|
import addFlagControl from './addFlagControl';
|
||||||
|
import addFlagsDropdown from './addFlagsDropdown';
|
||||||
|
import Flag from './models/Flag';
|
||||||
|
import FlagList from './components/FlagList';
|
||||||
|
import FlagPostModal from './components/FlagPostModal';
|
||||||
|
import FlagsPage from './components/FlagsPage';
|
||||||
|
import FlagsDropdown from './components/FlagsDropdown';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'flags/addFlagsToPosts': addFlagsToPosts,
|
||||||
|
'flags/addFlagControl': addFlagControl,
|
||||||
|
'flags/addFlagsDropdown': addFlagsDropdown,
|
||||||
|
'flags/models/Flag': Flag,
|
||||||
|
'flags/components/FlagList': FlagList,
|
||||||
|
'flags/components/FlagPostModal': FlagPostModal,
|
||||||
|
'flags/components/FlagsPage': FlagsPage,
|
||||||
|
'flags/components/FlagsDropdown': FlagsDropdown,
|
||||||
|
};
|
65
extensions/flags/js/src/forum/components/FlagList.js
Normal file
65
extensions/flags/js/src/forum/components/FlagList.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Component from 'flarum/common/Component';
|
||||||
|
import Link from 'flarum/common/components/Link';
|
||||||
|
import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
|
||||||
|
import avatar from 'flarum/common/helpers/avatar';
|
||||||
|
import username from 'flarum/common/helpers/username';
|
||||||
|
import icon from 'flarum/common/helpers/icon';
|
||||||
|
import humanTime from 'flarum/common/helpers/humanTime';
|
||||||
|
|
||||||
|
export default class FlagList extends Component {
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
this.state = this.attrs.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
const flags = this.state.cache || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="NotificationList FlagList">
|
||||||
|
<div className="NotificationList-header">
|
||||||
|
<h4 className="App-titleControl App-titleControl--text">{app.translator.trans('flarum-flags.forum.flagged_posts.title')}</h4>
|
||||||
|
</div>
|
||||||
|
<div className="NotificationList-content">
|
||||||
|
<ul className="NotificationGroup-content">
|
||||||
|
{flags.length ? (
|
||||||
|
flags.map((flag) => {
|
||||||
|
const post = flag.post();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href={app.route.post(post)}
|
||||||
|
className="Notification Flag"
|
||||||
|
onclick={(e) => {
|
||||||
|
app.flags.index = post;
|
||||||
|
e.redraw = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{avatar(post.user())}
|
||||||
|
{icon('fas fa-flag', { className: 'Notification-icon' })}
|
||||||
|
<span className="Notification-content">
|
||||||
|
{app.translator.trans('flarum-flags.forum.flagged_posts.item_text', {
|
||||||
|
username: username(post.user()),
|
||||||
|
em: <em />,
|
||||||
|
discussion: post.discussion().title(),
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
{humanTime(flag.createdAt())}
|
||||||
|
<div className="Notification-excerpt">{post.contentPlain()}</div>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : !this.state.loading ? (
|
||||||
|
<div className="NotificationList-empty">{app.translator.trans('flarum-flags.forum.flagged_posts.empty_text')}</div>
|
||||||
|
) : (
|
||||||
|
LoadingIndicator.component({ className: 'LoadingIndicator--block' })
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
171
extensions/flags/js/src/forum/components/FlagPostModal.js
Normal file
171
extensions/flags/js/src/forum/components/FlagPostModal.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Modal from 'flarum/common/components/Modal';
|
||||||
|
import Button from 'flarum/common/components/Button';
|
||||||
|
|
||||||
|
import Stream from 'flarum/common/utils/Stream';
|
||||||
|
import withAttr from 'flarum/common/utils/withAttr';
|
||||||
|
import ItemList from 'flarum/common/utils/ItemList';
|
||||||
|
|
||||||
|
export default class FlagPostModal extends Modal {
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
|
||||||
|
this.success = false;
|
||||||
|
|
||||||
|
this.reason = Stream('');
|
||||||
|
this.reasonDetail = Stream('');
|
||||||
|
}
|
||||||
|
|
||||||
|
className() {
|
||||||
|
return 'FlagPostModal Modal--medium';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return app.translator.trans('flarum-flags.forum.flag_post.title');
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
if (this.success) {
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<div className="Form Form--centered">
|
||||||
|
<p className="helpText">{app.translator.trans('flarum-flags.forum.flag_post.confirmation_message')}</p>
|
||||||
|
<div className="Form-group">
|
||||||
|
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
|
||||||
|
{app.translator.trans('flarum-flags.forum.flag_post.dismiss_button')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<div className="Form Form--centered">
|
||||||
|
<div className="Form-group">
|
||||||
|
<div>{this.flagReasons().toArray()}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
<Button className="Button Button--primary Button--block" type="submit" loading={this.loading} disabled={!this.reason()}>
|
||||||
|
{app.translator.trans('flarum-flags.forum.flag_post.submit_button')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
flagReasons() {
|
||||||
|
const items = new ItemList();
|
||||||
|
const guidelinesUrl = app.forum.attribute('guidelinesUrl');
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'off-topic',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="reason" checked={this.reason() === 'off_topic'} value="off_topic" onclick={withAttr('value', this.reason)} />
|
||||||
|
<strong>{app.translator.trans('flarum-flags.forum.flag_post.reason_off_topic_label')}</strong>
|
||||||
|
{app.translator.trans('flarum-flags.forum.flag_post.reason_off_topic_text')}
|
||||||
|
{this.reason() === 'off_topic' ? (
|
||||||
|
<textarea
|
||||||
|
className="FormControl"
|
||||||
|
placeholder={app.translator.trans('flarum-flags.forum.flag_post.reason_details_placeholder')}
|
||||||
|
value={this.reasonDetail()}
|
||||||
|
oninput={withAttr('value', this.reasonDetail)}
|
||||||
|
></textarea>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</label>,
|
||||||
|
70
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'inappropriate',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="reason"
|
||||||
|
checked={this.reason() === 'inappropriate'}
|
||||||
|
value="inappropriate"
|
||||||
|
onclick={withAttr('value', this.reason)}
|
||||||
|
/>
|
||||||
|
<strong>{app.translator.trans('flarum-flags.forum.flag_post.reason_inappropriate_label')}</strong>
|
||||||
|
{app.translator.trans('flarum-flags.forum.flag_post.reason_inappropriate_text', {
|
||||||
|
a: guidelinesUrl ? <a href={guidelinesUrl} target="_blank" /> : undefined,
|
||||||
|
})}
|
||||||
|
{this.reason() === 'inappropriate' ? (
|
||||||
|
<textarea
|
||||||
|
className="FormControl"
|
||||||
|
placeholder={app.translator.trans('flarum-flags.forum.flag_post.reason_details_placeholder')}
|
||||||
|
value={this.reasonDetail()}
|
||||||
|
oninput={withAttr('value', this.reasonDetail)}
|
||||||
|
></textarea>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</label>,
|
||||||
|
60
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'spam',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="reason" checked={this.reason() === 'spam'} value="spam" onclick={withAttr('value', this.reason)} />
|
||||||
|
<strong>{app.translator.trans('flarum-flags.forum.flag_post.reason_spam_label')}</strong>
|
||||||
|
{app.translator.trans('flarum-flags.forum.flag_post.reason_spam_text')}
|
||||||
|
{this.reason() === 'spam' ? (
|
||||||
|
<textarea
|
||||||
|
className="FormControl"
|
||||||
|
placeholder={app.translator.trans('flarum-flags.forum.flag_post.reason_details_placeholder')}
|
||||||
|
value={this.reasonDetail()}
|
||||||
|
oninput={withAttr('value', this.reasonDetail)}
|
||||||
|
></textarea>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</label>,
|
||||||
|
50
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'other',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="reason" checked={this.reason() === 'other'} value="other" onclick={withAttr('value', this.reason)} />
|
||||||
|
<strong>{app.translator.trans('flarum-flags.forum.flag_post.reason_other_label')}</strong>
|
||||||
|
{this.reason() === 'other' ? (
|
||||||
|
<textarea className="FormControl" value={this.reasonDetail()} oninput={withAttr('value', this.reasonDetail)}></textarea>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</label>,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
onsubmit(e) {
|
||||||
|
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.attrs.post,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ errorHandler: this.onerror.bind(this) }
|
||||||
|
)
|
||||||
|
.then(() => (this.success = true))
|
||||||
|
.catch(() => {})
|
||||||
|
.then(this.loaded.bind(this));
|
||||||
|
}
|
||||||
|
}
|
33
extensions/flags/js/src/forum/components/FlagsDropdown.js
Normal file
33
extensions/flags/js/src/forum/components/FlagsDropdown.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import NotificationsDropdown from 'flarum/components/NotificationsDropdown';
|
||||||
|
|
||||||
|
import FlagList from './FlagList';
|
||||||
|
|
||||||
|
export default class FlagsDropdown extends NotificationsDropdown {
|
||||||
|
static initAttrs(attrs) {
|
||||||
|
attrs.label = attrs.label || app.translator.trans('flarum-flags.forum.flagged_posts.tooltip');
|
||||||
|
attrs.icon = attrs.icon || 'fas fa-flag';
|
||||||
|
|
||||||
|
super.initAttrs(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMenu() {
|
||||||
|
return (
|
||||||
|
<div className={'Dropdown-menu ' + this.attrs.menuClassName} onclick={this.menuClick.bind(this)}>
|
||||||
|
{this.showing ? FlagList.component({ state: this.attrs.state }) : ''}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
goToRoute() {
|
||||||
|
m.route.set(app.route('flags'));
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnreadCount() {
|
||||||
|
return app.flags.cache ? app.flags.cache.length : app.forum.attribute('flagCount');
|
||||||
|
}
|
||||||
|
|
||||||
|
getNewCount() {
|
||||||
|
return app.session.user.attribute('newFlagCount');
|
||||||
|
}
|
||||||
|
}
|
28
extensions/flags/js/src/forum/components/FlagsPage.js
Normal file
28
extensions/flags/js/src/forum/components/FlagsPage.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Page from 'flarum/components/Page';
|
||||||
|
|
||||||
|
import FlagList from './FlagList';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `FlagsPage` component shows the flags list. It is only
|
||||||
|
* used on mobile devices where the flags dropdown is within the drawer.
|
||||||
|
*/
|
||||||
|
export default class FlagsPage extends Page {
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
|
||||||
|
app.history.push('flags');
|
||||||
|
|
||||||
|
app.flags.load();
|
||||||
|
|
||||||
|
this.bodyClass = 'App--flags';
|
||||||
|
}
|
||||||
|
|
||||||
|
view() {
|
||||||
|
return (
|
||||||
|
<div className="FlagsPage">
|
||||||
|
<FlagList state={app.flags}></FlagList>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
30
extensions/flags/js/src/forum/index.js
Normal file
30
extensions/flags/js/src/forum/index.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Model from 'flarum/common/Model';
|
||||||
|
|
||||||
|
import Flag from './models/Flag';
|
||||||
|
import FlagsPage from './components/FlagsPage';
|
||||||
|
import FlagListState from './states/FlagListState';
|
||||||
|
import addFlagControl from './addFlagControl';
|
||||||
|
import addFlagsDropdown from './addFlagsDropdown';
|
||||||
|
import addFlagsToPosts from './addFlagsToPosts';
|
||||||
|
|
||||||
|
app.initializers.add('flarum-flags', () => {
|
||||||
|
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: FlagsPage };
|
||||||
|
|
||||||
|
app.flags = new FlagListState(app);
|
||||||
|
|
||||||
|
addFlagControl();
|
||||||
|
addFlagsDropdown();
|
||||||
|
addFlagsToPosts();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expose compat API
|
||||||
|
import flagsCompat from './compat';
|
||||||
|
import { compat } from '@flarum/core/forum';
|
||||||
|
|
||||||
|
Object.assign(compat, flagsCompat);
|
15
extensions/flags/js/src/forum/models/Flag.js
Normal file
15
extensions/flags/js/src/forum/models/Flag.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Model from 'flarum/common/Model';
|
||||||
|
|
||||||
|
class Flag extends Model {}
|
||||||
|
|
||||||
|
Object.assign(Flag.prototype, {
|
||||||
|
type: Model.attribute('type'),
|
||||||
|
reason: Model.attribute('reason'),
|
||||||
|
reasonDetail: Model.attribute('reasonDetail'),
|
||||||
|
createdAt: Model.attribute('createdAt', Model.transformDate),
|
||||||
|
|
||||||
|
post: Model.hasOne('post'),
|
||||||
|
user: Model.hasOne('user'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Flag;
|
37
extensions/flags/js/src/forum/states/FlagListState.js
Normal file
37
extensions/flags/js/src/forum/states/FlagListState.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
export default class FlagListState {
|
||||||
|
constructor(app) {
|
||||||
|
this.app = app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the flags are loading.
|
||||||
|
*
|
||||||
|
* @type {Boolean}
|
||||||
|
*/
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load flags into the application's cache if they haven't already
|
||||||
|
* been loaded.
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
if (this.cache && !this.app.session.user.attribute('newFlagCount')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
m.redraw();
|
||||||
|
|
||||||
|
this.app.store
|
||||||
|
.find('flags')
|
||||||
|
.then((flags) => {
|
||||||
|
this.app.session.user.pushAttributes({ newFlagCount: 0 });
|
||||||
|
this.cache = flags.sort((a, b) => b.createdAt() - a.createdAt());
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.then(() => {
|
||||||
|
this.loading = false;
|
||||||
|
m.redraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
1
extensions/flags/js/webpack.config.js
Normal file
1
extensions/flags/js/webpack.config.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('flarum-webpack-config')();
|
4512
extensions/flags/js/yarn.lock
Normal file
4512
extensions/flags/js/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
0
extensions/flags/less/admin.less
Normal file
0
extensions/flags/less/admin.less
Normal file
72
extensions/flags/less/forum.less
Normal file
72
extensions/flags/less/forum.less
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
.Post--flagged {
|
||||||
|
padding-top: 0 !important;
|
||||||
|
border: 2px solid @primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Post-header .item-flagged {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.Post-flagged {
|
||||||
|
background: @primary-color;
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: -22px;
|
||||||
|
margin-right: -22px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: @border-radius @border-radius 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
.light-contents(@color: @body-bg; @control-color: @body-bg);
|
||||||
|
|
||||||
|
@media @tablet-up {
|
||||||
|
margin-left: -22px - 85px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&, a {
|
||||||
|
color: @body-bg !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Post-flagged-flags {
|
||||||
|
@media @tablet-up {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 7px 10px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.Post-flagged-detail {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 10px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.Post-flagged-actions {
|
||||||
|
@media @tablet-up {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Post-flagged-actions .Button {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.FlagsDropdown .Dropdown-toggle {
|
||||||
|
.Button-label,
|
||||||
|
.Button-caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.FlagPostModal {
|
||||||
|
.Form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.checkbox {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
display: block;
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
extensions/flags/locale/en.yml
Normal file
64
extensions/flags/locale/en.yml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
flarum-flags:
|
||||||
|
|
||||||
|
##
|
||||||
|
# UNIQUE KEYS - The following keys are used in only one location each.
|
||||||
|
##
|
||||||
|
|
||||||
|
# Translations in this namespace are used by the admin interface.
|
||||||
|
admin:
|
||||||
|
|
||||||
|
# These translations are used in the Permissions page of the admin interface.
|
||||||
|
permissions:
|
||||||
|
flag_posts_label: Flag posts
|
||||||
|
view_flags_label: View flagged posts
|
||||||
|
|
||||||
|
# These translations are used in the Flags Settings modal dialog.
|
||||||
|
settings:
|
||||||
|
flag_own_posts_label: Allow users to flag their own posts
|
||||||
|
guidelines_url_label: Community Guidelines URL
|
||||||
|
title: Flags Settings
|
||||||
|
|
||||||
|
# Translations in this namespace are used by the forum user interface.
|
||||||
|
forum:
|
||||||
|
|
||||||
|
# These translations are used by the Flag Post modal dialog.
|
||||||
|
flag_post:
|
||||||
|
confirmation_message: Thank you for flagging this post. Our moderators will look into it.
|
||||||
|
dismiss_button: => core.ref.okay
|
||||||
|
reason_details_placeholder: Additional details (optional)
|
||||||
|
reason_inappropriate_label: Inappropriate
|
||||||
|
reason_inappropriate_text: "This post is offensive, abusive, or violates our <a>community guidelines</a>."
|
||||||
|
reason_missing_message: Please provide some details for our moderators.
|
||||||
|
reason_off_topic_label: "Off-topic"
|
||||||
|
reason_off_topic_text: This post is not relevant to the current discussion and should be moved elsewhere.
|
||||||
|
reason_other_label: Other (please specify)
|
||||||
|
reason_spam_label: Spam
|
||||||
|
reason_spam_text: This post is an advertisement.
|
||||||
|
submit_button: => flarum-flags.ref.flag_post
|
||||||
|
title: => flarum-flags.ref.flag_post
|
||||||
|
|
||||||
|
# These translations are used by the Flagged Posts dropdown, a.k.a. "the flag".
|
||||||
|
flagged_posts:
|
||||||
|
empty_text: No Flags
|
||||||
|
item_text: "{username} in <em>{discussion}</em>"
|
||||||
|
title: => flarum-flags.ref.flagged_posts
|
||||||
|
tooltip: => flarum-flags.ref.flagged_posts
|
||||||
|
|
||||||
|
# These translations are used by the frame displayed around flagged posts.
|
||||||
|
post:
|
||||||
|
dismiss_flag_button: Dismiss Flag
|
||||||
|
flagged_by_text: "Flagged by {username}"
|
||||||
|
flagged_by_with_reason_text: "Flagged by {username} as {reason}"
|
||||||
|
|
||||||
|
# These translations are used by the post control buttons.
|
||||||
|
post_controls:
|
||||||
|
flag_button: Flag
|
||||||
|
|
||||||
|
##
|
||||||
|
# REUSED TRANSLATIONS - These keys should not be used directly in code!
|
||||||
|
##
|
||||||
|
|
||||||
|
# Translations in this namespace are referenced by two or more unique keys.
|
||||||
|
ref:
|
||||||
|
flag_post: Flag Post
|
||||||
|
flagged_posts: Flagged Posts
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
|
||||||
|
return Migration::addColumns('users', [
|
||||||
|
'flags_read_time' => ['dateTime', 'nullable' => true]
|
||||||
|
]);
|
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
return Migration::createTable(
|
||||||
|
'flags',
|
||||||
|
function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('post_id')->unsigned();
|
||||||
|
$table->string('type');
|
||||||
|
$table->integer('user_id')->unsigned()->nullable();
|
||||||
|
$table->string('reason')->nullable();
|
||||||
|
$table->string('reason_detail')->nullable();
|
||||||
|
$table->dateTime('time');
|
||||||
|
}
|
||||||
|
);
|
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
|
||||||
|
return Migration::addPermissions([
|
||||||
|
'discussion.flagPosts' => Group::MEMBER_ID,
|
||||||
|
'discussion.viewFlags' => Group::MODERATOR_ID
|
||||||
|
]);
|
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
|
||||||
|
return Migration::renameColumn('flags', 'time', 'created_at');
|
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'up' => function (Builder $schema) {
|
||||||
|
// Delete rows with non-existent entities so that we will be able to create
|
||||||
|
// foreign keys without any issues.
|
||||||
|
$schema->getConnection()
|
||||||
|
->table('flags')
|
||||||
|
->whereNotExists(function ($query) {
|
||||||
|
$query->selectRaw(1)->from('posts')->whereColumn('id', 'post_id');
|
||||||
|
})
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
$schema->getConnection()
|
||||||
|
->table('flags')
|
||||||
|
->whereNotExists(function ($query) {
|
||||||
|
$query->selectRaw(1)->from('users')->whereColumn('id', 'user_id');
|
||||||
|
})
|
||||||
|
->update(['user_id' => null]);
|
||||||
|
|
||||||
|
$schema->table('flags', function (Blueprint $table) {
|
||||||
|
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'down' => function (Builder $schema) {
|
||||||
|
$schema->table('flags', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['post_id']);
|
||||||
|
$table->dropForeign(['user_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
|
||||||
|
return Migration::renameColumn('users', 'flags_read_time', 'read_flags_at');
|
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'up' => function (Builder $schema) {
|
||||||
|
$schema->table('flags', function (Blueprint $table) {
|
||||||
|
$table->index('created_at');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'down' => function (Builder $schema) {
|
||||||
|
$schema->table('flags', function (Blueprint $table) {
|
||||||
|
$table->dropIndex(['created_at']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'up' => function (Builder $schema) {
|
||||||
|
$schema->table('flags', function (Blueprint $table) {
|
||||||
|
$table->text('reason_detail')->change();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'down' => function (Builder $schema) {
|
||||||
|
$schema->table('flags', function (Blueprint $table) {
|
||||||
|
$table->string('reason_detail')->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
58
extensions/flags/src/Access/ScopeFlagVisibility.php
Normal file
58
extensions/flags/src/Access/ScopeFlagVisibility.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Access;
|
||||||
|
|
||||||
|
use Flarum\Extension\ExtensionManager;
|
||||||
|
use Flarum\Tags\Tag;
|
||||||
|
use Flarum\User\User;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
class ScopeFlagVisibility
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ExtensionManager
|
||||||
|
*/
|
||||||
|
protected $extensions;
|
||||||
|
|
||||||
|
public function __construct(ExtensionManager $extensions)
|
||||||
|
{
|
||||||
|
$this->extensions = $extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(User $actor, Builder $query)
|
||||||
|
{
|
||||||
|
if ($this->extensions->isEnabled('flarum-tags')) {
|
||||||
|
$query
|
||||||
|
->select('flags.*')
|
||||||
|
->leftJoin('posts', 'posts.id', '=', 'flags.post_id')
|
||||||
|
->leftJoin('discussions', 'discussions.id', '=', 'posts.discussion_id')
|
||||||
|
->whereNotExists(function ($query) use ($actor) {
|
||||||
|
return $query->selectRaw('1')
|
||||||
|
->from('discussion_tag')
|
||||||
|
->whereNotIn('tag_id', function ($query) use ($actor) {
|
||||||
|
Tag::query()->setQuery($query->from('tags'))->whereHasPermission($actor, 'discussion.viewFlags')->select('tags.id');
|
||||||
|
})
|
||||||
|
->whereColumn('discussions.id', 'discussion_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (! $actor->hasPermission('discussion.viewFlags')) {
|
||||||
|
$query->whereExists(function ($query) {
|
||||||
|
return $query->selectRaw('1')
|
||||||
|
->from('discussion_tag')
|
||||||
|
->whereColumn('discussions.id', 'discussion_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $actor->hasPermission('discussion.viewFlags')) {
|
||||||
|
$query->orWhere('flags.user_id', $actor->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
extensions/flags/src/AddCanFlagAttribute.php
Normal file
46
extensions/flags/src/AddCanFlagAttribute.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags;
|
||||||
|
|
||||||
|
use Flarum\Api\Serializer\PostSerializer;
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class AddCanFlagAttribute
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SettingsRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SettingsRepositoryInterface $settings
|
||||||
|
*/
|
||||||
|
public function __construct(SettingsRepositoryInterface $settings)
|
||||||
|
{
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(PostSerializer $serializer, Post $post)
|
||||||
|
{
|
||||||
|
return $serializer->getActor()->can('flag', $post) && $this->checkFlagOwnPostSetting($serializer->getActor(), $post);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkFlagOwnPostSetting(User $actor, Post $post): bool
|
||||||
|
{
|
||||||
|
if ($actor->id === $post->user_id) {
|
||||||
|
// If $actor is the post author, check to see if the setting is enabled
|
||||||
|
return (bool) $this->settings->get('flarum-flags.can_flag_own');
|
||||||
|
}
|
||||||
|
// $actor is not the post author
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
52
extensions/flags/src/AddFlagsApiAttributes.php
Executable file
52
extensions/flags/src/AddFlagsApiAttributes.php
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags;
|
||||||
|
|
||||||
|
use Flarum\Api\Serializer\ForumSerializer;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class AddFlagsApiAttributes
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var SettingsRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SettingsRepositoryInterface $settings
|
||||||
|
*/
|
||||||
|
public function __construct(SettingsRepositoryInterface $settings)
|
||||||
|
{
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(ForumSerializer $serializer)
|
||||||
|
{
|
||||||
|
$attributes = [
|
||||||
|
'canViewFlags' => $serializer->getActor()->hasPermissionLike('discussion.viewFlags')
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($attributes['canViewFlags']) {
|
||||||
|
$attributes['flagCount'] = (int) $this->getFlagCount($serializer->getActor());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $actor
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected function getFlagCount(User $actor)
|
||||||
|
{
|
||||||
|
return Flag::whereVisibleTo($actor)->distinct()->count('flags.post_id');
|
||||||
|
}
|
||||||
|
}
|
36
extensions/flags/src/AddNewFlagCountAttribute.php
Normal file
36
extensions/flags/src/AddNewFlagCountAttribute.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags;
|
||||||
|
|
||||||
|
use Flarum\Api\Serializer\CurrentUserSerializer;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class AddNewFlagCountAttribute
|
||||||
|
{
|
||||||
|
public function __invoke(CurrentUserSerializer $serializer, User $user)
|
||||||
|
{
|
||||||
|
return (int) $this->getNewFlagCount($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $actor
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected function getNewFlagCount(User $actor)
|
||||||
|
{
|
||||||
|
$query = Flag::whereVisibleTo($actor);
|
||||||
|
|
||||||
|
if ($time = $actor->read_flags_at) {
|
||||||
|
$query->where('flags.created_at', '>', $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->distinct()->count('flags.post_id');
|
||||||
|
}
|
||||||
|
}
|
59
extensions/flags/src/Api/Controller/CreateFlagController.php
Normal file
59
extensions/flags/src/Api/Controller/CreateFlagController.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed 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 Flarum\Http\RequestUtil;
|
||||||
|
use Illuminate\Contracts\Bus\Dispatcher;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Tobscure\JsonApi\Document;
|
||||||
|
|
||||||
|
class CreateFlagController extends AbstractCreateController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public $serializer = FlagSerializer::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public $include = [
|
||||||
|
'post',
|
||||||
|
'post.flags',
|
||||||
|
'user'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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(RequestUtil::getActor($request), Arr::get($request->getParsedBody(), 'data', []))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed 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\AbstractDeleteController;
|
||||||
|
use Flarum\Flags\Command\DeleteFlags;
|
||||||
|
use Flarum\Http\RequestUtil;
|
||||||
|
use Illuminate\Contracts\Bus\Dispatcher;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class DeleteFlagsController extends AbstractDeleteController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Dispatcher
|
||||||
|
*/
|
||||||
|
protected $bus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Dispatcher $bus
|
||||||
|
*/
|
||||||
|
public function __construct(Dispatcher $bus)
|
||||||
|
{
|
||||||
|
$this->bus = $bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function delete(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$this->bus->dispatch(
|
||||||
|
new DeleteFlags(Arr::get($request->getQueryParams(), 'id'), RequestUtil::getActor($request), $request->getParsedBody())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
62
extensions/flags/src/Api/Controller/ListFlagsController.php
Normal file
62
extensions/flags/src/Api/Controller/ListFlagsController.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed 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\AbstractListController;
|
||||||
|
use Flarum\Flags\Api\Serializer\FlagSerializer;
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\Http\RequestUtil;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Tobscure\JsonApi\Document;
|
||||||
|
|
||||||
|
class ListFlagsController extends AbstractListController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public $serializer = FlagSerializer::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public $include = [
|
||||||
|
'user',
|
||||||
|
'post',
|
||||||
|
'post.user',
|
||||||
|
'post.discussion'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function data(ServerRequestInterface $request, Document $document)
|
||||||
|
{
|
||||||
|
$actor = RequestUtil::getActor($request);
|
||||||
|
$include = $this->extractInclude($request);
|
||||||
|
|
||||||
|
$actor->assertRegistered();
|
||||||
|
|
||||||
|
$actor->read_flags_at = time();
|
||||||
|
$actor->save();
|
||||||
|
|
||||||
|
$flags = Flag::whereVisibleTo($actor)
|
||||||
|
->latest('flags.created_at')
|
||||||
|
->groupBy('post_id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if (in_array('post.user', $include)) {
|
||||||
|
$include[] = 'post.user.groups';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadRelations($flags, $include);
|
||||||
|
|
||||||
|
return $flags;
|
||||||
|
}
|
||||||
|
}
|
51
extensions/flags/src/Api/Serializer/FlagSerializer.php
Normal file
51
extensions/flags/src/Api/Serializer/FlagSerializer.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Api\Serializer;
|
||||||
|
|
||||||
|
use Flarum\Api\Serializer\AbstractSerializer;
|
||||||
|
use Flarum\Api\Serializer\BasicUserSerializer;
|
||||||
|
use Flarum\Api\Serializer\PostSerializer;
|
||||||
|
|
||||||
|
class FlagSerializer extends AbstractSerializer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $type = 'flags';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getDefaultAttributes($flag)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'type' => $flag->type,
|
||||||
|
'reason' => $flag->reason,
|
||||||
|
'reasonDetail' => $flag->reason_detail,
|
||||||
|
'createdAt' => $this->formatDate($flag->created_at),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
|
*/
|
||||||
|
protected function post($flag)
|
||||||
|
{
|
||||||
|
return $this->hasOne($flag, PostSerializer::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
|
*/
|
||||||
|
protected function user($flag)
|
||||||
|
{
|
||||||
|
return $this->hasOne($flag, BasicUserSerializer::class);
|
||||||
|
}
|
||||||
|
}
|
39
extensions/flags/src/Command/CreateFlag.php
Normal file
39
extensions/flags/src/Command/CreateFlag.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Command;
|
||||||
|
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class CreateFlag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The user performing the action.
|
||||||
|
*
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes of the new flag.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $actor The user performing the action.
|
||||||
|
* @param array $data The attributes of the new flag.
|
||||||
|
*/
|
||||||
|
public function __construct(User $actor, array $data)
|
||||||
|
{
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
}
|
110
extensions/flags/src/Command/CreateFlagHandler.php
Normal file
110
extensions/flags/src/Command/CreateFlagHandler.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Command;
|
||||||
|
|
||||||
|
use Flarum\Flags\Event\Created;
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\Foundation\ValidationException;
|
||||||
|
use Flarum\Post\CommentPost;
|
||||||
|
use Flarum\Post\PostRepository;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Flarum\User\Exception\PermissionDeniedException;
|
||||||
|
use Illuminate\Events\Dispatcher;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
use Tobscure\JsonApi\Exception\InvalidParameterException;
|
||||||
|
|
||||||
|
class CreateFlagHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var PostRepository
|
||||||
|
*/
|
||||||
|
protected $posts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
protected $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SettingsRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Dispatcher
|
||||||
|
*/
|
||||||
|
protected $events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PostRepository $posts
|
||||||
|
* @param TranslatorInterface $translator
|
||||||
|
* @param SettingsRepositoryInterface $settings
|
||||||
|
* @param Dispatcher $events
|
||||||
|
*/
|
||||||
|
public function __construct(PostRepository $posts, TranslatorInterface $translator, SettingsRepositoryInterface $settings, Dispatcher $events)
|
||||||
|
{
|
||||||
|
$this->posts = $posts;
|
||||||
|
$this->translator = $translator;
|
||||||
|
$this->settings = $settings;
|
||||||
|
$this->events = $events;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CreateFlag $command
|
||||||
|
* @return Flag
|
||||||
|
* @throws InvalidParameterException
|
||||||
|
* @throws ValidationException
|
||||||
|
*/
|
||||||
|
public function handle(CreateFlag $command)
|
||||||
|
{
|
||||||
|
$actor = $command->actor;
|
||||||
|
$data = $command->data;
|
||||||
|
|
||||||
|
$postId = Arr::get($data, 'relationships.post.data.id');
|
||||||
|
$post = $this->posts->findOrFail($postId, $actor);
|
||||||
|
|
||||||
|
if (! ($post instanceof CommentPost)) {
|
||||||
|
throw new InvalidParameterException;
|
||||||
|
}
|
||||||
|
|
||||||
|
$actor->assertCan('flag', $post);
|
||||||
|
|
||||||
|
if ($actor->id === $post->user_id && ! $this->settings->get('flarum-flags.can_flag_own')) {
|
||||||
|
throw new PermissionDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Arr::get($data, 'attributes.reason') === null && Arr::get($data, 'attributes.reasonDetail') === '') {
|
||||||
|
throw new ValidationException([
|
||||||
|
'message' => $this->translator->trans('flarum-flags.forum.flag_post.reason_missing_message')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Flag::unguard();
|
||||||
|
|
||||||
|
$flag = Flag::firstOrNew([
|
||||||
|
'post_id' => $post->id,
|
||||||
|
'user_id' => $actor->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$flag->post_id = $post->id;
|
||||||
|
$flag->user_id = $actor->id;
|
||||||
|
$flag->type = 'user';
|
||||||
|
$flag->reason = Arr::get($data, 'attributes.reason');
|
||||||
|
$flag->reason_detail = Arr::get($data, 'attributes.reasonDetail');
|
||||||
|
$flag->created_at = time();
|
||||||
|
|
||||||
|
$flag->save();
|
||||||
|
|
||||||
|
$this->events->dispatch(new Created($flag, $actor, $data));
|
||||||
|
|
||||||
|
return $flag;
|
||||||
|
}
|
||||||
|
}
|
46
extensions/flags/src/Command/DeleteFlags.php
Normal file
46
extensions/flags/src/Command/DeleteFlags.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Command;
|
||||||
|
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class DeleteFlags
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The ID of the post to delete flags 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 flags 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;
|
||||||
|
}
|
||||||
|
}
|
63
extensions/flags/src/Command/DeleteFlagsHandler.php
Normal file
63
extensions/flags/src/Command/DeleteFlagsHandler.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Command;
|
||||||
|
|
||||||
|
use Flarum\Flags\Event\Deleting;
|
||||||
|
use Flarum\Flags\Event\FlagsWillBeDeleted;
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\Post\PostRepository;
|
||||||
|
use Illuminate\Events\Dispatcher;
|
||||||
|
|
||||||
|
class DeleteFlagsHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
$actor->assertCan('viewFlags', $post->discussion);
|
||||||
|
|
||||||
|
// Deprecated, removed v2.0
|
||||||
|
$this->events->dispatch(new FlagsWillBeDeleted($post, $actor, $command->data));
|
||||||
|
|
||||||
|
foreach ($post->flags as $flag) {
|
||||||
|
$this->events->dispatch(new Deleting($flag, $actor, $command->data));
|
||||||
|
}
|
||||||
|
|
||||||
|
$post->flags()->delete();
|
||||||
|
|
||||||
|
return $post;
|
||||||
|
}
|
||||||
|
}
|
43
extensions/flags/src/Event/Created.php
Normal file
43
extensions/flags/src/Event/Created.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Event;
|
||||||
|
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class Created
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Flag
|
||||||
|
*/
|
||||||
|
public $flag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Flag $flag
|
||||||
|
* @param User $actor
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(Flag $flag, User $actor, array $data = [])
|
||||||
|
{
|
||||||
|
$this->flag = $flag;
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
}
|
43
extensions/flags/src/Event/Deleting.php
Normal file
43
extensions/flags/src/Event/Deleting.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Event;
|
||||||
|
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class Deleting
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Flag
|
||||||
|
*/
|
||||||
|
public $flag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Flag $flag
|
||||||
|
* @param User $actor
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public function __construct(Flag $flag, User $actor, array $data = [])
|
||||||
|
{
|
||||||
|
$this->flag = $flag;
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
}
|
47
extensions/flags/src/Event/FlagsWillBeDeleted.php
Normal file
47
extensions/flags/src/Event/FlagsWillBeDeleted.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Event;
|
||||||
|
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated v2.0
|
||||||
|
* Listen for Flarum\Flags\Event\Deleting instead
|
||||||
|
*/
|
||||||
|
class FlagsWillBeDeleted
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
41
extensions/flags/src/Flag.php
Normal file
41
extensions/flags/src/Flag.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags;
|
||||||
|
|
||||||
|
use Flarum\Database\AbstractModel;
|
||||||
|
use Flarum\Database\ScopeVisibilityTrait;
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class Flag extends AbstractModel
|
||||||
|
{
|
||||||
|
use ScopeVisibilityTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $dates = ['created_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function post()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Post::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
23
extensions/flags/src/Listener/DeleteFlags.php
Normal file
23
extensions/flags/src/Listener/DeleteFlags.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Listener;
|
||||||
|
|
||||||
|
use Flarum\Post\Event\Deleted;
|
||||||
|
|
||||||
|
class DeleteFlags
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Deleted $event
|
||||||
|
*/
|
||||||
|
public function handle(Deleted $event)
|
||||||
|
{
|
||||||
|
$event->post->flags()->delete();
|
||||||
|
}
|
||||||
|
}
|
64
extensions/flags/src/PrepareFlagsApiData.php
Executable file
64
extensions/flags/src/PrepareFlagsApiData.php
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags;
|
||||||
|
|
||||||
|
use Flarum\Api\Controller;
|
||||||
|
use Flarum\Flags\Api\Controller\CreateFlagController;
|
||||||
|
use Flarum\Http\RequestUtil;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class PrepareFlagsApiData
|
||||||
|
{
|
||||||
|
public function __invoke(Controller\AbstractSerializeController $controller, $data, ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
// 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 ($controller instanceof Controller\ShowDiscussionController) {
|
||||||
|
if ($data->relationLoaded('posts')) {
|
||||||
|
$posts = $data->getRelation('posts');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($controller instanceof Controller\ListPostsController) {
|
||||||
|
$posts = $data->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($controller instanceof Controller\ShowPostController) {
|
||||||
|
$posts = [$data];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($controller instanceof CreateFlagController) {
|
||||||
|
$posts = [$data->post];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($posts)) {
|
||||||
|
$actor = RequestUtil::getActor($request);
|
||||||
|
$postsWithPermission = [];
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
if (is_object($post)) {
|
||||||
|
$post->setRelation('flags', null);
|
||||||
|
|
||||||
|
if ($actor->can('viewFlags', $post->discussion)) {
|
||||||
|
$postsWithPermission[] = $post;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($postsWithPermission)) {
|
||||||
|
(new Collection($postsWithPermission))
|
||||||
|
->load('flags', 'flags.user');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
0
extensions/flags/tests/fixtures/.gitkeep
vendored
Normal file
0
extensions/flags/tests/fixtures/.gitkeep
vendored
Normal file
133
extensions/flags/tests/integration/api/flags/ListTest.php
Normal file
133
extensions/flags/tests/integration/api/flags/ListTest.php
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Tests\integration\api\flags;
|
||||||
|
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class ListTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-flags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
[
|
||||||
|
'id' => 3,
|
||||||
|
'username' => 'mod',
|
||||||
|
'password' => '$2y$10$LO59tiT7uggl6Oe23o/O6.utnF6ipngYjvMvaxo1TciKqBttDNKim', // BCrypt hash for "too-obscure"
|
||||||
|
'email' => 'normal2@machine.local',
|
||||||
|
'is_email_confirmed' => 1,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'group_user' => [
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'user_id' => 3]
|
||||||
|
],
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'discussion.viewFlags'],
|
||||||
|
],
|
||||||
|
'discussions' => [
|
||||||
|
['id' => 1, 'title' => '', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
],
|
||||||
|
'posts' => [
|
||||||
|
['id' => 1, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 2, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 3, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
],
|
||||||
|
'flags' => [
|
||||||
|
['id' => 1, 'post_id' => 1, 'user_id' => 1],
|
||||||
|
['id' => 2, 'post_id' => 1, 'user_id' => 2],
|
||||||
|
['id' => 3, 'post_id' => 1, 'user_id' => 3],
|
||||||
|
['id' => 4, 'post_id' => 2, 'user_id' => 2],
|
||||||
|
['id' => 5, 'post_id' => 3, 'user_id' => 1],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function admin_can_see_one_flag_per_post()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags', [
|
||||||
|
'authenticatedAs' => 1
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['1', '4', '5'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function regular_user_sees_own_flags()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags', [
|
||||||
|
'authenticatedAs' => 2
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['2', '4'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function mod_can_see_one_flag_per_post()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags', [
|
||||||
|
'authenticatedAs' => 3
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['1', '4', '5'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function guest_cant_see_flags()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Tests\integration\api\flags;
|
||||||
|
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class ListWithTagsTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-flags');
|
||||||
|
$this->extension('flarum-tags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'tags' => [
|
||||||
|
['id' => 1, 'name' => 'Unrestricted', 'slug' => '1', 'position' => 0, 'parent_id' => null],
|
||||||
|
['id' => 2, 'name' => 'Mods can view discussions', 'slug' => '2', 'position' => 0, 'parent_id' => null, 'is_restricted' => true],
|
||||||
|
['id' => 3, 'name' => 'Mods can view flags', 'slug' => '3', 'position' => 0, 'parent_id' => null, 'is_restricted' => true],
|
||||||
|
['id' => 4, 'name' => 'Mods can view discussions and flags', 'slug' => '4', 'position' => 0, 'parent_id' => null, 'is_restricted' => true],
|
||||||
|
],
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
[
|
||||||
|
'id' => 3,
|
||||||
|
'username' => 'mod',
|
||||||
|
'password' => '$2y$10$LO59tiT7uggl6Oe23o/O6.utnF6ipngYjvMvaxo1TciKqBttDNKim', // BCrypt hash for "too-obscure"
|
||||||
|
'email' => 'normal2@machine.local',
|
||||||
|
'is_email_confirmed' => 1,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'group_user' => [
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'user_id' => 3]
|
||||||
|
],
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'discussion.viewFlags'],
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag2.viewDiscussions'],
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag3.discussion.viewFlags'],
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag4.viewDiscussions'],
|
||||||
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag4.discussion.viewFlags'],
|
||||||
|
],
|
||||||
|
'discussions' => [
|
||||||
|
['id' => 1, 'title' => 'no tags', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 2, 'title' => 'has tags where mods can view discussions but not flags', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 3, 'title' => 'has tags where mods can view flags but not discussions', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 4, 'title' => 'has tags where mods can view discussions and flags', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 5, 'title' => 'has unrestricted tag', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
],
|
||||||
|
'discussion_tag' => [
|
||||||
|
['discussion_id' => 2, 'tag_id' => 2],
|
||||||
|
['discussion_id' => 3, 'tag_id' => 3],
|
||||||
|
['discussion_id' => 4, 'tag_id' => 4],
|
||||||
|
['discussion_id' => 5, 'tag_id' => 1],
|
||||||
|
],
|
||||||
|
'posts' => [
|
||||||
|
// From regular ListTest
|
||||||
|
['id' => 1, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 2, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 3, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
// In tags
|
||||||
|
['id' => 4, 'discussion_id' => 2, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 5, 'discussion_id' => 3, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 6, 'discussion_id' => 4, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 7, 'discussion_id' => 5, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
],
|
||||||
|
'flags' => [
|
||||||
|
// From regular ListTest
|
||||||
|
['id' => 1, 'post_id' => 1, 'user_id' => 1],
|
||||||
|
['id' => 2, 'post_id' => 1, 'user_id' => 2],
|
||||||
|
['id' => 3, 'post_id' => 1, 'user_id' => 3],
|
||||||
|
['id' => 4, 'post_id' => 2, 'user_id' => 2],
|
||||||
|
['id' => 5, 'post_id' => 3, 'user_id' => 1],
|
||||||
|
// In tags
|
||||||
|
['id' => 6, 'post_id' => 4, 'user_id' => 1],
|
||||||
|
['id' => 7, 'post_id' => 5, 'user_id' => 1],
|
||||||
|
['id' => 8, 'post_id' => 6, 'user_id' => 1],
|
||||||
|
['id' => 9, 'post_id' => 7, 'user_id' => 1],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function admin_can_see_one_flag_per_post()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags', [
|
||||||
|
'authenticatedAs' => 1
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['1', '4', '5', '6', '7', '8', '9'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function regular_user_sees_own_flags()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags', [
|
||||||
|
'authenticatedAs' => 2
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['2', '4'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function mod_can_see_one_flag_per_post()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags', [
|
||||||
|
'authenticatedAs' => 3
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
// 7 is included, even though mods can't view discussions.
|
||||||
|
// This is because the UI doesnt allow discussions.viewFlags without viewDiscussions.
|
||||||
|
$this->assertEqualsCanonicalizing(['1', '4', '5', '7', '8', '9'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function guest_cant_see_flags()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/flags')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
16
extensions/flags/tests/integration/setup.php
Normal file
16
extensions/flags/tests/integration/setup.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Testing\integration\Setup\SetupScript;
|
||||||
|
|
||||||
|
require __DIR__.'/../../vendor/autoload.php';
|
||||||
|
|
||||||
|
$setup = new SetupScript();
|
||||||
|
|
||||||
|
$setup->run();
|
24
extensions/flags/tests/phpunit.integration.xml
Normal file
24
extensions/flags/tests/phpunit.integration.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||||
|
backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="true"
|
||||||
|
stopOnFailure="false"
|
||||||
|
>
|
||||||
|
<coverage processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">../src/</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Flarum Integration Tests">
|
||||||
|
<directory suffix="Test.php">./integration</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
27
extensions/flags/tests/phpunit.unit.xml
Normal file
27
extensions/flags/tests/phpunit.unit.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||||
|
backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false"
|
||||||
|
>
|
||||||
|
<coverage processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">../src/</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Flarum Unit Tests">
|
||||||
|
<directory suffix="Test.php">./unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<listeners>
|
||||||
|
<listener class="\Mockery\Adapter\Phpunit\TestListener" />
|
||||||
|
</listeners>
|
||||||
|
</phpunit>
|
0
extensions/flags/tests/unit/.gitkeep
Normal file
0
extensions/flags/tests/unit/.gitkeep
Normal file
Reference in New Issue
Block a user