mirror of
https://github.com/flarum/framework.git
synced 2024-11-26 18:33:40 +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