mirror of
https://github.com/flarum/core.git
synced 2025-08-20 07:11:31 +02:00
Compare commits
12 Commits
as/dropdow
...
as/writabl
Author | SHA1 | Date | |
---|---|---|---|
|
31a2f67462 | ||
|
3da7d7d221 | ||
|
02f351001c | ||
|
6a909386b2 | ||
|
17d25ba4ce | ||
|
c7662a320f | ||
|
5a9f60d250 | ||
|
c522657212 | ||
|
2b87f10738 | ||
|
29c290e78f | ||
|
38c3ccd6be | ||
|
71cb8c378f |
@@ -1,4 +1,6 @@
|
||||
export default class AppearancePage extends AdminPage<import("../../common/components/Page").IPageAttrs> {
|
||||
constructor();
|
||||
colorItems(): ItemList<any>;
|
||||
}
|
||||
import AdminPage from "./AdminPage";
|
||||
import ItemList from "../../common/utils/ItemList";
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/// <reference path="../../../src/common/translator-icu-rich.d.ts" />
|
||||
import Modal from '../../common/components/Modal';
|
||||
export default class LoadingModal<ModalAttrs = {}> extends Modal<ModalAttrs> {
|
||||
/**
|
||||
@@ -5,7 +6,7 @@ export default class LoadingModal<ModalAttrs = {}> extends Modal<ModalAttrs> {
|
||||
*/
|
||||
static readonly isDismissible: boolean;
|
||||
className(): string;
|
||||
title(): any;
|
||||
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
||||
content(): string;
|
||||
onsubmit(e: Event): void;
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/// <reference path="../../../src/common/translator-icu-rich.d.ts" />
|
||||
/// <reference types="mithril" />
|
||||
import type User from '../../common/models/User';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
@@ -65,8 +66,8 @@ export default class UserListPage extends AdminPage {
|
||||
headerInfo(): {
|
||||
className: string;
|
||||
icon: string;
|
||||
title: any;
|
||||
description: any;
|
||||
title: import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
||||
description: import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
||||
};
|
||||
/**
|
||||
* Asynchronously fetch the next set of users to be rendered.
|
||||
|
5
js/dist-typings/admin/routes.d.ts
vendored
5
js/dist-typings/admin/routes.d.ts
vendored
@@ -1,6 +1,5 @@
|
||||
import AdminApplication from './AdminApplication';
|
||||
/**
|
||||
* The `routes` initializer defines the forum app's routes.
|
||||
*
|
||||
* @param {App} app
|
||||
*/
|
||||
export default function _default(app: any): void;
|
||||
export default function (app: AdminApplication): void;
|
||||
|
13
js/dist-typings/common/Application.d.ts
vendored
13
js/dist-typings/common/Application.d.ts
vendored
@@ -13,10 +13,7 @@ import type Mithril from 'mithril';
|
||||
import type Component from './Component';
|
||||
import type { ComponentAttrs } from './Component';
|
||||
export declare type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
|
||||
export declare type FlarumGenericRoute = RouteItem<Record<string, unknown>, Component<{
|
||||
routeName: string;
|
||||
[key: string]: unknown;
|
||||
}>, Record<string, unknown>>;
|
||||
export declare type FlarumGenericRoute = RouteItem<any, any, any>;
|
||||
export interface FlarumRequestOptions<ResponseType> extends Omit<Mithril.RequestOptions<ResponseType>, 'extract'> {
|
||||
errorHandler?: (error: RequestError) => void;
|
||||
url: string;
|
||||
@@ -52,18 +49,14 @@ export declare type RouteItem<Attrs extends ComponentAttrs, Comp extends Compone
|
||||
/**
|
||||
* The component to render when this route matches.
|
||||
*/
|
||||
component: {
|
||||
new (): Comp;
|
||||
};
|
||||
component: new () => Comp;
|
||||
/**
|
||||
* A custom resolver class.
|
||||
*
|
||||
* This should be the class itself, and **not** an instance of the
|
||||
* class.
|
||||
*/
|
||||
resolverClass?: {
|
||||
new (): DefaultResolver<Attrs, Comp, RouteArgs>;
|
||||
};
|
||||
resolverClass?: new (component: new () => Comp, routeName: string) => DefaultResolver<Attrs, Comp, RouteArgs>;
|
||||
} | {
|
||||
/**
|
||||
* An instance of a route resolver.
|
||||
|
11
js/dist-typings/common/Translator.d.ts
vendored
11
js/dist-typings/common/Translator.d.ts
vendored
@@ -1,3 +1,6 @@
|
||||
/// <reference path="../../src/common/translator-icu-rich.d.ts" />
|
||||
import { RichMessageFormatter } from '@askvortsov/rich-icu-message-formatter';
|
||||
import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter';
|
||||
declare type Translations = Record<string, string>;
|
||||
declare type TranslatorParameters = Record<string, unknown>;
|
||||
export default class Translator {
|
||||
@@ -8,15 +11,15 @@ export default class Translator {
|
||||
/**
|
||||
* The underlying ICU MessageFormatter util.
|
||||
*/
|
||||
protected formatter: any;
|
||||
protected formatter: RichMessageFormatter;
|
||||
setLocale(locale: string): void;
|
||||
addTranslations(translations: Translations): void;
|
||||
/**
|
||||
* An extensible entrypoint for extenders to register type handlers for translations.
|
||||
*/
|
||||
protected formatterTypeHandlers(): {
|
||||
plural: any;
|
||||
select: any;
|
||||
plural: typeof pluralTypeHandler;
|
||||
select: typeof selectTypeHandler;
|
||||
};
|
||||
/**
|
||||
* A temporary system to preprocess parameters.
|
||||
@@ -26,6 +29,6 @@ export default class Translator {
|
||||
* @internal
|
||||
*/
|
||||
protected preprocessParameters(parameters: TranslatorParameters): TranslatorParameters;
|
||||
trans(id: string, parameters?: TranslatorParameters): any;
|
||||
trans(id: string, parameters?: TranslatorParameters): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
|
||||
}
|
||||
export {};
|
||||
|
@@ -11,13 +11,9 @@ import type { default as Component, ComponentAttrs } from '../Component';
|
||||
export default class DefaultResolver<Attrs extends ComponentAttrs, Comp extends Component<Attrs & {
|
||||
routeName: string;
|
||||
}>, RouteArgs extends Record<string, unknown> = {}> implements RouteResolver<Attrs, Comp, RouteArgs> {
|
||||
component: {
|
||||
new (): Comp;
|
||||
};
|
||||
component: new () => Comp;
|
||||
routeName: string;
|
||||
constructor(component: {
|
||||
new (): Comp;
|
||||
}, routeName: string);
|
||||
constructor(component: new () => Comp, routeName: string);
|
||||
/**
|
||||
* When a route change results in a changed key, a full page
|
||||
* rerender occurs. This method can be overriden in subclasses
|
||||
|
2
js/dist-typings/forum/ForumApplication.d.ts
vendored
2
js/dist-typings/forum/ForumApplication.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
import History from './utils/History';
|
||||
import Pane from './utils/Pane';
|
||||
import { makeRouteHelpers } from './routes';
|
||||
import Application from '../common/Application';
|
||||
import NotificationListState from './states/NotificationListState';
|
||||
import GlobalSearchState from './states/GlobalSearchState';
|
||||
@@ -44,6 +45,7 @@ export default class ForumApplication extends Application {
|
||||
* is used in the index page and the slideout pane.
|
||||
*/
|
||||
discussions: DiscussionListState;
|
||||
route: typeof Application.prototype.route & ReturnType<typeof makeRouteHelpers>;
|
||||
constructor();
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
22
js/dist-typings/forum/routes.d.ts
vendored
22
js/dist-typings/forum/routes.d.ts
vendored
@@ -1,6 +1,22 @@
|
||||
import ForumApplication from './ForumApplication';
|
||||
import Discussion from '../common/models/Discussion';
|
||||
import Post from '../common/models/Post';
|
||||
import User from '../common/models/User';
|
||||
/**
|
||||
* The `routes` initializer defines the forum app's routes.
|
||||
*
|
||||
* @param {App} app
|
||||
*/
|
||||
export default function _default(app: any): void;
|
||||
export default function (app: ForumApplication): void;
|
||||
export declare function makeRouteHelpers(app: ForumApplication): {
|
||||
/**
|
||||
* Generate a URL to a discussion.
|
||||
*/
|
||||
discussion: (discussion: Discussion, near: number) => string;
|
||||
/**
|
||||
* Generate a URL to a post.
|
||||
*/
|
||||
post: (post: Post) => string;
|
||||
/**
|
||||
* Generate a URL to a user.
|
||||
*/
|
||||
user: (user: User) => string;
|
||||
};
|
||||
|
2
js/dist/admin.js
generated
vendored
2
js/dist/admin.js
generated
vendored
File diff suppressed because one or more lines are too long
2
js/dist/admin.js.map
generated
vendored
2
js/dist/admin.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
js/dist/forum.js
generated
vendored
2
js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
js/dist/forum.js.map
generated
vendored
2
js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ import EditCustomHeaderModal from './EditCustomHeaderModal';
|
||||
import EditCustomFooterModal from './EditCustomFooterModal';
|
||||
import UploadImageButton from './UploadImageButton';
|
||||
import AdminPage from './AdminPage';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
|
||||
export default class AppearancePage extends AdminPage {
|
||||
headerInfo() {
|
||||
@@ -21,34 +22,7 @@ export default class AppearancePage extends AdminPage {
|
||||
<div className="Form">
|
||||
<fieldset className="AppearancePage-colors">
|
||||
<legend>{app.translator.trans('core.admin.appearance.colors_heading')}</legend>
|
||||
<div className="helpText">{app.translator.trans('core.admin.appearance.colors_text')}</div>
|
||||
|
||||
<div className="AppearancePage-colors-input">
|
||||
{this.buildSettingComponent({
|
||||
type: 'color-preview',
|
||||
setting: 'theme_primary_color',
|
||||
placeholder: '#aaaaaa',
|
||||
})}
|
||||
{this.buildSettingComponent({
|
||||
type: 'color-preview',
|
||||
setting: 'theme_secondary_color',
|
||||
placeholder: '#aaaaaa',
|
||||
})}
|
||||
</div>
|
||||
|
||||
{this.buildSettingComponent({
|
||||
type: 'switch',
|
||||
setting: 'theme_dark_mode',
|
||||
label: app.translator.trans('core.admin.appearance.dark_mode_label'),
|
||||
})}
|
||||
|
||||
{this.buildSettingComponent({
|
||||
type: 'switch',
|
||||
setting: 'theme_colored_header',
|
||||
label: app.translator.trans('core.admin.appearance.colored_header_label'),
|
||||
})}
|
||||
|
||||
{this.submitButton()}
|
||||
{this.colorItems().toArray()}
|
||||
</fieldset>
|
||||
</div>,
|
||||
|
||||
@@ -102,6 +76,53 @@ export default class AppearancePage extends AdminPage {
|
||||
];
|
||||
}
|
||||
|
||||
colorItems() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('helpText', <div className="helpText">{app.translator.trans('core.admin.appearance.colors_text')}</div>, 80);
|
||||
|
||||
items.add(
|
||||
'theme-colors',
|
||||
<div className="AppearancePage-colors-input">
|
||||
{this.buildSettingComponent({
|
||||
type: 'color-preview',
|
||||
setting: 'theme_primary_color',
|
||||
placeholder: '#aaaaaa',
|
||||
})}
|
||||
{this.buildSettingComponent({
|
||||
type: 'color-preview',
|
||||
setting: 'theme_secondary_color',
|
||||
placeholder: '#aaaaaa',
|
||||
})}
|
||||
</div>,
|
||||
70
|
||||
);
|
||||
|
||||
items.add(
|
||||
'dark-mode',
|
||||
this.buildSettingComponent({
|
||||
type: 'switch',
|
||||
setting: 'theme_dark_mode',
|
||||
label: app.translator.trans('core.admin.appearance.dark_mode_label'),
|
||||
}),
|
||||
60
|
||||
);
|
||||
|
||||
items.add(
|
||||
'colored-header',
|
||||
this.buildSettingComponent({
|
||||
type: 'switch',
|
||||
setting: 'theme_colored_header',
|
||||
label: app.translator.trans('core.admin.appearance.colored_header_label'),
|
||||
}),
|
||||
50
|
||||
);
|
||||
|
||||
items.add('submit', this.submitButton(), 0);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
onsaved() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import AdminApplication from './AdminApplication';
|
||||
import DashboardPage from './components/DashboardPage';
|
||||
import BasicsPage from './components/BasicsPage';
|
||||
import PermissionsPage from './components/PermissionsPage';
|
||||
@@ -9,10 +10,8 @@ import ExtensionPageResolver from './resolvers/ExtensionPageResolver';
|
||||
|
||||
/**
|
||||
* The `routes` initializer defines the forum app's routes.
|
||||
*
|
||||
* @param {App} app
|
||||
*/
|
||||
export default function (app) {
|
||||
export default function (app: AdminApplication) {
|
||||
app.routes = {
|
||||
dashboard: { path: '/', component: DashboardPage },
|
||||
basics: { path: '/basics', component: BasicsPage },
|
@@ -34,11 +34,7 @@ import type { ComponentAttrs } from './Component';
|
||||
|
||||
export type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
|
||||
|
||||
export type FlarumGenericRoute = RouteItem<
|
||||
Record<string, unknown>,
|
||||
Component<{ routeName: string; [key: string]: unknown }>,
|
||||
Record<string, unknown>
|
||||
>;
|
||||
export type FlarumGenericRoute = RouteItem<any, any, any>;
|
||||
|
||||
export interface FlarumRequestOptions<ResponseType> extends Omit<Mithril.RequestOptions<ResponseType>, 'extract'> {
|
||||
errorHandler?: (error: RequestError) => void;
|
||||
@@ -80,14 +76,14 @@ export type RouteItem<
|
||||
/**
|
||||
* The component to render when this route matches.
|
||||
*/
|
||||
component: { new (): Comp };
|
||||
component: new () => Comp;
|
||||
/**
|
||||
* A custom resolver class.
|
||||
*
|
||||
* This should be the class itself, and **not** an instance of the
|
||||
* class.
|
||||
*/
|
||||
resolverClass?: { new (): DefaultResolver<Attrs, Comp, RouteArgs> };
|
||||
resolverClass?: new (component: new () => Comp, routeName: string) => DefaultResolver<Attrs, Comp, RouteArgs>;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
|
@@ -15,10 +15,10 @@ export default class DefaultResolver<
|
||||
RouteArgs extends Record<string, unknown> = {}
|
||||
> implements RouteResolver<Attrs, Comp, RouteArgs>
|
||||
{
|
||||
component: { new (): Comp };
|
||||
component: new () => Comp;
|
||||
routeName: string;
|
||||
|
||||
constructor(component: { new (): Comp }, routeName: string) {
|
||||
constructor(component: new () => Comp, routeName: string) {
|
||||
this.component = component;
|
||||
this.routeName = routeName;
|
||||
}
|
||||
|
26
js/src/common/translator-icu-rich.d.ts
vendored
Normal file
26
js/src/common/translator-icu-rich.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
declare module '@askvortsov/rich-icu-message-formatter' {
|
||||
type IValues = Record<string, any>;
|
||||
|
||||
type ITypeHandler = (
|
||||
value: string,
|
||||
matches: string,
|
||||
locale: string,
|
||||
values: IValues,
|
||||
format: (message: string, values: IValues) => string
|
||||
) => string;
|
||||
type IRichHandler = (tag: any, values: IValues, contents: string) => any;
|
||||
|
||||
type ValueOrArray<T> = T | ValueOrArray<T>[];
|
||||
type NestedStringArray = ValueOrArray<string>;
|
||||
|
||||
export class RichMessageFormatter {
|
||||
locale: string | null;
|
||||
constructor(locale: string | null, typeHandlers: Record<string, ITypeHandler>, richHandler: IRichHandler);
|
||||
|
||||
format(message: string, values: IValues): string;
|
||||
process(message: string, values: IValues): NestedStringArray;
|
||||
rich(message: string, values: IValues): NestedStringArray;
|
||||
}
|
||||
|
||||
export function mithrilRichHandler(tag: any, values: IValues, contents: string): any;
|
||||
}
|
17
js/src/common/translator-icu.d.ts
vendored
Normal file
17
js/src/common/translator-icu.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
declare module '@ultraq/icu-message-formatter' {
|
||||
export function pluralTypeHandler(
|
||||
value: string,
|
||||
matches: string,
|
||||
locale: string,
|
||||
values: Record<string, any>,
|
||||
format: (text: string, values: Record<string, any>) => string
|
||||
): string;
|
||||
|
||||
export function selectTypeHandler(
|
||||
value: string,
|
||||
matches: string,
|
||||
locale: string,
|
||||
values: Record<string, any>,
|
||||
format: (text: string, values: Record<string, any>) => string
|
||||
): string;
|
||||
}
|
@@ -10,7 +10,7 @@ import Composer from './components/Composer';
|
||||
import DiscussionRenamedNotification from './components/DiscussionRenamedNotification';
|
||||
import CommentPost from './components/CommentPost';
|
||||
import DiscussionRenamedPost from './components/DiscussionRenamedPost';
|
||||
import routes from './routes';
|
||||
import routes, { makeRouteHelpers } from './routes';
|
||||
import alertEmailConfirmation from './utils/alertEmailConfirmation';
|
||||
import Application from '../common/Application';
|
||||
import Navigation from '../common/components/Navigation';
|
||||
@@ -73,10 +73,14 @@ export default class ForumApplication extends Application {
|
||||
*/
|
||||
discussions: DiscussionListState = new DiscussionListState({});
|
||||
|
||||
route: typeof Application.prototype.route & ReturnType<typeof makeRouteHelpers>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
routes(this);
|
||||
|
||||
this.route = Object.assign((Object.getPrototypeOf(Object.getPrototypeOf(this)) as Application).route.bind(this), makeRouteHelpers(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -149,7 +149,7 @@ export default class AvatarEditor extends Component {
|
||||
|
||||
// Create a hidden HTML input element and click on it so the user can select
|
||||
// an avatar file. Once they have, we will upload it via the API.
|
||||
const $input = $('<input type="file">');
|
||||
const $input = $('<input type="file" accept=".jpg, .jpeg, .png, .bmp, .gif">');
|
||||
|
||||
$input
|
||||
.appendTo('body')
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import ForumApplication from './ForumApplication';
|
||||
import IndexPage from './components/IndexPage';
|
||||
import DiscussionPage from './components/DiscussionPage';
|
||||
import PostsUserPage from './components/PostsUserPage';
|
||||
@@ -5,13 +6,14 @@ import DiscussionsUserPage from './components/DiscussionsUserPage';
|
||||
import SettingsPage from './components/SettingsPage';
|
||||
import NotificationsPage from './components/NotificationsPage';
|
||||
import DiscussionPageResolver from './resolvers/DiscussionPageResolver';
|
||||
import Discussion from '../common/models/Discussion';
|
||||
import Post from '../common/models/Post';
|
||||
import User from '../common/models/User';
|
||||
|
||||
/**
|
||||
* The `routes` initializer defines the forum app's routes.
|
||||
*
|
||||
* @param {App} app
|
||||
*/
|
||||
export default function (app) {
|
||||
export default function (app: ForumApplication) {
|
||||
app.routes = {
|
||||
index: { path: '/all', component: IndexPage },
|
||||
|
||||
@@ -25,40 +27,34 @@ export default function (app) {
|
||||
settings: { path: '/settings', component: SettingsPage },
|
||||
notifications: { path: '/notifications', component: NotificationsPage },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a URL to a discussion.
|
||||
*
|
||||
* @param {Discussion} discussion
|
||||
* @param {Integer} [near]
|
||||
* @return {String}
|
||||
*/
|
||||
app.route.discussion = (discussion, near) => {
|
||||
return app.route(near && near !== 1 ? 'discussion.near' : 'discussion', {
|
||||
id: discussion.slug(),
|
||||
near: near && near !== 1 ? near : undefined,
|
||||
});
|
||||
};
|
||||
export function makeRouteHelpers(app: ForumApplication) {
|
||||
return {
|
||||
/**
|
||||
* Generate a URL to a discussion.
|
||||
*/
|
||||
discussion: (discussion: Discussion, near: number) => {
|
||||
return app.route(near && near !== 1 ? 'discussion.near' : 'discussion', {
|
||||
id: discussion.slug(),
|
||||
near: near && near !== 1 ? near : undefined,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a URL to a post.
|
||||
*
|
||||
* @param {Post} post
|
||||
* @return {String}
|
||||
*/
|
||||
app.route.post = (post) => {
|
||||
return app.route.discussion(post.discussion(), post.number());
|
||||
};
|
||||
/**
|
||||
* Generate a URL to a post.
|
||||
*/
|
||||
post: (post: Post) => {
|
||||
return app.route.discussion(post.discussion(), post.number());
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a URL to a user.
|
||||
*
|
||||
* @param {User} user
|
||||
* @return {String}
|
||||
*/
|
||||
app.route.user = (user) => {
|
||||
return app.route('user', {
|
||||
username: user.slug(),
|
||||
});
|
||||
/**
|
||||
* Generate a URL to a user.
|
||||
*/
|
||||
user: (user: User) => {
|
||||
return app.route('user', {
|
||||
username: user.slug(),
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
@@ -28,8 +28,33 @@ class WritablePaths implements PrerequisiteInterface
|
||||
|
||||
public function problems(): Collection
|
||||
{
|
||||
return $this->getMissingPaths()
|
||||
$problems = $this->getMissingPaths()
|
||||
->concat($this->getNonWritablePaths());
|
||||
|
||||
if (! $problems->isEmpty()) {
|
||||
return $problems->prepend($this->getServerInfo());
|
||||
}
|
||||
|
||||
return $problems;
|
||||
}
|
||||
|
||||
private function getServerInfo(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Server Metadata',
|
||||
'detail' => implode('<br />', [
|
||||
'The following information might be useful for troubleshooting the errors below.',
|
||||
'Current User:'.get_current_user(),
|
||||
'Current File Permissions:',
|
||||
$this->paths->map(function ($path) {
|
||||
return " - $path: ".substr(sprintf('%o', fileperms($path)), -4);
|
||||
})->implode('<br />'),
|
||||
'Current File Ownership:',
|
||||
$this->paths->map(function ($path) {
|
||||
return " - $path: ".posix_getpwuid(fileowner($path))['name'].':'.posix_getgrgid(filegroup($path))['name'];
|
||||
})->implode('<br />'),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
private function getMissingPaths(): Collection
|
||||
|
@@ -11,6 +11,8 @@ namespace Flarum\User;
|
||||
|
||||
use Flarum\Foundation\AbstractValidator;
|
||||
use Flarum\Foundation\ValidationException;
|
||||
use Intervention\Image\Exception\NotReadableException;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
@@ -69,6 +71,12 @@ class AvatarValidator extends AbstractValidator
|
||||
if (! in_array($guessedExtension, $allowedTypes)) {
|
||||
$this->raise('mimes', [':values' => implode(', ', $allowedTypes)]);
|
||||
}
|
||||
|
||||
try {
|
||||
(new ImageManager)->make($file->getStream());
|
||||
} catch (NotReadableException $_e) {
|
||||
$this->raise('image');
|
||||
}
|
||||
}
|
||||
|
||||
protected function assertFileSize(UploadedFileInterface $file)
|
||||
@@ -103,6 +111,6 @@ class AvatarValidator extends AbstractValidator
|
||||
|
||||
protected function getAllowedTypes()
|
||||
{
|
||||
return ['jpg', 'png', 'bmp', 'gif'];
|
||||
return ['jpeg', 'jpg', 'png', 'bmp', 'gif'];
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user