mirror of
https://github.com/flarum/core.git
synced 2025-08-16 13:24:11 +02:00
Compare commits
29 Commits
dk/DISCUSS
...
v1.8.2
Author | SHA1 | Date | |
---|---|---|---|
|
82e08e3fa5 | ||
|
2c4a2b8d9e | ||
|
00866fbba9 | ||
|
0d1d4d46d1 | ||
|
b1383a955f | ||
|
daeab48ae8 | ||
|
e03ca4406d | ||
|
7894c6a69b | ||
|
102e31754a | ||
|
8538f9c8f6 | ||
|
5a4bb7ccf2 | ||
|
d2a6329689 | ||
|
2bc2899a1d | ||
|
5437bf5c23 | ||
|
717af13bb1 | ||
|
e72541e35d | ||
|
577890d89c | ||
|
253a3d281d | ||
|
d27f952584 | ||
|
e5abffc75b | ||
|
d1059c1cc7 | ||
|
777c304ab7 | ||
|
789246b621 | ||
|
980cfd6c28 | ||
|
65390a4fc0 | ||
|
c7c86a77e9 | ||
|
f1f6051deb | ||
|
bded3da42d | ||
|
231cee1f78 |
2
.github/workflows/frontend.yml
vendored
2
.github/workflows/frontend.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
backend_directory: ./
|
backend_directory: ./
|
||||||
js_package_manager: yarn
|
js_package_manager: yarn
|
||||||
cache_dependency_path: ./yarn.lock
|
cache_dependency_path: ./yarn.lock
|
||||||
main_git_branch: main
|
main_git_branch: 1.x
|
||||||
enable_tests: true
|
enable_tests: true
|
||||||
# @TODO: fix bundlewatch
|
# @TODO: fix bundlewatch
|
||||||
enable_bundlewatch: false
|
enable_bundlewatch: false
|
||||||
|
8
.github/workflows/prepare-release.yml
vendored
8
.github/workflows/prepare-release.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Prepare release
|
- name: Prepare release
|
||||||
uses: flarum/action-release@master
|
uses: flarum/action-release@master
|
||||||
env:
|
with:
|
||||||
NEXT_TAG: ${{ inputs.version }}
|
next_tag: ${{ inputs.version }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
OPEN_COLLECTIVE_TOKEN: ${{ secrets.OPEN_COLLECTIVE_TOKEN }}
|
open_collective_token: ${{ secrets.OPEN_COLLECTIVE_TOKEN }}
|
||||||
|
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,5 +1,63 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
* custom contrast color affected by parents (577890d89c593ae5b6cb96083fab69e2f1ae600c)
|
||||||
|
* reply placeholder wrong positioning (253a3d281dbf5ce3fa712b629b80587cf67e7dbe)
|
||||||
|
* (mentions) missed post mentions UI changes with lazy loading [#3832]
|
||||||
|
* (mentions) cannot use newly introduced mentionables extender [#3849]
|
||||||
|
* (mentions) missing slug from post mention links ([5a4bb7c](5a4bb7ccf226f66dd44816cb69b3d7cfe4ad7f7c))
|
||||||
|
|
||||||
|
## [v1.8.0](https://github.com/flarum/framework/compare/v1.7.1...v1.8.0)
|
||||||
|
### Fixed
|
||||||
|
- (a11y) reply placeholder not accessible [#3793]
|
||||||
|
- (bbcode) highlight.js does not work after changing post content [#3817]
|
||||||
|
- (bbcode) localize quote `wrote` string [#3809]
|
||||||
|
- (mentions) mentions XHR fired even after mentioning is done [#3806]
|
||||||
|
- (package-manager) available core updates cause an error in the dashboard ([fab71f2](fab71f2d01fa20ce9b3002833339dc5ea3ea6301))
|
||||||
|
- (tags) not all tags are loaded in the permission grid [#3804]
|
||||||
|
- (tags) tag discussion modal filters with exact matches only after first index [#3786]
|
||||||
|
- (testing) always clear cache in integration test's tearDown [#3818]
|
||||||
|
- `UserSecurityPage` not exported ([232618a](232618aba604ab003425df38b895208c863d3260))
|
||||||
|
- `isDark()` utility can receive null value [#3774]
|
||||||
|
- approving a post does not bump user `comment_count` [#3790]
|
||||||
|
- circular dependencies disable all involved extensions [#3785]
|
||||||
|
- color input overflowing the input box [#3796]
|
||||||
|
- deleting a discussion from the profile does not visually remove it [#3799]
|
||||||
|
- discussion page showing horizontal scroll on iOS [#3821]
|
||||||
|
- empty string displayed as SelectDropdown title [#3773]
|
||||||
|
- filter values are not validated [#3795]
|
||||||
|
- infinite scroll not initialized for notifications on big screens [#3733]
|
||||||
|
- notification subject discussion eager loading fails [#3788]
|
||||||
|
- null as 2nd param in `preg_match` is deprecated [#3801]
|
||||||
|
- unread count in post stream not visible [#3791]
|
||||||
|
- unreadable badge icon on certain colors [#3810]
|
||||||
|
- integrity constraint violation [#3772]
|
||||||
|
### Changed
|
||||||
|
- (core,mentions) limit `mentionedBy` post relation results [#3780]
|
||||||
|
- (likes) limit `likes` relationship results [#3781]
|
||||||
|
- Change some methods from private to protected, to be able to extend the affected classes [#3802]
|
||||||
|
- Do not catch exceptions when testing Console commands [#3813]
|
||||||
|
- drop usage of jquery in `install` and `update` interfaces [#3797]
|
||||||
|
- extensibility improvements [#3729]
|
||||||
|
- major frontend JS cleanup [#3609]
|
||||||
|
- revert ineffective code for encoding of page title [#3768]
|
||||||
|
- speed up post creation time [#3808]
|
||||||
|
### Added
|
||||||
|
- (mentions,tags) tag mentions [#3769]
|
||||||
|
- add delete own posts permission [#3784]
|
||||||
|
- add a trait to flush the formatter cache in tests [#3811]
|
||||||
|
- add user creation to users list page [#3744]
|
||||||
|
- cli command for enabling or disabling an extension [#3816]
|
||||||
|
- conditional extenders [#3759]
|
||||||
|
- provide old content to `Revised` event [#3789]
|
||||||
|
|
||||||
## [v1.7.1](https://github.com/flarum/framework/compare/v1.7.0...v1.7.1)
|
## [v1.7.1](https://github.com/flarum/framework/compare/v1.7.0...v1.7.1)
|
||||||
### Fixed
|
### Fixed
|
||||||
- (tags) composer tag selection modal using wrong primary max & min numbers (abc9670659426b765274376945b818b70d84848c)
|
- (tags) composer tag selection modal using wrong primary max & min numbers (abc9670659426b765274376945b818b70d84848c)
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7",
|
"flarum/core": "^1.8",
|
||||||
"flarum/approval": "^1.7"
|
"flarum/approval": "^1.7"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7",
|
"flarum/core": "^1.8",
|
||||||
"flarum/flags": "^1.7"
|
"flarum/flags": "^1.7"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -44,7 +44,6 @@ class LoadLikesRelationship
|
|||||||
$loadable = null;
|
$loadable = null;
|
||||||
|
|
||||||
if ($data instanceof Discussion) {
|
if ($data instanceof Discussion) {
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
$loadable = $data->newCollection($data->posts)->filter(function ($post) {
|
$loadable = $data->newCollection($data->posts)->filter(function ($post) {
|
||||||
return $post instanceof Post;
|
return $post instanceof Post;
|
||||||
});
|
});
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -78,15 +78,17 @@ return [
|
|||||||
->addInclude(['posts.mentionedBy', 'posts.mentionedBy.user', 'posts.mentionedBy.discussion'])
|
->addInclude(['posts.mentionedBy', 'posts.mentionedBy.user', 'posts.mentionedBy.discussion'])
|
||||||
->load([
|
->load([
|
||||||
'posts.mentionsUsers', 'posts.mentionsPosts', 'posts.mentionsPosts.user',
|
'posts.mentionsUsers', 'posts.mentionsPosts', 'posts.mentionsPosts.user',
|
||||||
'posts.mentionsGroups'
|
'posts.mentionsPosts.discussion', 'posts.mentionsGroups'
|
||||||
])
|
])
|
||||||
->loadWhere('posts.mentionedBy', [LoadMentionedByRelationship::class, 'mutateRelation'])
|
->loadWhere('posts.mentionedBy', [LoadMentionedByRelationship::class, 'mutateRelation'])
|
||||||
->prepareDataForSerialization([LoadMentionedByRelationship::class, 'countRelation']),
|
->prepareDataForSerialization([LoadMentionedByRelationship::class, 'countRelation']),
|
||||||
|
|
||||||
(new Extend\ApiController(Controller\ListDiscussionsController::class))
|
(new Extend\ApiController(Controller\ListDiscussionsController::class))
|
||||||
->load([
|
->load([
|
||||||
'firstPost.mentionsUsers', 'firstPost.mentionsPosts', 'firstPost.mentionsPosts.user', 'firstPost.mentionsGroups',
|
'firstPost.mentionsUsers', 'firstPost.mentionsPosts',
|
||||||
'lastPost.mentionsUsers', 'lastPost.mentionsPosts', 'lastPost.mentionsPosts.user', 'lastPost.mentionsGroups',
|
'firstPost.mentionsPosts.user', 'firstPost.mentionsPosts.discussion', 'firstPost.mentionsGroups',
|
||||||
|
'lastPost.mentionsUsers', 'lastPost.mentionsPosts',
|
||||||
|
'lastPost.mentionsPosts.user', 'lastPost.mentionsPosts.discussion', 'lastPost.mentionsGroups',
|
||||||
]),
|
]),
|
||||||
|
|
||||||
(new Extend\ApiController(Controller\ShowPostController::class))
|
(new Extend\ApiController(Controller\ShowPostController::class))
|
||||||
@@ -98,7 +100,7 @@ return [
|
|||||||
|
|
||||||
(new Extend\ApiController(Controller\ListPostsController::class))
|
(new Extend\ApiController(Controller\ListPostsController::class))
|
||||||
->addInclude(['mentionedBy', 'mentionedBy.user', 'mentionedBy.discussion'])
|
->addInclude(['mentionedBy', 'mentionedBy.user', 'mentionedBy.discussion'])
|
||||||
->load(['mentionsUsers', 'mentionsPosts', 'mentionsPosts.user', 'mentionsGroups'])
|
->load(['mentionsUsers', 'mentionsPosts', 'mentionsPosts.user', 'mentionsPosts.discussion', 'mentionsGroups'])
|
||||||
->loadWhere('mentionedBy', [LoadMentionedByRelationship::class, 'mutateRelation'])
|
->loadWhere('mentionedBy', [LoadMentionedByRelationship::class, 'mutateRelation'])
|
||||||
->prepareDataForSerialization([LoadMentionedByRelationship::class, 'countRelation']),
|
->prepareDataForSerialization([LoadMentionedByRelationship::class, 'countRelation']),
|
||||||
|
|
||||||
|
2
extensions/mentions/js/dist/forum.js
generated
vendored
2
extensions/mentions/js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/mentions/js/dist/forum.js.map
generated
vendored
2
extensions/mentions/js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -5,12 +5,9 @@ import TextEditorButton from 'flarum/common/components/TextEditorButton';
|
|||||||
import KeyboardNavigatable from 'flarum/common/utils/KeyboardNavigatable';
|
import KeyboardNavigatable from 'flarum/common/utils/KeyboardNavigatable';
|
||||||
|
|
||||||
import AutocompleteDropdown from './fragments/AutocompleteDropdown';
|
import AutocompleteDropdown from './fragments/AutocompleteDropdown';
|
||||||
import MentionFormats from './mentionables/formats/MentionFormats';
|
|
||||||
import MentionableModels from './mentionables/MentionableModels';
|
import MentionableModels from './mentionables/MentionableModels';
|
||||||
|
|
||||||
export default function addComposerAutocomplete() {
|
export default function addComposerAutocomplete() {
|
||||||
app.mentionFormats = new MentionFormats();
|
|
||||||
|
|
||||||
const $container = $('<div class="ComposerBody-mentionsDropdownContainer"></div>');
|
const $container = $('<div class="ComposerBody-mentionsDropdownContainer"></div>');
|
||||||
const dropdown = new AutocompleteDropdown();
|
const dropdown = new AutocompleteDropdown();
|
||||||
|
|
||||||
|
@@ -118,7 +118,7 @@ export default function addMentionedByList() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const limit = 4;
|
const limit = 4;
|
||||||
const overLimit = repliers.length > limit;
|
const overLimit = post.mentionedByCount() > limit;
|
||||||
|
|
||||||
// Create a list of unique users who have replied. So even if a user has
|
// Create a list of unique users who have replied. So even if a user has
|
||||||
// replied twice, they will only be in this array once.
|
// replied twice, they will only be in this array once.
|
||||||
@@ -136,7 +136,7 @@ export default function addMentionedByList() {
|
|||||||
// others" name to the end of the list. Clicking on it will display a modal
|
// others" name to the end of the list. Clicking on it will display a modal
|
||||||
// with a full list of names.
|
// with a full list of names.
|
||||||
if (overLimit) {
|
if (overLimit) {
|
||||||
const count = repliers.length - names.length;
|
const count = post.mentionedByCount() - names.length;
|
||||||
|
|
||||||
names.push(app.translator.trans('flarum-mentions.forum.post.others_text', { count }));
|
names.push(app.translator.trans('flarum-mentions.forum.post.others_text', { count }));
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ import type MentionFormat from '../mentionables/formats/MentionFormat';
|
|||||||
|
|
||||||
export default class Mentionables implements IExtender<ForumApplication> {
|
export default class Mentionables implements IExtender<ForumApplication> {
|
||||||
protected formats: (new () => MentionFormat)[] = [];
|
protected formats: (new () => MentionFormat)[] = [];
|
||||||
protected mentionables: Record<string, (new () => MentionableModel)[]> = {};
|
protected mentionables: Record<string, (new (...args: any[]) => MentionableModel)[]> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a new mention format.
|
* Register a new mention format.
|
||||||
@@ -26,7 +26,7 @@ export default class Mentionables implements IExtender<ForumApplication> {
|
|||||||
* @param mentionable The mentionable instance to register.
|
* @param mentionable The mentionable instance to register.
|
||||||
* Must extend MentionableModel.
|
* Must extend MentionableModel.
|
||||||
*/
|
*/
|
||||||
mentionable(symbol: string, mentionable: new () => MentionableModel): this {
|
mentionable(symbol: string, mentionable: new (...args: any[]) => MentionableModel): this {
|
||||||
if (!this.mentionables[symbol]) {
|
if (!this.mentionables[symbol]) {
|
||||||
this.mentionables[symbol] = [];
|
this.mentionables[symbol] = [];
|
||||||
}
|
}
|
||||||
|
@@ -13,11 +13,14 @@ import addComposerAutocomplete from './addComposerAutocomplete';
|
|||||||
import PostMentionedNotification from './components/PostMentionedNotification';
|
import PostMentionedNotification from './components/PostMentionedNotification';
|
||||||
import UserMentionedNotification from './components/UserMentionedNotification';
|
import UserMentionedNotification from './components/UserMentionedNotification';
|
||||||
import GroupMentionedNotification from './components/GroupMentionedNotification';
|
import GroupMentionedNotification from './components/GroupMentionedNotification';
|
||||||
|
import MentionFormats from './mentionables/formats/MentionFormats';
|
||||||
import UserPage from 'flarum/forum/components/UserPage';
|
import UserPage from 'flarum/forum/components/UserPage';
|
||||||
import LinkButton from 'flarum/common/components/LinkButton';
|
import LinkButton from 'flarum/common/components/LinkButton';
|
||||||
import User from 'flarum/common/models/User';
|
import User from 'flarum/common/models/User';
|
||||||
import Model from 'flarum/common/Model';
|
import Model from 'flarum/common/Model';
|
||||||
|
|
||||||
|
app.mentionFormats = new MentionFormats();
|
||||||
|
|
||||||
export { default as extend } from './extend';
|
export { default as extend } from './extend';
|
||||||
|
|
||||||
app.initializers.add('flarum-mentions', function () {
|
app.initializers.add('flarum-mentions', function () {
|
||||||
|
@@ -4,7 +4,7 @@ import TagMention from '../TagMention';
|
|||||||
|
|
||||||
export default class HashMentionFormat extends MentionFormat {
|
export default class HashMentionFormat extends MentionFormat {
|
||||||
public mentionables: (new (...args: any[]) => MentionableModel)[] = [TagMention];
|
public mentionables: (new (...args: any[]) => MentionableModel)[] = [TagMention];
|
||||||
protected extendable: boolean = false;
|
protected extendable: boolean = true;
|
||||||
|
|
||||||
public trigger(): string {
|
public trigger(): string {
|
||||||
return '#';
|
return '#';
|
||||||
|
@@ -29,7 +29,7 @@ class LoadMentionedByRelationship
|
|||||||
$actor = RequestUtil::getActor($request);
|
$actor = RequestUtil::getActor($request);
|
||||||
|
|
||||||
return $query
|
return $query
|
||||||
->with(['mentionsPosts', 'mentionsPosts.user', 'mentionsUsers'])
|
->with(['mentionsPosts', 'mentionsPosts.user', 'mentionsPosts.discussion', 'mentionsUsers'])
|
||||||
->whereVisibleTo($actor)
|
->whereVisibleTo($actor)
|
||||||
->oldest()
|
->oldest()
|
||||||
// Limiting a relationship results is only possible because
|
// Limiting a relationship results is only possible because
|
||||||
@@ -47,10 +47,19 @@ class LoadMentionedByRelationship
|
|||||||
$loadable = null;
|
$loadable = null;
|
||||||
|
|
||||||
if ($data instanceof Discussion) {
|
if ($data instanceof Discussion) {
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
$loadable = $data->newCollection($data->posts)->filter(function ($post) {
|
$loadable = $data->newCollection($data->posts)->filter(function ($post) {
|
||||||
return $post instanceof Post;
|
return $post instanceof Post;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// firstPost and lastPost might have been included in the API response,
|
||||||
|
// so we have to make sure counts are also loaded for them.
|
||||||
|
if ($data->firstPost) {
|
||||||
|
$loadable->push($data->firstPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data->lastPost) {
|
||||||
|
$loadable->push($data->lastPost);
|
||||||
|
}
|
||||||
} elseif ($data instanceof Collection) {
|
} elseif ($data instanceof Collection) {
|
||||||
$loadable = $data;
|
$loadable = $data;
|
||||||
} elseif ($data instanceof Post) {
|
} elseif ($data instanceof Post) {
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
namespace Flarum\Mentions\Formatter;
|
namespace Flarum\Mentions\Formatter;
|
||||||
|
|
||||||
|
use Flarum\Discussion\Discussion;
|
||||||
|
use Flarum\Http\SlugManager;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
use s9e\TextFormatter\Renderer;
|
use s9e\TextFormatter\Renderer;
|
||||||
use s9e\TextFormatter\Utils;
|
use s9e\TextFormatter\Utils;
|
||||||
@@ -21,9 +23,15 @@ class FormatPostMentions
|
|||||||
*/
|
*/
|
||||||
private $translator;
|
private $translator;
|
||||||
|
|
||||||
public function __construct(TranslatorInterface $translator)
|
/**
|
||||||
|
* @var SlugManager
|
||||||
|
*/
|
||||||
|
private $slugManager;
|
||||||
|
|
||||||
|
public function __construct(TranslatorInterface $translator, SlugManager $slugManager)
|
||||||
{
|
{
|
||||||
$this->translator = $translator;
|
$this->translator = $translator;
|
||||||
|
$this->slugManager = $slugManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +64,12 @@ class FormatPostMentions
|
|||||||
$attributes['displayname'] = $this->translator->trans('core.lib.username.deleted_text');
|
$attributes['displayname'] = $this->translator->trans('core.lib.username.deleted_text');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($post) {
|
||||||
|
$attributes['discussionid'] = $this->slugManager
|
||||||
|
->forResource(Discussion::class)
|
||||||
|
->toSlug($post->discussion);
|
||||||
|
}
|
||||||
|
|
||||||
return $attributes;
|
return $attributes;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
"source": "https://github.com/flarum/package-manager"
|
"source": "https://github.com/flarum/package-manager"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.5.0",
|
"flarum/core": "^1.8",
|
||||||
"composer/composer": "^2.3"
|
"composer/composer": "^2.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7",
|
"flarum/core": "^1.8",
|
||||||
"pusher/pusher-php-server": "^2.2"
|
"pusher/pusher-php-server": "^2.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -25,4 +25,11 @@ class UserPolicy extends AbstractPolicy
|
|||||||
return $this->deny();
|
return $this->deny();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function uploadAvatar(User $actor, User $user)
|
||||||
|
{
|
||||||
|
if ($actor->suspended_until && $actor->suspended_until->isFuture()) {
|
||||||
|
return $this->deny();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
extensions/suspend/tests/fixtures/avatar.png
vendored
Normal file
BIN
extensions/suspend/tests/fixtures/avatar.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Suspend\Tests\integration\api\users;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Laminas\Diactoros\UploadedFile;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class UploadAvatarTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-suspend');
|
||||||
|
|
||||||
|
$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, 'suspended_until' => Carbon::now()->addDay(), 'suspend_message' => 'You have been suspended.', 'suspend_reason' => 'Suspended for acme reasons.'],
|
||||||
|
['id' => 4, 'username' => 'acme4', 'email' => 'acme4@machine.local', 'is_email_confirmed' => 1],
|
||||||
|
['id' => 5, 'username' => 'acme5', 'email' => 'acme5@machine.local', 'is_email_confirmed' => 1, 'suspended_until' => Carbon::now()->subDay(), 'suspend_message' => 'You have been suspended.', 'suspend_reason' => 'Suspended for acme reasons.'],
|
||||||
|
],
|
||||||
|
'groups' => [
|
||||||
|
['id' => 5, 'name_singular' => 'can_edit_users', 'name_plural' => 'can_edit_users', 'is_hidden' => 0]
|
||||||
|
],
|
||||||
|
'group_user' => [
|
||||||
|
['user_id' => 2, 'group_id' => 5]
|
||||||
|
],
|
||||||
|
'group_permission' => [
|
||||||
|
['permission' => 'user.edit', 'group_id' => 5],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider allowedToUploadAvatar
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function can_suspend_user_if_allowed(?int $authenticatedAs, int $targetUserId, string $message)
|
||||||
|
{
|
||||||
|
$response = $this->sendUploadAvatarRequest($authenticatedAs, $targetUserId);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode(), $response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider unallowedToUploadAvatar
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function cannot_suspend_user_if_not_allowed(?int $authenticatedAs, int $targetUserId, string $message)
|
||||||
|
{
|
||||||
|
$response = $this->sendUploadAvatarRequest($authenticatedAs, $targetUserId);
|
||||||
|
|
||||||
|
$this->assertEquals(403, $response->getStatusCode(), $response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function allowedToUploadAvatar(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[1, 2, 'Admin can upload avatar for any user'],
|
||||||
|
[2, 3, 'User with permission can upload avatar for suspended user'],
|
||||||
|
[2, 2, 'User with permission can upload avatar for self'],
|
||||||
|
[2, 4, 'User with permission can upload avatar for other user'],
|
||||||
|
[1, 1, 'Admin can upload avatar for self'],
|
||||||
|
[5, 5, 'Suspended user can upload avatar for self if suspension expired'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unallowedToUploadAvatar(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[3, 3, 'Suspended user cannot upload avatar for self'],
|
||||||
|
[3, 2, 'Suspended user cannot upload avatar for other user'],
|
||||||
|
[4, 3, 'User without permission cannot upload avatar for suspended user'],
|
||||||
|
[4, 2, 'User without permission cannot upload avatar for other user'],
|
||||||
|
[5, 2, 'Suspended user cannot upload avatar for other user if suspension expired'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sendUploadAvatarRequest(?int $authenticatedAs, int $targetUserId): ResponseInterface
|
||||||
|
{
|
||||||
|
return $this->send(
|
||||||
|
$this->request('POST', "/api/users/$targetUserId/avatar", [
|
||||||
|
'authenticatedAs' => $authenticatedAs,
|
||||||
|
])->withHeader('Content-Type', 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW')->withUploadedFiles([
|
||||||
|
'avatar' => new UploadedFile(__DIR__.'/../../../fixtures/avatar.png', 0, UPLOAD_ERR_OK, 'avatar.png', 'image/png')
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"flarum/core": "^1.7"
|
"flarum/core": "^1.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@@ -19,6 +19,10 @@
|
|||||||
&.colored &-text, &--colored &-text {
|
&.colored &-text, &--colored &-text {
|
||||||
color: var(--tag-color) !important;
|
color: var(--tag-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.TagLabel {
|
.TagLabel {
|
||||||
|
@@ -26,7 +26,7 @@ class TagRepository
|
|||||||
return Tag::query();
|
return Tag::query();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function queryVisibleTo(User $actor): Builder
|
public function queryVisibleTo(?User $actor = null): Builder
|
||||||
{
|
{
|
||||||
return $this->scopeVisibleTo($this->query(), $actor);
|
return $this->scopeVisibleTo($this->query(), $actor);
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ class TagRepository
|
|||||||
* @param User|null $user
|
* @param User|null $user
|
||||||
* @return Builder<Tag>
|
* @return Builder<Tag>
|
||||||
*/
|
*/
|
||||||
protected function scopeVisibleTo(Builder $query, User $user = null)
|
protected function scopeVisibleTo(Builder $query, ?User $user = null)
|
||||||
{
|
{
|
||||||
if ($user !== null) {
|
if ($user !== null) {
|
||||||
$query->whereVisibleTo($user);
|
$query->whereVisibleTo($user);
|
||||||
|
1
framework/core/js/dist-typings/admin/compat.d.ts
generated
vendored
1
framework/core/js/dist-typings/admin/compat.d.ts
generated
vendored
@@ -122,6 +122,7 @@ declare const _default: {
|
|||||||
'components/TextEditorButton': typeof import("../common/components/TextEditorButton").default;
|
'components/TextEditorButton': typeof import("../common/components/TextEditorButton").default;
|
||||||
'components/Tooltip': typeof import("../common/components/Tooltip").default;
|
'components/Tooltip': typeof import("../common/components/Tooltip").default;
|
||||||
'components/EditUserModal': typeof import("../common/components/EditUserModal").default;
|
'components/EditUserModal': typeof import("../common/components/EditUserModal").default;
|
||||||
|
'components/LabelValue': typeof import("../common/components/LabelValue").default;
|
||||||
Model: typeof import("../common/Model").default;
|
Model: typeof import("../common/Model").default;
|
||||||
Application: typeof import("../common/Application").default;
|
Application: typeof import("../common/Application").default;
|
||||||
'helpers/fullTime': typeof import("../common/helpers/fullTime").default;
|
'helpers/fullTime': typeof import("../common/helpers/fullTime").default;
|
||||||
|
2
framework/core/js/dist-typings/common/compat.d.ts
generated
vendored
2
framework/core/js/dist-typings/common/compat.d.ts
generated
vendored
@@ -86,6 +86,7 @@ import isObject from './utils/isObject';
|
|||||||
import AlertManagerState from './states/AlertManagerState';
|
import AlertManagerState from './states/AlertManagerState';
|
||||||
import ModalManagerState from './states/ModalManagerState';
|
import ModalManagerState from './states/ModalManagerState';
|
||||||
import PageState from './states/PageState';
|
import PageState from './states/PageState';
|
||||||
|
import LabelValue from './components/LabelValue';
|
||||||
declare const _default: {
|
declare const _default: {
|
||||||
extenders: {
|
extenders: {
|
||||||
Model: typeof import("./extenders/Model").default;
|
Model: typeof import("./extenders/Model").default;
|
||||||
@@ -174,6 +175,7 @@ declare const _default: {
|
|||||||
'components/TextEditorButton': typeof TextEditorButton;
|
'components/TextEditorButton': typeof TextEditorButton;
|
||||||
'components/Tooltip': typeof Tooltip;
|
'components/Tooltip': typeof Tooltip;
|
||||||
'components/EditUserModal': typeof EditUserModal;
|
'components/EditUserModal': typeof EditUserModal;
|
||||||
|
'components/LabelValue': typeof LabelValue;
|
||||||
Model: typeof Model;
|
Model: typeof Model;
|
||||||
Application: typeof Application;
|
Application: typeof Application;
|
||||||
'helpers/fullTime': typeof fullTime;
|
'helpers/fullTime': typeof fullTime;
|
||||||
|
2
framework/core/js/dist-typings/common/helpers/textContrastClass.d.ts
generated
vendored
2
framework/core/js/dist-typings/common/helpers/textContrastClass.d.ts
generated
vendored
@@ -1 +1 @@
|
|||||||
export default function textContrastClass(hexcolor: string | null): string;
|
export default function textContrastClass(hexcolor: string | null | undefined): string;
|
||||||
|
3
framework/core/js/dist-typings/forum/compat.d.ts
generated
vendored
3
framework/core/js/dist-typings/forum/compat.d.ts
generated
vendored
@@ -71,6 +71,7 @@ import BasicEditorDriver from '../common/utils/BasicEditorDriver';
|
|||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import ForumApplication from './ForumApplication';
|
import ForumApplication from './ForumApplication';
|
||||||
import isSafariMobile from './utils/isSafariMobile';
|
import isSafariMobile from './utils/isSafariMobile';
|
||||||
|
import AccessTokensList from './components/AccessTokensList';
|
||||||
declare const _default: {
|
declare const _default: {
|
||||||
extenders: {
|
extenders: {
|
||||||
Model: typeof import("../common/extenders/Model").default;
|
Model: typeof import("../common/extenders/Model").default;
|
||||||
@@ -159,6 +160,7 @@ declare const _default: {
|
|||||||
'components/TextEditorButton': typeof import("../common/components/TextEditorButton").default;
|
'components/TextEditorButton': typeof import("../common/components/TextEditorButton").default;
|
||||||
'components/Tooltip': typeof import("../common/components/Tooltip").default;
|
'components/Tooltip': typeof import("../common/components/Tooltip").default;
|
||||||
'components/EditUserModal': typeof import("../common/components/EditUserModal").default;
|
'components/EditUserModal': typeof import("../common/components/EditUserModal").default;
|
||||||
|
'components/LabelValue': typeof import("../common/components/LabelValue").default;
|
||||||
Model: typeof import("../common/Model").default;
|
Model: typeof import("../common/Model").default;
|
||||||
Application: typeof import("../common/Application").default;
|
Application: typeof import("../common/Application").default;
|
||||||
'helpers/fullTime': typeof import("../common/helpers/fullTime").default;
|
'helpers/fullTime': typeof import("../common/helpers/fullTime").default;
|
||||||
@@ -276,6 +278,7 @@ declare const _default: {
|
|||||||
'components/DiscussionListItem': typeof DiscussionListItem;
|
'components/DiscussionListItem': typeof DiscussionListItem;
|
||||||
'components/LoadingPost': typeof LoadingPost;
|
'components/LoadingPost': typeof LoadingPost;
|
||||||
'components/PostsUserPage': typeof PostsUserPage;
|
'components/PostsUserPage': typeof PostsUserPage;
|
||||||
|
'components/AccessTokensList': typeof AccessTokensList;
|
||||||
'resolvers/DiscussionPageResolver': typeof DiscussionPageResolver;
|
'resolvers/DiscussionPageResolver': typeof DiscussionPageResolver;
|
||||||
routes: typeof routes;
|
routes: typeof routes;
|
||||||
ForumApplication: typeof ForumApplication;
|
ForumApplication: typeof ForumApplication;
|
||||||
|
2
framework/core/js/dist-typings/forum/components/AvatarEditor.d.ts
generated
vendored
2
framework/core/js/dist-typings/forum/components/AvatarEditor.d.ts
generated
vendored
@@ -72,7 +72,7 @@ export default class AvatarEditor extends Component<import("../../common/Compone
|
|||||||
remove(): void;
|
remove(): void;
|
||||||
/**
|
/**
|
||||||
* After a successful upload/removal, push the updated user data into the
|
* After a successful upload/removal, push the updated user data into the
|
||||||
* store, and force a recomputation of the user's avatar color.
|
* store, and force a re-computation of the user's avatar color.
|
||||||
*
|
*
|
||||||
* @param {object} response
|
* @param {object} response
|
||||||
* @protected
|
* @protected
|
||||||
|
2
framework/core/js/dist/admin.js
generated
vendored
2
framework/core/js/dist/admin.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/admin.js.map
generated
vendored
2
framework/core/js/dist/admin.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum.js
generated
vendored
2
framework/core/js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum.js.map
generated
vendored
2
framework/core/js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -411,12 +411,23 @@ export default class Application {
|
|||||||
pageNumber: 1,
|
pageNumber: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const title =
|
let title =
|
||||||
onHomepage || !this.title
|
onHomepage || !this.title
|
||||||
? extractText(app.translator.trans('core.lib.meta_titles.without_page_title', params))
|
? extractText(app.translator.trans('core.lib.meta_titles.without_page_title', params))
|
||||||
: extractText(app.translator.trans('core.lib.meta_titles.with_page_title', params));
|
: extractText(app.translator.trans('core.lib.meta_titles.with_page_title', params));
|
||||||
|
|
||||||
document.title = count + title;
|
title = count + title;
|
||||||
|
|
||||||
|
// We pass the title through a DOMParser to allow HTML entities
|
||||||
|
// to be rendered correctly, while still preventing XSS attacks
|
||||||
|
// from user input by using a script-disabled environment.
|
||||||
|
// https://github.com/flarum/framework/issues/3514
|
||||||
|
// https://github.com/flarum/framework/pull/3684
|
||||||
|
// This is only a temporary solution for 1.x,
|
||||||
|
// and the actual source of the issue will be fixed in 2.x
|
||||||
|
// Actual source of the issue: https://github.com/flarum/framework/issues/3685
|
||||||
|
const parser = new DOMParser();
|
||||||
|
document.title = parser.parseFromString(title, 'text/html').body.innerText;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected transformRequestOptions<ResponseType>(flarumOptions: FlarumRequestOptions<ResponseType>): InternalFlarumRequestOptions<ResponseType> {
|
protected transformRequestOptions<ResponseType>(flarumOptions: FlarumRequestOptions<ResponseType>): InternalFlarumRequestOptions<ResponseType> {
|
||||||
|
@@ -90,6 +90,7 @@ import isObject from './utils/isObject';
|
|||||||
import AlertManagerState from './states/AlertManagerState';
|
import AlertManagerState from './states/AlertManagerState';
|
||||||
import ModalManagerState from './states/ModalManagerState';
|
import ModalManagerState from './states/ModalManagerState';
|
||||||
import PageState from './states/PageState';
|
import PageState from './states/PageState';
|
||||||
|
import LabelValue from './components/LabelValue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extenders,
|
extenders,
|
||||||
@@ -167,6 +168,7 @@ export default {
|
|||||||
'components/TextEditorButton': TextEditorButton,
|
'components/TextEditorButton': TextEditorButton,
|
||||||
'components/Tooltip': Tooltip,
|
'components/Tooltip': Tooltip,
|
||||||
'components/EditUserModal': EditUserModal,
|
'components/EditUserModal': EditUserModal,
|
||||||
|
'components/LabelValue': LabelValue,
|
||||||
Model: Model,
|
Model: Model,
|
||||||
Application: Application,
|
Application: Application,
|
||||||
'helpers/fullTime': fullTime,
|
'helpers/fullTime': fullTime,
|
||||||
|
@@ -28,7 +28,7 @@ export default class Badge<CustomAttrs extends IBadgeAttrs = IBadgeAttrs> extend
|
|||||||
view() {
|
view() {
|
||||||
const { type, icon: iconName, label, color, style = {}, ...attrs } = this.attrs;
|
const { type, icon: iconName, label, color, style = {}, ...attrs } = this.attrs;
|
||||||
|
|
||||||
const className = classList('Badge', [type && `Badge--${type}`], attrs.className, color && textContrastClass(color));
|
const className = classList('Badge', [type && `Badge--${type}`], attrs.className, textContrastClass(color));
|
||||||
|
|
||||||
const iconChild = iconName ? icon(iconName, { className: 'Badge-icon' }) : m.trust(' ');
|
const iconChild = iconName ? icon(iconName, { className: 'Badge-icon' }) : m.trust(' ');
|
||||||
|
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
import isDark from '../utils/isDark';
|
import isDark from '../utils/isDark';
|
||||||
|
|
||||||
export default function textContrastClass(hexcolor: string | null): string {
|
export default function textContrastClass(hexcolor: string | null | undefined): string {
|
||||||
|
if (!hexcolor) return 'text-contrast--unchanged';
|
||||||
|
|
||||||
return isDark(hexcolor) ? 'text-contrast--light' : 'text-contrast--dark';
|
return isDark(hexcolor) ? 'text-contrast--light' : 'text-contrast--dark';
|
||||||
}
|
}
|
||||||
|
@@ -75,6 +75,7 @@ import BasicEditorDriver from '../common/utils/BasicEditorDriver';
|
|||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import ForumApplication from './ForumApplication';
|
import ForumApplication from './ForumApplication';
|
||||||
import isSafariMobile from './utils/isSafariMobile';
|
import isSafariMobile from './utils/isSafariMobile';
|
||||||
|
import AccessTokensList from './components/AccessTokensList';
|
||||||
|
|
||||||
export default Object.assign(compat, {
|
export default Object.assign(compat, {
|
||||||
'utils/PostControls': PostControls,
|
'utils/PostControls': PostControls,
|
||||||
@@ -150,6 +151,7 @@ export default Object.assign(compat, {
|
|||||||
'components/DiscussionListItem': DiscussionListItem,
|
'components/DiscussionListItem': DiscussionListItem,
|
||||||
'components/LoadingPost': LoadingPost,
|
'components/LoadingPost': LoadingPost,
|
||||||
'components/PostsUserPage': PostsUserPage,
|
'components/PostsUserPage': PostsUserPage,
|
||||||
|
'components/AccessTokensList': AccessTokensList,
|
||||||
'resolvers/DiscussionPageResolver': DiscussionPageResolver,
|
'resolvers/DiscussionPageResolver': DiscussionPageResolver,
|
||||||
routes: routes,
|
routes: routes,
|
||||||
ForumApplication: ForumApplication,
|
ForumApplication: ForumApplication,
|
||||||
|
@@ -204,7 +204,7 @@ export default class AvatarEditor extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* After a successful upload/removal, push the updated user data into the
|
* After a successful upload/removal, push the updated user data into the
|
||||||
* store, and force a recomputation of the user's avatar color.
|
* store, and force a re-computation of the user's avatar color.
|
||||||
*
|
*
|
||||||
* @param {object} response
|
* @param {object} response
|
||||||
* @protected
|
* @protected
|
||||||
|
@@ -98,7 +98,7 @@ export default class DiscussionListItem<CustomAttrs extends IDiscussionListItemA
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={classList('Slidable-underneath Slidable-underneath--left Slidable-underneath--elastic', { disabled: isUnread })}
|
className={classList('Slidable-underneath Slidable-underneath--left Slidable-underneath--elastic', { disabled: !isUnread })}
|
||||||
onclick={this.markAsRead.bind(this)}
|
onclick={this.markAsRead.bind(this)}
|
||||||
>
|
>
|
||||||
{icon('fas fa-check')}
|
{icon('fas fa-check')}
|
||||||
|
@@ -176,4 +176,10 @@ blockquote ol:last-child {
|
|||||||
--contrast-color: var(--text-on-dark);
|
--contrast-color: var(--text-on-dark);
|
||||||
color: var(--contrast-color);
|
color: var(--contrast-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This exists to prevent inheriting the contrast color from a parent element.
|
||||||
|
// Like when a badge is inside a tag hero.
|
||||||
|
&--unchanged {
|
||||||
|
--contrast-color: var(--unchanged-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -441,7 +441,7 @@
|
|||||||
color: var(--muted-color);
|
color: var(--muted-color);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
width: 100%;
|
width: calc(~"100% + 20px * 2");
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.Post-header {
|
.Post-header {
|
||||||
|
@@ -62,7 +62,7 @@ class UploadFaviconController extends UploadImageController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$encodedImage = $this->imageManager->make($file->getStream())->resize(64, 64, function ($constraint) {
|
$encodedImage = $this->imageManager->make($file->getStream()->getMetadata('uri'))->resize(64, 64, function ($constraint) {
|
||||||
$constraint->aspectRatio();
|
$constraint->aspectRatio();
|
||||||
$constraint->upsize();
|
$constraint->upsize();
|
||||||
})->encode('png');
|
})->encode('png');
|
||||||
|
@@ -38,7 +38,7 @@ class UploadLogoController extends UploadImageController
|
|||||||
*/
|
*/
|
||||||
protected function makeImage(UploadedFileInterface $file): Image
|
protected function makeImage(UploadedFileInterface $file): Image
|
||||||
{
|
{
|
||||||
$encodedImage = $this->imageManager->make($file->getStream())->heighten(60, function ($constraint) {
|
$encodedImage = $this->imageManager->make($file->getStream()->getMetadata('uri'))->heighten(60, function ($constraint) {
|
||||||
$constraint->upsize();
|
$constraint->upsize();
|
||||||
})->encode('png');
|
})->encode('png');
|
||||||
|
|
||||||
|
@@ -256,7 +256,10 @@ abstract class AbstractModel extends Eloquent
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newCollection(array $models = [])
|
/**
|
||||||
|
* @param \Illuminate\Support\Collection|array $models
|
||||||
|
*/
|
||||||
|
public function newCollection($models = [])
|
||||||
{
|
{
|
||||||
return new Collection($models);
|
return new Collection($models);
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ class Application
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.8.0-dev';
|
const VERSION = '1.8.2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The IoC container for the Flarum application.
|
* The IoC container for the Flarum application.
|
||||||
|
@@ -41,7 +41,7 @@ class GroupRepository
|
|||||||
return $this->scopeVisibleTo($query, $actor)->firstOrFail();
|
return $this->scopeVisibleTo($query, $actor)->firstOrFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function queryVisibleTo(User $actor = null)
|
public function queryVisibleTo(?User $actor = null)
|
||||||
{
|
{
|
||||||
return $this->scopeVisibleTo($this->query(), $actor);
|
return $this->scopeVisibleTo($this->query(), $actor);
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ class GroupRepository
|
|||||||
* @param User|null $actor
|
* @param User|null $actor
|
||||||
* @return Builder<Group>
|
* @return Builder<Group>
|
||||||
*/
|
*/
|
||||||
protected function scopeVisibleTo(Builder $query, User $actor = null)
|
protected function scopeVisibleTo(Builder $query, ?User $actor = null)
|
||||||
{
|
{
|
||||||
if ($actor !== null) {
|
if ($actor !== null) {
|
||||||
$query->whereVisibleTo($actor);
|
$query->whereVisibleTo($actor);
|
||||||
|
@@ -29,7 +29,7 @@ class PostRepository
|
|||||||
* @param User|null $user
|
* @param User|null $user
|
||||||
* @return Builder<Post>
|
* @return Builder<Post>
|
||||||
*/
|
*/
|
||||||
public function queryVisibleTo(User $user = null)
|
public function queryVisibleTo(?User $user = null)
|
||||||
{
|
{
|
||||||
$query = $this->query();
|
$query = $this->query();
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
namespace Flarum\Queue;
|
namespace Flarum\Queue;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandling;
|
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandling;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
@@ -39,4 +39,15 @@ class UserPolicy extends AbstractPolicy
|
|||||||
return $this->allow();
|
return $this->allow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function uploadAvatar(User $actor, User $user)
|
||||||
|
{
|
||||||
|
if ($actor->id === $user->id) {
|
||||||
|
return $this->allow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($actor->id !== $user->id) {
|
||||||
|
return $actor->can('edit', $user);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -87,7 +87,7 @@ class AvatarValidator extends AbstractValidator
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->imageManager->make($file->getStream());
|
$this->imageManager->make($file->getStream()->getMetadata('uri'));
|
||||||
} catch (NotReadableException $_e) {
|
} catch (NotReadableException $_e) {
|
||||||
$this->raise('image');
|
$this->raise('image');
|
||||||
}
|
}
|
||||||
|
@@ -68,13 +68,11 @@ class UploadAvatarHandler
|
|||||||
|
|
||||||
$user = $this->users->findOrFail($command->userId);
|
$user = $this->users->findOrFail($command->userId);
|
||||||
|
|
||||||
if ($actor->id !== $user->id) {
|
$actor->assertCan('uploadAvatar', $user);
|
||||||
$actor->assertCan('edit', $user);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->validator->assertValid(['avatar' => $command->file]);
|
$this->validator->assertValid(['avatar' => $command->file]);
|
||||||
|
|
||||||
$image = $this->imageManager->make($command->file->getStream());
|
$image = $this->imageManager->make($command->file->getStream()->getMetadata('uri'));
|
||||||
|
|
||||||
$this->events->dispatch(
|
$this->events->dispatch(
|
||||||
new AvatarSaving($user, $actor, $image)
|
new AvatarSaving($user, $actor, $image)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "flarum-webpack-config",
|
"name": "flarum-webpack-config",
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"description": "Webpack config for Flarum JS and TS transpilation.",
|
"description": "Webpack config for Flarum JS and TS transpilation.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "Flarum Team",
|
"author": "Flarum Team",
|
||||||
|
Reference in New Issue
Block a user