1
0
mirror of https://github.com/flarum/core.git synced 2025-08-14 12:24:33 +02:00

Compare commits

..

21 Commits

Author SHA1 Message Date
IanM
2a693db1b6 chore: prep 1.8.5 release 2024-01-05 15:47:32 +00:00
IanM
7d70328471 [1.x] fix: Logout controller allows open redirects (#3948)
* fix: prevent open redirects on logout controller

* use clearer config key

* cast url as string, reinstate guest redirect

* clean up a little

* simplify

* return Uri

* resolve ternary always true

* simplify some more

* remove extra newline

* handle malformed uri

* chore: requested changes
2024-01-05 15:33:10 +00:00
Sami Mazouz
45a8b572e3 fix: package name require format 2024-01-04 14:14:45 +01:00
flarum-bot
5d14f96c32 Bundled output for commit 2299541e4d
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2023-12-18 12:50:33 +00:00
Sami Mazouz
2299541e4d feat: package manager improvements (#3943)
* chore: track
* Apply fixes from StyleCI
* chore: track
* Apply fixes from StyleCI
* fix: installing beta packages #3792
* chore: guess package not found error
* Apply fixes from StyleCI
* feat: queue improvements
* feat: queue improvements
* fix: issues with job failure and unique runs
* fix: enforce one composer action at a time
* feat: add cause to queued command output
* Apply fixes from StyleCI
* feat: add soft & hard extension update options
* chore: explain why an extension cannot be removed
* chore: remove test
* chore: simplify
* docs: readme
* Apply fixes from StyleCI
* fea: allow adding repositories and auth methods
* chore: prevent future issues when min stability is set
* chore: typings check
* fix: phpstan
* Apply fixes from StyleCI
* fix: bugs
2023-12-18 13:44:33 +01:00
Sami Mazouz
a131132654 chore: prepare 1.8.4 2023-12-14 14:56:10 +01:00
Sami Mazouz
7743a2bcd4 chore: apply textformatter constraint to monorepo as well 2023-12-14 14:51:46 +01:00
Sami Mazouz
62080303bf fix(1.x): textformatter 2.15 has breaking changes (#3946) 2023-12-14 14:44:30 +01:00
IanM
480093d023 chore(1.x): enable testing on PHP 8.3 (#3933) 2023-11-23 19:02:08 +01:00
Rafał Całka
1c421fc266 fix(1.x,approval): correct PostWasApproved event trigger condition (#3925) 2023-11-17 17:58:14 +01:00
Ian Morland
f07336e204 chore: prep 1.8.3 release 2023-10-18 19:47:50 +01:00
IanM
95061a2ed4 fix: console extender does not accept ::class (#3900) 2023-10-18 19:39:45 +01:00
IanM
c3fadbf6b1 [1.x] Conditional extender instantiation (#3898)
* chore: create tests to highlight the conditional instantiation problem

* Apply fixes from StyleCI

* add callback and invokable class + tests

* Apply fixes from StyleCI

* address stan issue on php 8.2

* Revert "address stan issue on php 8.2"

This reverts commit 1fc2c8801a.

* attempt to make stan happy

* Revert "attempt to make stan happy"

This reverts commit 1cc327bb3b.

* is it really that simple?

* Revert "is it really that simple?"

This reverts commit 2006755cf1.

* let's try this

* Update framework/core/src/Extend/Conditional.php

Co-authored-by: Sami Mazouz <sychocouldy@gmail.com>

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
Co-authored-by: Sami Mazouz <sychocouldy@gmail.com>
2023-10-18 19:36:59 +01:00
Ian Morland
82e08e3fa5 chore: prep 1.8.2 release 2023-09-22 20:01:59 +01:00
flarum-bot
2c4a2b8d9e Bundled output for commit 00866fbba9
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2023-09-22 18:51:39 +00:00
Ian Morland
00866fbba9 chore: bump version 2023-09-22 19:46:39 +01:00
IanM
0d1d4d46d1 fix: missing compat exports (#3888) 2023-09-22 19:38:59 +01:00
Sami Mazouz
b1383a955f fix(1.x,suspend): suspended users can abuse avatar upload (#3890)
* fix(1.x,suspend): suspended users can abuse avatar upload

* test: works as expected

* Apply fixes from StyleCI

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
2023-09-22 19:38:33 +01:00
Sami Mazouz
daeab48ae8 chore: turn on frontend build on 1.x branch 2023-09-20 21:12:54 +01:00
Ian Morland
e03ca4406d chore: build js 2023-07-06 12:03:30 +01:00
StyleCI Bot
7894c6a69b Apply fixes from StyleCI 2023-07-05 09:31:05 +00:00
1856 changed files with 33230 additions and 33852 deletions

View File

@@ -1,25 +0,0 @@
{
"files": [
{
"path": "./framework/core/js/dist/*.js",
"maxSize": "150KB"
},
{
"path": "./framework/core/js/dist/*/**/*.js",
"maxSize": "30KB"
},
{
"path": "./extensions/*/js/dist/*.js",
"maxSize": "30KB"
},
{
"path": "./extensions/*/js/dist/*/**/*.js",
"maxSize": "30KB"
}
],
"defaultCompression": "gzip",
"ci": {
"repoBranchBase": "2.x",
"trackBranches": ["2.x"]
}
}

View File

@@ -23,6 +23,3 @@ indent_size = 2
[*.neon]
indent_style = tab
[{install,update}.php]
indent_size = 2

View File

@@ -21,18 +21,11 @@ on:
required: false
default: '.'
# Only relevant in mono-repos.
monorepo_tests:
description: "The list of directories to test in a monorepo. This should be a space-separated list of directories relative to the backend directory."
type: string
required: false
php_versions:
description: Versions of PHP to test with. Should be array of strings encoded as JSON array
type: string
required: false
# Keep PHP versions synced with build-install-packages.yml
default: '["8.1", "8.2", "8.3"]'
default: '["7.3", "7.4", "8.0", "8.1", "8.2", "8.3"]'
php_extensions:
description: PHP extensions to install.
@@ -44,7 +37,7 @@ on:
description: Versions of databases to test with. Should be array of strings encoded as JSON array
type: string
required: false
default: '["mysql:5.7", "mysql:8.0.30", "mysql:8.1.0", "mariadb", "sqlite:3"]'
default: '["mysql:5.7", "mysql:8.0.30", "mariadb"]'
php_ini_values:
description: PHP ini values
@@ -52,26 +45,13 @@ on:
required: false
default: error_reporting=E_ALL
runner_type:
description: The type of runner to use for the jobs. This should be one of the types supported by the `runs-on` keyword.
type: string
required: false
default: 'ubuntu-latest'
secrets:
composer_auth:
description: The Composer auth tokens to use for private packages.
required: false
env:
COMPOSER_ROOT_VERSION: dev-main
# `inputs.composer_directory` defaults to `inputs.backend_directory`
FLARUM_TEST_TMP_DIR_LOCAL: tests/integration/tmp
COMPOSER_AUTH: ${{ secrets.composer_auth }}
jobs:
test:
runs-on: ${{ inputs.runner_type }}
runs-on: ubuntu-latest
strategy:
matrix:
@@ -85,72 +65,52 @@ jobs:
# Expands the matrix by naming DBs.
- service: 'mysql:5.7'
db: MySQL 5.7
driver: mysql
- service: 'mysql:8.0.30'
db: MySQL 8.0
driver: mysql
- service: mariadb
db: MariaDB
driver: mysql
- service: 'mysql:8.1.0'
db: MySQL 8.1
driver: mysql
- service: 'sqlite:3'
db: SQLite
driver: sqlite
# Include Database prefix tests with only one PHP version.
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: 'mysql:5.7'
db: MySQL 5.7
driver: mysql
prefix: flarum_
prefixStr: (prefix)
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: 'mysql:8.0.30'
db: MySQL 8.0
driver: mysql
prefix: flarum_
prefixStr: (prefix)
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: mariadb
db: MariaDB
driver: mysql
prefix: flarum_
prefixStr: (prefix)
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: 'mysql:8.1.0'
db: MySQL 8.1
driver: mysql
prefix: flarum_
prefixStr: (prefix)
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: 'sqlite:3'
db: SQLite
driver: sqlite
prefix: flarum_
prefixStr: (prefix)
# @TODO: remove in 2.0
# Include testing PHP 8.2 with deprecation warnings disabled.
- php: 8.2
php_ini_values: error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
- php: 8.3
php_ini_values: error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
# To reduce number of actions, we exclude some PHP versions from running with some DB versions.
exclude:
- php: ${{ fromJSON(inputs.php_versions)[1] }}
service: 'mysql:8.0.30'
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: mariadb
- php: ${{ fromJSON(inputs.php_versions)[1] }}
service: mariadb
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: 'mysql:8.1.0'
- php: ${{ fromJSON(inputs.php_versions)[1] }}
service: 'mysql:8.1.0'
- php: ${{ fromJSON(inputs.php_versions)[0] }}
service: 'sqlite:3'
- php: ${{ fromJSON(inputs.php_versions)[1] }}
service: 'sqlite:3'
- php: ${{ fromJSON(inputs.php_versions)[2] }}
service: 'mysql:8.0.30'
- php: ${{ fromJSON(inputs.php_versions)[3] }}
service: 'mysql:8.0.30'
# @TODO: remove in 2.0
# Exclude testing PHP 8.2 with deprecation warnings enabled.
- php: 8.2
php_ini_values: error_reporting=E_ALL
services:
mysql:
image: ${{ matrix.service != 'sqlite:3' && matrix.service || '' }}
image: ${{ matrix.service }}
ports:
- 13306:3306
@@ -172,53 +132,40 @@ jobs:
tools: phpunit, composer:v2
ini-values: ${{ matrix.php_ini_values }}
# The authentication alter is necessary because newer mysql versions use the `caching_sha2_password` driver,
# which isn't supported prior to PHP7.4
# When we drop support for PHP7.3, we should remove this from the setup.
- name: Create MySQL Database
if: ${{ matrix.service != 'sqlite:3' }}
run: |
sudo systemctl start mysql
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" --port 13306
- name: Install Composer dependencies
run: composer install
working-directory: ${{ inputs.backend_directory }}
# If we have a `inputs.monorepo_tests`, we will run tests for each item of the provided array in a ::group::item
# If we don't have a `inputs.monorepo_tests`, we will run tests for the current repository
# We also have to run the `composer test:setup` script first before running each test
- name: Run tests
run: |
if [ -z "${{ inputs.monorepo_tests }}" ]; then
composer test:setup
composer test
else
for test in ${{ inputs.monorepo_tests }}; do
echo "::group::Running tests for $test"
composer test:setup --working-dir=$test
composer test --working-dir=$test
echo "::endgroup::"
done
fi
- name: Setup Composer tests
run: composer test:setup
working-directory: ${{ inputs.backend_directory }}
env:
DB_PORT: 13306
DB_PASSWORD: root
DB_PREFIX: ${{ matrix.prefix }}
DB_DRIVER: ${{ matrix.driver }}
- name: Run Composer tests
run: composer test
working-directory: ${{ inputs.backend_directory }}
env:
COMPOSER_PROCESS_TIMEOUT: 600
phpstan:
runs-on: ${{ inputs.runner_type }}
runs-on: ubuntu-latest
strategy:
matrix:
php: ${{ fromJSON(inputs.php_versions) }}
services:
mysql:
image: mysql:8.0.30
ports:
- 33306:3306
name: 'PHPStan PHP ${{ matrix.php }}'
if: >-
@@ -241,15 +188,5 @@ jobs:
run: composer install
working-directory: ${{ inputs.backend_directory }}
- name: Create MySQL Database
run: |
sudo systemctl start mysql
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 33306
- name: Run PHPStan
run: composer analyse:phpstan
env:
DB_PORT: 33306
DB_PASSWORD: root
COMPOSER_PROCESS_TIMEOUT: 600
FLARUM_TEST_TMP_DIR_LOCAL: ./tmp

View File

@@ -86,30 +86,20 @@ on:
type: string
required: false
runner_type:
description: The type of runner to use for the jobs. This should be one of the types supported by the `runs-on` keyword.
type: string
required: false
default: 'ubuntu-latest'
secrets:
bundlewatch_github_token:
description: The GitHub token to use for Bundlewatch.
required: false
composer_auth:
description: The Composer auth tokens to use for private packages.
required: false
env:
COMPOSER_ROOT_VERSION: dev-main
ci_script: ${{ inputs.js_package_manager == 'yarn' && 'yarn install --immutable' || 'npm ci' }}
cache_dependency_path: ${{ inputs.cache_dependency_path || format(inputs.js_package_manager == 'yarn' && '{0}/yarn.lock' || '{0}/package-lock.json', inputs.frontend_directory) }}
COMPOSER_AUTH: ${{ secrets.composer_auth }}
jobs:
build:
name: Checks & Build
runs-on: ${{ inputs.runner_type }}
runs-on: ubuntu-latest
if: >-
((github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) || github.event_name != 'pull_request')
@@ -162,4 +152,3 @@ jobs:
env:
BUNDLEWATCH_GITHUB_TOKEN: ${{ secrets.bundlewatch_github_token }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_BRANCH_BASE: ${{ github.event.pull_request.base.ref }}

View File

@@ -1,11 +0,0 @@
name: Backend Tests
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: .
monorepo_tests: "framework/core extensions/akismet extensions/approval extensions/flags extensions/likes extensions/mentions extensions/nicknames extensions/statistics extensions/sticky extensions/subscriptions extensions/suspend extensions/tags"

View File

@@ -1,29 +0,0 @@
name: Build Install Packages
on:
release:
types: [released]
env:
VERSION: ${{ github.event.release.tag_name }}
PHP_VERSIONS: '8.1 8.2 8.3'
INSTALL_PACKAGES_INPUTS: '{ "flarum_version": "{0}", "php_versions": "{1}" }'
jobs:
delay:
name: Wait for packagist to publish new packages
runs-on: ubuntu-latest
steps:
- run: sleep 30m
build:
name: Build Installation Packages
runs-on: ubuntu-latest
steps:
- name: Trigger build in flarum/installation-packages
uses: benc-uk/workflow-dispatch@v1
with:
workflow: Build Flarum Install Packages
repo: flarum/installation-packages
token: ${{ secrets.PACKAGES_BUILD_TOKEN }}
inputs: ${{ format(env.INSTALL_PACKAGES_INPUTS, env.VERSION, env.PHP_VERSIONS) }}

View File

@@ -0,0 +1,11 @@
name: Akismet PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/akismet

View File

@@ -0,0 +1,11 @@
name: Approval PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/approval

View File

@@ -0,0 +1,11 @@
name: Core PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./framework/core

View File

@@ -0,0 +1,11 @@
name: Embed PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
backend_directory: ./extensions/embed

View File

@@ -0,0 +1,11 @@
name: Emoji PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
backend_directory: ./extensions/emoji

View File

@@ -0,0 +1,11 @@
name: Flags PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/flags

View File

@@ -0,0 +1,11 @@
name: Likes PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/likes

View File

@@ -0,0 +1,11 @@
name: Lock PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
backend_directory: ./extensions/lock

View File

@@ -0,0 +1,11 @@
name: Markdown PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
backend_directory: ./extensions/markdown

View File

@@ -0,0 +1,11 @@
name: Mentions PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/mentions

View File

@@ -0,0 +1,11 @@
name: Nicknames PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/nicknames

View File

@@ -0,0 +1,11 @@
name: Package Manager PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
backend_directory: ./extensions/package-manager

View File

@@ -0,0 +1,11 @@
name: Pusher PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
backend_directory: ./extensions/pusher

View File

@@ -0,0 +1,11 @@
name: Statistics PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/statistics

View File

@@ -0,0 +1,11 @@
name: Sticky PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/sticky

View File

@@ -0,0 +1,11 @@
name: Subscriptions PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/subscriptions

View File

@@ -0,0 +1,11 @@
name: Suspend PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/suspend

View File

@@ -0,0 +1,11 @@
name: Tags PHP
on: [workflow_dispatch, push, pull_request]
jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: true
backend_directory: ./extensions/tags

View File

@@ -1,4 +1,4 @@
name: Frontend Workflow
name: Framework JS
on: [workflow_dispatch, push, pull_request]
@@ -10,9 +10,10 @@ jobs:
backend_directory: ./
js_package_manager: yarn
cache_dependency_path: ./yarn.lock
main_git_branch: 2.x
main_git_branch: 1.x
enable_tests: true
enable_bundlewatch: true
# @TODO: fix bundlewatch
enable_bundlewatch: false
secrets:
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}

View File

@@ -1,4 +1,4 @@
name: Static Code Analysis
name: Framework PHP
on: [workflow_dispatch, push, pull_request]
@@ -8,4 +8,5 @@ jobs:
with:
enable_backend_testing: false
enable_phpstan: true
backend_directory: .

View File

@@ -1,5 +1,23 @@
# Changelog
## [v1.8.5](https://github.com/flarum/framework/compare/v1.8.4...v1.8.5)
### Fixed
* Logout controller allows open redirects [#3948]
## [v1.8.4](https://github.com/flarum/framework/compare/v1.8.3...v1.8.4)
### Fixed
* `s9e/textformatter` 2.15 has breaking changes [#3946]
## [v1.8.3](https://github.com/flarum/framework/compare/v1.8.2...v1.8.3)
### Fixed
* Console extender does not accept ::class [#3900]
* Conditional extender instantiation [#3898]
## [v1.8.2](https://github.com/flarum/framework/compare/v1.8.1...v1.8.2)
### Fixed
* suspended users can abuse avatar upload [#3890]
* missing compat exports [#3888]
## [v1.8.1](https://github.com/flarum/framework/compare/v1.8.0...v1.8.1)
### Fixed
* recover temporary solution for html entities in browser title (e72541e35de4f71f9d870bbd9bb46ddf586bdf1d)

View File

@@ -38,4 +38,3 @@ If you discover a security vulnerability within Flarum, please send an e-mail to
## License
Flarum is open-source software licensed under the [MIT License](https://github.com/flarum/flarum/blob/master/LICENSE).

View File

@@ -46,7 +46,7 @@
"Flarum\\Lock\\": "extensions/lock/src",
"Flarum\\Mentions\\": "extensions/mentions/src",
"Flarum\\Nicknames\\": "extensions/nicknames/src",
"Flarum\\ExtensionManager\\": "extensions/package-manager/src",
"Flarum\\PackageManager\\": "extensions/package-manager/src",
"Flarum\\Pusher\\": "extensions/pusher/src",
"Flarum\\Statistics\\": "extensions/statistics/src",
"Flarum\\Sticky\\": "extensions/sticky/src",
@@ -60,26 +60,6 @@
"framework/core/src/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Flarum\\Tests\\": "tests",
"Flarum\\Akismet\\Tests\\": "extensions/akismet/tests",
"Flarum\\Approval\\Tests\\": "extensions/approval/tests",
"Flarum\\Flags\\Tests\\": "extensions/flags/tests",
"Flarum\\Likes\\Tests\\": "extensions/likes/tests",
"Flarum\\Lock\\Tests\\": "extensions/lock/tests",
"Flarum\\Mentions\\Tests\\": "extensions/mentions/tests",
"Flarum\\Nicknames\\Tests\\": "extensions/nicknames/tests",
"Flarum\\ExtensionManager\\Tests\\": "extensions/package-manager/tests",
"Flarum\\Pusher\\Tests\\": "extensions/pusher/tests",
"Flarum\\Statistics\\Tests\\": "extensions/statistics/tests",
"Flarum\\Sticky\\Tests\\": "extensions/sticky/tests",
"Flarum\\Subscriptions\\Tests\\": "extensions/subscriptions/tests",
"Flarum\\Suspend\\Tests\\": "extensions/suspend/tests",
"Flarum\\Tags\\Tests\\": "extensions/tags/tests",
"Flarum\\Testing\\Tests\\": "php-packages/testing/tests"
}
},
"replace": {
"flarum/core": "self.version",
"flarum/akismet": "self.version",
@@ -94,7 +74,7 @@
"flarum/markdown": "self.version",
"flarum/mentions": "self.version",
"flarum/nicknames": "self.version",
"flarum/extension-manager": "self.version",
"flarum/package-manager": "self.version",
"flarum/pusher": "self.version",
"flarum/statistics": "self.version",
"flarum/sticky": "self.version",
@@ -105,72 +85,66 @@
"flarum/testing": "self.version"
},
"require": {
"php": "^8.1",
"php": ">=7.3",
"ext-json": "*",
"components/font-awesome": "^5.15.0",
"composer/composer": "^2.7",
"dflydev/fig-cookies": "^3.0",
"doctrine/dbal": "^3.6.2",
"dragonmantank/cron-expression": "^3.3",
"fakerphp/faker": "^1.9.1",
"franzl/whoops-middleware": "2.0",
"guzzlehttp/guzzle": "*",
"illuminate/bus": "^10.0",
"illuminate/cache": "^10.0",
"illuminate/config": "^10.0",
"illuminate/console": "^10.0",
"illuminate/container": "^10.0",
"illuminate/contracts": "^10.0",
"illuminate/database": "^10.0",
"illuminate/events": "^10.0",
"illuminate/filesystem": "^10.0",
"illuminate/hashing": "^10.0",
"illuminate/mail": "^10.0",
"illuminate/queue": "^10.0",
"illuminate/session": "^10.0",
"illuminate/support": "^10.0",
"illuminate/validation": "^10.0",
"illuminate/view": "^10.0",
"intervention/image": "^3.2",
"components/font-awesome": "^5.14.0",
"composer/composer": "^2.0",
"dflydev/fig-cookies": "^3.0.0",
"doctrine/dbal": "^2.7",
"dragonmantank/cron-expression": "^3.1.0",
"franzl/whoops-middleware": "^2.0.0",
"guzzlehttp/guzzle": "^6.0|^7.4",
"illuminate/bus": "^8.0",
"illuminate/cache": "^8.0",
"illuminate/config": "^8.0",
"illuminate/console": "^8.0",
"illuminate/container": "^8.0",
"illuminate/contracts": "^8.0",
"illuminate/database": "^8.0",
"illuminate/events": "^8.0",
"illuminate/filesystem": "^8.0",
"illuminate/hashing": "^8.0",
"illuminate/mail": "^8.0",
"illuminate/queue": "^8.0",
"illuminate/session": "^8.0",
"illuminate/support": "^8.0",
"illuminate/validation": "^8.0",
"illuminate/view": "^8.0",
"intervention/image": "2.5.* || ^2.6.1",
"jenssegers/agent": "^2.6",
"laminas/laminas-diactoros": "^3.0",
"laminas/laminas-httphandlerrunner": "^2.6",
"laminas/laminas-stratigility": "^3.10",
"league/flysystem": "^3.15",
"league/flysystem-memory": "^3.15",
"laminas/laminas-diactoros": "^2.4.1",
"laminas/laminas-httphandlerrunner": "^1.2.0 || ^2.3.0",
"laminas/laminas-stratigility": "^3.2.2",
"league/flysystem": "^1.0.11",
"matthiasmullie/minify": "^1.3",
"middlewares/base-path": "^v2.1",
"middlewares/base-path": "^2.0.1",
"middlewares/base-path-router": "^2.0.1",
"middlewares/request-handler": "^2.0.2",
"monolog/monolog": "^3.3",
"nesbot/carbon": "^2.66",
"nikic/fast-route": "^1.3",
"psr/http-message": "^1.1",
"psr/http-server-handler": "^1.0.2",
"psr/http-server-middleware": "^1.0.2",
"pusher/pusher-php-server": "^7.2",
"s9e/text-formatter": "^2.13",
"staudenmeir/eloquent-eager-limit": "^1.8.2",
"middlewares/request-handler": "^2.0.1",
"monolog/monolog": "^1.16.0",
"nesbot/carbon": "^2.0",
"nikic/fast-route": "^0.6",
"psr/http-message": "^1.0",
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"pusher/pusher-php-server": "^2.2",
"s9e/text-formatter": ">=2.3.6 <2.15",
"staudenmeir/eloquent-eager-limit": "^1.0",
"sycho/json-api": "^0.5.0",
"sycho/sourcemap": "^2.0.0",
"symfony/config": "^6.3",
"symfony/console": "^6.3",
"symfony/event-dispatcher": "^6.3",
"symfony/http-client": "^6.3",
"symfony/mailgun-mailer": "^6.3",
"symfony/mime": "^6.3",
"symfony/polyfill-intl-messageformatter": "^1.27",
"symfony/postmark-mailer": "^6.3",
"symfony/translation": "^6.3",
"symfony/yaml": "^6.3",
"flarum/json-api-server": "^0.1.0",
"wikimedia/less.php": "^4.1"
"symfony/config": "^5.2.2",
"symfony/console": "^5.2.2",
"symfony/event-dispatcher": "^5.2.2",
"symfony/mime": "^5.2.0",
"symfony/polyfill-intl-messageformatter": "^1.22.0",
"symfony/translation": "^5.1.5",
"symfony/yaml": "^5.2.2",
"wikimedia/less.php": "^3.0"
},
"require-dev": {
"mockery/mockery": "^1.5",
"mockery/mockery": "^1.4",
"phpunit/phpunit": "^9.0",
"phpstan/phpstan": "^1.10.0",
"larastan/larastan": "^2.7",
"symfony/var-dumper": "^6.3"
"phpstan/phpstan": ">=1.8.11 < 1.9.0",
"nunomaduro/larastan": "^1.0"
},
"config": {
"sort-packages": true
@@ -200,7 +174,7 @@
"extensions/tags"
],
"branch-alias": {
"dev-main": "2.x-dev"
"dev-main": "1.x-dev"
},
"phpstan": {
"includes": [

View File

@@ -19,8 +19,8 @@
}
],
"require": {
"flarum/core": "^2.0",
"flarum/approval": "^2.0"
"flarum/core": "^1.8",
"flarum/approval": "^1.7"
},
"autoload": {
"psr-4": {
@@ -29,7 +29,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "2.x-dev"
"dev-main": "1.x-dev"
},
"flarum-extension": {
"title": "Akismet",
@@ -75,7 +75,7 @@
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
},
"require-dev": {
"flarum/testing": "^2.0"
"flarum/testing": "^1.0.0"
},
"repositories": [
{

View File

@@ -1,2 +1,2 @@
(()=>{var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t);const a=flarum.reg.get("core","admin/app");var r=e.n(a);r().initializers.add("flarum-akismet",(()=>{r().extensionData.for("flarum-akismet").registerSetting({setting:"flarum-akismet.api_key",type:"text",label:r().translator.trans("flarum-akismet.admin.akismet_settings.api_key_label")}).registerSetting({setting:"flarum-akismet.delete_blatant_spam",type:"boolean",label:r().translator.trans("flarum-akismet.admin.akismet_settings.delete_blatant_spam_label"),help:r().translator.trans("flarum-akismet.admin.akismet_settings.delete_blatant_spam_help")}).registerPermission({icon:"fas fa-vote-yea",label:r().translator.trans("flarum-akismet.admin.permissions.bypass_akismet"),permission:"bypassAkismet"},"start")}))})(),module.exports=t})();
(()=>{var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t);const a=flarum.core.compat["admin/app"];var r=e.n(a);r().initializers.add("flarum-akismet",(function(){r().extensionData.for("flarum-akismet").registerSetting({setting:"flarum-akismet.api_key",type:"text",label:r().translator.trans("flarum-akismet.admin.akismet_settings.api_key_label")}).registerSetting({setting:"flarum-akismet.delete_blatant_spam",type:"boolean",label:r().translator.trans("flarum-akismet.admin.akismet_settings.delete_blatant_spam_label"),help:r().translator.trans("flarum-akismet.admin.akismet_settings.delete_blatant_spam_help")}).registerPermission({icon:"fas fa-vote-yea",label:r().translator.trans("flarum-akismet.admin.permissions.bypass_akismet"),permission:"bypassAkismet"},"start")}))})(),module.exports=t})();
//# sourceMappingURL=admin.js.map

View File

@@ -1 +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,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,IAAIV,IAAI,OAAQ,a,aCC5D,qBAAqB,kBAAkB,KACrC,sBAAsB,kBAAkBW,gBAAgB,CACtDC,QAAS,yBACTC,KAAM,OACNC,MAAO,qBAAqB,yDAC3BH,gBAAgB,CAEjBC,QAAS,qCACTC,KAAM,UACNC,MAAO,qBAAqB,mEAC5BC,KAAM,qBAAqB,oEAC1BC,mBAAmB,CACpBC,KAAM,kBACNH,MAAO,qBAAqB,mDAC5BI,WAAY,iBACX,QAAQ,G","sources":["webpack://@flarum/akismet/webpack/bootstrap","webpack://@flarum/akismet/webpack/runtime/compat get default export","webpack://@flarum/akismet/webpack/runtime/define property getters","webpack://@flarum/akismet/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/akismet/webpack/runtime/make namespace object","webpack://@flarum/akismet/external root \"flarum.reg.get('core', 'admin/app')\"","webpack://@flarum/akismet/./src/admin/index.ts"],"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.reg.get('core', 'admin/app');","import app from 'flarum/admin/app';\napp.initializers.add('flarum-akismet', () => {\n app.extensionData.for('flarum-akismet').registerSetting({\n setting: 'flarum-akismet.api_key',\n type: 'text',\n label: app.translator.trans('flarum-akismet.admin.akismet_settings.api_key_label')\n }).registerSetting({\n //https://blog.akismet.com/2014/04/23/theres-a-ninja-in-your-akismet/\n setting: 'flarum-akismet.delete_blatant_spam',\n type: 'boolean',\n label: app.translator.trans('flarum-akismet.admin.akismet_settings.delete_blatant_spam_label'),\n help: app.translator.trans('flarum-akismet.admin.akismet_settings.delete_blatant_spam_help')\n }).registerPermission({\n icon: 'fas fa-vote-yea',\n label: app.translator.trans('flarum-akismet.admin.permissions.bypass_akismet'),\n permission: 'bypassAkismet'\n }, 'start');\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","reg","registerSetting","setting","type","label","help","registerPermission","icon","permission"],"sourceRoot":""}
{"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,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,a,aCExDC,IAAAA,aAAAA,IAAqB,kBAAkB,WACrCA,IAAAA,cAAAA,IACO,kBACJC,gBAAgB,CACfC,QAAS,yBACTC,KAAM,OACNC,MAAOJ,IAAAA,WAAAA,MAAqB,yDAE7BC,gBAAgB,CAEfC,QAAS,qCACTC,KAAM,UACNC,MAAOJ,IAAAA,WAAAA,MAAqB,mEAC5BK,KAAML,IAAAA,WAAAA,MAAqB,oEAE5BM,mBACC,CACEC,KAAM,kBACNH,MAAOJ,IAAAA,WAAAA,MAAqB,mDAC5BQ,WAAY,iBAEd,QAEN,G","sources":["webpack://@flarum/akismet/webpack/bootstrap","webpack://@flarum/akismet/webpack/runtime/compat get default export","webpack://@flarum/akismet/webpack/runtime/define property getters","webpack://@flarum/akismet/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/akismet/webpack/runtime/make namespace object","webpack://@flarum/akismet/external root \"flarum.core.compat['admin/app']\"","webpack://@flarum/akismet/./src/admin/index.ts"],"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['admin/app'];","import app from 'flarum/admin/app';\n\napp.initializers.add('flarum-akismet', () => {\n app.extensionData\n .for('flarum-akismet')\n .registerSetting({\n setting: 'flarum-akismet.api_key',\n type: 'text',\n label: app.translator.trans('flarum-akismet.admin.akismet_settings.api_key_label'),\n })\n .registerSetting({\n //https://blog.akismet.com/2014/04/23/theres-a-ninja-in-your-akismet/\n setting: 'flarum-akismet.delete_blatant_spam',\n type: 'boolean',\n label: app.translator.trans('flarum-akismet.admin.akismet_settings.delete_blatant_spam_label'),\n help: app.translator.trans('flarum-akismet.admin.akismet_settings.delete_blatant_spam_help'),\n })\n .registerPermission(\n {\n icon: 'fas fa-vote-yea',\n label: app.translator.trans('flarum-akismet.admin.permissions.bypass_akismet'),\n permission: 'bypassAkismet',\n },\n 'start'\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","registerSetting","setting","type","label","help","registerPermission","icon","permission"],"sourceRoot":""}

View File

@@ -1,2 +1,2 @@
(()=>{var e={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return e.d(r,{a:r}),r},d:(t,r)=>{for(var o in r)e.o(r,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:r[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t);const r=flarum.reg.get("core","common/extend"),o=flarum.reg.get("core","forum/app");var a=e.n(o);const n=flarum.reg.get("core","forum/utils/PostControls");var s=e.n(n);const l=flarum.reg.get("core","forum/components/Post");var u=e.n(l);a().initializers.add("flarum-akismet",(()=>{(0,r.extend)(s(),"destructiveControls",(function(e,t){if(e.has("approve")){const r=t.flags();if(r&&r.some((e=>"akismet"===(null==e?void 0:e.type())))){const t=e.get("approve");t&&"object"==typeof t&&"children"in t&&(t.children=a().translator.trans("flarum-akismet.forum.post.not_spam_button"))}}})),(0,r.override)(u().prototype,"flagReason",(function(e,t){return"akismet"===t.type()?a().translator.trans("flarum-akismet.forum.post.akismet_flagged_text"):e(t)}))}))})(),module.exports=t})();
(()=>{var t={n:e=>{var o=e&&e.__esModule?()=>e.default:()=>e;return t.d(o,{a:o}),o},d:(e,o)=>{for(var r in o)t.o(o,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:o[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};(()=>{"use strict";t.r(e);const o=flarum.core.compat["common/extend"],r=flarum.core.compat["forum/app"];var a=t.n(r);const n=flarum.core.compat["forum/utils/PostControls"];var s=t.n(n);const l=flarum.core.compat["forum/components/Post"];var u=t.n(l);a().initializers.add("flarum-akismet",(function(){(0,o.extend)(s(),"destructiveControls",(function(t,e){if(t.has("approve")){var o=e.flags();if(o&&o.some((function(t){return"akismet"===(null==t?void 0:t.type())}))){var r=t.get("approve");r&&"object"==typeof r&&"children"in r&&(r.children=a().translator.trans("flarum-akismet.forum.post.not_spam_button"))}}})),(0,o.override)(u().prototype,"flagReason",(function(t,e){return"akismet"===e.type()?a().translator.trans("flarum-akismet.forum.post.akismet_flagged_text"):t(e)}))}))})(),module.exports=e})();
//# sourceMappingURL=forum.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"forum.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,IAAIV,IAAI,OAAQ,iBCAtD,EAA+BS,OAAOC,IAAIV,IAAI,OAAQ,a,aCA5D,MAAM,EAA+BS,OAAOC,IAAIV,IAAI,OAAQ,4B,aCA5D,MAAM,EAA+BS,OAAOC,IAAIV,IAAI,OAAQ,yB,aCI5D,qBAAqB,kBAAkB,MACrC,IAAAW,QAAO,IAAc,uBAAuB,SAAUC,EAAOC,GAC3D,GAAID,EAAME,IAAI,WAAY,CACxB,MAAMC,EAAQF,EAAKE,QACnB,GAAIA,GAASA,EAAMC,MAAKC,GAAkD,aAAjC,MAARA,OAAe,EAASA,EAAKC,UAAwB,CACpF,MAAMC,EAAcP,EAAMZ,IAAI,WAC1BmB,GAAsC,iBAAhBA,GAA4B,aAAcA,IAClEA,EAAYC,SAAW,qBAAqB,6CAEhD,CACF,CACF,KACA,IAAAC,UAAS,cAAyB,cAAc,SAAUC,EAAUL,GAClE,MAAoB,YAAhBA,EAAKC,OACA,qBAAqB,kDAEvBI,EAASL,EAClB,GAAE,G","sources":["webpack://@flarum/akismet/webpack/bootstrap","webpack://@flarum/akismet/webpack/runtime/compat get default export","webpack://@flarum/akismet/webpack/runtime/define property getters","webpack://@flarum/akismet/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/akismet/webpack/runtime/make namespace object","webpack://@flarum/akismet/external root \"flarum.reg.get('core', 'common/extend')\"","webpack://@flarum/akismet/external root \"flarum.reg.get('core', 'forum/app')\"","webpack://@flarum/akismet/external root \"flarum.reg.get('core', 'forum/utils/PostControls')\"","webpack://@flarum/akismet/external root \"flarum.reg.get('core', 'forum/components/Post')\"","webpack://@flarum/akismet/./src/forum/index.ts"],"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.reg.get('core', 'common/extend');","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.reg.get('core', 'forum/app');","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.reg.get('core', 'forum/utils/PostControls');","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.reg.get('core', 'forum/components/Post');","import { extend, override } from 'flarum/common/extend';\nimport app from 'flarum/forum/app';\nimport PostControls from 'flarum/forum/utils/PostControls';\nimport PostComponent from 'flarum/forum/components/Post';\napp.initializers.add('flarum-akismet', () => {\n extend(PostControls, 'destructiveControls', function (items, post) {\n if (items.has('approve')) {\n const flags = post.flags();\n if (flags && flags.some(flag => (flag == null ? void 0 : flag.type()) === 'akismet')) {\n const approveItem = items.get('approve');\n if (approveItem && typeof approveItem === 'object' && 'children' in approveItem) {\n approveItem.children = app.translator.trans('flarum-akismet.forum.post.not_spam_button');\n }\n }\n }\n });\n override(PostComponent.prototype, 'flagReason', function (original, flag) {\n if (flag.type() === 'akismet') {\n return app.translator.trans('flarum-akismet.forum.post.akismet_flagged_text');\n }\n return original(flag);\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","reg","extend","items","post","has","flags","some","flag","type","approveItem","children","override","original"],"sourceRoot":""}
{"version":3,"file":"forum.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,iBCAlD,EAA+BF,OAAOC,KAAKC,OAAO,a,aCAxD,MAAM,EAA+BF,OAAOC,KAAKC,OAAO,4B,aCAxD,MAAM,EAA+BF,OAAOC,KAAKC,OAAO,yB,aCSxDC,IAAAA,aAAAA,IAAqB,kBAAkB,YACrCC,EAAAA,EAAAA,QAAOC,IAAc,uBAAuB,SAAUC,EAAmCC,GACvF,GAAID,EAAME,IAAI,WAAY,CACxB,IAAMC,EAAQF,EAAKE,QAEnB,GAAIA,GAASA,EAAMC,MAAK,SAACC,GAAI,MAAsB,aAAb,MAAJA,OAAI,EAAJA,EAAMC,OAAoB,IAAG,CAC7D,IAAMC,EAAcP,EAAMf,IAAI,WAC1BsB,GAAsC,iBAAhBA,GAA4B,aAAcA,IAClEA,EAAYC,SAAWX,IAAAA,WAAAA,MAAqB,6CAEhD,CACF,CACF,KAEAY,EAAAA,EAAAA,UAASC,IAAAA,UAAyB,cAAc,SAAUC,EAAUN,GAClE,MAAoB,YAAhBA,EAAKC,OACAT,IAAAA,WAAAA,MAAqB,kDAGvBc,EAASN,EAClB,GACF,G","sources":["webpack://@flarum/akismet/webpack/bootstrap","webpack://@flarum/akismet/webpack/runtime/compat get default export","webpack://@flarum/akismet/webpack/runtime/define property getters","webpack://@flarum/akismet/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/akismet/webpack/runtime/make namespace object","webpack://@flarum/akismet/external root \"flarum.core.compat['common/extend']\"","webpack://@flarum/akismet/external root \"flarum.core.compat['forum/app']\"","webpack://@flarum/akismet/external root \"flarum.core.compat['forum/utils/PostControls']\"","webpack://@flarum/akismet/external root \"flarum.core.compat['forum/components/Post']\"","webpack://@flarum/akismet/./src/forum/index.ts"],"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['common/extend'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['forum/app'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['forum/utils/PostControls'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['forum/components/Post'];","import { extend, override } from 'flarum/common/extend';\nimport app from 'flarum/forum/app';\nimport type Post from 'flarum/common/models/Post';\nimport type ItemList from 'flarum/common/utils/ItemList';\n\nimport PostControls from 'flarum/forum/utils/PostControls';\nimport PostComponent from 'flarum/forum/components/Post';\nimport type Mithril from 'mithril';\n\napp.initializers.add('flarum-akismet', () => {\n extend(PostControls, 'destructiveControls', function (items: ItemList<Mithril.Children>, post: Post) {\n if (items.has('approve')) {\n const flags = post.flags();\n\n if (flags && flags.some((flag) => flag?.type() === 'akismet')) {\n const approveItem = items.get('approve');\n if (approveItem && typeof approveItem === 'object' && 'children' in approveItem) {\n approveItem.children = app.translator.trans('flarum-akismet.forum.post.not_spam_button');\n }\n }\n }\n });\n\n override(PostComponent.prototype, 'flagReason', function (original, flag) {\n if (flag.type() === 'akismet') {\n return app.translator.trans('flarum-akismet.forum.post.akismet_flagged_text');\n }\n\n return original(flag);\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","extend","PostControls","items","post","has","flags","some","flag","type","approveItem","children","override","PostComponent","original"],"sourceRoot":""}

View File

@@ -15,7 +15,7 @@
"declarationDir": "./dist-typings",
"paths": {
"flarum/*": ["../../../framework/core/js/dist-typings/*"],
"ext:flarum/flags/*": ["../../flags/js/dist-typings/*"]
"flarum/flags/*": ["../../flags/js/dist-typings/*"]
}
}
}

View File

@@ -15,19 +15,22 @@ use Psr\Http\Message\ResponseInterface;
class Akismet
{
private string $apiUrl;
private array $params = [];
private $apiKey;
private $apiUrl;
private $flarumVersion;
private $extensionVersion;
public function __construct(
private readonly string $apiKey,
string $homeUrl,
private readonly string $flarumVersion,
private readonly string $extensionVersion,
bool $inDebugMode = false
) {
private $params = [];
public function __construct(string $apiKey, string $homeUrl, string $flarumVersion, string $extensionVersion, bool $inDebugMode = false)
{
$this->apiKey = $apiKey;
$this->apiUrl = "https://$apiKey.rest.akismet.com/1.1";
$this->params['blog'] = $homeUrl;
$this->flarumVersion = $flarumVersion;
$this->extensionVersion = $extensionVersion;
if ($inDebugMode) {
$this->params['is_test'] = true;
}
@@ -47,7 +50,7 @@ class Akismet
$client = new Client();
return $client->request('POST', "$this->apiUrl/$type", [
'headers' => [
'headers' => [
'User-Agent' => "Flarum/$this->flarumVersion | Akismet/$this->extensionVersion",
],
'form_params' => $this->params,
@@ -70,7 +73,7 @@ class Akismet
/**
* @throws GuzzleException
*/
public function submitSpam(): void
public function submitSpam()
{
$this->sendRequest('submit-spam');
}
@@ -78,7 +81,7 @@ class Akismet
/**
* @throws GuzzleException
*/
public function submitHam(): void
public function submitHam()
{
$this->sendRequest('submit-ham');
}
@@ -87,7 +90,7 @@ class Akismet
* Allows you to set additional parameter
* This lets you use Akismet features not supported directly in this util.
*/
public function withParam(string $key, mixed $value): Akismet
public function withParam(string $key, $value): Akismet
{
$new = clone $this;
$new->params[$key] = $value;

View File

@@ -14,12 +14,17 @@ use Flarum\Approval\Event\PostWasApproved;
class SubmitHam
{
public function __construct(
protected Akismet $akismet
) {
/**
* @var Akismet
*/
protected $akismet;
public function __construct(Akismet $akismet)
{
$this->akismet = $akismet;
}
public function handle(PostWasApproved $event): void
public function handle(PostWasApproved $event)
{
if (! $this->akismet->isConfigured()) {
return;

View File

@@ -14,12 +14,17 @@ use Flarum\Post\Event\Hidden;
class SubmitSpam
{
public function __construct(
protected Akismet $akismet
) {
/**
* @var Akismet
*/
protected $akismet;
public function __construct(Akismet $akismet)
{
$this->akismet = $akismet;
}
public function handle(Hidden $event): void
public function handle(Hidden $event)
{
if (! $this->akismet->isConfigured()) {
return;

View File

@@ -18,13 +18,22 @@ use Flarum\Settings\SettingsRepositoryInterface;
class ValidatePost
{
public function __construct(
protected Akismet $akismet,
protected SettingsRepositoryInterface $settings
) {
/**
* @var Akismet
*/
protected $akismet;
/**
* @var SettingsRepositoryInterface
*/
private $settings;
public function __construct(Akismet $akismet, SettingsRepositoryInterface $settings)
{
$this->akismet = $akismet;
$this->settings = $settings;
}
public function handle(Saving $event): void
public function handle(Saving $event)
{
if (! $this->akismet->isConfigured()) {
return;

View File

@@ -20,7 +20,7 @@ use Illuminate\Container\Container;
class AkismetProvider extends AbstractServiceProvider
{
public function register(): void
public function register()
{
$this->container->bind(Akismet::class, function (Container $container) {
/** @var SettingsRepositoryInterface $settings */

View File

@@ -7,6 +7,10 @@
* LICENSE file that was distributed with this source code.
*/
$setup = require __DIR__.'/../../../../php-packages/testing/bootstrap/monorepo.php';
use Flarum\Testing\integration\Setup\SetupScript;
require __DIR__.'/../../vendor/autoload.php';
$setup = new SetupScript();
$setup->run();

View File

@@ -10,7 +10,6 @@
convertWarningsToExceptions="true"
processIsolation="true"
stopOnFailure="false"
bootstrap="../../../php-packages/testing/bootstrap/monorepo.php"
>
<coverage processUncoveredFiles="true">
<include>

View File

@@ -10,7 +10,6 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="../../../php-packages/testing/bootstrap/monorepo.php"
>
<coverage processUncoveredFiles="true">
<include>

View File

@@ -19,8 +19,8 @@
}
],
"require": {
"flarum/core": "^2.0",
"flarum/flags": "^2.0"
"flarum/core": "^1.8",
"flarum/flags": "^1.7"
},
"autoload": {
"psr-4": {
@@ -29,7 +29,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "2.x-dev"
"dev-main": "1.x-dev"
},
"flarum-extension": {
"title": "Approval",
@@ -87,6 +87,6 @@
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
},
"require-dev": {
"flarum/testing": "^2.0"
"flarum/testing": "^1.0.0"
}
}

View File

@@ -7,10 +7,9 @@
* LICENSE file that was distributed with this source code.
*/
use Flarum\Api\Resource;
use Flarum\Api\Schema;
use Flarum\Api\Serializer\BasicDiscussionSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Approval\Access;
use Flarum\Approval\Api\PostResourceFields;
use Flarum\Approval\Event\PostWasApproved;
use Flarum\Approval\Listener;
use Flarum\Discussion\Discussion;
@@ -37,13 +36,17 @@ return [
->default('is_approved', true)
->cast('is_approved', 'bool'),
(new Extend\ApiResource(Resource\DiscussionResource::class))
->fields(fn () => [
Schema\Boolean::make('isApproved'),
]),
(new Extend\ApiSerializer(BasicDiscussionSerializer::class))
->attribute('isApproved', function ($serializer, Discussion $discussion) {
return $discussion->is_approved;
}),
(new Extend\ApiResource(Resource\PostResource::class))
->fields(PostResourceFields::class),
(new Extend\ApiSerializer(PostSerializer::class))
->attribute('isApproved', function ($serializer, Post $post) {
return (bool) $post->is_approved;
})->attribute('canApprove', function (PostSerializer $serializer, Post $post) {
return (bool) $serializer->getActor()->can('approvePosts', $post->discussion);
}),
new Extend\Locales(__DIR__.'/locale'),
@@ -62,8 +65,8 @@ return [
->scope(Access\ScopePrivateDiscussionVisibility::class, 'viewPrivate'),
(new Extend\ModelPrivate(Discussion::class))
->checker(Listener\UnapproveNewContent::markUnapprovedContentAsPrivate(...)),
->checker([Listener\UnapproveNewContent::class, 'markUnapprovedContentAsPrivate']),
(new Extend\ModelPrivate(CommentPost::class))
->checker(Listener\UnapproveNewContent::markUnapprovedContentAsPrivate(...)),
->checker([Listener\UnapproveNewContent::class, 'markUnapprovedContentAsPrivate']),
];

View File

@@ -1,2 +1,2 @@
(()=>{var r={n:e=>{var s=e&&e.__esModule?()=>e.default:()=>e;return r.d(s,{a:s}),s},d:(e,s)=>{for(var a in s)r.o(s,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:s[a]})},o:(r,e)=>Object.prototype.hasOwnProperty.call(r,e),r:r=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(r,"__esModule",{value:!0})}},e={};(()=>{"use strict";r.r(e);const s=flarum.reg.get("core","common/extend"),a=flarum.reg.get("core","admin/app");var o=r.n(a);o().initializers.add("flarum-approval",(()=>{(0,s.extend)(o(),"getRequiredPermissions",(function(r,e){"discussion.startWithoutApproval"===e&&r.push("startDiscussion"),"discussion.replyWithoutApproval"===e&&r.push("discussion.reply")})),o().extensionData.for("flarum-approval").registerPermission({icon:"fas fa-check",label:o().translator.trans("flarum-approval.admin.permissions.start_discussions_without_approval_label"),permission:"discussion.startWithoutApproval"},"start",95).registerPermission({icon:"fas fa-check",label:o().translator.trans("flarum-approval.admin.permissions.reply_without_approval_label"),permission:"discussion.replyWithoutApproval"},"reply",95).registerPermission({icon:"fas fa-check",label:o().translator.trans("flarum-approval.admin.permissions.approve_posts_label"),permission:"discussion.approvePosts"},"moderate",65)}))})(),module.exports=e})();
(()=>{var r={n:s=>{var e=s&&s.__esModule?()=>s.default:()=>s;return r.d(e,{a:e}),e},d:(s,e)=>{for(var a in e)r.o(e,a)&&!r.o(s,a)&&Object.defineProperty(s,a,{enumerable:!0,get:e[a]})},o:(r,s)=>Object.prototype.hasOwnProperty.call(r,s),r:r=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(r,"__esModule",{value:!0})}},s={};(()=>{"use strict";r.r(s);const e=flarum.core.compat["common/extend"],a=flarum.core.compat["admin/app"];var o=r.n(a);o().initializers.add("flarum-approval",(function(){(0,e.extend)(o(),"getRequiredPermissions",(function(r,s){"discussion.startWithoutApproval"===s&&r.push("startDiscussion"),"discussion.replyWithoutApproval"===s&&r.push("discussion.reply")})),o().extensionData.for("flarum-approval").registerPermission({icon:"fas fa-check",label:o().translator.trans("flarum-approval.admin.permissions.start_discussions_without_approval_label"),permission:"discussion.startWithoutApproval"},"start",95).registerPermission({icon:"fas fa-check",label:o().translator.trans("flarum-approval.admin.permissions.reply_without_approval_label"),permission:"discussion.replyWithoutApproval"},"reply",95).registerPermission({icon:"fas fa-check",label:o().translator.trans("flarum-approval.admin.permissions.approve_posts_label"),permission:"discussion.approvePosts"},"moderate",65)}))})(),module.exports=s})();
//# sourceMappingURL=admin.js.map

View File

@@ -1 +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,EAAGH,IAC5BA,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,IAAIV,IAAI,OAAQ,iBCAtD,EAA+BS,OAAOC,IAAIV,IAAI,OAAQ,a,aCE5D,qBAAqB,mBAAmB,MACtC,IAAAW,QAAO,IAAK,0BAA0B,SAAUC,EAAUC,GACrC,oCAAfA,GACFD,EAASE,KAAK,mBAEG,oCAAfD,GACFD,EAASE,KAAK,mBAElB,IACA,sBAAsB,mBAAmBC,mBAAmB,CAC1DC,KAAM,eACNC,MAAO,qBAAqB,8EAC5BJ,WAAY,mCACX,QAAS,IAAIE,mBAAmB,CACjCC,KAAM,eACNC,MAAO,qBAAqB,kEAC5BJ,WAAY,mCACX,QAAS,IAAIE,mBAAmB,CACjCC,KAAM,eACNC,MAAO,qBAAqB,yDAC5BJ,WAAY,2BACX,WAAY,GAAG,G","sources":["webpack://@flarum/approval/webpack/bootstrap","webpack://@flarum/approval/webpack/runtime/compat get default export","webpack://@flarum/approval/webpack/runtime/define property getters","webpack://@flarum/approval/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/approval/webpack/runtime/make namespace object","webpack://@flarum/approval/external root \"flarum.reg.get('core', 'common/extend')\"","webpack://@flarum/approval/external root \"flarum.reg.get('core', 'admin/app')\"","webpack://@flarum/approval/./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.reg.get('core', 'common/extend');","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.reg.get('core', 'admin/app');","import { extend } from 'flarum/common/extend';\nimport app from 'flarum/admin/app';\napp.initializers.add('flarum-approval', () => {\n extend(app, 'getRequiredPermissions', function (required, permission) {\n if (permission === 'discussion.startWithoutApproval') {\n required.push('startDiscussion');\n }\n if (permission === 'discussion.replyWithoutApproval') {\n required.push('discussion.reply');\n }\n });\n app.extensionData.for('flarum-approval').registerPermission({\n icon: 'fas fa-check',\n label: app.translator.trans('flarum-approval.admin.permissions.start_discussions_without_approval_label'),\n permission: 'discussion.startWithoutApproval'\n }, 'start', 95).registerPermission({\n icon: 'fas fa-check',\n label: app.translator.trans('flarum-approval.admin.permissions.reply_without_approval_label'),\n permission: 'discussion.replyWithoutApproval'\n }, 'reply', 95).registerPermission({\n icon: 'fas fa-check',\n label: app.translator.trans('flarum-approval.admin.permissions.approve_posts_label'),\n permission: 'discussion.approvePosts'\n }, 'moderate', 65);\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","reg","extend","required","permission","push","registerPermission","icon","label"],"sourceRoot":""}
{"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,EAAGH,IAC5BA,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,iBCAlD,EAA+BF,OAAOC,KAAKC,OAAO,a,aCGxDC,IAAAA,aAAAA,IAAqB,mBAAmB,YACtCC,EAAAA,EAAAA,QAAOD,IAAK,0BAA0B,SAAUE,EAAUC,GACrC,oCAAfA,GACFD,EAASE,KAAK,mBAEG,oCAAfD,GACFD,EAASE,KAAK,mBAElB,IAEAJ,IAAAA,cAAAA,IACO,mBACJK,mBACC,CACEC,KAAM,eACNC,MAAOP,IAAAA,WAAAA,MAAqB,8EAC5BG,WAAY,mCAEd,QACA,IAEDE,mBACC,CACEC,KAAM,eACNC,MAAOP,IAAAA,WAAAA,MAAqB,kEAC5BG,WAAY,mCAEd,QACA,IAEDE,mBACC,CACEC,KAAM,eACNC,MAAOP,IAAAA,WAAAA,MAAqB,yDAC5BG,WAAY,2BAEd,WACA,GAEN,G","sources":["webpack://@flarum/approval/webpack/bootstrap","webpack://@flarum/approval/webpack/runtime/compat get default export","webpack://@flarum/approval/webpack/runtime/define property getters","webpack://@flarum/approval/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/approval/webpack/runtime/make namespace object","webpack://@flarum/approval/external root \"flarum.core.compat['common/extend']\"","webpack://@flarum/approval/external root \"flarum.core.compat['admin/app']\"","webpack://@flarum/approval/./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['common/extend'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['admin/app'];","import { extend } from 'flarum/common/extend';\nimport app from 'flarum/admin/app';\n\napp.initializers.add('flarum-approval', () => {\n extend(app, 'getRequiredPermissions', function (required, permission) {\n if (permission === 'discussion.startWithoutApproval') {\n required.push('startDiscussion');\n }\n if (permission === 'discussion.replyWithoutApproval') {\n required.push('discussion.reply');\n }\n });\n\n app.extensionData\n .for('flarum-approval')\n .registerPermission(\n {\n icon: 'fas fa-check',\n label: app.translator.trans('flarum-approval.admin.permissions.start_discussions_without_approval_label'),\n permission: 'discussion.startWithoutApproval',\n },\n 'start',\n 95\n )\n .registerPermission(\n {\n icon: 'fas fa-check',\n label: app.translator.trans('flarum-approval.admin.permissions.reply_without_approval_label'),\n permission: 'discussion.replyWithoutApproval',\n },\n 'reply',\n 95\n )\n .registerPermission(\n {\n icon: 'fas fa-check',\n label: app.translator.trans('flarum-approval.admin.permissions.approve_posts_label'),\n permission: 'discussion.approvePosts',\n },\n 'moderate',\n 65\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","extend","required","permission","push","registerPermission","icon","label"],"sourceRoot":""}

View File

@@ -1,2 +1,2 @@
(()=>{var o={n:t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},d:(t,e)=>{for(var r in e)o.o(e,r)&&!o.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},o:(o,t)=>Object.prototype.hasOwnProperty.call(o,t),r:o=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(o,"__esModule",{value:!0})}},t={};(()=>{"use strict";o.r(t);const e=flarum.reg.get("core","common/extend"),r=flarum.reg.get("core","forum/app");var a=o.n(r);const n=flarum.reg.get("core","common/models/Discussion");var p=o.n(n);const s=flarum.reg.get("core","common/models/Post");var i=o.n(s);const c=flarum.reg.get("core","common/components/Badge");var u=o.n(c);const l=flarum.reg.get("core","forum/components/DiscussionListItem");var d=o.n(l);const v=flarum.reg.get("core","forum/components/Post");var f=o.n(v);const g=flarum.reg.get("core","forum/components/CommentPost");var A=o.n(g);const y=flarum.reg.get("core","common/components/Button");var b=o.n(y);const h=flarum.reg.get("core","forum/utils/PostControls");var _=o.n(h);a().initializers.add("flarum-approval",(()=>{p().prototype.isApproved=p().attribute("isApproved"),(0,e.extend)(p().prototype,"badges",(function(o){this.isApproved()||o.has("hidden")||o.add("awaitingApproval",m(u(),{type:"awaitingApproval",icon:"fas fa-gavel",label:a().translator.trans("flarum-approval.forum.badge.awaiting_approval_tooltip")}))})),i().prototype.isApproved=i().attribute("isApproved"),i().prototype.canApprove=i().attribute("canApprove"),(0,e.extend)(d().prototype,"elementAttrs",(function(o){this.attrs.discussion.isApproved()||(o.className+=" DiscussionListItem--unapproved")})),(0,e.extend)(f().prototype,"elementAttrs",(function(o){this.attrs.post.isApproved()||(o.className+=" Post--unapproved")})),(0,e.extend)(A().prototype,"headerItems",(function(o){this.attrs.post.isApproved()||this.attrs.post.isHidden()||o.add("unapproved",a().translator.trans("flarum-approval.forum.post.awaiting_approval_text"))})),(0,e.override)(f().prototype,"flagReason",(function(o,t){return"approval"===t.type()?a().translator.trans("flarum-approval.forum.post.awaiting_approval_text"):o(t)})),(0,e.extend)(_(),"destructiveControls",(function(o,t){!t.isApproved()&&t.canApprove()&&o.add("approve",m(b(),{icon:"fas fa-check",onclick:_().approveAction.bind(t)},a().translator.trans("flarum-approval.forum.post_controls.approve_button")),10)})),_().approveAction=function(){this.save({isApproved:!0}),1===this.number()&&this.discussion().pushAttributes({isApproved:!0})}}),-10)})(),module.exports=t})();
(()=>{var o={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return o.d(r,{a:r}),r},d:(t,r)=>{for(var e in r)o.o(r,e)&&!o.o(t,e)&&Object.defineProperty(t,e,{enumerable:!0,get:r[e]})},o:(o,t)=>Object.prototype.hasOwnProperty.call(o,t),r:o=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(o,"__esModule",{value:!0})}},t={};(()=>{"use strict";o.r(t);const r=flarum.core.compat["common/extend"],e=flarum.core.compat["forum/app"];var a=o.n(e);const p=flarum.core.compat["common/models/Discussion"];var n=o.n(p);const s=flarum.core.compat["common/models/Post"];var i=o.n(s);const c=flarum.core.compat["common/components/Badge"];var u=o.n(c);const l=flarum.core.compat["forum/components/DiscussionListItem"];var d=o.n(l);const v=flarum.core.compat["forum/components/Post"];var f=o.n(v);const A=flarum.core.compat["forum/components/CommentPost"];var y=o.n(A);const b=flarum.core.compat["common/components/Button"];var g=o.n(b);const h=flarum.core.compat["forum/utils/PostControls"];var _=o.n(h);a().initializers.add("flarum-approval",(function(){n().prototype.isApproved=n().attribute("isApproved"),(0,r.extend)(n().prototype,"badges",(function(o){this.isApproved()||o.has("hidden")||o.add("awaitingApproval",m(u(),{type:"awaitingApproval",icon:"fas fa-gavel",label:a().translator.trans("flarum-approval.forum.badge.awaiting_approval_tooltip")}))})),i().prototype.isApproved=i().attribute("isApproved"),i().prototype.canApprove=i().attribute("canApprove"),(0,r.extend)(d().prototype,"elementAttrs",(function(o){this.attrs.discussion.isApproved()||(o.className+=" DiscussionListItem--unapproved")})),(0,r.extend)(f().prototype,"elementAttrs",(function(o){this.attrs.post.isApproved()||(o.className+=" Post--unapproved")})),(0,r.extend)(y().prototype,"headerItems",(function(o){this.attrs.post.isApproved()||this.attrs.post.isHidden()||o.add("unapproved",a().translator.trans("flarum-approval.forum.post.awaiting_approval_text"))})),(0,r.override)(f().prototype,"flagReason",(function(o,t){return"approval"===t.type()?a().translator.trans("flarum-approval.forum.post.awaiting_approval_text"):o(t)})),(0,r.extend)(_(),"destructiveControls",(function(o,t){!t.isApproved()&&t.canApprove()&&o.add("approve",m(g(),{icon:"fas fa-check",onclick:_().approveAction.bind(t)},a().translator.trans("flarum-approval.forum.post_controls.approve_button")),10)})),_().approveAction=function(){this.save({isApproved:!0}),1===this.number()&&this.discussion().pushAttributes({isApproved:!0})}}),-10)})(),module.exports=t})();
//# sourceMappingURL=forum.js.map

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,11 @@ use Illuminate\Database\Eloquent\Builder;
class ScopePrivateDiscussionVisibility
{
public function __invoke(User $actor, Builder $query): void
/**
* @param Builder $query
* @param User $actor
*/
public function __invoke(User $actor, Builder $query)
{
// All statements need to be wrapped in an orWhere, since we're adding a
// subset of private discussions that should be visible, not restricting the visible

View File

@@ -16,7 +16,11 @@ use Illuminate\Database\Eloquent\Builder;
class ScopePrivatePostVisibility
{
public function __invoke(User $actor, Builder $query): void
/**
* @param Builder $query
* @param User $actor
*/
public function __invoke(User $actor, Builder $query)
{
// All statements need to be wrapped in an orWhere, since we're adding a
// subset of private posts that should be visible, not restricting the visible

View File

@@ -15,7 +15,10 @@ use Flarum\User\User;
class TagPolicy extends AbstractPolicy
{
public function addToDiscussion(User $actor, Tag $tag): bool
/**
* @return bool|null
*/
public function addToDiscussion(User $actor, Tag $tag)
{
return $actor->can('discussion.startWithoutApproval', $tag);
}

View File

@@ -1,29 +0,0 @@
<?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\Approval\Api;
use Flarum\Api\Context;
use Flarum\Api\Schema;
use Flarum\Post\Post;
class PostResourceFields
{
public function __invoke(): array
{
return [
Schema\Boolean::make('isApproved')
->writable(fn (Post $post, Context $context) => $context->getActor()->can('approve', $post))
// set by the ApproveContent listener.
->set(fn () => null),
Schema\Boolean::make('canApprove')
->get(fn (Post $post, Context $context) => $context->getActor()->can('approvePosts', $post->discussion)),
];
}
}

View File

@@ -14,9 +14,25 @@ use Flarum\User\User;
class PostWasApproved
{
public function __construct(
public Post $post,
public User $actor
) {
/**
* The post that was approved.
*
* @var Post
*/
public $post;
/**
* @var User
*/
public $actor;
/**
* @param Post $post
* @param User $actor
*/
public function __construct(Post $post, User $actor)
{
$this->post = $post;
$this->actor = $actor;
}
}

View File

@@ -16,15 +16,18 @@ use Illuminate\Contracts\Events\Dispatcher;
class ApproveContent
{
public function subscribe(Dispatcher $events): void
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(Saving::class, $this->approvePost(...));
$events->listen(Saving::class, [$this, 'approvePost']);
}
/**
* @throws PermissionDeniedException
* @throws PermissionDeniedException
*/
public function approvePost(Saving $event): void
public function approvePost(Saving $event)
{
$attributes = $event->data['attributes'];
$post = $event->post;

View File

@@ -18,12 +18,18 @@ use Illuminate\Contracts\Events\Dispatcher;
class UnapproveNewContent
{
public function subscribe(Dispatcher $events): void
/**
* @param Dispatcher $events
*/
public function subscribe(Dispatcher $events)
{
$events->listen(Saving::class, $this->unapproveNewPosts(...));
$events->listen(Saving::class, [$this, 'unapproveNewPosts']);
}
public function unapproveNewPosts(Saving $event): void
/**
* @param Saving $event
*/
public function unapproveNewPosts(Saving $event)
{
$post = $event->post;
@@ -57,12 +63,14 @@ class UnapproveNewContent
}
}
public static function markUnapprovedContentAsPrivate(Discussion|CommentPost $instance): ?bool
/**
* @param Discussion|CommentPost $instance
* @return bool|null
*/
public static function markUnapprovedContentAsPrivate($instance)
{
if (! $instance->is_approved) {
return true;
}
return null;
}
}

View File

@@ -13,7 +13,7 @@ use Flarum\Approval\Event\PostWasApproved;
class UpdateDiscussionAfterPostApproval
{
public function handle(PostWasApproved $event): void
public function handle(PostWasApproved $event)
{
$post = $event->post;
$discussion = $post->discussion;

View File

@@ -10,23 +10,19 @@
namespace Flarum\Approval\Tests\integration;
use Carbon\Carbon;
use Flarum\Discussion\Discussion;
use Flarum\Group\Group;
use Flarum\Post\Post;
use Flarum\User\User;
trait InteractsWithUnapprovedContent
{
protected function prepareUnapprovedDatabaseContent()
{
$this->prepareDatabase([
User::class => [
'users' => [
['id' => 1, 'username' => 'Muralf', 'email' => 'muralf@machine.local', 'is_email_confirmed' => 1],
$this->normalUser(),
['id' => 3, 'username' => 'acme', 'email' => 'acme@machine.local', 'is_email_confirmed' => 1],
['id' => 4, 'username' => 'luceos', 'email' => 'luceos@machine.local', 'is_email_confirmed' => 1],
],
Discussion::class => [
'discussions' => [
['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 1, 'comment_count' => 1, 'is_approved' => 1, 'is_private' => 0],
['id' => 2, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 2, 'comment_count' => 1, 'is_approved' => 0, 'is_private' => 1],
['id' => 3, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 3, 'comment_count' => 1, 'is_approved' => 0, 'is_private' => 1],
@@ -35,7 +31,7 @@ trait InteractsWithUnapprovedContent
['id' => 6, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 6, 'comment_count' => 1, 'is_approved' => 0, 'is_private' => 1],
['id' => 7, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 7, 'comment_count' => 1, 'is_approved' => 1, 'is_private' => 0],
],
Post::class => [
'posts' => [
['id' => 1, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 2, 'discussion_id' => 2, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 3, 'discussion_id' => 3, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
@@ -49,7 +45,7 @@ trait InteractsWithUnapprovedContent
['id' => 10, 'discussion_id' => 7, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 4],
['id' => 11, 'discussion_id' => 7, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 1, 'is_approved' => 0, 'number' => 5],
],
Group::class => [
'groups' => [
['id' => 4, 'name_singular' => 'Acme', 'name_plural' => 'Acme', 'is_hidden' => 0]
],
'group_user' => [

View File

@@ -1,123 +0,0 @@
<?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\Approval\Tests\integration\api;
use Carbon\Carbon;
use Flarum\Approval\Tests\integration\InteractsWithUnapprovedContent;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
class ApprovePostsTest extends TestCase
{
use RetrievesAuthorizedUsers;
use InteractsWithUnapprovedContent;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-approval');
$this->prepareDatabase([
'users' => [
['id' => 1, 'username' => 'Muralf', 'email' => 'muralf@machine.local', 'is_email_confirmed' => 1],
$this->normalUser(),
['id' => 3, 'username' => 'acme', 'email' => 'acme@machine.local', 'is_email_confirmed' => 1],
['id' => 4, 'username' => 'luceos', 'email' => 'luceos@machine.local', 'is_email_confirmed' => 1],
],
'discussions' => [
['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 1, 'comment_count' => 1, 'is_approved' => 1],
],
'posts' => [
['id' => 1, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'hidden_at' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 2, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'hidden_at' => 0, 'is_approved' => 1, 'number' => 2],
['id' => 3, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'hidden_at' => 0, 'is_approved' => 0, 'number' => 3],
['id' => 4, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'hidden_at' => Carbon::now(), 'is_approved' => 1, 'number' => 4],
['id' => 5, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'hidden_at' => 0, 'is_approved' => 0, 'number' => 5],
],
'groups' => [
['id' => 4, 'name_singular' => 'Acme', 'name_plural' => 'Acme', 'is_hidden' => 0],
['id' => 5, 'name_singular' => 'Acme', 'name_plural' => 'Acme', 'is_hidden' => 0],
],
'group_user' => [
['user_id' => 3, 'group_id' => 4],
],
'group_permission' => [
['group_id' => 4, 'permission' => 'discussion.approvePosts'],
]
]);
}
/**
* @test
*/
public function can_approve_unapproved_post()
{
$response = $this->send(
$this->request('PATCH', '/api/posts/3', [
'authenticatedAs' => 3,
'json' => [
'data' => [
'attributes' => [
'isApproved' => true
]
]
]
])
);
$this->assertEquals(200, $response->getStatusCode(), $response->getBody()->getContents());
$this->assertEquals(1, $this->database()->table('posts')->where('id', 3)->where('is_approved', 1)->count());
}
/**
* @test
*/
public function cannot_approve_post_without_permission()
{
$response = $this->send(
$this->request('PATCH', '/api/posts/3', [
'authenticatedAs' => 4,
'json' => [
'data' => [
'attributes' => [
'isApproved' => true
]
]
]
])
);
$this->assertEquals(403, $response->getStatusCode(), $response->getBody()->getContents());
$this->assertEquals(0, $this->database()->table('posts')->where('id', 3)->where('is_approved', 1)->count());
}
/**
* @test
*/
public function hiding_post_silently_approves_it()
{
$response = $this->send(
$this->request('PATCH', '/api/posts/5', [
'authenticatedAs' => 3,
'json' => [
'data' => [
'attributes' => [
'isHidden' => true
]
]
]
])
);
$this->assertEquals(200, $response->getStatusCode(), $response->getBody()->getContents());
$this->assertEquals(1, $this->database()->table('posts')->where('id', 5)->where('is_approved', 1)->count());
}
}

View File

@@ -1,153 +0,0 @@
<?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\Approval\Tests\integration\api;
use Carbon\Carbon;
use Flarum\Approval\Tests\integration\InteractsWithUnapprovedContent;
use Flarum\Group\Group;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
class CreatePostsTest extends TestCase
{
use RetrievesAuthorizedUsers;
use InteractsWithUnapprovedContent;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-flags', 'flarum-approval');
$this->prepareDatabase([
'users' => [
['id' => 1, 'username' => 'Muralf', 'email' => 'muralf@machine.local', 'is_email_confirmed' => 1],
$this->normalUser(),
['id' => 3, 'username' => 'acme', 'email' => 'acme@machine.local', 'is_email_confirmed' => 1],
['id' => 4, 'username' => 'luceos', 'email' => 'luceos@machine.local', 'is_email_confirmed' => 1],
],
'discussions' => [
['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 1, 'comment_count' => 1, 'is_approved' => 1],
['id' => 2, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 2, 'comment_count' => 1, 'is_approved' => 0],
['id' => 3, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 3, 'comment_count' => 1, 'is_approved' => 0],
],
'posts' => [
['id' => 1, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 2, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 2],
['id' => 3, 'discussion_id' => 1, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 3],
['id' => 4, 'discussion_id' => 2, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 5, 'discussion_id' => 2, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 2],
['id' => 6, 'discussion_id' => 2, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 3],
['id' => 7, 'discussion_id' => 3, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 8, 'discussion_id' => 3, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 2],
['id' => 9, 'discussion_id' => 3, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 0, 'number' => 3],
],
'groups' => [
['id' => 4, 'name_singular' => 'Acme', 'name_plural' => 'Acme', 'is_hidden' => 0],
['id' => 5, 'name_singular' => 'Acme', 'name_plural' => 'Acme', 'is_hidden' => 0],
],
'group_user' => [
['user_id' => 3, 'group_id' => 4],
['user_id' => 2, 'group_id' => 5],
],
'group_permission' => [
['group_id' => 4, 'permission' => 'discussion.startWithoutApproval'],
['group_id' => 5, 'permission' => 'discussion.replyWithoutApproval'],
]
]);
}
/**
* @dataProvider startDiscussionDataProvider
* @test
*/
public function can_start_discussion_without_approval_when_allowed(int $authenticatedAs, bool $allowed)
{
$this->database()->table('group_permission')->where('group_id', Group::MEMBER_ID)->where('permission', 'discussion.startWithoutApproval')->delete();
$response = $this->send(
$this->request('POST', '/api/discussions', [
'authenticatedAs' => $authenticatedAs,
'json' => [
'data' => [
'type' => 'discussions',
'attributes' => [
'title' => 'This is a new discussion',
'content' => 'This is a new discussion',
]
]
]
])
);
$body = $response->getBody()->getContents();
$json = json_decode($body, true);
$this->assertEquals(201, $response->getStatusCode(), $body);
$this->assertEquals($allowed ? 1 : 0, $this->database()->table('discussions')->where('id', $json['data']['id'])->value('is_approved'));
}
/**
* @dataProvider replyToDiscussionDataProvider
* @test
*/
public function can_reply_without_approval_when_allowed(?int $authenticatedAs, bool $allowed)
{
$this->database()->table('group_permission')->where('group_id', Group::MEMBER_ID)->where('permission', 'discussion.replyWithoutApproval')->delete();
$response = $this->send(
$this->request('POST', '/api/posts', [
'authenticatedAs' => $authenticatedAs,
'json' => [
'data' => [
'type' => 'posts',
'attributes' => [
'content' => 'This is a new reply',
],
'relationships' => [
'discussion' => [
'data' => [
'type' => 'discussions',
'id' => 1
]
]
]
]
]
])
);
$body = $response->getBody()->getContents();
$json = json_decode($body, true);
$this->assertEquals(201, $response->getStatusCode(), $body);
$this->assertEquals($allowed ? 1 : 0, $this->database()->table('posts')->where('id', $json['data']['id'])->value('is_approved'));
}
public static function startDiscussionDataProvider(): array
{
return [
'Admin' => [1, true],
'User without permission' => [2, false],
'Permission Given' => [3, true],
'Another user without permission' => [4, false],
];
}
public static function replyToDiscussionDataProvider(): array
{
return [
'Admin' => [1, true],
'User without permission' => [3, false],
'Permission Given' => [2, true],
'Another user without permission' => [4, false],
];
}
}

View File

@@ -7,6 +7,10 @@
* LICENSE file that was distributed with this source code.
*/
$setup = require __DIR__.'/../../../../php-packages/testing/bootstrap/monorepo.php';
use Flarum\Testing\integration\Setup\SetupScript;
require __DIR__.'/../../vendor/autoload.php';
$setup = new SetupScript();
$setup->run();

View File

@@ -10,7 +10,6 @@
convertWarningsToExceptions="true"
processIsolation="true"
stopOnFailure="false"
bootstrap="../../../php-packages/testing/bootstrap/monorepo.php"
>
<coverage processUncoveredFiles="true">
<include>

View File

@@ -10,7 +10,6 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="../../../php-packages/testing/bootstrap/monorepo.php"
>
<coverage processUncoveredFiles="true">
<include>

View File

@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^2.0"
"flarum/core": "^1.8"
},
"autoload": {
"psr-4": {
@@ -28,7 +28,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "2.x-dev"
"dev-main": "1.x-dev"
},
"flarum-extension": {
"title": "BBCode",

View File

@@ -13,7 +13,7 @@ use s9e\TextFormatter\Configurator;
class Configure
{
public function __invoke(Configurator $config): void
public function __invoke(Configurator $config)
{
$this->addTagsFromRepositories($config);
$this->adaptHighlightJs($config);

View File

@@ -9,14 +9,19 @@
namespace Flarum\BBCode;
use Flarum\Locale\TranslatorInterface;
use s9e\TextFormatter\Renderer;
use Symfony\Contracts\Translation\TranslatorInterface;
class Render
{
public function __construct(
protected TranslatorInterface $translator
) {
/**
* @var TranslatorInterface
*/
protected $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function __invoke(Renderer $renderer, $context, string $xml): string

View File

@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^2.0"
"flarum/core": "^1.8"
},
"autoload": {
"psr-4": {
@@ -28,7 +28,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "2.x-dev"
"dev-main": "1.x-dev"
},
"flarum-extension": {
"title": "Embed",

2
extensions/embed/js/dist/forum.js generated vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,8 @@ import { override, extend } from 'flarum/common/extend';
import app from 'flarum/forum/app';
import Stream from 'flarum/common/utils/Stream';
import ForumApplication from 'flarum/forum/ForumApplication';
import Composer from 'flarum/forum/components/Composer';
import PostStream from 'flarum/forum/components/PostStream';
import ModalManager from 'flarum/common/components/ModalManager';
import PostMeta from 'flarum/forum/components/PostMeta';
@@ -11,7 +13,7 @@ import DiscussionPage from 'flarum/forum/components/DiscussionPage';
extend(ForumApplication.prototype, 'mount', function () {
if (m.route.param('hideFirstPost')) {
extend('flarum/forum/components/PostStream', 'view', (vdom) => {
extend(PostStream.prototype, 'view', (vdom) => {
if (vdom.children[0].attrs['data-number'] === 1) {
vdom.children.splice(0, 1);
}
@@ -40,7 +42,7 @@ const reposition = function () {
};
extend(ModalManager.prototype, 'show', reposition);
extend('flarum/forum/components/Composer', 'show', reposition);
extend(Composer.prototype, 'show', reposition);
window.iFrameResizer = {
readyCallback: function () {
@@ -48,7 +50,7 @@ window.iFrameResizer = {
},
};
extend('flarum/forum/components/PostStream', 'goToNumber', function (promise, number) {
extend(PostStream.prototype, 'goToNumber', function (promise, number) {
if (number === 'reply' && 'parentIFrame' in window && app.composer.isFullScreen()) {
const itemTop = this.$('.PostStream-item:last').offset().top;
window.parentIFrame.scrollToOffset(0, itemTop);

View File

@@ -50,7 +50,7 @@
padding: 15px 15px;
.scrolled & {
box-shadow: 0 2px 6px var(--shadow-color);
.box-shadow(0 2px 6px @shadow-color);
}
}
@@ -69,7 +69,7 @@
margin: 0;
&, a {
color: var(--muted-color);
color: @muted-color;
}
}
}

View File

@@ -19,11 +19,11 @@
}
],
"require": {
"flarum/core": "^2.0"
"flarum/core": "^1.8"
},
"extra": {
"branch-alias": {
"dev-main": "2.x-dev"
"dev-main": "1.x-dev"
},
"flarum-extension": {
"title": "Emoji",

View File

@@ -13,8 +13,7 @@ use s9e\TextFormatter\Configurator;
return [
(new Extend\Frontend('forum'))
->js(__DIR__.'/js/dist/forum.js')
->css(__DIR__.'/less/forum.less')
->jsDirectory(__DIR__.'/js/dist/forum'),
->css(__DIR__.'/less/forum.less'),
(new Extend\Formatter)
->configure(function (Configurator $config) {

View File

@@ -0,0 +1,6 @@
{
"printWidth": 150,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}

2
extensions/emoji/js/dist/forum.js generated vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"forum/emojiMap.js","mappings":"kJACA,E","sources":["webpack://@flarum/emoji/./src/forum/emojiMap.ts"],"sourcesContent":["import emojiMap from 'simple-emoji-map';\nexport default emojiMap;"],"names":[],"sourceRoot":""}

View File

@@ -1,47 +1,44 @@
import emojiMap from 'simple-emoji-map';
import { extend } from 'flarum/common/extend';
import TextEditor from 'flarum/common/components/TextEditor';
import TextEditorButton from 'flarum/common/components/TextEditorButton';
import KeyboardNavigatable from 'flarum/common/utils/KeyboardNavigatable';
import Tooltip from 'flarum/common/components/Tooltip';
import AutocompleteReader from 'flarum/common/utils/AutocompleteReader';
import AutocompleteDropdown from './fragments/AutocompleteDropdown';
import getEmojiIconCode from './helpers/getEmojiIconCode';
import cdn from './cdn';
export default function addComposerAutocomplete() {
let emojiMap = null;
const emojiKeys = Object.keys(emojiMap);
const $container = $('<div class="ComposerBody-emojiDropdownContainer"></div>');
const dropdown = new AutocompleteDropdown();
extend('flarum/common/components/TextEditor', 'oninit', function () {
this._loaders.push(async () => await import('./emojiMap').then((m) => (emojiMap = m.default)));
// prettier-ignore
this.commonEmoji = [
'😀', '😁', '😂', '😃', '😄', '😅', '😆', '😇', '😈', '😉', '😊', '😋', '😌', '😍', '😎', '😏', '😐️', '😑', '😒',
'😓', '😔', '😕', '😖', '😗', '😘', '😙', '😚', '😛', '😜', '😝', '😞', '😟', '😠', '😡', '😢', '😣', '😤', '😥',
'😦', '😧', '😨', '😩', '😪', '😫', '😬', '😭', '😮', '😮‍💨', '😯', '😰', '😱', '😲', '😳', '😴', '😵', '😵‍💫',
'😶', '😶‍🌫️', '😷', '😸', '😹', '😺', '😻', '😼', '😽', '😾', '😿', '🙀', '🙁', '🙂', '🙃', '🙄',
];
});
extend('flarum/common/components/TextEditor', 'onbuild', function () {
this.emojiDropdown = new AutocompleteDropdown();
extend(TextEditor.prototype, 'oncreate', function () {
const $editor = this.$('.TextEditor-editor').wrap('<div class="ComposerBody-emojiWrapper"></div>');
this.navigator = new KeyboardNavigatable();
this.navigator
.when(() => this.emojiDropdown.active)
.onUp(() => this.emojiDropdown.navigate(-1))
.onDown(() => this.emojiDropdown.navigate(1))
.onSelect(this.emojiDropdown.complete.bind(this.emojiDropdown))
.onCancel(this.emojiDropdown.hide.bind(this.emojiDropdown))
.when(() => dropdown.active)
.onUp(() => dropdown.navigate(-1))
.onDown(() => dropdown.navigate(1))
.onSelect(dropdown.complete.bind(dropdown))
.onCancel(dropdown.hide.bind(dropdown))
.bindTo($editor);
$editor.after($('<div class="ComposerBody-emojiDropdownContainer"></div>'));
$editor.after($container);
});
extend('flarum/common/components/TextEditor', 'buildEditorParams', function (params) {
const emojiKeys = Object.keys(emojiMap);
extend(TextEditor.prototype, 'buildEditorParams', function (params) {
let relEmojiStart;
let absEmojiStart;
let typed;
const autocompleteReader = new AutocompleteReader(':');
const applySuggestion = (replacement) => {
this.attrs.composer.editor.replaceBeforeCursor(absEmojiStart - 1, replacement + ' ');
dropdown.hide();
};
params.inputListeners.push(() => {
const selection = this.attrs.composer.editor.getSelectionRange();
@@ -50,34 +47,42 @@ export default function addComposerAutocomplete() {
if (selection[1] - cursor > 0) return;
// Search backwards from the cursor for an ':' symbol. If we find
// one and followed by a whitespace, we will want to show the
// autocomplete dropdown!
const lastChunk = this.attrs.composer.editor.getLastNChars(15);
const autocompleting = autocompleteReader.check(lastChunk, cursor, /[a-z0-9]|\+|\-|_|\:/);
absEmojiStart = 0;
for (let i = lastChunk.length - 1; i >= 0; i--) {
const character = lastChunk.substr(i, 1);
// check what user typed, emoji names only contains alphanumeric,
// underline, '+' and '-'
if (!/[a-z0-9]|\+|\-|_|\:/.test(character)) break;
// make sure ':' preceded by a whitespace or newline
if (character === ':' && (i == 0 || /\s/.test(lastChunk.substr(i - 1, 1)))) {
relEmojiStart = i + 1;
absEmojiStart = cursor - lastChunk.length + i + 1;
break;
}
}
this.emojiDropdown.hide();
this.emojiDropdown.active = false;
dropdown.hide();
dropdown.active = false;
if (autocompleting) {
const typed = autocompleting.typed;
const emojiDropdown = this.emojiDropdown;
const applySuggestion = (replacement) => {
this.attrs.composer.editor.replaceBeforeCursor(autocompleting.absoluteStart - 1, replacement + ' ');
this.emojiDropdown.hide();
};
if (absEmojiStart) {
typed = lastChunk.substring(relEmojiStart).toLowerCase();
const makeSuggestion = function ({ emoji, name, code }) {
return (
<Tooltip text={name}>
<button
key={emoji}
onclick={() => applySuggestion(emoji)}
onmouseenter={function () {
emojiDropdown.setIndex($(this).parent().index() - 1);
}}
>
<img alt={emoji} className="emoji" draggable="false" loading="lazy" src={`${cdn}72x72/${code}.png`} title={name} />
</button>
</Tooltip>
<button
key={emoji}
onclick={() => applySuggestion(emoji)}
onmouseenter={function () {
dropdown.setIndex($(this).parent().index() - 1);
}}
>
<img alt={emoji} className="emoji" draggable="false" loading="lazy" src={`${cdn}72x72/${code}.png`} />
{name}
</button>
);
};
@@ -91,7 +96,7 @@ export default function addComposerAutocomplete() {
};
const regTyped = fuzzyRegexp(typed);
let maxSuggestions = 40;
let maxSuggestions = 7;
const findMatchingEmojis = (matcher) => {
for (let i = 0; i < emojiKeys.length && maxSuggestions > 0; i++) {
@@ -100,7 +105,7 @@ export default function addComposerAutocomplete() {
if (similarEmoji.indexOf(curEmoji) === -1) {
const names = emojiMap[curEmoji];
for (let name of names) {
if (matcher(name, curEmoji)) {
if (matcher(name)) {
--maxSuggestions;
similarEmoji.push(curEmoji);
break;
@@ -111,17 +116,10 @@ export default function addComposerAutocomplete() {
};
// First, try to find all emojis starting with the given string
findMatchingEmojis((emojiName, emoji) => {
// If no input is provided yet, match the most common emojis.
if (!typed) {
return this.commonEmoji?.includes(emoji);
}
return emojiName.indexOf(typed) === 0;
});
findMatchingEmojis((emoji) => emoji.indexOf(typed) === 0);
// If there are still suggestions left, try for some fuzzy matches
findMatchingEmojis((emojiName) => regTyped.test(emojiName));
findMatchingEmojis((emoji) => regTyped.test(emoji));
const suggestions = similarEmoji
.map((emoji) => ({
@@ -132,14 +130,14 @@ export default function addComposerAutocomplete() {
.map(makeSuggestion);
if (suggestions.length) {
this.emojiDropdown.items = suggestions;
m.render(this.$('.ComposerBody-emojiDropdownContainer')[0], this.emojiDropdown.render());
dropdown.items = suggestions;
m.render($container[0], dropdown.render());
this.emojiDropdown.show();
const coordinates = this.attrs.composer.editor.getCaretCoordinates(autocompleting.absoluteStart);
const width = this.emojiDropdown.$().outerWidth();
const height = this.emojiDropdown.$().outerHeight();
const parent = this.emojiDropdown.$().offsetParent();
dropdown.show();
const coordinates = this.attrs.composer.editor.getCaretCoordinates(absEmojiStart);
const width = dropdown.$().outerWidth();
const height = dropdown.$().outerHeight();
const parent = dropdown.$().offsetParent();
let left = coordinates.left;
let top = coordinates.top + 15;
@@ -155,20 +153,20 @@ export default function addComposerAutocomplete() {
top = Math.max(-(parent.offset().top - $(document).scrollTop()), top);
left = Math.max(-parent.offset().left, left);
this.emojiDropdown.show(left, top);
dropdown.show(left, top);
}
};
buildSuggestions();
this.emojiDropdown.setIndex(0);
this.emojiDropdown.$().scrollTop(0);
this.emojiDropdown.active = true;
dropdown.setIndex(0);
dropdown.$().scrollTop(0);
dropdown.active = true;
}
});
});
extend('flarum/common/components/TextEditor', 'toolbarItems', function (items) {
extend(TextEditor.prototype, 'toolbarItems', function (items) {
items.add(
'emoji',
<TextEditorButton onclick={() => this.attrs.composer.editor.insertAtCursor(' :')} icon="far fa-smile">

View File

@@ -1,3 +0,0 @@
import emojiMap from 'simple-emoji-map';
export default emojiMap;

View File

@@ -7,28 +7,29 @@ img.emoji {
.EmojiDropdown {
max-width: 500px;
max-height: 200px;
overflow: visible;
overflow: auto;
position: absolute;
padding: 8px;
margin: 5px 0 !important;
> li {
display: inline-block;
> li > button {
color: @text-color;
font-weight: bold;
padding-top: 6px;
padding-bottom: 6px;
padding-left: 45px;
> button {
color: var(--text-color);
font-weight: bold;
padding: 8px;
border-radius: var(--border-radius);
.emoji {
float: left;
margin-left: -30px;
}
}
> .Dropdown-header {
display: block;
color: var(--muted-more-color);
.Dropdown-header {
color: @muted-more-color;
text-transform: none;
font-weight: normal;
padding: 4px 8px;
margin: 0 0 4px 0;
padding-bottom: 5px;
font-size: 11px;
}
}

View File

@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^2.0"
"flarum/core": "^1.8"
},
"autoload": {
"psr-4": {
@@ -28,7 +28,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "2.x-dev"
"dev-main": "1.x-dev"
},
"flarum-extension": {
"title": "Flags",
@@ -75,7 +75,7 @@
"require-dev": {
"flarum/core": "*@dev",
"flarum/tags": "*@dev",
"flarum/testing": "^2.0"
"flarum/testing": "^1.0.0"
},
"repositories": [
{

View File

@@ -7,17 +7,25 @@
* LICENSE file that was distributed with this source code.
*/
use Flarum\Api\Endpoint;
use Flarum\Api\Resource;
use Flarum\Api\Controller\AbstractSerializeController;
use Flarum\Api\Controller\ListPostsController;
use Flarum\Api\Controller\ShowDiscussionController;
use Flarum\Api\Controller\ShowPostController;
use Flarum\Api\Serializer\CurrentUserSerializer;
use Flarum\Api\Serializer\ForumSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Extend;
use Flarum\Flags\Access\ScopeFlagVisibility;
use Flarum\Flags\AddCanFlagAttribute;
use Flarum\Flags\AddFlagsApiAttributes;
use Flarum\Flags\AddNewFlagCountAttribute;
use Flarum\Flags\Api\Controller\CreateFlagController;
use Flarum\Flags\Api\Controller\DeleteFlagsController;
use Flarum\Flags\Api\ForumResourceFields;
use Flarum\Flags\Api\PostResourceFields;
use Flarum\Flags\Api\Resource\FlagResource;
use Flarum\Flags\Api\UserResourceFields;
use Flarum\Flags\Api\Controller\ListFlagsController;
use Flarum\Flags\Api\Serializer\FlagSerializer;
use Flarum\Flags\Flag;
use Flarum\Flags\Listener;
use Flarum\Flags\PrepareFlagsApiData;
use Flarum\Forum\Content\AssertRegistered;
use Flarum\Post\Event\Deleted;
use Flarum\Post\Post;
@@ -33,6 +41,8 @@ return [
->js(__DIR__.'/js/dist/admin.js'),
(new Extend\Routes('api'))
->get('/flags', 'flags.index', ListFlagsController::class)
->post('/flags', 'flags.create', CreateFlagController::class)
->delete('/posts/{id}/flags', 'flags.delete', DeleteFlagsController::class),
(new Extend\Model(User::class))
@@ -41,26 +51,27 @@ return [
(new Extend\Model(Post::class))
->hasMany('flags', Flag::class, 'post_id'),
new Extend\ApiResource(FlagResource::class),
(new Extend\ApiSerializer(PostSerializer::class))
->hasMany('flags', FlagSerializer::class)
->attribute('canFlag', AddCanFlagAttribute::class),
(new Extend\ApiResource(Resource\PostResource::class))
->fields(PostResourceFields::class),
(new Extend\ApiSerializer(CurrentUserSerializer::class))
->attribute('newFlagCount', AddNewFlagCountAttribute::class),
(new Extend\ApiResource(Resource\UserResource::class))
->fields(UserResourceFields::class),
(new Extend\ApiSerializer(ForumSerializer::class))
->attributes(AddFlagsApiAttributes::class),
(new Extend\ApiResource(Resource\ForumResource::class))
->fields(ForumResourceFields::class),
(new Extend\ApiController(ShowDiscussionController::class))
->addInclude(['posts.flags', 'posts.flags.user']),
(new Extend\ApiResource(Resource\DiscussionResource::class))
->endpoint(Endpoint\Show::class, function (Endpoint\Show $endpoint) {
return $endpoint->addDefaultInclude(['posts.flags', 'posts.flags.user']);
}),
(new Extend\ApiController(ListPostsController::class))
->addInclude(['flags', 'flags.user']),
(new Extend\ApiResource(Resource\PostResource::class))
->endpoint([Endpoint\Index::class, Endpoint\Show::class], function (Endpoint\Index|Endpoint\Show $endpoint) {
return $endpoint->addDefaultInclude(['flags', 'flags.user']);
}),
(new Extend\ApiController(ShowPostController::class))
->addInclude(['flags', 'flags.user']),
(new Extend\ApiController(AbstractSerializeController::class))
->prepareDataForSerialization(PrepareFlagsApiData::class),
(new Extend\Settings())
->serializeToForum('guidelinesUrl', 'flarum-flags.guidelines_url'),

19
extensions/flags/js/dist-typings/forum/compat.d.ts generated vendored Normal file
View File

@@ -0,0 +1,19 @@
declare const _default: {
'flags/addFlagsToPosts': typeof addFlagsToPosts;
'flags/addFlagControl': typeof addFlagControl;
'flags/addFlagsDropdown': typeof addFlagsDropdown;
'flags/models/Flag': typeof Flag;
'flags/components/FlagList': typeof FlagList;
'flags/components/FlagPostModal': typeof FlagPostModal;
'flags/components/FlagsPage': typeof FlagsPage;
'flags/components/FlagsDropdown': typeof FlagsDropdown;
};
export default _default;
import addFlagsToPosts from "./addFlagsToPosts";
import addFlagControl from "./addFlagControl";
import addFlagsDropdown from "./addFlagsDropdown";
import Flag from "./models/Flag";
import FlagList from "./components/FlagList";
import FlagPostModal from "./components/FlagPostModal";
import FlagsPage from "./components/FlagsPage";
import FlagsDropdown from "./components/FlagsDropdown";

View File

@@ -1,12 +1,7 @@
import Component from 'flarum/common/Component';
import type { ComponentAttrs } from 'flarum/common/Component';
import type Mithril from 'mithril';
import type FlagListState from '../states/FlagListState';
export interface IFlagListAttrs extends ComponentAttrs {
state: FlagListState;
}
export default class FlagList<CustomAttrs extends IFlagListAttrs = IFlagListAttrs> extends Component<CustomAttrs, FlagListState> {
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
export default class FlagList extends Component<import("flarum/common/Component").ComponentAttrs, undefined> {
constructor();
oninit(vnode: any): void;
state: any;
view(): JSX.Element;
content(state: FlagListState): JSX.Element[][] | null;
}
import Component from "flarum/common/Component";

View File

@@ -1,5 +1,5 @@
/// <reference types="flarum/@types/translator-icu-rich" />
export default class FlagPostModal extends FormModal<import("flarum/common/components/FormModal").IFormModalAttrs, undefined> {
export default class FlagPostModal extends Modal<import("flarum/common/components/Modal").IInternalModalAttrs, undefined> {
constructor();
oninit(vnode: any): void;
success: boolean | undefined;
@@ -10,6 +10,6 @@ export default class FlagPostModal extends FormModal<import("flarum/common/compo
flagReasons(): ItemList<any>;
onsubmit(e: any): void;
}
import FormModal from "flarum/common/components/FormModal";
import Modal from "flarum/common/components/Modal";
import Stream from "flarum/common/utils/Stream";
import ItemList from "flarum/common/utils/ItemList";

View File

@@ -1,12 +1,7 @@
/// <reference types="mithril" />
import HeaderDropdown from 'flarum/forum/components/HeaderDropdown';
import type { IHeaderDropdownAttrs } from 'flarum/forum/components/HeaderDropdown';
export interface IFlagsDropdownAttrs extends IHeaderDropdownAttrs {
}
export default class FlagsDropdown<CustomAttrs extends IFlagsDropdownAttrs = IFlagsDropdownAttrs> extends HeaderDropdown<CustomAttrs> {
static initAttrs(attrs: IFlagsDropdownAttrs): void;
getContent(): JSX.Element;
goToRoute(): void;
getUnreadCount(): number;
getNewCount(): number;
export default class FlagsDropdown extends NotificationsDropdown<import("flarum/common/components/Dropdown").IDropdownAttrs> {
static initAttrs(attrs: any): void;
constructor();
getUnreadCount(): any;
getNewCount(): unknown;
}
import NotificationsDropdown from "flarum/forum/components/NotificationsDropdown";

View File

@@ -2,9 +2,8 @@
* The `FlagsPage` component shows the flags list. It is only
* used on mobile devices where the flags dropdown is within the drawer.
*/
export default class FlagsPage extends Page<import("flarum/common/components/Page").IPageAttrs, undefined> {
constructor();
export default class FlagsPage {
oninit(vnode: any): void;
bodyClass: string | undefined;
view(): JSX.Element;
}
import Page from "flarum/common/components/Page";

View File

@@ -1,8 +0,0 @@
import './addFlagsToPosts';
import './addFlagControl';
import './addFlagsDropdown';
import './models/Flag';
import './components/FlagList';
import './components/FlagPostModal';
import './components/FlagsPage';
import './components/FlagsDropdown';

View File

@@ -1,2 +1 @@
export { default as extend } from './extend';
import './forum';

View File

@@ -1,13 +1,16 @@
import type ForumApplication from 'flarum/forum/ForumApplication';
import type Flag from '../models/Flag';
import PaginatedListState from 'flarum/common/states/PaginatedListState';
export default class FlagListState extends PaginatedListState<Flag> {
app: ForumApplication;
constructor(app: ForumApplication);
get type(): string;
export default class FlagListState {
constructor(app: any);
app: any;
/**
* Whether or not the flags are loading.
*
* @type {Boolean}
*/
loading: boolean;
/**
* Load flags into the application's cache if they haven't already
* been loaded.
*/
load(): Promise<void>;
load(): void;
cache: any;
}

2
extensions/flags/js/dist/admin.js generated vendored
View File

@@ -1,2 +1,2 @@
(()=>{var e={n:a=>{var s=a&&a.__esModule?()=>a.default:()=>a;return e.d(s,{a:s}),s},d:(a,s)=>{for(var r in s)e.o(s,r)&&!e.o(a,r)&&Object.defineProperty(a,r,{enumerable:!0,get:s[r]})},o:(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},a={};(()=>{"use strict";e.r(a);const s=flarum.reg.get("core","admin/app");var r=e.n(s);r().initializers.add("flarum-flags",(()=>{r().extensionData.for("flarum-flags").registerSetting({setting:"flarum-flags.guidelines_url",type:"text",label:r().translator.trans("flarum-flags.admin.settings.guidelines_url_label")},15).registerSetting({setting:"flarum-flags.can_flag_own",type:"boolean",label:r().translator.trans("flarum-flags.admin.settings.flag_own_posts_label")}).registerPermission({icon:"fas fa-flag",label:r().translator.trans("flarum-flags.admin.permissions.view_flags_label"),permission:"discussion.viewFlags"},"moderate",65).registerPermission({icon:"fas fa-flag",label:r().translator.trans("flarum-flags.admin.permissions.flag_posts_label"),permission:"discussion.flagPosts"},"reply",65)}))})(),module.exports=a})();
(()=>{var e={n:a=>{var s=a&&a.__esModule?()=>a.default:()=>a;return e.d(s,{a:s}),s},d:(a,s)=>{for(var l in s)e.o(s,l)&&!e.o(a,l)&&Object.defineProperty(a,l,{enumerable:!0,get:s[l]})},o:(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},a={};(()=>{"use strict";e.r(a);const s=flarum.core.compat["admin/app"];var l=e.n(s);l().initializers.add("flarum-flags",(function(){l().extensionData.for("flarum-flags").registerSetting({setting:"flarum-flags.guidelines_url",type:"text",label:l().translator.trans("flarum-flags.admin.settings.guidelines_url_label")},15).registerSetting({setting:"flarum-flags.can_flag_own",type:"boolean",label:l().translator.trans("flarum-flags.admin.settings.flag_own_posts_label")}).registerPermission({icon:"fas fa-flag",label:l().translator.trans("flarum-flags.admin.permissions.view_flags_label"),permission:"discussion.viewFlags"},"moderate",65).registerPermission({icon:"fas fa-flag",label:l().translator.trans("flarum-flags.admin.permissions.flag_posts_label"),permission:"discussion.flagPosts"},"reply",65)}))})(),module.exports=a})();
//# sourceMappingURL=admin.js.map

View File

@@ -1 +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,EAAGH,IAC5BA,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,IAAIV,IAAI,OAAQ,a,aCC5D,qBAAqB,gBAAgB,KACnC,sBAAsB,gBAAgBW,gBAAgB,CACpDC,QAAS,8BACTC,KAAM,OACNC,MAAO,qBAAqB,qDAC3B,IAAIH,gBAAgB,CACrBC,QAAS,4BACTC,KAAM,UACNC,MAAO,qBAAqB,sDAC3BC,mBAAmB,CACpBC,KAAM,cACNF,MAAO,qBAAqB,mDAC5BG,WAAY,wBACX,WAAY,IAAIF,mBAAmB,CACpCC,KAAM,cACNF,MAAO,qBAAqB,mDAC5BG,WAAY,wBACX,QAAS,GAAG,G","sources":["webpack://@flarum/flags/webpack/bootstrap","webpack://@flarum/flags/webpack/runtime/compat get default export","webpack://@flarum/flags/webpack/runtime/define property getters","webpack://@flarum/flags/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/flags/webpack/runtime/make namespace object","webpack://@flarum/flags/external root \"flarum.reg.get('core', 'admin/app')\"","webpack://@flarum/flags/./src/admin/index.ts"],"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.reg.get('core', 'admin/app');","import app from 'flarum/admin/app';\napp.initializers.add('flarum-flags', () => {\n app.extensionData.for('flarum-flags').registerSetting({\n setting: 'flarum-flags.guidelines_url',\n type: 'text',\n label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label')\n }, 15).registerSetting({\n setting: 'flarum-flags.can_flag_own',\n type: 'boolean',\n label: app.translator.trans('flarum-flags.admin.settings.flag_own_posts_label')\n }).registerPermission({\n icon: 'fas fa-flag',\n label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'),\n permission: 'discussion.viewFlags'\n }, 'moderate', 65).registerPermission({\n icon: 'fas fa-flag',\n label: app.translator.trans('flarum-flags.admin.permissions.flag_posts_label'),\n permission: 'discussion.flagPosts'\n }, 'reply', 65);\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","reg","registerSetting","setting","type","label","registerPermission","icon","permission"],"sourceRoot":""}
{"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,EAAGH,IAC5BA,CAAM,ECLdF,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,IAE1E,ECNDR,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,GAAO,G,+BCL9D,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,a,aCExDC,IAAAA,aAAAA,IAAqB,gBAAgB,WACnCA,IAAAA,cAAAA,IACO,gBACJC,gBACC,CACEC,QAAS,8BACTC,KAAM,OACNC,MAAOJ,IAAAA,WAAAA,MAAqB,qDAE9B,IAEDC,gBAAgB,CACfC,QAAS,4BACTC,KAAM,UACNC,MAAOJ,IAAAA,WAAAA,MAAqB,sDAE7BK,mBACC,CACEC,KAAM,cACNF,MAAOJ,IAAAA,WAAAA,MAAqB,mDAC5BO,WAAY,wBAEd,WACA,IAGDF,mBACC,CACEC,KAAM,cACNF,MAAOJ,IAAAA,WAAAA,MAAqB,mDAC5BO,WAAY,wBAEd,QACA,GAEN,G","sources":["webpack://@flarum/flags/webpack/bootstrap","webpack://@flarum/flags/webpack/runtime/compat get default export","webpack://@flarum/flags/webpack/runtime/define property getters","webpack://@flarum/flags/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/flags/webpack/runtime/make namespace object","webpack://@flarum/flags/external root \"flarum.core.compat['admin/app']\"","webpack://@flarum/flags/./src/admin/index.ts"],"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['admin/app'];","import app from 'flarum/admin/app';\n\napp.initializers.add('flarum-flags', () => {\n app.extensionData\n .for('flarum-flags')\n .registerSetting(\n {\n setting: 'flarum-flags.guidelines_url',\n type: 'text',\n label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label'),\n },\n 15\n )\n .registerSetting({\n setting: 'flarum-flags.can_flag_own',\n type: 'boolean',\n label: app.translator.trans('flarum-flags.admin.settings.flag_own_posts_label'),\n })\n .registerPermission(\n {\n icon: 'fas fa-flag',\n label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'),\n permission: 'discussion.viewFlags',\n },\n 'moderate',\n 65\n )\n\n .registerPermission(\n {\n icon: 'fas fa-flag',\n label: app.translator.trans('flarum-flags.admin.permissions.flag_posts_label'),\n permission: 'discussion.flagPosts',\n },\n 'reply',\n 65\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","registerSetting","setting","type","label","registerPermission","icon","permission"],"sourceRoot":""}

2
extensions/flags/js/dist/forum.js generated vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
import { extend, override } from 'flarum/common/extend';
import { extend } from 'flarum/common/extend';
import app from 'flarum/forum/app';
import Post from 'flarum/forum/components/Post';
import Button from 'flarum/common/components/Button';
@@ -54,7 +54,7 @@ export default function () {
const controls = PostControls.destructiveControls(this.attrs.post);
Object.keys(controls.toObject()).forEach((k) => {
Object.keys(controls.items).forEach((k) => {
const attrs = controls.get(k).attrs;
attrs.className = 'Button';
@@ -75,7 +75,7 @@ export default function () {
return items;
};
override(Post.prototype, 'header', function (vdom) {
extend(Post.prototype, 'content', function (vdom) {
const post = this.attrs.post;
const flags = post.flags();
@@ -83,7 +83,7 @@ export default function () {
if (post.isHidden()) this.revealContent = true;
return (
vdom.unshift(
<div className="Post-flagged">
<div className="Post-flagged-flags">
{flags.map((flag) => (

View File

@@ -0,0 +1,19 @@
import addFlagsToPosts from './addFlagsToPosts';
import addFlagControl from './addFlagControl';
import addFlagsDropdown from './addFlagsDropdown';
import Flag from './models/Flag';
import FlagList from './components/FlagList';
import FlagPostModal from './components/FlagPostModal';
import FlagsPage from './components/FlagsPage';
import FlagsDropdown from './components/FlagsDropdown';
export default {
'flags/addFlagsToPosts': addFlagsToPosts,
'flags/addFlagControl': addFlagControl,
'flags/addFlagsDropdown': addFlagsDropdown,
'flags/models/Flag': Flag,
'flags/components/FlagList': FlagList,
'flags/components/FlagPostModal': FlagPostModal,
'flags/components/FlagsPage': FlagsPage,
'flags/components/FlagsDropdown': FlagsDropdown,
};

View File

@@ -0,0 +1,65 @@
import app from 'flarum/forum/app';
import Component from 'flarum/common/Component';
import Link from 'flarum/common/components/Link';
import LoadingIndicator from 'flarum/common/components/LoadingIndicator';
import avatar from 'flarum/common/helpers/avatar';
import username from 'flarum/common/helpers/username';
import icon from 'flarum/common/helpers/icon';
import humanTime from 'flarum/common/helpers/humanTime';
export default class FlagList extends Component {
oninit(vnode) {
super.oninit(vnode);
this.state = this.attrs.state;
}
view() {
const flags = this.state.cache || [];
return (
<div className="NotificationList FlagList">
<div className="NotificationList-header">
<h4 className="App-titleControl App-titleControl--text">{app.translator.trans('flarum-flags.forum.flagged_posts.title')}</h4>
</div>
<div className="NotificationList-content">
<ul className="NotificationGroup-content">
{flags.length ? (
flags.map((flag) => {
const post = flag.post();
return (
<li>
<Link
href={app.route.post(post)}
className="Notification Flag"
onclick={(e) => {
app.flags.index = post;
e.redraw = false;
}}
>
{avatar(post.user())}
{icon('fas fa-flag', { className: 'Notification-icon' })}
<span className="Notification-content">
{app.translator.trans('flarum-flags.forum.flagged_posts.item_text', {
username: username(post.user()),
em: <em />,
discussion: post.discussion().title(),
})}
</span>
{humanTime(flag.createdAt())}
<div className="Notification-excerpt">{post.contentPlain()}</div>
</Link>
</li>
);
})
) : !this.state.loading ? (
<div className="NotificationList-empty">{app.translator.trans('flarum-flags.forum.flagged_posts.empty_text')}</div>
) : (
<LoadingIndicator className="LoadingIndicator--block" />
)}
</ul>
</div>
</div>
);
}
}

View File

@@ -1,72 +0,0 @@
import app from 'flarum/forum/app';
import Component from 'flarum/common/Component';
import type { ComponentAttrs } from 'flarum/common/Component';
import Avatar from 'flarum/common/components/Avatar';
import username from 'flarum/common/helpers/username';
import HeaderList from 'flarum/forum/components/HeaderList';
import HeaderListItem from 'flarum/forum/components/HeaderListItem';
import type Mithril from 'mithril';
import type Post from 'flarum/common/models/Post';
import type FlagListState from '../states/FlagListState';
import type Flag from '../models/Flag';
import { Page } from 'flarum/common/states/PaginatedListState';
export interface IFlagListAttrs extends ComponentAttrs {
state: FlagListState;
}
export default class FlagList<CustomAttrs extends IFlagListAttrs = IFlagListAttrs> extends Component<CustomAttrs, FlagListState> {
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
super.oninit(vnode);
}
view() {
const state = this.attrs.state;
return (
<HeaderList
className="FlagList"
title={app.translator.trans('flarum-flags.forum.flagged_posts.title')}
hasItems={state.hasItems()}
loading={state.isLoading()}
emptyText={app.translator.trans('flarum-flags.forum.flagged_posts.empty_text')}
loadMore={() => state.hasNext() && !state.isLoadingNext() && state.loadNext()}
>
<ul className="HeaderListGroup-content">{this.content(state)}</ul>
</HeaderList>
);
}
content(state: FlagListState) {
if (!state.isLoading() && state.hasItems()) {
return state.getPages().map((page: Page<Flag>) => {
return page.items.map((flag: Flag) => {
const post = flag.post() as Post;
return (
<li>
<HeaderListItem
className="Flag"
avatar={<Avatar user={post.user() || null} />}
icon="fas fa-flag"
content={app.translator.trans('flarum-flags.forum.flagged_posts.item_text', {
username: username(post.user()),
em: <em />,
discussion: post.discussion().title(),
})}
excerpt={post.contentPlain()}
datetime={flag.createdAt()}
href={app.route.post(post)}
onclick={(e: MouseEvent) => {
e.redraw = false;
}}
/>
</li>
);
});
});
}
return null;
}
}

View File

@@ -1,13 +1,12 @@
import app from 'flarum/forum/app';
import FormModal from 'flarum/common/components/FormModal';
import Form from 'flarum/common/components/Form';
import Modal from 'flarum/common/components/Modal';
import Button from 'flarum/common/components/Button';
import Stream from 'flarum/common/utils/Stream';
import withAttr from 'flarum/common/utils/withAttr';
import ItemList from 'flarum/common/utils/ItemList';
export default class FlagPostModal extends FormModal {
export default class FlagPostModal extends Modal {
oninit(vnode) {
super.oninit(vnode);
@@ -29,31 +28,31 @@ export default class FlagPostModal extends FormModal {
if (this.success) {
return (
<div className="Modal-body">
<Form className="Form--centered">
<div className="Form Form--centered">
<p className="helpText">{app.translator.trans('flarum-flags.forum.flag_post.confirmation_message')}</p>
<div className="Form-group Form-controls">
<div className="Form-group">
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
{app.translator.trans('flarum-flags.forum.flag_post.dismiss_button')}
</Button>
</div>
</Form>
</div>
</div>
);
}
return (
<div className="Modal-body">
<Form className="Form--centered">
<div className="Form Form--centered">
<div className="Form-group">
<div>{this.flagReasons().toArray()}</div>
</div>
<div className="Form-group Form-controls">
<div className="Form-group">
<Button className="Button Button--primary Button--block" type="submit" loading={this.loading} disabled={!this.reason()}>
{app.translator.trans('flarum-flags.forum.flag_post.submit_button')}
</Button>
</div>
</Form>
</div>
</div>
);
}
@@ -151,6 +150,7 @@ export default class FlagPostModal extends FormModal {
reason: this.reason() === 'other' ? null : this.reason(),
reasonDetail: this.reasonDetail(),
relationships: {
user: app.session.user,
post: this.attrs.post,
},
},

Some files were not shown because too many files have changed in this diff Show More