mirror of
https://github.com/flarum/framework.git
synced 2024-11-30 05:13:37 +08:00
Merge remote-tracking branch 'extensions_suspend/REWRITE'
This commit is contained in:
commit
2550f547b1
19
extensions/suspend/.editorconfig
Normal file
19
extensions/suspend/.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/suspend/.gitattributes
vendored
Normal file
18
extensions/suspend/.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/suspend/.github/workflows/backend.yml
vendored
Normal file
15
extensions/suspend/.github/workflows/backend.yml
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
name: Suspend 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: false
|
||||||
|
|
||||||
|
backend_directory: .
|
21
extensions/suspend/.github/workflows/frontend.yml
vendored
Normal file
21
extensions/suspend/.github/workflows/frontend.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: Suspend 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: false
|
||||||
|
|
||||||
|
frontend_directory: ./js
|
||||||
|
main_git_branch: master
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
|
12
extensions/suspend/.gitignore
vendored
Normal file
12
extensions/suspend/.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/suspend/.styleci.yml
Normal file
14
extensions/suspend/.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
|
62
extensions/suspend/CHANGELOG.md
Normal file
62
extensions/suspend/CHANGELOG.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [1.2.0](https://github.com/flarum/suspend/compare/v1.1.0...v1.2.0)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Display suspension to user on first visit (https://github.com/flarum/suspend/pull/41).
|
||||||
|
|
||||||
|
## [1.1.0](https://github.com/flarum/suspend/compare/v1.0.0...v1.1.0)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Suspensions are now anonymous (https://github.com/flarum/suspend/pulls/39)
|
||||||
|
- General repo maintenance (https://github.com/flarum/suspend/pulls/37)
|
||||||
|
|
||||||
|
## [1.0.0](https://github.com/flarum/suspend/compare/v0.1.0-beta.16...v1.0.0)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Compatibility with Flarum v1.0.0.
|
||||||
|
|
||||||
|
## [0.1.0-beta.16](https://github.com/flarum/suspend/compare/v0.1.0-beta.15...v0.1.0-beta.16)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated admin category from moderation to feature (https://github.com/flarum/suspend/pull/33)
|
||||||
|
- Moved locale files from translation pack to extension (https://github.com/flarum/suspend/pull/28)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Incorrect suspension length in suspended notification (https://github.com/flarum/suspend/pull/32)
|
||||||
|
- Unable to view suspended users with permission granted (https://github.com/flarum/suspend/pull/35)
|
||||||
|
|
||||||
|
## [0.1.0-beta.15](https://github.com/flarum/suspend/compare/v0.1.0-beta.14...v0.1.0-beta.15)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated composer.json and admin javascript for new admin area.
|
||||||
|
- Updated to use newest extenders.
|
||||||
|
- Implement new authorization layer ([87a5182](https://github.com/flarum/suspend/commit/87a518286b87064d1919f5a8a4b9f2cb384f44fe).
|
||||||
|
|
||||||
|
## [0.1.0-beta.14](https://github.com/flarum/suspend/compare/v0.1.0-beta.13...v0.1.0-beta.14)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Ability added to filter users by their suspended state (#23)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated mithril to version 2
|
||||||
|
- Load language strings correctly on en-/disable
|
||||||
|
- Updated JS dependencies
|
||||||
|
- Replace momentjs with dayjs
|
||||||
|
|
||||||
|
## [0.1.0-beta.13](https://github.com/flarum/suspend/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.10](https://github.com/flarum/suspend/compare/v0.1.0-beta.9...v0.1.0-beta.10)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Replace the Font Awesome 4 with its version 5 counterpart ([d52f9e9](https://github.com/flarum/suspend/pull/21/commits/d52f9e9b810c51f294fafb4a3f580e5bd8c3ded8))
|
||||||
|
|
||||||
|
## [0.1.0-beta.9](https://github.com/flarum/suspend/compare/v0.1.0-beta.8...v0.1.0-beta.9)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Replace event subscribers (that resolve services too early) with listeners ([e84082e](https://github.com/flarum/suspend/commit/e84082ecb41262aa0a48001396759c72a892219e))
|
||||||
|
|
22
extensions/suspend/LICENSE
Normal file
22
extensions/suspend/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.
|
60
extensions/suspend/composer.json
Normal file
60
extensions/suspend/composer.json
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"name": "flarum/suspend",
|
||||||
|
"description": "Suspend users so they can't post.",
|
||||||
|
"type": "flarum-extension",
|
||||||
|
"keywords": [
|
||||||
|
"moderation"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/flarum/core/issues",
|
||||||
|
"source": "https://github.com/flarum/suspend",
|
||||||
|
"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\\Suspend\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.x-dev"
|
||||||
|
},
|
||||||
|
"flarum-extension": {
|
||||||
|
"title": "Suspend",
|
||||||
|
"category": "feature",
|
||||||
|
"icon": {
|
||||||
|
"name": "fas fa-ban",
|
||||||
|
"backgroundColor": "#ddd",
|
||||||
|
"color": "#666"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flarum-cli": {
|
||||||
|
"modules": {
|
||||||
|
"admin": true,
|
||||||
|
"forum": true,
|
||||||
|
"js": true,
|
||||||
|
"jsCommon": false,
|
||||||
|
"css": true,
|
||||||
|
"gitConf": true,
|
||||||
|
"githubActions": true,
|
||||||
|
"prettier": true,
|
||||||
|
"typescript": false,
|
||||||
|
"bundlewatch": false,
|
||||||
|
"backendTesting": false,
|
||||||
|
"editorConfig": true,
|
||||||
|
"styleci": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
extensions/suspend/extend.php
Normal file
67
extensions/suspend/extend.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?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\Serializer\BasicUserSerializer;
|
||||||
|
use Flarum\Api\Serializer\UserSerializer;
|
||||||
|
use Flarum\Extend;
|
||||||
|
use Flarum\Suspend\Access\UserPolicy;
|
||||||
|
use Flarum\Suspend\AddUserSuspendAttributes;
|
||||||
|
use Flarum\Suspend\Event\Suspended;
|
||||||
|
use Flarum\Suspend\Event\Unsuspended;
|
||||||
|
use Flarum\Suspend\Listener;
|
||||||
|
use Flarum\Suspend\Notification\UserSuspendedBlueprint;
|
||||||
|
use Flarum\Suspend\Notification\UserUnsuspendedBlueprint;
|
||||||
|
use Flarum\Suspend\Query\SuspendedFilterGambit;
|
||||||
|
use Flarum\Suspend\RevokeAccessFromSuspendedUsers;
|
||||||
|
use Flarum\User\Event\Saving;
|
||||||
|
use Flarum\User\Filter\UserFilterer;
|
||||||
|
use Flarum\User\Search\UserSearcher;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
return [
|
||||||
|
(new Extend\Frontend('forum'))
|
||||||
|
->js(__DIR__.'/js/dist/forum.js')
|
||||||
|
->css(__DIR__.'/less/forum.less'),
|
||||||
|
|
||||||
|
(new Extend\Frontend('admin'))
|
||||||
|
->js(__DIR__.'/js/dist/admin.js')
|
||||||
|
->css(__DIR__.'/less/admin.less'),
|
||||||
|
|
||||||
|
(new Extend\Model(User::class))
|
||||||
|
->dateAttribute('suspended_until'),
|
||||||
|
|
||||||
|
(new Extend\ApiSerializer(UserSerializer::class))
|
||||||
|
->attributes(AddUserSuspendAttributes::class),
|
||||||
|
|
||||||
|
new Extend\Locales(__DIR__.'/locale'),
|
||||||
|
|
||||||
|
(new Extend\Notification())
|
||||||
|
->type(UserSuspendedBlueprint::class, BasicUserSerializer::class, ['alert', 'email'])
|
||||||
|
->type(UserUnsuspendedBlueprint::class, BasicUserSerializer::class, ['alert', 'email']),
|
||||||
|
|
||||||
|
(new Extend\Event())
|
||||||
|
->listen(Saving::class, Listener\SaveSuspensionToDatabase::class)
|
||||||
|
->listen(Suspended::class, Listener\SendNotificationWhenUserIsSuspended::class)
|
||||||
|
->listen(Unsuspended::class, Listener\SendNotificationWhenUserIsUnsuspended::class),
|
||||||
|
|
||||||
|
(new Extend\Policy())
|
||||||
|
->modelPolicy(User::class, UserPolicy::class),
|
||||||
|
|
||||||
|
(new Extend\User())
|
||||||
|
->permissionGroups(RevokeAccessFromSuspendedUsers::class),
|
||||||
|
|
||||||
|
(new Extend\Filter(UserFilterer::class))
|
||||||
|
->addFilter(SuspendedFilterGambit::class),
|
||||||
|
|
||||||
|
(new Extend\SimpleFlarumSearch(UserSearcher::class))
|
||||||
|
->addGambit(SuspendedFilterGambit::class),
|
||||||
|
|
||||||
|
(new Extend\View())
|
||||||
|
->namespace('flarum-suspend', __DIR__.'/views'),
|
||||||
|
];
|
9
extensions/suspend/js/.gitignore
vendored
Normal file
9
extensions/suspend/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/suspend/js/admin.js
Normal file
1
extensions/suspend/js/admin.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './src/admin';
|
2
extensions/suspend/js/dist/admin.js
generated
vendored
Normal file
2
extensions/suspend/js/dist/admin.js
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(()=>{var e={n:r=>{var a=r&&r.__esModule?()=>r.default:()=>r;return e.d(a,{a}),a},d:(r,a)=>{for(var o in a)e.o(a,o)&&!e.o(r,o)&&Object.defineProperty(r,o,{enumerable:!0,get:a[o]})},o:(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},r={};(()=>{"use strict";e.r(r);const a=flarum.core.compat.app;var o=e.n(a);o().initializers.add("flarum-suspend",(function(){o().extensionData.for("flarum-suspend").registerPermission({icon:"fas fa-ban",label:o().translator.trans("flarum-suspend.admin.permissions.suspend_users_label"),permission:"user.suspend"},"moderate")}))})(),module.exports=r})();
|
||||||
|
//# sourceMappingURL=admin.js.map
|
1
extensions/suspend/js/dist/admin.js.map
generated
vendored
Normal file
1
extensions/suspend/js/dist/admin.js.map
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"file":"admin.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,IACzBH,GCLRF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3ER,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,M,+BCLvD,MAAM,EAA+BC,OAAOC,KAAKC,OAAY,I,aCE7DC,IAAAA,aAAAA,IAAqB,kBAAkB,WACrCA,IAAAA,cAAAA,IAAsB,kBAAkBC,mBACtC,CACEC,KAAM,aACNC,MAAOH,IAAAA,WAAAA,MAAqB,wDAC5BI,WAAY,gBAEd,gB","sources":["webpack://@flarum/suspend/webpack/bootstrap","webpack://@flarum/suspend/webpack/runtime/compat get default export","webpack://@flarum/suspend/webpack/runtime/define property getters","webpack://@flarum/suspend/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/suspend/webpack/runtime/make namespace object","webpack://@flarum/suspend/external root \"flarum.core.compat['app']\"","webpack://@flarum/suspend/./src/admin/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['app'];","import app from 'flarum/app';\n\napp.initializers.add('flarum-suspend', () => {\n app.extensionData.for('flarum-suspend').registerPermission(\n {\n icon: 'fas fa-ban',\n label: app.translator.trans('flarum-suspend.admin.permissions.suspend_users_label'),\n permission: 'user.suspend',\n },\n 'moderate'\n );\n});\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","core","compat","app","registerPermission","icon","label","permission"],"sourceRoot":""}
|
2
extensions/suspend/js/dist/forum.js
generated
vendored
Normal file
2
extensions/suspend/js/dist/forum.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
extensions/suspend/js/dist/forum.js.map
generated
vendored
Normal file
1
extensions/suspend/js/dist/forum.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
extensions/suspend/js/forum.js
Normal file
1
extensions/suspend/js/forum.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './src/forum';
|
20
extensions/suspend/js/package.json
Normal file
20
extensions/suspend/js/package.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "@flarum/suspend",
|
||||||
|
"prettier": "@flarum/prettier-config",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "webpack --mode development --watch",
|
||||||
|
"build": "webpack --mode production",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"analyze": "cross-env ANALYZER=true yarn build",
|
||||||
|
"format-check": "prettier --check src"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@flarum/prettier-config": "^1.0.0",
|
||||||
|
"flarum-tsconfig": "^1.0.0",
|
||||||
|
"flarum-webpack-config": "^2.0.0",
|
||||||
|
"prettier": "^2.5.1",
|
||||||
|
"webpack": "^5.65.0",
|
||||||
|
"webpack-cli": "^4.9.1"
|
||||||
|
}
|
||||||
|
}
|
12
extensions/suspend/js/src/admin/index.js
Normal file
12
extensions/suspend/js/src/admin/index.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import app from 'flarum/app';
|
||||||
|
|
||||||
|
app.initializers.add('flarum-suspend', () => {
|
||||||
|
app.extensionData.for('flarum-suspend').registerPermission(
|
||||||
|
{
|
||||||
|
icon: 'fas fa-ban',
|
||||||
|
label: app.translator.trans('flarum-suspend.admin.permissions.suspend_users_label'),
|
||||||
|
permission: 'user.suspend',
|
||||||
|
},
|
||||||
|
'moderate'
|
||||||
|
);
|
||||||
|
});
|
19
extensions/suspend/js/src/forum/checkForSuspension.js
Normal file
19
extensions/suspend/js/src/forum/checkForSuspension.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import SuspensionInfoModal from './components/SuspensionInfoModal';
|
||||||
|
import { localStorageKey } from './helpers/suspensionHelper';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return setTimeout(() => {
|
||||||
|
if (app.session.user) {
|
||||||
|
const message = app.session.user.suspendMessage();
|
||||||
|
const until = app.session.user.suspendedUntil();
|
||||||
|
const alreadyDisplayed = localStorage.getItem(localStorageKey()) === until?.getTime().toString();
|
||||||
|
|
||||||
|
if (message && !alreadyDisplayed) {
|
||||||
|
app.modal.show(SuspensionInfoModal, { message, until });
|
||||||
|
} else if (!until && localStorage.getItem(localStorageKey())) {
|
||||||
|
localStorage.removeItem(localStorageKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
13
extensions/suspend/js/src/forum/compat.js
Normal file
13
extensions/suspend/js/src/forum/compat.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import SuspendUserModal from './components/SuspendUserModal';
|
||||||
|
import SuspensionInfoModal from './components/SuspensionInfoModal';
|
||||||
|
import UserSuspendedNotification from './components/UserSuspendedNotification';
|
||||||
|
import UserUnsuspendedNotification from './components/UserUnsuspendedNotification';
|
||||||
|
import checkForSuspension from './checkForSuspension';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'suspend/components/suspendUserModal': SuspendUserModal,
|
||||||
|
'suspend/components/suspensionInfoModal': SuspensionInfoModal,
|
||||||
|
'suspend/components/UserSuspendedNotification': UserSuspendedNotification,
|
||||||
|
'suspend/components/UserUnsuspendedNotification': UserUnsuspendedNotification,
|
||||||
|
'suspend/checkForSuspension': checkForSuspension,
|
||||||
|
};
|
172
extensions/suspend/js/src/forum/components/SuspendUserModal.js
Normal file
172
extensions/suspend/js/src/forum/components/SuspendUserModal.js
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Modal from 'flarum/components/Modal';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
|
||||||
|
import Stream from 'flarum/utils/Stream';
|
||||||
|
import withAttr from 'flarum/utils/withAttr';
|
||||||
|
import ItemList from 'flarum/common/utils/ItemList';
|
||||||
|
import { getPermanentSuspensionDate } from '../helpers/suspensionHelper';
|
||||||
|
|
||||||
|
export default class SuspendUserModal extends Modal {
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
|
||||||
|
let until = this.attrs.user.suspendedUntil();
|
||||||
|
const reason = this.attrs.user.suspendReason();
|
||||||
|
const message = this.attrs.user.suspendMessage();
|
||||||
|
let status = null;
|
||||||
|
|
||||||
|
if (new Date() > until) until = null;
|
||||||
|
|
||||||
|
if (until) {
|
||||||
|
if (until.getFullYear() === 9999) status = 'indefinitely';
|
||||||
|
else status = 'limited';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = Stream(status);
|
||||||
|
this.reason = Stream(reason);
|
||||||
|
this.message = Stream(message);
|
||||||
|
this.daysRemaining = Stream(status === 'limited' && -dayjs().diff(until, 'days') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
className() {
|
||||||
|
return 'SuspendUserModal Modal--medium';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return app.translator.trans('flarum-suspend.forum.suspend_user.title', { user: this.attrs.user });
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<div className="Form">
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>{app.translator.trans('flarum-suspend.forum.suspend_user.status_heading')}</label>
|
||||||
|
<div>{this.formItems().toArray()}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
<Button className="Button Button--primary" loading={this.loading} type="submit">
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.submit_button')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
radioItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'not-suspended',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="status" checked={!this.status()} value="" onclick={withAttr('value', this.status)} />
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.not_suspended_label')}
|
||||||
|
</label>,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'indefinitely',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="status" checked={this.status() === 'indefinitely'} value="indefinitely" onclick={withAttr('value', this.status)} />
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.indefinitely_label')}
|
||||||
|
</label>,
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'time-suspension',
|
||||||
|
<label className="checkbox SuspendUserModal-days">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="status"
|
||||||
|
checked={this.status() === 'limited'}
|
||||||
|
value="limited"
|
||||||
|
onclick={(e) => {
|
||||||
|
this.status(e.target.value);
|
||||||
|
m.redraw.sync();
|
||||||
|
this.$('.SuspendUserModal-days-input input').select();
|
||||||
|
e.redraw = false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_label')}
|
||||||
|
{this.status() === 'limited' && (
|
||||||
|
<div className="SuspendUserModal-days-input">
|
||||||
|
<input type="number" min="0" value={this.daysRemaining()} oninput={withAttr('value', this.daysRemaining)} className="FormControl" />
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_days_text')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</label>,
|
||||||
|
80
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
formItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('radioItems', <div className="Form-group">{this.radioItems().toArray()}</div>, 100);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'reason',
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.reason')}
|
||||||
|
<textarea
|
||||||
|
className="FormControl"
|
||||||
|
bidi={this.reason}
|
||||||
|
placeholder={app.translator.trans('flarum-suspend.forum.suspend_user.placeholder_optional')}
|
||||||
|
rows="2"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>,
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'message',
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.display_message')}
|
||||||
|
<textarea
|
||||||
|
className="FormControl"
|
||||||
|
bidi={this.message}
|
||||||
|
placeholder={app.translator.trans('flarum-suspend.forum.suspend_user.placeholder_optional')}
|
||||||
|
rows="2"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>,
|
||||||
|
80
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
onsubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
let suspendedUntil = null;
|
||||||
|
switch (this.status()) {
|
||||||
|
case 'indefinitely':
|
||||||
|
suspendedUntil = getPermanentSuspensionDate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'limited':
|
||||||
|
suspendedUntil = dayjs().add(this.daysRemaining(), 'days').toDate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// no default
|
||||||
|
}
|
||||||
|
|
||||||
|
this.attrs.user
|
||||||
|
.save({ suspendedUntil, suspendReason: this.reason(), suspendMessage: this.message() })
|
||||||
|
.then(() => this.hide(), this.loaded.bind(this));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Modal from 'flarum/common/components/Modal';
|
||||||
|
import Button from 'flarum/common/components/Button';
|
||||||
|
import fullTime from 'flarum/common/helpers/fullTime';
|
||||||
|
import { isPermanentSuspensionDate, localStorageKey } from '../helpers/suspensionHelper';
|
||||||
|
|
||||||
|
export default class SuspensionInfoModal extends Modal {
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
|
||||||
|
this.message = this.attrs.message;
|
||||||
|
this.until = this.attrs.until;
|
||||||
|
}
|
||||||
|
|
||||||
|
className() {
|
||||||
|
return 'SuspensionInfoModal Modal';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return app.translator.trans('flarum-suspend.forum.suspension_info.title');
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
const timespan = isPermanentSuspensionDate(new Date(this.until))
|
||||||
|
? app.translator.trans('flarum-suspend.forum.suspension_info.indefinite')
|
||||||
|
: app.translator.trans('flarum-suspend.forum.suspension_info.limited', { date: fullTime(this.until) });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<div className="Form Form--centered">
|
||||||
|
<p className="helpText">{this.message}</p>
|
||||||
|
<p className="helpText">{timespan}</p>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspension_info.dismiss_button')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
localStorage.setItem(localStorageKey(), this.attrs.until.getTime());
|
||||||
|
this.attrs.state.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Notification from 'flarum/components/Notification';
|
||||||
|
import { isPermanentSuspensionDate } from '../helpers/suspensionHelper';
|
||||||
|
|
||||||
|
export default class UserSuspendedNotification extends Notification {
|
||||||
|
icon() {
|
||||||
|
return 'fas fa-ban';
|
||||||
|
}
|
||||||
|
|
||||||
|
href() {
|
||||||
|
return app.route.user(this.attrs.notification.subject());
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
const notification = this.attrs.notification;
|
||||||
|
const suspendedUntil = notification.content();
|
||||||
|
const timeReadable = dayjs(suspendedUntil).from(notification.createdAt(), true);
|
||||||
|
|
||||||
|
return isPermanentSuspensionDate(suspendedUntil)
|
||||||
|
? app.translator.trans('flarum-suspend.forum.notifications.user_suspended_indefinite_text')
|
||||||
|
: app.translator.trans('flarum-suspend.forum.notifications.user_suspended_text', {
|
||||||
|
timeReadable,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Notification from 'flarum/components/Notification';
|
||||||
|
|
||||||
|
export default class UserUnsuspendedNotification extends Notification {
|
||||||
|
icon() {
|
||||||
|
return 'fas fa-ban';
|
||||||
|
}
|
||||||
|
|
||||||
|
href() {
|
||||||
|
return app.route.user(this.attrs.notification.subject());
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
const notification = this.attrs.notification;
|
||||||
|
|
||||||
|
return app.translator.trans('flarum-suspend.forum.notifications.user_unsuspended_text');
|
||||||
|
}
|
||||||
|
}
|
16
extensions/suspend/js/src/forum/helpers/suspensionHelper.ts
Normal file
16
extensions/suspend/js/src/forum/helpers/suspensionHelper.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import utc from 'dayjs/plugin/utc';
|
||||||
|
|
||||||
|
dayjs.extend(utc);
|
||||||
|
|
||||||
|
export function getPermanentSuspensionDate(): Date {
|
||||||
|
return new Date('2038-01-01');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPermanentSuspensionDate(date: Date): boolean {
|
||||||
|
return dayjs.utc(date).isSame(dayjs.utc('2038-01-01'));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function localStorageKey(): string {
|
||||||
|
return 'flarum-suspend.acknowledge-suspension';
|
||||||
|
}
|
60
extensions/suspend/js/src/forum/index.js
Normal file
60
extensions/suspend/js/src/forum/index.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import { extend } from 'flarum/extend';
|
||||||
|
import app from 'flarum/app';
|
||||||
|
import UserControls from 'flarum/utils/UserControls';
|
||||||
|
import Button from 'flarum/components/Button';
|
||||||
|
import Badge from 'flarum/components/Badge';
|
||||||
|
import Model from 'flarum/Model';
|
||||||
|
import User from 'flarum/models/User';
|
||||||
|
|
||||||
|
import SuspendUserModal from './components/SuspendUserModal';
|
||||||
|
import UserSuspendedNotification from './components/UserSuspendedNotification';
|
||||||
|
import UserUnsuspendedNotification from './components/UserUnsuspendedNotification';
|
||||||
|
import checkForSuspension from './checkForSuspension';
|
||||||
|
|
||||||
|
app.initializers.add('flarum-suspend', () => {
|
||||||
|
app.notificationComponents.userSuspended = UserSuspendedNotification;
|
||||||
|
app.notificationComponents.userUnsuspended = UserUnsuspendedNotification;
|
||||||
|
|
||||||
|
User.prototype.canSuspend = Model.attribute('canSuspend');
|
||||||
|
User.prototype.suspendedUntil = Model.attribute('suspendedUntil', Model.transformDate);
|
||||||
|
User.prototype.suspendReason = Model.attribute('suspendReason');
|
||||||
|
User.prototype.suspendMessage = Model.attribute('suspendMessage');
|
||||||
|
|
||||||
|
extend(UserControls, 'moderationControls', (items, user) => {
|
||||||
|
if (user.canSuspend()) {
|
||||||
|
items.add(
|
||||||
|
'suspend',
|
||||||
|
Button.component(
|
||||||
|
{
|
||||||
|
icon: 'fas fa-ban',
|
||||||
|
onclick: () => app.modal.show(SuspendUserModal, { user }),
|
||||||
|
},
|
||||||
|
app.translator.trans('flarum-suspend.forum.user_controls.suspend_button')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
extend(User.prototype, 'badges', function (items) {
|
||||||
|
const until = this.suspendedUntil();
|
||||||
|
|
||||||
|
if (new Date() < until) {
|
||||||
|
items.add(
|
||||||
|
'suspended',
|
||||||
|
Badge.component({
|
||||||
|
icon: 'fas fa-ban',
|
||||||
|
type: 'suspended',
|
||||||
|
label: app.translator.trans('flarum-suspend.forum.user_badge.suspended_tooltip'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
checkForSuspension();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Expose compat API
|
||||||
|
import suspendCompat from './compat';
|
||||||
|
import { compat } from '@flarum/core/forum';
|
||||||
|
|
||||||
|
Object.assign(compat, suspendCompat);
|
1
extensions/suspend/js/webpack.config.js
Executable file
1
extensions/suspend/js/webpack.config.js
Executable file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('flarum-webpack-config')();
|
2321
extensions/suspend/js/yarn.lock
Normal file
2321
extensions/suspend/js/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
0
extensions/suspend/less/admin.less
Normal file
0
extensions/suspend/less/admin.less
Normal file
18
extensions/suspend/less/forum.less
Normal file
18
extensions/suspend/less/forum.less
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
.Badge--suspended {
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
.SuspendUserModal-days-input {
|
||||||
|
margin-top: 5px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 75px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.Notification--userSuspended,
|
||||||
|
.Notification--userUnsuspended {
|
||||||
|
> .Avatar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
70
extensions/suspend/locale/en.yml
Normal file
70
extensions/suspend/locale/en.yml
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
flarum-suspend:
|
||||||
|
|
||||||
|
##
|
||||||
|
# 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:
|
||||||
|
suspend_users_label: Suspend users
|
||||||
|
|
||||||
|
# Translations in this namespace are used by the forum user interface.
|
||||||
|
forum:
|
||||||
|
# These translations are used in the suspension notifications
|
||||||
|
notifications:
|
||||||
|
user_suspended_text: "You have been suspended for {timeReadable}"
|
||||||
|
user_suspended_indefinite_text: You have been suspended indefinitely
|
||||||
|
user_unsuspended_text: You have been unsuspended
|
||||||
|
|
||||||
|
# These translations are used for the suspension reason informational modal to the suspended user.
|
||||||
|
suspension_info:
|
||||||
|
dismiss_button: Dismiss
|
||||||
|
indefinite: This is an indefinite suspension
|
||||||
|
limited: "This suspension will be in force until {date}"
|
||||||
|
title: This account is suspended
|
||||||
|
|
||||||
|
# These translations are used in the Suspend User modal dialog (admin function).
|
||||||
|
suspend_user:
|
||||||
|
display_message: Display message for user
|
||||||
|
indefinitely_label: Suspended indefinitely
|
||||||
|
limited_time_days_text: " days"
|
||||||
|
limited_time_label: Suspended for a limited time...
|
||||||
|
not_suspended_label: Not suspended
|
||||||
|
placeholder_optional: Optional
|
||||||
|
reason: Reason for suspension
|
||||||
|
status_heading: Suspension Status
|
||||||
|
submit_button: => core.ref.save_changes
|
||||||
|
title: "Suspend {username}"
|
||||||
|
|
||||||
|
# These translations are displayed as tooltips for user badges.
|
||||||
|
user_badge:
|
||||||
|
suspended_tooltip: Suspended
|
||||||
|
|
||||||
|
# These translations are found on the user profile page (admin function).
|
||||||
|
user_controls:
|
||||||
|
suspend_button: Suspend
|
||||||
|
|
||||||
|
# Translations in this namespace are used by suspension email notifications
|
||||||
|
email:
|
||||||
|
suspended:
|
||||||
|
subject: Your account has been suspended
|
||||||
|
body: |
|
||||||
|
Hey {recipient_display_name},
|
||||||
|
|
||||||
|
You have been suspended for the following reason:
|
||||||
|
|
||||||
|
---
|
||||||
|
{suspension_message}
|
||||||
|
---
|
||||||
|
|
||||||
|
unsuspended:
|
||||||
|
subject: Your account has been unsuspended
|
||||||
|
body: |
|
||||||
|
Hey {recipient_display_name},
|
||||||
|
|
||||||
|
You have been unsuspended. You can head back to the forum by clicking on the following link:
|
||||||
|
|
||||||
|
{forum_url}
|
|
@ -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', [
|
||||||
|
'suspended_until' => ['dateTime', 'nullable' => true]
|
||||||
|
]);
|
|
@ -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', 'suspended_until', 'suspend_until');
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?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([
|
||||||
|
'user.suspend' => 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('users', 'suspend_until', 'suspended_until');
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?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', [
|
||||||
|
'suspend_reason' => ['text', 'nullable' => true],
|
||||||
|
'suspend_message' => ['text', 'nullable' => true]
|
||||||
|
]);
|
28
extensions/suspend/src/Access/UserPolicy.php
Normal file
28
extensions/suspend/src/Access/UserPolicy.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?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\Suspend\Access;
|
||||||
|
|
||||||
|
use Flarum\User\Access\AbstractPolicy;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class UserPolicy extends AbstractPolicy
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param User $actor
|
||||||
|
* @param User $user
|
||||||
|
* @return bool|null
|
||||||
|
*/
|
||||||
|
public function suspend(User $actor, User $user)
|
||||||
|
{
|
||||||
|
if ($user->isAdmin() || $user->id === $actor->id) {
|
||||||
|
return $this->deny();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
extensions/suspend/src/AddUserSuspendAttributes.php
Executable file
35
extensions/suspend/src/AddUserSuspendAttributes.php
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
<?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\Suspend;
|
||||||
|
|
||||||
|
use Flarum\Api\Serializer\UserSerializer;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class AddUserSuspendAttributes
|
||||||
|
{
|
||||||
|
public function __invoke(UserSerializer $serializer, User $user)
|
||||||
|
{
|
||||||
|
$attributes = [];
|
||||||
|
$canSuspend = $serializer->getActor()->can('suspend', $user);
|
||||||
|
|
||||||
|
if ($canSuspend) {
|
||||||
|
$attributes['suspendReason'] = $user->suspend_reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($serializer->getActor()->id === $user->id || $canSuspend) {
|
||||||
|
$attributes['suspendMessage'] = $user->suspend_message;
|
||||||
|
$attributes['suspendedUntil'] = $serializer->formatDate($user->suspended_until);
|
||||||
|
}
|
||||||
|
|
||||||
|
$attributes['canSuspend'] = $canSuspend;
|
||||||
|
|
||||||
|
return $attributes;
|
||||||
|
}
|
||||||
|
}
|
30
extensions/suspend/src/Event/Suspended.php
Normal file
30
extensions/suspend/src/Event/Suspended.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?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\Suspend\Event;
|
||||||
|
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class Suspended
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $user;
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
public function __construct(User $user, User $actor)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->actor = $actor;
|
||||||
|
}
|
||||||
|
}
|
30
extensions/suspend/src/Event/Unsuspended.php
Normal file
30
extensions/suspend/src/Event/Unsuspended.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?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\Suspend\Event;
|
||||||
|
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class Unsuspended
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $user;
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $actor;
|
||||||
|
|
||||||
|
public function __construct(User $user, User $actor)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->actor = $actor;
|
||||||
|
}
|
||||||
|
}
|
75
extensions/suspend/src/Listener/SaveSuspensionToDatabase.php
Executable file
75
extensions/suspend/src/Listener/SaveSuspensionToDatabase.php
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
<?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\Suspend\Listener;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Flarum\Suspend\Event\Suspended;
|
||||||
|
use Flarum\Suspend\Event\Unsuspended;
|
||||||
|
use Flarum\Suspend\SuspendValidator;
|
||||||
|
use Flarum\User\Event\Saving;
|
||||||
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class SaveSuspensionToDatabase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Validator for limited suspension.
|
||||||
|
*
|
||||||
|
* @var SuspendValidator
|
||||||
|
*/
|
||||||
|
protected $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Dispatcher
|
||||||
|
*/
|
||||||
|
protected $events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SuspendValidator $validator
|
||||||
|
* @param Dispatcher $events
|
||||||
|
*/
|
||||||
|
public function __construct(SuspendValidator $validator, Dispatcher $events)
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
$this->events = $events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Saving $event)
|
||||||
|
{
|
||||||
|
$attributes = Arr::get($event->data, 'attributes', []);
|
||||||
|
|
||||||
|
if (array_key_exists('suspendedUntil', $attributes)) {
|
||||||
|
$this->validator->assertValid($attributes);
|
||||||
|
|
||||||
|
$user = $event->user;
|
||||||
|
$actor = $event->actor;
|
||||||
|
|
||||||
|
$actor->assertCan('suspend', $user);
|
||||||
|
|
||||||
|
if ($attributes['suspendedUntil']) {
|
||||||
|
$user->suspended_until = new DateTime($attributes['suspendedUntil']);
|
||||||
|
$user->suspend_reason = empty($attributes['suspendReason']) ? null : $attributes['suspendReason'];
|
||||||
|
$user->suspend_message = empty($attributes['suspendMessage']) ? null : $attributes['suspendMessage'];
|
||||||
|
} else {
|
||||||
|
$user->suspended_until = null;
|
||||||
|
$user->suspend_reason = null;
|
||||||
|
$user->suspend_message = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->isDirty(['suspended_until', 'suspend_reason', 'suspend_message'])) {
|
||||||
|
$this->events->dispatch(
|
||||||
|
$user->suspended_until === null ?
|
||||||
|
new Unsuspended($user, $actor) :
|
||||||
|
new Suspended($user, $actor)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?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\Suspend\Listener;
|
||||||
|
|
||||||
|
use Flarum\Notification\NotificationSyncer;
|
||||||
|
use Flarum\Suspend\Event\Suspended;
|
||||||
|
use Flarum\Suspend\Notification\UserSuspendedBlueprint;
|
||||||
|
|
||||||
|
class SendNotificationWhenUserIsSuspended
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var NotificationSyncer
|
||||||
|
*/
|
||||||
|
protected $notifications;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param NotificationSyncer $notifications
|
||||||
|
*/
|
||||||
|
public function __construct(NotificationSyncer $notifications)
|
||||||
|
{
|
||||||
|
$this->notifications = $notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Suspended $event)
|
||||||
|
{
|
||||||
|
$this->notifications->sync(
|
||||||
|
new UserSuspendedBlueprint($event->user),
|
||||||
|
[$event->user]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?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\Suspend\Listener;
|
||||||
|
|
||||||
|
use Flarum\Notification\NotificationSyncer;
|
||||||
|
use Flarum\Suspend\Event\Unsuspended;
|
||||||
|
use Flarum\Suspend\Notification\UserUnsuspendedBlueprint;
|
||||||
|
|
||||||
|
class SendNotificationWhenUserIsUnsuspended
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var NotificationSyncer
|
||||||
|
*/
|
||||||
|
protected $notifications;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param NotificationSyncer $notifications
|
||||||
|
*/
|
||||||
|
public function __construct(NotificationSyncer $notifications)
|
||||||
|
{
|
||||||
|
$this->notifications = $notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Unsuspended $event)
|
||||||
|
{
|
||||||
|
$this->notifications->sync(
|
||||||
|
new UserUnsuspendedBlueprint($event->user),
|
||||||
|
[$event->user]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?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\Suspend\Notification;
|
||||||
|
|
||||||
|
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||||
|
use Flarum\Notification\MailableInterface;
|
||||||
|
use Flarum\User\User;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
class UserSuspendedBlueprint implements BlueprintInterface, MailableInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
*/
|
||||||
|
public function __construct(User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubject()
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFromUser()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getData()
|
||||||
|
{
|
||||||
|
return $this->user->suspended_until;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function getType()
|
||||||
|
{
|
||||||
|
return 'userSuspended';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function getSubjectModel()
|
||||||
|
{
|
||||||
|
return User::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailView()
|
||||||
|
{
|
||||||
|
return ['text' => 'flarum-suspend::emails.suspended'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailSubject(TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
return $translator->trans('flarum-suspend.email.suspended.subject');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?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\Suspend\Notification;
|
||||||
|
|
||||||
|
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||||
|
use Flarum\Notification\MailableInterface;
|
||||||
|
use Flarum\User\User;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
class UserUnsuspendedBlueprint implements BlueprintInterface, MailableInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
*/
|
||||||
|
public function __construct(User $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSubject()
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getFromUser()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getData()
|
||||||
|
{
|
||||||
|
return Carbon::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function getType()
|
||||||
|
{
|
||||||
|
return 'userUnsuspended';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function getSubjectModel()
|
||||||
|
{
|
||||||
|
return User::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailView()
|
||||||
|
{
|
||||||
|
return ['text' => 'flarum-suspend::emails.unsuspended'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailSubject(TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
return $translator->trans('flarum-suspend.email.unsuspended.subject');
|
||||||
|
}
|
||||||
|
}
|
85
extensions/suspend/src/Query/SuspendedFilterGambit.php
Normal file
85
extensions/suspend/src/Query/SuspendedFilterGambit.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?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\Suspend\Query;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Flarum\Filter\FilterInterface;
|
||||||
|
use Flarum\Filter\FilterState;
|
||||||
|
use Flarum\Search\AbstractRegexGambit;
|
||||||
|
use Flarum\Search\SearchState;
|
||||||
|
use Flarum\User\Guest;
|
||||||
|
use Flarum\User\UserRepository;
|
||||||
|
use Illuminate\Database\Query\Builder;
|
||||||
|
|
||||||
|
class SuspendedFilterGambit extends AbstractRegexGambit implements FilterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Flarum\User\UserRepository
|
||||||
|
*/
|
||||||
|
protected $users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Flarum\User\UserRepository $users
|
||||||
|
*/
|
||||||
|
public function __construct(UserRepository $users)
|
||||||
|
{
|
||||||
|
$this->users = $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getGambitPattern()
|
||||||
|
{
|
||||||
|
return 'is:suspended';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply(SearchState $search, $bit)
|
||||||
|
{
|
||||||
|
if (! $search->getActor()->can('suspend', new Guest())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::apply($search, $bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function conditions(SearchState $search, array $matches, $negate)
|
||||||
|
{
|
||||||
|
$this->constrain($search->getQuery(), $negate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilterKey(): string
|
||||||
|
{
|
||||||
|
return 'suspended';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter(FilterState $filterState, string $filterValue, bool $negate)
|
||||||
|
{
|
||||||
|
if (! $filterState->getActor()->can('suspend', new Guest())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->constrain($filterState->getQuery(), $negate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function constrain(Builder $query, bool $negate)
|
||||||
|
{
|
||||||
|
$query->where(function ($query) use ($negate) {
|
||||||
|
if ($negate) {
|
||||||
|
$query->where('suspended_until', null)->orWhere('suspended_until', '<', Carbon::now());
|
||||||
|
} else {
|
||||||
|
$query->where('suspended_until', '>', Carbon::now());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
extensions/suspend/src/RevokeAccessFromSuspendedUsers.php
Normal file
32
extensions/suspend/src/RevokeAccessFromSuspendedUsers.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?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\Suspend;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class RevokeAccessFromSuspendedUsers
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @param array $groupIds
|
||||||
|
*/
|
||||||
|
public function __invoke(User $user, array $groupIds)
|
||||||
|
{
|
||||||
|
$suspendedUntil = $user->suspended_until;
|
||||||
|
|
||||||
|
if ($suspendedUntil && $suspendedUntil->gt(Carbon::now())) {
|
||||||
|
return [Group::GUEST_ID];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groupIds;
|
||||||
|
}
|
||||||
|
}
|
22
extensions/suspend/src/SuspendValidator.php
Normal file
22
extensions/suspend/src/SuspendValidator.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?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\Suspend;
|
||||||
|
|
||||||
|
use Flarum\Foundation\AbstractValidator;
|
||||||
|
|
||||||
|
class SuspendValidator extends AbstractValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $rules = [
|
||||||
|
'suspendedUntil' => ['nullable', 'date'],
|
||||||
|
];
|
||||||
|
}
|
4
extensions/suspend/views/emails/suspended.blade.php
Normal file
4
extensions/suspend/views/emails/suspended.blade.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{!! $translator->trans('flarum-suspend.email.suspended.body', [
|
||||||
|
'{recipient_display_name}' => $user->display_name,
|
||||||
|
'{suspension_message}' => $blueprint->user->suspend_message,
|
||||||
|
]) !!}
|
4
extensions/suspend/views/emails/unsuspended.blade.php
Normal file
4
extensions/suspend/views/emails/unsuspended.blade.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{!! $translator->trans('flarum-suspend.email.unsuspended.body', [
|
||||||
|
'{recipient_display_name}' => $user->display_name,
|
||||||
|
'{forum_url}' => $url->to('forum')->base(),
|
||||||
|
]) !!}
|
Loading…
Reference in New Issue
Block a user