1
0
mirror of https://github.com/flarum/core.git synced 2025-08-13 20:04:24 +02:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Davide Iadeluca
a94bf44072 1.8.8 & 1.8.9 change log (#4123)
* docs: add `1.8.8` change log

* docs: add `1.8.9` change log
2024-11-20 16:14:08 +00:00
IanM
ce82834a58 chore: response can return ResponseInterface 2024-11-20 13:51:54 +00:00
Davide Iadeluca
397642ab5a [1.x] Implement Support for Translatable Validation Attribute Errors (#4070)
* chore: add validation translations

* feat: implement ability to translate validation attributes

* chore: change translation key

* style: formatting

* perf: cache `getAttributeNames`

* perf: cache `getAttributeNames`

* chore

* style: formatting
2024-11-20 08:51:17 +00:00
IanM
79e17b3bde chore: comment 8.4 for now 2024-11-19 20:11:38 +00:00
IanM
c6ba2cd0d5 chore: remove 8.4 until 1.8.9 release 2024-11-19 20:08:02 +00:00
IanM
5d6795c353 [1.x] chore: PHP 8.4 support (#4105)
* chore: php8.4 testing

* update laminas

* chore: exclude phpstan on php8.4
2024-11-19 17:26:02 +00:00
IanM
d61c3cf277 chore: change private to protected, allowing extensibility (#4119) 2024-11-19 17:24:02 +00:00
flarum-bot
a0b9add2d8 Bundled output for commit 88abe63a8f
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2024-11-19 17:22:16 +00:00
Davide Iadeluca
88abe63a8f fix: change condition when unread label is shown in Scrubber (#4116) 2024-11-19 17:18:11 +00:00
IanM
9d00490591 chore: bump ver (#4120) 2024-11-19 17:11:05 +00:00
flarum-bot
7c1f55d985 Bundled output for commit 345023242b
Includes transpiled JS/TS, and Typescript declaration files (typings).

[skip ci]
2024-11-19 17:09:27 +00:00
Davide Iadeluca
345023242b [1.x] chore(core, tags): resolve a11y warnings in Admin Frontend (#4114)
* chore: add accessible label to scope removal button

* chore: add accessible label to edit tag button
2024-11-19 16:57:49 +00:00
Davide Iadeluca
00329eaee0 fix: return empty object if selected mail driver is unavailable (#4113) 2024-11-19 16:56:56 +00:00
Davide Iadeluca
6c89f8161e [1.x] fix: change length of email field (#4117)
* test: write failing tests for email length

* style: formatting

* style: formatting

* fix: change length of email field

* test: write test for email with too long local part

* style: formatting

* chore: remove unnecessary tests
2024-11-19 16:49:17 +00:00
IanM
8eb56b16cf [1.x] feat: conditional extend whenExtensionDisabled (#4107)
* feat: conditional extend whenExtensionDisabled

* Apply fixes from StyleCI

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
2024-11-12 09:40:05 +00:00
Daniël Klabbers
634132e06e chore: remove duplicated mentionsUsers relation 2024-11-06 15:59:27 +01:00
29 changed files with 292 additions and 21 deletions

View File

@@ -105,6 +105,8 @@ jobs:
php_ini_values: error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
- php: 8.3
php_ini_values: error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
# - php: 8.4
# php_ini_values: error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
# To reduce number of actions, we exclude some PHP versions from running with some DB versions.
exclude:
@@ -177,6 +179,8 @@ jobs:
strategy:
matrix:
php: ${{ fromJSON(inputs.php_versions) }}
# exclude:
# - php: 8.4
name: 'PHPStan PHP ${{ matrix.php }}'

View File

@@ -1,5 +1,24 @@
# Changelog
## [v1.8.9](https://github.com/flarum/framework/compare/v1.8.8...v1.8.9)
### Fixed
* change condition when unread label is shown in Scrubber (https://github.com/flarum/framework/pull/4116)
* resolve a11y warnings in Admin Frontend (https://github.com/flarum/framework/pull/4114)
* return empty object if selected mail driver is unavailable (https://github.com/flarum/framework/pull/4113)
### Changed
* change private to protected, allowing extensibility (https://github.com/flarum/framework/pull/4119)
* change length of email field (https://github.com/flarum/framework/pull/4117)
### Added
* Implement Support for Translatable Validation Attribute Errors (https://github.com/flarum/framework/pull/4070)
* PHP 8.4 support (https://github.com/flarum/framework/pull/4105)
* conditional extend whenExtensionDisabled (https://github.com/flarum/framework/pull/4107)
## [v1.8.8](https://github.com/flarum/framework/compare/v1.8.7...v1.8.8)
### Fixed
* previously suspended admin users cannot remove their avatar after suspension (https://github.com/flarum/framework/pull/4071)
* new search term not being passed (https://github.com/flarum/framework/pull/4083)
* postfooter did not apply the empty subclass (https://github.com/flarum/framework/pull/4085)
## [v1.8.7](https://github.com/flarum/framework/compare/v1.8.6...v1.8.7)
### Fixed
* BasicsPage not viewable if only one language pack enabled, and/or `flarum/nicknames` not enabled (https://github.com/flarum/framework/pull/4062)

View File

@@ -112,9 +112,9 @@
"illuminate/view": "^8.0",
"intervention/image": "2.5.* || ^2.6.1",
"jenssegers/agent": "^2.6",
"laminas/laminas-diactoros": "^2.4.1",
"laminas/laminas-httphandlerrunner": "^1.2.0 || ^2.3.0",
"laminas/laminas-stratigility": "^3.2.2",
"laminas/laminas-diactoros": "^2.4.1 || ^3.0.0",
"laminas/laminas-httphandlerrunner": "^1.2.0 || ^2.3.0 || ^3.0.0",
"laminas/laminas-stratigility": "^3.2.2 || ^4.0.0",
"league/flysystem": "^1.0.11",
"matthiasmullie/minify": "^1.3",
"middlewares/base-path": "^2.0.1",

View File

@@ -51,8 +51,7 @@ return [
->belongsToMany('mentionedBy', Post::class, 'post_mentions_post', 'mentions_post_id', 'post_id')
->belongsToMany('mentionsPosts', Post::class, 'post_mentions_post', 'post_id', 'mentions_post_id')
->belongsToMany('mentionsUsers', User::class, 'post_mentions_user', 'post_id', 'mentions_user_id')
->belongsToMany('mentionsGroups', Group::class, 'post_mentions_group', 'post_id', 'mentions_group_id')
->belongsToMany('mentionsUsers', User::class, 'post_mentions_user', 'post_id', 'mentions_user_id'),
->belongsToMany('mentionsGroups', Group::class, 'post_mentions_group', 'post_id', 'mentions_group_id'),
new Extend\Locales(__DIR__.'/locale'),

View File

@@ -161,3 +161,23 @@ flarum-extension-manager:
why_not_modal:
title: Why Won't it Update
validation:
attributes:
minimum_stability: minimum stability
repositories: repositories
repositories.*: repositories
repositories.*.type: repository type
repositories.*.url: repository URL
extension_id: extension ID
update_mode: update mode
package: package
version: version
github_oauth: GitHub OAuth
github_oauth.*: GitHub OAuth
gitlab_oauth: GitLab OAuth
gitlab_oauth.*: GitLab OAuth
gitlab_token: GitLab Token
gitlab_token.*: GitLab Token
bearer: HTTP Bearer
bearer.*: HTTP Bearer

View File

@@ -69,3 +69,7 @@ flarum-suspend:
You have been unsuspended. You can head back to the forum by clicking on the following link:
{forum_url}
validation:
attributes:
suspendedUntil: suspended until

2
extensions/tags/js/dist/admin.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

@@ -16,7 +16,12 @@ function tagItem(tag) {
<div className="TagListItem-info">
{tagIcon(tag)}
<span className="TagListItem-name">{tag.name()}</span>
<Button className="Button Button--link" icon="fas fa-pencil-alt" onclick={() => app.modal.show(EditTagModal, { model: tag })} />
<Button
className="Button Button--link"
icon="fas fa-pencil-alt"
aria-label={app.translator.trans('flarum-tags.admin.tags.edit_tag_label', { tag: tag.name() })}
onclick={() => app.modal.show(EditTagModal, { model: tag })}
/>
</div>
{!tag.isChild() && tag.position() !== null && (
<ol className="TagListItem-children TagList">

View File

@@ -56,6 +56,7 @@ flarum-tags:
about_tags_text: "Tags are used to categorize discussions. Primary tags are like traditional forum categories: they can be arranged in a two-level hierarchy. Secondary tags do not have hierarchy or order, and are useful for micro-categorization."
create_primary_tag_button: Create Primary Tag
create_secondary_tag_button: Create Secondary Tag
edit_tag_label: Edit Tag {tag}
primary_heading: Primary Tags
secondary_heading: Secondary Tags
settings_heading: Settings
@@ -126,3 +127,13 @@ flarum-tags:
choose_tags_placeholder: "{count, plural, one {Choose 1 more tag} other {Choose # more tags}}"
name: Name
tags: Tags
validation:
attributes:
name: name
slug: slug
is_hidden: hidden
description: description
color: color
tag_count_primary: => validation.attributes.tag_count_primary
tag_count_secondary: => validation.attributes.tag_count_secondary

View File

@@ -61,9 +61,9 @@
"illuminate/view": "^8.0",
"intervention/image": "2.5.* || ^2.6.1",
"jenssegers/agent": "^2.6",
"laminas/laminas-diactoros": "^2.4.1",
"laminas/laminas-httphandlerrunner": "^1.2.0 || ^2.3.0",
"laminas/laminas-stratigility": "^3.2.2",
"laminas/laminas-diactoros": "^2.4.1 || ^3.0.0",
"laminas/laminas-httphandlerrunner": "^1.2.0 || ^2.3.0 || ^3.0.0",
"laminas/laminas-stratigility": "^3.2.2 || ^4.0.0",
"league/flysystem": "^1.0.11",
"matthiasmullie/minify": "^1.3",
"middlewares/base-path": "^2.0.1",

2
framework/core/js/dist/admin.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

2
framework/core/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

@@ -98,7 +98,7 @@ export default class MailPage<CustomAttrs extends IPageAttrs = IPageAttrs> exten
contentItems(): ItemList<Mithril.Children> {
const items = new ItemList<Mithril.Children>();
const fields = this.driverFields![this.setting('mail_driver')()];
const fields = this.driverFields![this.setting('mail_driver')()] || {};
const fieldKeys = Object.keys(fields);
items.add(

View File

@@ -57,7 +57,12 @@ export default class PermissionGrid<CustomAttrs extends IPermissionGridAttrs = I
<th>
{scope.label}{' '}
{!!scope.onremove && (
<Button icon="fas fa-times" className="Button Button--text PermissionGrid-removeScope" onclick={scope.onremove} />
<Button
icon="fas fa-times"
className="Button Button--text PermissionGrid-removeScope"
aria-label={app.translator.trans('core.admin.permissions.remove_scope_label', { scope: scope.label })}
onclick={scope.onremove}
/>
)}
</th>
))}

View File

@@ -41,7 +41,7 @@ export default class PostStreamScrubber extends Component {
const newStyle = {
top: 100 - unreadPercent * 100 + '%',
height: unreadPercent * 100 + '%',
opacity: unreadPercent ? 1 : 0,
opacity: unreadPercent > 0 ? 1 : 0,
};
if (vnode.state.oldStyle) {

View File

@@ -226,6 +226,7 @@ core:
participate_heading: Participate
post_without_throttle_label: Reply multiple times without waiting
read_heading: Read
remove_scope_label: Remove scope of {scope}
rename_discussions_label: Rename discussions
reply_to_discussions_label: Reply to discussions
search_users_label: => core.ref.search_users

View File

@@ -79,6 +79,7 @@ validation:
present: "The :attribute field must be present."
regex: "The :attribute format is invalid."
required: "The :attribute field is required."
required_array_keys: "The :attribute array must contain entries for: :values."
required_if: "The :attribute field is required when :other is :value."
required_unless: "The :attribute field is required unless :other is in :values."
required_with: "The :attribute field is required when :values is present."

View File

@@ -0,0 +1,25 @@
<?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 Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
return [
'up' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$table->string('email', 254)->change();
});
},
'down' => function (Builder $schema) {
$schema->table('users', function (Blueprint $table) {
$table->string('email', 150)->change();
});
}
];

View File

@@ -47,6 +47,20 @@ class Conditional implements ExtenderInterface
}, $extenders);
}
/**
* Apply extenders only if a specific extension is disabled/not installed.
*
* @param string $extensionId The ID of the extension.
* @param ExtenderInterface[]|callable|string $extenders A callable returning an array of extenders, or an invokable class string.
* @return self
*/
public function whenExtensionDisabled(string $extensionId, $extenders): self
{
return $this->when(function (ExtensionManager $extensions) use ($extensionId) {
return ! $extensions->isEnabled($extensionId);
}, $extenders);
}
/**
* Apply extenders based on a condition.
*

View File

@@ -333,6 +333,19 @@ class Extension implements Arrayable
return $this->composerJsonAttribute('extra.flarum-extension.title');
}
/**
* @return string|null
*/
public function getNamespace(): ?string
{
return Collection::make($this->composerJsonAttribute('autoload.psr-4'))
->filter(function ($source) {
return $source === 'src/';
})
->keys()
->first();
}
/**
* @return string
*/

View File

@@ -62,7 +62,7 @@ class ResponseFactory
));
}
private function makeResponse(array $payload): HtmlResponse
protected function makeResponse(array $payload): ResponseInterface
{
$content = sprintf(
'<script>window.close(); window.opener.app.authenticationComplete(%s);</script>',
@@ -72,7 +72,7 @@ class ResponseFactory
return new HtmlResponse($content);
}
private function makeLoggedInResponse(User $user)
protected function makeLoggedInResponse(User $user)
{
$response = $this->makeResponse(['loggedIn' => true]);

View File

@@ -9,6 +9,7 @@
namespace Flarum\Foundation;
use Illuminate\Contracts\Cache\Store as Cache;
use Illuminate\Support\Arr;
use Illuminate\Validation\Factory;
use Illuminate\Validation\ValidationException;
@@ -16,6 +17,13 @@ use Symfony\Contracts\Translation\TranslatorInterface;
abstract class AbstractValidator
{
use ExtensionIdTrait;
/**
* @var string
*/
public static $CORE_VALIDATION_CACHE_KEY = 'core.validation.extension_id_class_names';
/**
* @var array
*/
@@ -81,6 +89,30 @@ abstract class AbstractValidator
return [];
}
/**
* @return array
*/
protected function getAttributeNames()
{
$cache = resolve(Cache::class);
if ($cache->get(self::$CORE_VALIDATION_CACHE_KEY) !== null) {
return $cache->get(self::$CORE_VALIDATION_CACHE_KEY);
}
$extId = $this->getClassExtensionId();
$attributeNames = [];
foreach (array_keys($this->rules) as $attribute) {
$key = $extId ? "$extId.validation.attributes.$attribute" : "validation.attributes.$attribute";
$attributeNames[$attribute] = $this->translator->trans($key);
}
$cache->forever(self::$CORE_VALIDATION_CACHE_KEY, $attributeNames);
return $attributeNames;
}
/**
* Make a new validator instance for this model.
*
@@ -92,6 +124,7 @@ abstract class AbstractValidator
$rules = Arr::only($this->getRules(), array_keys($attributes));
$validator = $this->validator->make($attributes, $rules, $this->getMessages());
$validator->setAttributeNames($this->getAttributeNames());
foreach ($this->configuration as $callable) {
$callable($this, $validator);

View File

@@ -21,7 +21,7 @@ class Application
*
* @var string
*/
const VERSION = '1.8.8';
const VERSION = '1.8.9';
/**
* The IoC container for the Flarum application.

View File

@@ -0,0 +1,31 @@
<?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\Foundation;
use Flarum\Extension\Extension;
use Flarum\Extension\ExtensionManager;
trait ExtensionIdTrait
{
protected function getClassExtensionId(): ?string
{
$extensions = resolve(ExtensionManager::class);
return $extensions->getExtensions()
->mapWithKeys(function (Extension $extension) {
return [$extension->getId() => $extension->getNamespace()];
})
->filter(function ($namespace) {
return $namespace && str_starts_with(static::class, $namespace);
})
->keys()
->first();
}
}

View File

@@ -139,6 +139,71 @@ class CreateTest extends TestCase
$this->assertEquals(1, $user->is_email_confirmed);
}
/**
* @test
*/
public function admin_can_create_user_with_longest_valid_email()
{
$localPart = str_repeat('a', 64);
$domain = str_repeat('a', 61).'.'.str_repeat('a', 60).'.'.str_repeat('a', 60).'.local';
$email = $localPart.'@'.$domain;
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'authenticatedAs' => 1,
'json' => [
'data' => [
'attributes' => [
'username' => 'test',
'password' => 'too-obscure',
'email' => $email,
],
]
],
]
)
);
$this->assertEquals(201, $response->getStatusCode());
/** @var User $user */
$user = User::where('username', 'test')->firstOrFail();
$this->assertEquals($email, $user->email);
}
/**
* @test
*/
public function admin_cannot_create_user_with_invalid_email_length()
{
$email = str_repeat('a', 65).'@'.str_repeat('a', 256).'.local';
$response = $this->send(
$this->request(
'POST',
'/api/users',
[
'authenticatedAs' => 1,
'json' => [
'data' => [
'attributes' => [
'username' => 'test',
'password' => 'too-obscure',
'email' => $email,
],
]
],
]
)
);
$this->assertEquals(422, $response->getStatusCode());
}
/**
* @test
*/

View File

@@ -160,6 +160,27 @@ class ConditionalTest extends TestCase
$this->app();
}
/** @test */
public function conditional_disabled_extension_not_enabled_applies_extender()
{
$this->extend(
(new Extend\Conditional())
->whenExtensionDisabled('flarum-dummy-extension', TestExtender::class)
);
$this->app();
$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customConditionalAttribute', $payload['data']['attributes']);
}
/** @test */
public function conditional_does_not_instantiate_extender_if_condition_is_false_using_callable()
{