1
0
mirror of https://github.com/flarum/core.git synced 2025-08-27 10:05:47 +02:00

Compare commits

..

60 Commits

Author SHA1 Message Date
StyleCI Bot
b7c583b208 Apply fixes from StyleCI 2022-07-26 00:25:09 +00:00
Daniël Klabbers
01bdfcca33 wip 2022-07-26 02:24:56 +02:00
flarum-bot
fe20e2c212 Bundled output for commit 795a500adb
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-24 13:06:41 +00:00
Sami Mazouz
795a500adb feat: Queue package manager commands (#3418)
* feat: Queue package manager commands
* adjust tests
* fix: force run whynot command synchronously
* chore: maximize command output box's height
* chore: more user instructions on background queue
* feat: track command peak memory usage
* feat: exit of CLI php version doesn't match web php version
* chore: install deps
* chore: format and typing workflow fix

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-24 14:02:13 +01:00
Daniël Klabbers
75aaef7d76 fix: MyISAM tables for extensions during installation 2022-07-23 23:53:14 +02:00
Daniël Klabbers
f128190f14 Revert "fix: MyISAM tables for extensions during installation"
This reverts commit f926c58e01.
2022-07-23 23:52:57 +02:00
Daniël Klabbers
f926c58e01 fix: MyISAM tables for extensions during installation 2022-07-23 23:42:34 +02:00
flarum-bot
e5a3598bf6 Bundled output for commit 7cafd9f51a
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-21 10:54:05 +00:00
David Wheatley
7cafd9f51a fix: titles positioned wrongly with custom header height (#3550) 2022-07-21 11:47:37 +01:00
flarum-bot
e9fec9b22f Bundled output for commit 31a00eeb95
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-19 18:26:37 +00:00
Sami Mazouz
31a00eeb95 fix(regression): loading next discussion list pages fails (#3547)
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-19 19:22:45 +01:00
flarum-bot
6b577e6f1f Bundled output for commit 64f0ae7c33
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-19 09:36:41 +00:00
Sami Mazouz
64f0ae7c33 chore: revert tsconfig changes for local development (#3545)
* chore: delete unwanted dist typings
* chore: revert 4e52f0e420

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-19 10:30:23 +01:00
flarum-bot
343fe0e317 Bundled output for commit 07b2f86dcc
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-18 23:14:26 +00:00
Daniël Klabbers
07b2f86dcc fix: minor backward compatible fix for php 8.1 in st_replace 2022-07-19 01:09:43 +02:00
flarum-bot
ffaea861e5 Bundled output for commit 46b3b7a952
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-18 18:21:14 +00:00
Sami Mazouz
46b3b7a952 chore: require guzzle 6 or 7
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-18 19:16:12 +01:00
flarum-bot
78544ce68d Bundled output for commit af3116bce9
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-18 18:12:03 +00:00
David Wheatley
af3116bce9 perf(statistics): rewrite for performance on very large communities (#3531)
Co-authored-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-18 19:07:38 +01:00
flarum-bot
6dde236d77 Bundled output for commit d0998be8fa
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-18 15:20:01 +00:00
David Wheatley
d0998be8fa fix: html entities shown raw in page title (#3542) 2022-07-18 16:10:34 +01:00
Ian Morland
8dcfa6c474 fix: move guzzle composer requirement to core (#3544) 2022-07-18 16:10:04 +01:00
flarum-bot
744927215a Bundled output for commit 54c21459d6
Includes transpiled JS/TS.

[skip ci]
2022-07-18 14:47:05 +00:00
Ian Morland
54c21459d6 feat(likes): Option to prevent users liking their own posts (#3534)
* Option to prevent users liking their own posts
* test: user can only like own post if setting ON

Co-authored-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-18 15:32:27 +01:00
flarum-bot
91f8bd34b1 Bundled output for commit 4e52f0e420
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-18 14:30:42 +00:00
Sami Mazouz
4e52f0e420 chore: monorepo local development improvements (#3523)
* chore: centralize editorconfig
* chore: tweak tsconfigs for local development
* revert src path change
* fix: akismet type checking requires flag ext `@types`
* fix: make tags route optional to avoid typescript typing error
2022-07-18 15:19:11 +01:00
Sami Mazouz
ac23d79fe7 fix: unread notifications are globally cached between users. (#3543)
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-18 15:17:46 +01:00
David Wheatley
29179e27c6 feat: use position: sticky for discussion side nav (#3540) 2022-07-17 11:06:19 +01:00
flarum-bot
cd610a1cf2 Bundled output for commit 0c017c2aa0
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-15 22:33:28 +00:00
Sami Mazouz
0c017c2aa0 refactor: convert AlertManager IndexPage and UserPage components to TS (#3536)
* chore: convert `AlertManager` component to TypeScript
* chore: `compat.js` to `compat.ts`
* chore: convert `IndexPage` component to TypeScript
* chore: convert `UserPage` component and inheritors to TypeScript
* chore: `yarn format`
* chore: import types instead
2022-07-15 23:27:47 +01:00
David Wheatley
5721a2f487 fix: replace .fa() mixin usage with .fas() (#3537) 2022-07-15 23:16:49 +01:00
David Wheatley
b673d36f33 chore: split FA imports into separate Less file for easy overriding (#3535) 2022-07-15 09:42:02 +01:00
flarum-bot
bacb095382 Bundled output for commit 7471ef64d5
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-14 15:02:48 +00:00
Sami Mazouz
7471ef64d5 refactor: convert Badge, Checkbox and Navigation components to TS (#3532)
* chore: convert badge components to TypeScript

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>

* chore: convert checkbox components to TypeScript

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>

* chore: convert `Navigation` component to TypeScript

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>

* chore: import mithril type instead
2022-07-14 15:54:16 +01:00
flarum-bot
707ca2d16d Bundled output for commit 3246f5a8f6
Includes transpiled JS/TS.

[skip ci]
2022-07-14 13:48:19 +00:00
Ian Morland
3246f5a8f6 feat(likes): Add likes tab to user profile (#3528) 2022-07-14 14:38:31 +01:00
David Wheatley
bf6f63cfe1 feat: extender to add custom less variables (#3530) 2022-07-14 13:04:38 +01:00
flarum-bot
819602520a Bundled output for commit 16f59f514b
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-14 11:39:02 +00:00
Ian Morland
16f59f514b fix: set translator locale to user preference for email notifications (#3525) 2022-07-14 12:02:55 +01:00
David Wheatley
759f7ef327 fix(a11y): present post streams as feeds, setting busy when loading new pages (#3522) 2022-07-14 12:02:20 +01:00
David Wheatley
9c825aaa2b fix(a11y): set aria-busy when editing a post stream item (#3521)
* fix(a11y): add `aria-busy="true"` to posts being edited

* fix(a11y): add `aria-busy="true"` to reply placeholder
2022-07-14 12:02:12 +01:00
David Wheatley
952a5891bb fix(a11y): add accessible labels to notification grid options (#3520) 2022-07-14 12:02:00 +01:00
flarum-bot
0daa24cf4b Bundled output for commit 33bf2284c7
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-14 08:52:21 +00:00
Daniël Klabbers
33bf2284c7 fix: core settings header has no priority
Due to missing sort the category title sorts underneath the core settings nav items.
2022-07-14 10:44:39 +02:00
Sami Mazouz
d3e456a1bf chore: fix github issue templates
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-13 22:39:10 +01:00
Sami Mazouz
51472625ba choe: use github issue template forms (#3526)
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-13 22:22:22 +01:00
flarum-bot
ec22162cf4 Bundled output for commit 1af506d4b8
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2022-07-13 20:47:43 +00:00
Sami Mazouz
1af506d4b8 refactor: convert core modals to TypeScript (#3515)
* refactor: convert core modals to TypeScript
* chore: excplicitly specify return type instead
* chore: `yarn format`

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-13 21:42:45 +01:00
Daniël Klabbers
d86440506d chore: update version constant during cycle 22 2022-07-13 20:01:01 +02:00
Daniël Klabbers
556d461cfb chore: update v1.4 constraints and update issues url 2022-07-12 23:30:03 +02:00
Daniël Klabbers
6cfebe381a chore: update version constant for the v1.4 release 2022-07-12 23:29:45 +02:00
Daniël Klabbers
8ab2827f4c chore: add changelog for v1.4 2022-07-12 23:27:03 +02:00
Sami Mazouz
024155a608 test(likes): like action behavior (#3512)
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-11 10:42:21 +01:00
Sami Mazouz
4da21463c1 fix: multiple createdAt columns in query causes conflicts (#3506)
* test: list posts with mentions filter and `createdAt` sort
* fix: multiple `createdAt` columns in query causes conflicts
* chore: link to pull request for context

Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-09 10:36:48 +01:00
Sami Mazouz
abc12b4ba5 chore: add .gitattributes to extensions to export-ignore files. (#3513)
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-08 20:38:20 +01:00
Sami Mazouz
d8d4eae9f5 test(suspend): test that suspension works as expected (#3511)
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-08 19:24:59 +01:00
Sami Mazouz
d82a73feed fix(approval): unapproved posts visible to all when no visibility scopers are added
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-08 19:16:53 +01:00
Sami Mazouz
ab6cee1a25 test(approval): unapproved content can only be seen by allowed users
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-08 19:16:53 +01:00
Sami Mazouz
2e840dc73d test(sticky): list discussions works as expected with stickies (#3507)
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-08 19:07:03 +01:00
Sami Mazouz
ae9139bd73 chore(subscriptions): enable backend tests
Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com>
2022-07-07 23:35:29 +01:00
316 changed files with 5369 additions and 2066 deletions

View File

@@ -17,3 +17,6 @@ trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4
[tsconfig.json]
indent_size = 2

View File

@@ -1,39 +0,0 @@
---
name: "🐛 Bug Report"
about: "If something isn't working as expected"
---
## Bug Report
**Current Behavior**
A clear and concise description of the behavior.
**Steps to Reproduce**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected Behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment**
- Flarum version: x.y.z
- Website URL: http://example.com
- Webserver: [e.g. apache, nginx]
- Hosting environment: [e.g. shared, vps]
- PHP version: x.y.z
- Browser: [e.g. chrome 67, safari 11]
```
Output of "php flarum info", run this in terminal in your Flarum directory.
```
**Possible Solution**
<!--- Only if you have suggestions or a fix for the bug -->
**Additional Context**
Add any other context about the problem here.

78
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: "🐛 Bug Report"
description: If something isn't working as expected
labels: ["type/bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: current_behavior
attributes:
label: Current Behavior
description: A clear and concise description of the behavior.
validations:
required: true
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to Reproduce
description: The exact steps to reproduce the bug.
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error message '....'
validations:
required: true
- type: textarea
id: expected_behavior
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
validations:
required: false
- type: textarea
id: enironment
attributes:
label: Environment
value: |
- Flarum version: x.y.z
- Website URL: http://example.com
- Webserver: [e.g. apache, nginx]
- Hosting environment: [e.g. shared, vps]
- PHP version: x.y.z
- Browser: [e.g. chrome 67, safari 11]
validations:
required: true
- type: textarea
id: php_flarum_info
attributes:
label: "Output of `php flarum info`"
value: |
```
Output of "php flarum info", run this in terminal in your Flarum directory.
```
validations:
required: false
- type: textarea
id: possible_solution
attributes:
label: Possible Solution
description: Only if you have suggestions or a fix for the bug.
validations:
required: false
- type: textarea
id: additional_context
attributes:
label: Additional Context
description: Add any other context about the problem here.
validations:
required: false

17
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
blank_issues_enabled: false
contact_links:
- name: "🚀 Feature Request"
url: https://discuss.flarum.org/t/proposals
about: |
Thanks for taking the time to fill out this feature request!
We primarily use GitHub as a bug tracker and issue tracker for items we are sure to tackle in the near future.
For feature requests, ideas and feedback please post in the Flarum Community.
Feature requests are added to GitHub only when they have been accepted by the development team and implementation details have been laid out.
- name: "🙋‍ Support Question"
url: https://discuss.flarum.org/t/support
about: |
We primarily use GitHub as a bug tracker and issue tracker for items we are sure to tackle in the near future; for usage and support questions, please check out these resources below. Thanks!
* Flarum Community: https://discuss.flarum.org/
* Discord Chat: https://flarum.org/discord/
* Twitter: https://twitter.com/Flarum

View File

@@ -1,7 +0,0 @@
---
name: "🚀 Feature Request"
about: "If you have a suggestion please head over to our forum!"
---
We primarily use GitHub as a bug tracker and issue tracker for items we are sure to tackle in the near future. For feature requests, ideas and feedback please post in the Flarum Community: https://discuss.flarum.org/t/proposals. Feature requests are added to GitHub only when they have been accepted by the development team and implementation details have been laid out.

View File

@@ -1,11 +0,0 @@
---
name: "🙋‍ Support Question"
about: "If you have a question, please check out our forum or Discord!"
---
We primarily use GitHub as a bug tracker and issue tracker for items we are sure to tackle in the near future; for usage and support questions, please check out these resources below. Thanks!
* Flarum Community: https://discuss.flarum.org/
* Discord Chat: https://flarum.org/discord/
* Twitter: https://twitter.com/Flarum

View File

@@ -6,6 +6,6 @@ jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
enable_backend_testing: true
backend_directory: ./extensions/approval

View File

@@ -6,6 +6,6 @@ jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
enable_backend_testing: true
backend_directory: ./extensions/likes

View File

@@ -6,6 +6,6 @@ jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
enable_backend_testing: true
backend_directory: ./extensions/subscriptions

View File

@@ -6,6 +6,6 @@ jobs:
run:
uses: ./.github/workflows/REUSABLE_backend.yml
with:
enable_backend_testing: false
enable_backend_testing: true
backend_directory: ./extensions/suspend

View File

@@ -1,5 +1,32 @@
# Changelog
## [1.4.0](https://github.com/flarum/framework/compare/v1.3.1...v1.4.0)
### Added
- `created_at` and `updated_at` columns added to several tables (https://github.com/flarum/framework/pull/3435)
- Priorities added to AdminNav links (https://github.com/flarum/framework/pull/3453)
- `app.translator` allows retrieving and setting locale (https://github.com/flarum/framework/pull/3451)
- Extensions can now declare custom settings components for use with `buildSettingComponent` (https://github.com/flarum/framework/pull/3494)
- Implement extensibility on `rel` and `target` attributes on links (https://github.com/flarum/framework/pull/3455)
- New backend tests were added to some of the bundled extensions (https://github.com/flarum/framework/issues/3508)
### Changed
- Split boot script for Flarum in HTML footer into two parts for CSP hashing (https://github.com/flarum/framework/pull/3461)
- Split asset compilation by giving assembling compilers its own method (https://github.com/flarum/framework/pull/3446)
- Increase visibility of Component typescript class for better extensibility (https://github.com/flarum/framework/pull/3437)
### Fixed
- Mentioning an event post breaks the notification dropdown (https://github.com/flarum/framework/pull/3493)
- Suspension modal shows after suspension is over (https://github.com/flarum/framework/pull/3449)
- CLI based installations don't exit with an error code on failure (https://github.com/flarum/framework/pull/3452)
- Tabbing through dropdown controls doesn't make them visible (https://github.com/flarum/framework/pull/3450)
- Requiring zero tags on new discussions forces the user to select tags (https://github.com/flarum/framework/pull/3448)
- Long topic titles in the notification list don't overflow (https://github.com/flarum/framework/pull/3500)
- Subtags of tags the user has access to are visible even if these are not accessible (https://github.com/flarum/framework/pull/3419)
- `assertAdmin` tests access based on wrong gate ability (https://github.com/flarum/framework/pull/3501)
- Increasing the composer header size causes elements to slip underneath (https://github.com/flarum/framework/pull/3502)
- The profile mentions tab errors when sorting by `created_at` (https://github.com/flarum/framework/pull/3506)
## [1.3.1](https://github.com/flarum/framework/compare/v1.3.0...v1.3.1)
### Changed

View File

@@ -29,8 +29,8 @@
}
],
"support": {
"issues": "https://github.com/flarum/core/issues",
"source": "https://github.com/flarum/core",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/framework",
"docs": "https://docs.flarum.org",
"forum": "https://discuss.flarum.org",
"chat": "https://flarum.org/chat"
@@ -92,7 +92,7 @@
"doctrine/dbal": "^2.7",
"dragonmantank/cron-expression": "^3.1.0",
"franzl/whoops-middleware": "^2.0.0",
"guzzlehttp/guzzle": "^7.4",
"guzzlehttp/guzzle": "^6.0|^7.4",
"illuminate/bus": "^8.0",
"illuminate/cache": "^8.0",
"illuminate/config": "^8.0",

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/akismet/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/akismet",
"forum": "https://discuss.flarum.org"
},
@@ -19,9 +19,8 @@
}
],
"require": {
"flarum/core": "^1.3",
"flarum/approval": "^1.2",
"guzzlehttp/guzzle": "^7.4"
"flarum/core": "^1.4",
"flarum/approval": "^1.2"
},
"autoload": {
"psr-4": {

20
extensions/approval/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/approval",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3",
"flarum/core": "^1.4",
"flarum/flags": "^1.2"
},
"autoload": {
@@ -52,7 +52,7 @@
"prettier": true,
"typescript": false,
"bundlewatch": false,
"backendTesting": false,
"backendTesting": true,
"editorConfig": true,
"styleci": true
}
@@ -65,5 +65,28 @@
}
],
"minimum-stability": "dev",
"prefer-stable": true
"prefer-stable": true,
"autoload-dev": {
"psr-4": {
"Flarum\\Approval\\Tests\\": "tests/"
}
},
"scripts": {
"test": [
"@test:unit",
"@test:integration"
],
"test:unit": "phpunit -c tests/phpunit.unit.xml",
"test:integration": "phpunit -c tests/phpunit.integration.xml",
"test:setup": "@php tests/integration/setup.php"
},
"scripts-descriptions": {
"test": "Runs all tests.",
"test:unit": "Runs all unit tests.",
"test:integration": "Runs all integration tests.",
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
},
"require-dev": {
"flarum/testing": "^1.0.0"
}
}

View File

@@ -9,6 +9,7 @@
namespace Flarum\Approval\Access;
use Closure;
use Flarum\Discussion\Discussion;
use Flarum\User\User;
use Illuminate\Database\Eloquent\Builder;
@@ -39,14 +40,23 @@ class ScopePrivatePostVisibility
});
}
private function discussionWhereCanApprovePosts(User $actor)
/**
* Looks if the actor has permission to approve posts,
* within the discussion which the post is a part of.
*
* For example, the tags extension,
* turns the `approvePosts` ability into per tag basis.
*/
private function discussionWhereCanApprovePosts(User $actor): Closure
{
return function ($query) use ($actor) {
$query->selectRaw('1')
->from('discussions')
->whereColumn('discussions.id', 'posts.discussion_id')
->where(function ($query) use ($actor) {
Discussion::query()->setQuery($query)->whereVisibleTo($actor, 'approvePosts');
$query->whereRaw('1 != 1')->orWhere(function ($query) use ($actor) {
Discussion::query()->setQuery($query)->whereVisibleTo($actor, 'approvePosts');
});
});
};
}

View File

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Approval\Tests\integration;
use Carbon\Carbon;
trait InteractsWithUnapprovedContent
{
protected function prepareUnapprovedDatabaseContent()
{
$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, '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],
['id' => 4, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 4, 'comment_count' => 1, 'is_approved' => 1, 'is_private' => 0],
['id' => 5, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 4, 'first_post_id' => 5, 'comment_count' => 1, 'is_approved' => 1, 'is_private' => 0],
['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],
],
'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],
['id' => 4, 'discussion_id' => 4, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 5, 'discussion_id' => 5, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 6, 'discussion_id' => 6, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 7, 'discussion_id' => 7, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 1],
['id' => 8, 'discussion_id' => 7, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 0, 'is_approved' => 1, 'number' => 2],
['id' => 9, 'discussion_id' => 7, 'user_id' => 4, 'type' => 'comment', 'content' => '<t><p>Text</p></t>', 'is_private' => 1, 'is_approved' => 0, 'number' => 3],
['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],
],
'groups' => [
['id' => 4, 'name_singular' => 'Acme', 'name_plural' => 'Acme', 'is_hidden' => 0]
],
'group_user' => [
['user_id' => 3, 'group_id' => 4]
],
'group_permission' => [
['permission' => 'discussion.approvePosts', 'group_id' => 4]
]
]);
}
/**
* null: Guest, 2: Normal User.
*/
public function unallowedUsers(): array
{
return [[null], [2]];
}
/**
* 1: Admin, 3: Permission Given, 4: Discussions Author.
*/
public function allowedUsers(): array
{
return [[1], [3], [4]];
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Approval\Tests\integration\api;
use Flarum\Approval\Tests\integration\InteractsWithUnapprovedContent;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Illuminate\Support\Arr;
class ListDiscussionsTest extends TestCase
{
use RetrievesAuthorizedUsers;
use InteractsWithUnapprovedContent;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-approval');
$this->prepareUnapprovedDatabaseContent();
}
/**
* @dataProvider unallowedUsers
* @test
*/
public function can_only_see_approved_if_not_allowed_to_approve(?int $authenticatedAs)
{
$response = $this->send(
$this->request('GET', '/api/discussions', compact('authenticatedAs'))
);
$body = json_decode($response->getBody()->getContents(), true);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEqualsCanonicalizing([1, 4, 5, 7], Arr::pluck($body['data'], 'id'));
}
/**
* @dataProvider allowedUsers
* @test
*/
public function can_see_unapproved_if_allowed_to_approve(int $authenticatedAs)
{
$response = $this->send(
$this->request('GET', '/api/discussions', compact('authenticatedAs'))
);
$body = json_decode($response->getBody()->getContents(), true);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEqualsCanonicalizing([1, 2, 3, 4, 5, 6, 7], Arr::pluck($body['data'], 'id'));
}
}

View File

@@ -0,0 +1,74 @@
<?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 Flarum\Approval\Tests\integration\InteractsWithUnapprovedContent;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Illuminate\Support\Arr;
class ListPostsTest extends TestCase
{
use RetrievesAuthorizedUsers;
use InteractsWithUnapprovedContent;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-approval');
$this->prepareUnapprovedDatabaseContent();
}
/**
* @dataProvider unallowedUsers
* @test
*/
public function can_only_see_approved_if_not_allowed_to_approve(?int $authenticatedAs)
{
$response = $this->send(
$this
->request('GET', '/api/posts', compact('authenticatedAs'))
->withQueryParams([
'filter' => [
'discussion' => 7
]
])
);
$body = json_decode($response->getBody()->getContents(), true);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEqualsCanonicalizing([7, 8, 10], Arr::pluck($body['data'], 'id'));
}
/**
* @dataProvider allowedUsers
* @test
*/
public function can_see_unapproved_if_allowed_to_approve(int $authenticatedAs)
{
$response = $this->send(
$this
->request('GET', '/api/posts', compact('authenticatedAs'))
->withQueryParams([
'filter' => [
'discussion' => 7
]
])
);
$body = json_decode($response->getBody()->getContents(), true);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEqualsCanonicalizing([7, 8, 9, 10, 11], Arr::pluck($body['data'], 'id'));
}
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
use Flarum\Testing\integration\Setup\SetupScript;
require __DIR__.'/../../vendor/autoload.php';
$setup = new SetupScript();
$setup->run();

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="true"
stopOnFailure="false"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Flarum Integration Tests">
<directory suffix="Test.php">./integration</directory>
<exclude>./integration/tmp</exclude>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Flarum Unit Tests">
<directory suffix="Test.php">./unit</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener" />
</listeners>
</phpunit>

View File

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/bbcode/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/bbcode",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"extra": {
"branch-alias": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/embed/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/embed",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"autoload": {
"psr-4": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/emoji/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/emoji",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"extra": {
"branch-alias": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/flags/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/flags",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"autoload": {
"psr-4": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/lang-english/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"extra": {
"branch-alias": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/likes/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/likes",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"autoload": {
"psr-4": {
@@ -51,7 +51,7 @@
"prettier": true,
"typescript": false,
"bundlewatch": false,
"backendTesting": false,
"backendTesting": true,
"editorConfig": true,
"styleci": true
}
@@ -64,5 +64,28 @@
}
],
"minimum-stability": "dev",
"prefer-stable": true
"prefer-stable": true,
"autoload-dev": {
"psr-4": {
"Flarum\\Likes\\Tests\\": "tests/"
}
},
"scripts": {
"test": [
"@test:unit",
"@test:integration"
],
"test:unit": "phpunit -c tests/phpunit.unit.xml",
"test:integration": "phpunit -c tests/phpunit.integration.xml",
"test:setup": "@php tests/integration/setup.php"
},
"scripts-descriptions": {
"test": "Runs all tests.",
"test:unit": "Runs all unit tests.",
"test:integration": "Runs all integration tests.",
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
},
"require-dev": {
"flarum/testing": "^1.0.0"
}
}

View File

@@ -7,16 +7,19 @@
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Likes;
use Flarum\Api\Controller;
use Flarum\Api\Serializer\BasicUserSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Extend;
use Flarum\Likes\Event\PostWasLiked;
use Flarum\Likes\Event\PostWasUnliked;
use Flarum\Likes\Listener;
use Flarum\Likes\Notification\PostLikedBlueprint;
use Flarum\Likes\Query\LikedByFilter;
use Flarum\Post\Event\Deleted;
use Flarum\Post\Event\Saving;
use Flarum\Post\Filter\PostFilterer;
use Flarum\Post\Post;
use Flarum\User\User;
@@ -59,4 +62,13 @@ return [
->listen(PostWasUnliked::class, Listener\SendNotificationWhenPostIsUnliked::class)
->listen(Deleted::class, [Listener\SaveLikesToDatabase::class, 'whenPostIsDeleted'])
->listen(Saving::class, [Listener\SaveLikesToDatabase::class, 'whenPostIsSaving']),
(new Extend\Filter(PostFilterer::class))
->addFilter(LikedByFilter::class),
(new Extend\Settings())
->default('flarum-likes.like_own_post', true),
(new Extend\Policy())
->modelPolicy(Post::class, Access\LikePostPolicy::class),
];

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

@@ -1,2 +1,2 @@
(()=>{var e={n:r=>{var o=r&&r.__esModule?()=>r.default:()=>r;return e.d(o,{a:o}),o},d:(r,o)=>{for(var t in o)e.o(o,t)&&!e.o(r,t)&&Object.defineProperty(r,t,{enumerable:!0,get:o[t]})},o:(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},r={};(()=>{"use strict";e.r(r);const o=flarum.core.compat["admin/app"];var t=e.n(o);t().initializers.add("flarum-likes",(function(){t().extensionData.for("flarum-likes").registerPermission({icon:"far fa-thumbs-up",label:t().translator.trans("flarum-likes.admin.permissions.like_posts_label"),permission:"discussion.likePosts"},"reply")}))})(),module.exports=r})();
(()=>{var e={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return e.d(r,{a:r}),r},d:(t,r)=>{for(var s in r)e.o(r,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:r[s]})},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.core.compat["admin/app"];var s=e.n(r);s().initializers.add("flarum-likes",(function(){s().extensionData.for("flarum-likes").registerPermission({icon:"far fa-thumbs-up",label:s().translator.trans("flarum-likes.admin.permissions.like_posts_label"),permission:"discussion.likePosts"},"reply").registerSetting({setting:"flarum-likes.like_own_post",type:"bool",label:s().translator.trans("flarum-likes.admin.settings.like_own_posts_label"),help:s().translator.trans("flarum-likes.admin.settings.like_own_posts_help")})}))})(),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,EAAGH,IAC5BA,GCLRF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3ER,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,M,+BCLvD,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,a,aCExDC,IAAAA,aAAAA,IAAqB,gBAAgB,WACnCA,IAAAA,cAAAA,IAAsB,gBAAgBC,mBACpC,CACEC,KAAM,mBACNC,MAAOH,IAAAA,WAAAA,MAAqB,mDAC5BI,WAAY,wBAEd,a","sources":["webpack://@flarum/likes/webpack/bootstrap","webpack://@flarum/likes/webpack/runtime/compat get default export","webpack://@flarum/likes/webpack/runtime/define property getters","webpack://@flarum/likes/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/likes/webpack/runtime/make namespace object","webpack://@flarum/likes/external root \"flarum.core.compat['admin/app']\"","webpack://@flarum/likes/./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['admin/app'];","import app from 'flarum/admin/app';\n\napp.initializers.add('flarum-likes', () => {\n app.extensionData.for('flarum-likes').registerPermission(\n {\n icon: 'far fa-thumbs-up',\n label: app.translator.trans('flarum-likes.admin.permissions.like_posts_label'),\n permission: 'discussion.likePosts',\n },\n 'reply'\n );\n});\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","core","compat","app","registerPermission","icon","label","permission"],"sourceRoot":""}
{"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,GCLRF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3ER,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,M,+BCLvD,MAAM,EAA+BC,OAAOC,KAAKC,OAAO,a,aCExDC,IAAAA,aAAAA,IAAqB,gBAAgB,WACnCA,IAAAA,cAAAA,IACO,gBACJC,mBACC,CACEC,KAAM,mBACNC,MAAOH,IAAAA,WAAAA,MAAqB,mDAC5BI,WAAY,wBAEd,SAEDC,gBAAgB,CACfC,QAAS,6BACTC,KAAM,OACNJ,MAAOH,IAAAA,WAAAA,MAAqB,oDAC5BQ,KAAMR,IAAAA,WAAAA,MAAqB,yD","sources":["webpack://@flarum/likes/webpack/bootstrap","webpack://@flarum/likes/webpack/runtime/compat get default export","webpack://@flarum/likes/webpack/runtime/define property getters","webpack://@flarum/likes/webpack/runtime/hasOwnProperty shorthand","webpack://@flarum/likes/webpack/runtime/make namespace object","webpack://@flarum/likes/external root \"flarum.core.compat['admin/app']\"","webpack://@flarum/likes/./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['admin/app'];","import app from 'flarum/admin/app';\n\napp.initializers.add('flarum-likes', () => {\n app.extensionData\n .for('flarum-likes')\n .registerPermission(\n {\n icon: 'far fa-thumbs-up',\n label: app.translator.trans('flarum-likes.admin.permissions.like_posts_label'),\n permission: 'discussion.likePosts',\n },\n 'reply'\n )\n .registerSetting({\n setting: 'flarum-likes.like_own_post',\n type: 'bool',\n label: app.translator.trans('flarum-likes.admin.settings.like_own_posts_label'),\n help: app.translator.trans('flarum-likes.admin.settings.like_own_posts_help'),\n });\n});\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","core","compat","app","registerPermission","icon","label","permission","registerSetting","setting","type","help"],"sourceRoot":""}

2
extensions/likes/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,12 +1,20 @@
import app from 'flarum/admin/app';
app.initializers.add('flarum-likes', () => {
app.extensionData.for('flarum-likes').registerPermission(
{
icon: 'far fa-thumbs-up',
label: app.translator.trans('flarum-likes.admin.permissions.like_posts_label'),
permission: 'discussion.likePosts',
},
'reply'
);
app.extensionData
.for('flarum-likes')
.registerPermission(
{
icon: 'far fa-thumbs-up',
label: app.translator.trans('flarum-likes.admin.permissions.like_posts_label'),
permission: 'discussion.likePosts',
},
'reply'
)
.registerSetting({
setting: 'flarum-likes.like_own_post',
type: 'bool',
label: app.translator.trans('flarum-likes.admin.settings.like_own_posts_label'),
help: app.translator.trans('flarum-likes.admin.settings.like_own_posts_help'),
});
});

View File

@@ -0,0 +1,21 @@
import { extend } from 'flarum/common/extend';
import app from 'flarum/forum/app';
import UserPage from 'flarum/forum/components/UserPage';
import LinkButton from 'flarum/common/components/LinkButton';
import LikesUserPage from './components/LikesUserPage';
import ItemList from 'flarum/common/utils/ItemList';
import type Mithril from 'mithril';
export default function addLikesTabToUserProfile() {
app.routes['user.likes'] = { path: '/u/:username/likes', component: LikesUserPage };
extend(UserPage.prototype, 'navItems', function (items: ItemList<Mithril.Children>) {
const user = this.user;
items.add(
'likes',
<LinkButton href={app.route('user.likes', { username: user.slug() })} icon="far fa-thumbs-up">
{app.translator.trans('flarum-likes.forum.user.likes_link')}
</LinkButton>,
88
);
});
}

View File

@@ -0,0 +1,24 @@
import app from 'flarum/forum/app';
import PostsUserPage from 'flarum/forum/components/PostsUserPage';
/**
* The `LikesUserPage` component shows posts which user the user liked.
*/
export default class LikesUserPage extends PostsUserPage {
/**
* Load a new page of the user's activity feed.
*
* @param offset The position to start getting results from.
* @protected
*/
loadResults(offset: number) {
return app.store.find('posts', {
filter: {
type: 'comment',
likedBy: this.user.id(),
},
page: { offset, limit: this.loadLimit },
sort: '-createdAt',
});
}
}

View File

@@ -7,6 +7,7 @@ import NotificationGrid from 'flarum/forum/components/NotificationGrid';
import addLikeAction from './addLikeAction';
import addLikesList from './addLikesList';
import PostLikedNotification from './components/PostLikedNotification';
import addLikesTabToUserProfile from './addLikesTabToUserProfile';
app.initializers.add('flarum-likes', () => {
app.notificationComponents.postLiked = PostLikedNotification;
@@ -16,6 +17,7 @@ app.initializers.add('flarum-likes', () => {
addLikeAction();
addLikesList();
addLikesTabToUserProfile();
extend(NotificationGrid.prototype, 'notificationTypes', function (items) {
items.add('postLiked', {

View File

@@ -11,6 +11,10 @@ flarum-likes:
permissions:
like_posts_label: Like posts
settings:
like_own_posts_help: When enabled, subject to permission, users may 'like' their own posts on the forum. To prevent users placing a 'like' on their own posts, disable this setting.
like_own_posts_label: Users may like their own posts
# Translations in this namespace are used by the forum user interface.
forum:
@@ -35,3 +39,7 @@ flarum-likes:
# These translations are used in the Settings page.
settings:
notify_post_liked_label: Someone likes one of my posts
# These translations are used in the User profile page.
user:
likes_link: Likes

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Likes\Access;
use Flarum\Post\Post;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\Access\AbstractPolicy;
use Flarum\User\User;
class LikePostPolicy extends AbstractPolicy
{
/**
* @var SettingsRepositoryInterface
*/
protected $settings;
public function __construct(SettingsRepositoryInterface $settings)
{
$this->settings = $settings;
}
public function like(User $actor, Post $post)
{
if ($actor->id === $post->user_id && ! (bool) $this->settings->get('flarum-likes.like_own_post')) {
return $this->deny();
}
}
}

View File

@@ -0,0 +1,34 @@
<?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\Likes\Query;
use Flarum\Filter\FilterInterface;
use Flarum\Filter\FilterState;
class LikedByFilter implements FilterInterface
{
public function getFilterKey(): string
{
return 'likedBy';
}
public function filter(FilterState $filterState, string $filterValue, bool $negate)
{
$likedId = trim($filterValue, '"');
$filterState
->getQuery()
->whereIn('id', function ($query) use ($likedId, $negate) {
$query->select('post_id')
->from('post_likes')
->where('user_id', $negate ? '!=' : '=', $likedId);
});
}
}

View File

View File

@@ -0,0 +1,167 @@
<?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\Likes\Tests\integration\api;
use Carbon\Carbon;
use Flarum\Post\CommentPost;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Psr\Http\Message\ResponseInterface;
class LikePostTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-likes');
$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],
],
'discussions' => [
['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 1, 'first_post_id' => 1, 'comment_count' => 2],
],
'posts' => [
['id' => 1, 'number' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>something</p></t>'],
['id' => 3, 'number' => 2, 'discussion_id' => 1, 'created_at' => Carbon::now(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>something</p></t>'],
['id' => 5, 'number' => 3, 'discussion_id' => 1, 'created_at' => Carbon::now(), 'user_id' => 3, 'type' => 'discussionRenamed', 'content' => '<t><p>something</p></t>'],
['id' => 6, 'number' => 4, 'discussion_id' => 1, 'created_at' => Carbon::now(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>something</p></t>'],
],
'groups' => [
['id' => 5, 'name_singular' => 'Acme', 'name_plural' => 'Acme', 'is_hidden' => 0],
['id' => 6, 'name_singular' => 'Acme1', 'name_plural' => 'Acme1', 'is_hidden' => 0]
],
'group_user' => [
['user_id' => 3, 'group_id' => 5]
]
]);
}
protected function rewriteDefaultPermissionsAfterBoot()
{
$this->database()->table('group_permission')->where('permission', 'discussion.likePosts')->delete();
$this->database()->table('group_permission')->insert(['permission' => 'discussion.likePosts', 'group_id' => 5]);
}
/**
* @dataProvider allowedUsersToLike
* @test
*/
public function can_like_a_post_if_allowed(int $postId, ?int $authenticatedAs, string $message, bool $canLikeOwnPost = null)
{
if (! is_null($canLikeOwnPost)) {
$this->setting('flarum-likes.like_own_post', $canLikeOwnPost);
}
$this->rewriteDefaultPermissionsAfterBoot();
$response = $this->sendLikeRequest($postId, $authenticatedAs);
$post = CommentPost::query()->find($postId);
$this->assertEquals(200, $response->getStatusCode());
$this->assertNotNull($post->likes->where('id', $authenticatedAs)->first(), $message);
}
/**
* @dataProvider unallowedUsersToLike
* @test
*/
public function cannot_like_a_post_if_not_allowed(int $postId, ?int $authenticatedAs, string $message, bool $canLikeOwnPost = null)
{
if (! is_null($canLikeOwnPost)) {
$this->setting('flarum-likes.like_own_post', $canLikeOwnPost);
}
$this->rewriteDefaultPermissionsAfterBoot();
$response = $this->sendLikeRequest($postId, $authenticatedAs);
$post = CommentPost::query()->find($postId);
$this->assertEquals(403, $response->getStatusCode(), $message);
$this->assertNull($post->likes->where('id', $authenticatedAs)->first());
}
/**
* @dataProvider allowedUsersToLike
* @test
*/
public function can_dislike_a_post_if_liked_and_allowed(int $postId, ?int $authenticatedAs, string $message, bool $canLikeOwnPost = null)
{
if (! is_null($canLikeOwnPost)) {
$this->setting('flarum-likes.like_own_post', $canLikeOwnPost);
}
$this->rewriteDefaultPermissionsAfterBoot();
$this->sendLikeRequest($postId, $authenticatedAs);
$response = $this->sendLikeRequest($postId, $authenticatedAs, false);
$post = CommentPost::query()->find($postId);
$this->assertEquals(200, $response->getStatusCode());
$this->assertNull($post->likes->where('id', $authenticatedAs)->first(), $message);
}
public function allowedUsersToLike(): array
{
return [
[1, 1, 'Admin can like any post'],
[1, 3, 'User with permission can like other posts'],
[5, 3, 'User with permission can like own post by default'],
];
}
public function unallowedUsersToLike(): array
{
return [
[1, null, 'Guest cannot like any post'],
[1, 2, 'User without permission cannot like any post'],
[5, 3, 'User with permission cannot like own post if setting off', false],
[6, 1, 'Admin cannot like own post if setting off', false],
];
}
protected function sendLikeRequest(int $postId, ?int $authenticatedAs, bool $liked = true): ResponseInterface
{
if (! isset($authenticatedAs)) {
$initial = $this->send(
$this->request('GET', '/')
);
$token = $initial->getHeaderLine('X-CSRF-Token');
}
$request = $this->request('PATCH', "/api/posts/$postId", [
'authenticatedAs' => $authenticatedAs,
'cookiesFrom' => $initial ?? null,
'json' => [
'data' => [
'attributes' => [
'isLiked' => $liked
]
]
]
]);
if (! isset($authenticatedAs)) {
$request = $request->withHeader('X-CSRF-Token', $token);
}
return $this->send($request);
}
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
use Flarum\Testing\integration\Setup\SetupScript;
require __DIR__.'/../../vendor/autoload.php';
$setup = new SetupScript();
$setup->run();

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="true"
stopOnFailure="false"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Flarum Integration Tests">
<directory suffix="Test.php">./integration</directory>
<exclude>./integration/tmp</exclude>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Flarum Unit Tests">
<directory suffix="Test.php">./unit</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="\Mockery\Adapter\Phpunit\TestListener" />
</listeners>
</phpunit>

View File

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/lock/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/lock",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"autoload": {
"psr-4": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/markdown/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/markdown",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"extra": {
"branch-alias": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/mentions/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/mentions",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"autoload": {
"psr-4": {

View File

@@ -28,7 +28,7 @@
}
&:before {
.fa();
.fas();
content: @fa-var-reply;
margin-right: 5px;
}

View File

@@ -105,6 +105,6 @@ class ListPostsTest extends TestCase
// Order-independent comparison
$ids = Arr::pluck($data, 'id');
$this->assertEqualsCanonicalizing(['2', '3'], $ids, 'IDs do not match');
$this->assertEqualsCanonicalizing(['3', '2'], $ids, 'IDs do not match');
}
}

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

20
extensions/nicknames/.gitattributes vendored Normal file
View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -7,7 +7,7 @@
],
"license": "MIT",
"support": {
"issues": "https://github.com/flarum/core/issues",
"issues": "https://github.com/flarum/framework/issues",
"source": "https://github.com/flarum/nicknames",
"forum": "https://discuss.flarum.org"
},
@@ -19,7 +19,7 @@
}
],
"require": {
"flarum/core": "^1.3"
"flarum/core": "^1.4"
},
"autoload": {
"psr-4": {

View File

@@ -1,19 +0,0 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

View File

@@ -0,0 +1,20 @@
**/.gitattributes export-ignore
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.github export-ignore
**/.travis export-ignore
**/.travis.yml export-ignore
**/.editorconfig export-ignore
**/.styleci.yml export-ignore
**/phpunit.xml export-ignore
**/tests export-ignore
**/js/dist/**/* -diff
**/js/dist/**/* linguist-generated
**/js/dist-typings/**/* -diff
**/js/dist-typings/**/* linguist-generated
**/js/yarn.lock -diff
**/js/package-lock.json -diff
* text=auto eol=lf

View File

@@ -19,6 +19,8 @@ use Flarum\PackageManager\Exception\ExceptionHandler;
use Flarum\PackageManager\Exception\MajorUpdateFailedException;
use Flarum\PackageManager\Settings\LastUpdateCheck;
use Flarum\PackageManager\Settings\LastUpdateRun;
use Illuminate\Contracts\Queue\Queue;
use Illuminate\Queue\SyncQueue;
return [
(new Extend\Routes('api'))
@@ -29,7 +31,8 @@ return [
->post('/package-manager/why-not', 'package-manager.why-not', Api\Controller\WhyNotController::class)
->post('/package-manager/minor-update', 'package-manager.minor-update', Api\Controller\MinorUpdateController::class)
->post('/package-manager/major-update', 'package-manager.major-update', Api\Controller\MajorUpdateController::class)
->post('/package-manager/global-update', 'package-manager.global-update', Api\Controller\GlobalUpdateController::class),
->post('/package-manager/global-update', 'package-manager.global-update', Api\Controller\GlobalUpdateController::class)
->get('/package-manager-tasks', 'package-manager.tasks.index', Api\Controller\ListTasksController::class),
(new Extend\Frontend('admin'))
->css(__DIR__.'/less/admin.less')
@@ -37,17 +40,20 @@ return [
->content(function (Document $document) {
$paths = resolve(Paths::class);
$document->payload['isRequiredDirectoriesWritable'] = is_writable($paths->vendor)
$document->payload['flarum-package-manager.writable_dirs'] = is_writable($paths->vendor)
&& is_writable($paths->storage.'/.composer')
&& is_writable($paths->base.'/composer.json')
&& is_writable($paths->base.'/composer.lock');
$document->payload['flarum-package-manager.using_sync_queue'] = resolve(Queue::class) instanceof SyncQueue;
}),
new Extend\Locales(__DIR__.'/locale'),
(new Extend\Settings())
->default(LastUpdateCheck::key(), json_encode(LastUpdateCheck::default()))
->default(LastUpdateRun::key(), json_encode(LastUpdateRun::default())),
->default(LastUpdateRun::key(), json_encode(LastUpdateRun::default()))
->default('flarum-package-manager.queue_jobs', false),
(new Extend\ServiceProvider)
->register(PackageManagerServiceProvider::class),

View File

@@ -0,0 +1,5 @@
/// <reference types="mithril" />
import Component from 'flarum/common/Component';
export default class ControlSection extends Component {
view(): JSX.Element;
}

View File

@@ -1,10 +1,7 @@
import Mithril from 'mithril';
import type Mithril from 'mithril';
import Component, { ComponentAttrs } from 'flarum/common/Component';
import { Extension as BaseExtension } from 'flarum/admin/AdminApplication';
import { Extension } from 'flarum/admin/AdminApplication';
import { UpdatedPackage } from './Updater';
export declare type Extension = BaseExtension & {
name: string;
};
export interface ExtensionItemAttrs extends ComponentAttrs {
extension: Extension;
updates: UpdatedPackage;
@@ -16,5 +13,5 @@ export interface ExtensionItemAttrs extends ComponentAttrs {
}
export default class ExtensionItem<Attrs extends ExtensionItemAttrs = ExtensionItemAttrs> extends Component<Attrs> {
view(vnode: Mithril.Vnode<Attrs, this>): Mithril.Children;
private version;
version(v: string): string;
}

View File

@@ -1,11 +1,14 @@
import type Mithril from 'mithril';
import Component from 'flarum/common/Component';
import Component, { ComponentAttrs } from 'flarum/common/Component';
import Stream from 'flarum/common/utils/Stream';
export default class Installer<Attrs> extends Component<Attrs> {
interface InstallerAttrs extends ComponentAttrs {
}
export default class Installer extends Component<InstallerAttrs> {
packageName: Stream<string>;
isLoading: boolean;
oninit(vnode: Mithril.Vnode<Attrs, this>): void;
oninit(vnode: Mithril.Vnode<InstallerAttrs, this>): void;
view(): Mithril.Children;
data(): any;
onsubmit(): void;
}
export {};

View File

@@ -0,0 +1,9 @@
import type Mithril from 'mithril';
import Component, { ComponentAttrs } from 'flarum/common/Component';
interface LabelAttrs extends ComponentAttrs {
type: 'success' | 'error' | 'neutral' | 'warning';
}
export default class Label extends Component<LabelAttrs> {
view(vnode: Mithril.Vnode<LabelAttrs, this>): JSX.Element;
}
export {};

View File

@@ -1,5 +1,5 @@
import type Mithril from 'mithril';
import Component, { ComponentAttrs } from 'flarum/common/Component';
import Mithril from 'mithril';
import { UpdatedPackage, UpdateState } from './Updater';
interface MajorUpdaterAttrs extends ComponentAttrs {
coreUpdate: UpdatedPackage;

View File

@@ -0,0 +1,13 @@
/// <reference types="mithril" />
import Component, { ComponentAttrs } from 'flarum/common/Component';
import QueueState from '../states/QueueState';
interface PaginationAttrs extends ComponentAttrs {
list: QueueState;
}
/**
* @todo make it abstract in core for reusability.
*/
export default class Pagination extends Component<PaginationAttrs> {
view(): JSX.Element;
}
export {};

View File

@@ -0,0 +1,10 @@
import type Mithril from 'mithril';
import Component from 'flarum/common/Component';
import { TaskOperations } from '../models/Task';
export default class QueueSection extends Component<{}> {
oninit(vnode: Mithril.Vnode<{}, this>): void;
view(): JSX.Element;
columns(): any;
queueTable(): JSX.Element;
operationIcon(operation: TaskOperations): Mithril.Children;
}

View File

@@ -0,0 +1,6 @@
import type Mithril from 'mithril';
import ExtensionPage, { ExtensionPageAttrs } from 'flarum/admin/components/ExtensionPage';
import ItemList from 'flarum/common/utils/ItemList';
export default class SettingsPage extends ExtensionPage {
sections(vnode: Mithril.VnodeDOM<ExtensionPageAttrs, this>): ItemList<unknown>;
}

View File

@@ -0,0 +1,12 @@
/// <reference types="mithril" />
import Modal, { IInternalModalAttrs } from 'flarum/common/components/Modal';
import Task from '../models/Task';
interface TaskOutputModalAttrs extends IInternalModalAttrs {
task: Task;
}
export default class TaskOutputModal<CustomAttrs extends TaskOutputModalAttrs = TaskOutputModalAttrs> extends Modal<CustomAttrs> {
className(): string;
title(): any;
content(): JSX.Element;
}
export {};

View File

@@ -1,6 +1,6 @@
import Mithril from 'mithril';
import Component from 'flarum/common/Component';
import { Extension } from './ExtensionItem';
import Component, { ComponentAttrs } from 'flarum/common/Component';
import { Extension } from 'flarum/admin/AdminApplication';
export declare type UpdatedPackage = {
name: string;
version: string;
@@ -30,12 +30,14 @@ export declare type LastUpdateRun = {
} & {
limitedPackages: () => string[];
};
export default class Updater<Attrs> extends Component<Attrs> {
interface UpdaterAttrs extends ComponentAttrs {
}
export default class Updater extends Component<UpdaterAttrs> {
isLoading: string | null;
packageUpdates: Record<string, UpdatedPackage>;
lastUpdateCheck: LastUpdateCheck;
get lastUpdateRun(): LastUpdateRun;
oninit(vnode: Mithril.Vnode<Attrs, this>): void;
oninit(vnode: Mithril.Vnode<UpdaterAttrs, this>): void;
view(): (JSX.Element | null)[];
getExtensionUpdates(): Extension[];
getCoreUpdate(): UpdatedPackage | undefined;

View File

@@ -1,14 +1,14 @@
import Mithril from 'mithril';
import type Mithril from 'mithril';
import Modal, { IInternalModalAttrs } from 'flarum/common/components/Modal';
export interface WhyNotModalAttrs extends IInternalModalAttrs {
package: string;
}
export default class WhyNotModal<Attrs extends WhyNotModalAttrs = WhyNotModalAttrs> extends Modal<Attrs> {
export default class WhyNotModal<CustomAttrs extends WhyNotModalAttrs = WhyNotModalAttrs> extends Modal<CustomAttrs> {
loading: boolean;
whyNot: string | null;
className(): string;
title(): any;
oncreate(vnode: Mithril.VnodeDOM<Attrs, this>): void;
oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
content(): JSX.Element;
requestWhyNot(): void;
}

View File

@@ -0,0 +1,13 @@
import Model from 'flarum/common/Model';
export declare type TaskOperations = 'extension_install' | 'extension_remove' | 'extension_update' | 'update_global' | 'update_minor' | 'update_major' | 'update_check' | 'why_not';
export default class Task extends Model {
status(): any;
operation(): any;
command(): any;
package(): any;
output(): any;
createdAt(): any;
startedAt(): any;
finishedAt(): any;
peakMemoryUsed(): string;
}

View File

@@ -0,0 +1,16 @@
import Task from '../models/Task';
import { ApiQueryParamsPlural } from 'flarum/common/Store';
export default class QueueState {
private tasks;
private limit;
private offset;
private total;
load(params?: ApiQueryParamsPlural): any;
getItems(): Task[] | null;
getTotalPages(): number;
pageNumber(): number;
hasPrev(): boolean;
hasNext(): boolean;
prev(): void;
next(): void;
}

View File

@@ -0,0 +1 @@
export default function humanDuration(start: Date, end: Date): any;

View File

@@ -0,0 +1 @@
export default function jumpToQueue(): void;

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,17 +1,17 @@
{
"private": true,
"name": "@flarum/package-manager",
"version": "0.0.0",
"private": true,
"prettier": "@flarum/prettier-config",
"devDependencies": {
"prettier": "^2.5.1",
"flarum-webpack-config": "^2.0.0",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1",
"@flarum/prettier-config": "^1.0.0",
"flarum-tsconfig": "^1.0.2",
"flarum-webpack-config": "^2.0.0",
"prettier": "^2.5.1",
"typescript": "^4.5.4",
"typescript-coverage-report": "^0.6.1"
"typescript-coverage-report": "^0.6.1",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1"
},
"scripts": {
"dev": "webpack --mode development --watch",
@@ -21,9 +21,11 @@
"ci": "yarn install --immutable --immutable-cache",
"analyze": "cross-env ANALYZER=true yarn run build",
"clean-typings": "npx rimraf dist-typings && mkdir dist-typings",
"build-typings": "yarn run clean-typings && ([ -e src/@types ] && cp -r src/@types dist-typings/@types || true) && tsc && yarn run post-build-typings",
"build-typings": "yarn run clean-typings && tsc && [ -e src/@types ] && cp -r src/@types dist-typings/@types",
"check-typings": "tsc --noEmit --emitDeclarationOnly false",
"check-typings-coverage": "typescript-coverage-report",
"post-build-typings": "find dist-typings -type f -name '*.d.ts' -print0 | xargs -0 sed -i 's,../src/@types,@types,g'"
"check-typings-coverage": "typescript-coverage-report"
},
"dependencies": {
"pretty-bytes": "^6.0.0"
}
}

View File

@@ -0,0 +1,34 @@
import app from 'flarum/admin/app';
import Component from 'flarum/common/Component';
import Alert from 'flarum/common/components/Alert';
import Installer from './Installer';
import Updater from './Updater';
export default class ControlSection extends Component {
view() {
return (
<div className="ExtensionPage-permissions PackageManager-controlSection">
<div className="ExtensionPage-permissions-header">
<div className="container">
<h2 className="ExtensionTitle">{app.translator.trans('flarum-package-manager.admin.sections.control.title')}</h2>
</div>
</div>
<div className="container">
{app.data['flarum-package-manager.writable_dirs'] ? (
<>
<Installer />
<Updater />
</>
) : (
<div className="Form-group">
<Alert type="warning" dismissible={false}>
{app.translator.trans('flarum-package-manager.admin.file_permissions')}
</Alert>
</div>
)}
</div>
</div>
);
}
}

View File

@@ -1,20 +1,15 @@
import Mithril from 'mithril';
import type Mithril from 'mithril';
import app from 'flarum/admin/app';
import Component, { ComponentAttrs } from 'flarum/common/Component';
import classList from 'flarum/common/utils/classList';
import icon from 'flarum/common/helpers/icon';
import Tooltip from 'flarum/common/components/Tooltip';
import Button from 'flarum/common/components/Button';
import { Extension as BaseExtension } from 'flarum/admin/AdminApplication';
import { Extension } from 'flarum/admin/AdminApplication';
import { UpdatedPackage } from './Updater';
import WhyNotModal from './WhyNotModal';
/*
* @todo fix in core
*/
export type Extension = BaseExtension & {
name: string;
};
import Label from './Label';
export interface ExtensionItemAttrs extends ComponentAttrs {
extension: Extension;
@@ -29,6 +24,7 @@ export interface ExtensionItemAttrs extends ComponentAttrs {
export default class ExtensionItem<Attrs extends ExtensionItemAttrs = ExtensionItemAttrs> extends Component<Attrs> {
view(vnode: Mithril.Vnode<Attrs, this>): Mithril.Children {
const { extension, updates, onClickUpdate, whyNotWarning, isCore, isDanger } = this.attrs;
const latestVersion = updates['latest-minor'] ?? (updates['latest-major'] && !isCore ? updates['latest-major'] : null);
return (
<div
@@ -45,15 +41,10 @@ export default class ExtensionItem<Attrs extends ExtensionItemAttrs = ExtensionI
<div className="PackageManager-extension-name">{extension.extra['flarum-extension'].title}</div>
<div className="PackageManager-extension-version">
<span className="PackageManager-extension-version-current">{this.version(extension.version)}</span>
{updates['latest-minor'] ? (
<span className="PackageManager-extension-version-latest PackageManager-extension-version-latest--minor">
{this.version(updates['latest-minor']!)}
</span>
) : null}
{updates['latest-major'] && !isCore ? (
<span className="PackageManager-extension-version-latest PackageManager-extension-version-latest--major">
{this.version(updates['latest-major']!)}
</span>
{latestVersion ? (
<Label className="PackageManager-extension-version-latest" type={updates['latest-minor'] ? 'success' : 'warning'}>
{this.version(latestVersion)}
</Label>
) : null}
</div>
</div>
@@ -83,7 +74,7 @@ export default class ExtensionItem<Attrs extends ExtensionItemAttrs = ExtensionI
);
}
private version(v: string): string {
version(v: string): string {
return 'v' + v.replace('v', '');
}
}

View File

@@ -1,16 +1,21 @@
import type Mithril from 'mithril';
import app from 'flarum/admin/app';
import Component from 'flarum/common/Component';
import Component, { ComponentAttrs } from 'flarum/common/Component';
import Button from 'flarum/common/components/Button';
import Stream from 'flarum/common/utils/Stream';
import LoadingModal from 'flarum/admin/components/LoadingModal';
import errorHandler from '../utils/errorHandler';
export default class Installer<Attrs> extends Component<Attrs> {
import errorHandler from '../utils/errorHandler';
import jumpToQueue from '../utils/jumpToQueue';
import { AsyncBackendResponse } from '../shims';
interface InstallerAttrs extends ComponentAttrs {}
export default class Installer extends Component<InstallerAttrs> {
packageName!: Stream<string>;
isLoading: boolean = false;
oninit(vnode: Mithril.Vnode<Attrs, this>): void {
oninit(vnode: Mithril.Vnode<InstallerAttrs, this>): void {
super.oninit(vnode);
this.packageName = Stream('');
@@ -18,7 +23,7 @@ export default class Installer<Attrs> extends Component<Attrs> {
view(): Mithril.Children {
return (
<div className="Form-group">
<div className="Form-group PackageManager-installer">
<label htmlFor="install-extension">{app.translator.trans('flarum-package-manager.admin.extensions.install')}</label>
<p className="helpText">
{app.translator.trans('flarum-package-manager.admin.extensions.install_help', {
@@ -46,7 +51,7 @@ export default class Installer<Attrs> extends Component<Attrs> {
app.modal.show(LoadingModal);
app
.request<{ id: string }>({
.request<AsyncBackendResponse & { id: number }>({
method: 'POST',
url: `${app.forum.attribute('apiUrl')}/package-manager/extensions`,
body: {
@@ -55,13 +60,17 @@ export default class Installer<Attrs> extends Component<Attrs> {
errorHandler,
})
.then((response) => {
const extensionId = response.id;
app.alerts.show(
{ type: 'success' },
app.translator.trans('flarum-package-manager.admin.extensions.successful_install', { extension: extensionId })
);
window.location.href = `${app.forum.attribute('adminUrl')}#/extension/${extensionId}`;
window.location.reload();
if (response.processing) {
jumpToQueue();
} else {
const extensionId = response.id;
app.alerts.show(
{ type: 'success' },
app.translator.trans('flarum-package-manager.admin.extensions.successful_install', { extension: extensionId })
);
window.location.href = `${app.forum.attribute('adminUrl')}#/extension/${extensionId}`;
window.location.reload();
}
})
.finally(() => {
this.isLoading = false;

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