mirror of
https://github.com/flarum/framework.git
synced 2025-06-19 16:51:23 +08:00
feat: advanced maintenance modes (#3977)
* feat: low maintenance mode (maintenance with admin access) (#3975) * feat: low maintenance mode (maintenance with admin access) * Apply fixes from StyleCI * chore: only required when basic * chore: more concise code * chore(review): enum * feat: enable through settings * Apply fixes from StyleCI * core: typing * feat: safe mode (#3978) * feat: safe mode * feat: add extension page warning * feat: `safe_mode_extensions` * Apply fixes from StyleCI
This commit is contained in:
63
framework/core/js/dist/admin.js.LICENSE.txt
generated
vendored
63
framework/core/js/dist/admin.js.LICENSE.txt
generated
vendored
@ -1,63 +0,0 @@
|
||||
/*!
|
||||
* quantize.js Copyright 2008 Nick Rabinowitz.
|
||||
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Sizzle CSS Selector Engine v2.3.6
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright JS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://js.foundation/
|
||||
*
|
||||
* Date: 2021-02-16
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Block below copied from Protovis: http://mbostock.github.com/protovis/
|
||||
* Copyright 2010 Stanford Visualization Group
|
||||
* Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Color Thief v2.0
|
||||
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||
*
|
||||
* Thanks
|
||||
* ------
|
||||
* Nick Rabinowitz - For creating quantize.js.
|
||||
* John Schulz - For clean up and optimization. @JFSIII
|
||||
* Nathan Spady - For adding drag and drop support to the demo page.
|
||||
*
|
||||
* License
|
||||
* -------
|
||||
* Copyright 2011, 2015 Lokesh Dhakar
|
||||
* Released under the MIT license
|
||||
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.6.1
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2022-08-26T17:52Z
|
||||
*/
|
||||
|
||||
/*!
|
||||
* focus-trap 6.9.4
|
||||
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
/*!
|
||||
* tabbable 5.3.3
|
||||
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
|
||||
*/
|
201
framework/core/js/dist/common/components/EditUserModal.js
generated
vendored
201
framework/core/js/dist/common/components/EditUserModal.js
generated
vendored
@ -1,2 +1,201 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[841],{4292:(s,t,i)=>{i.r(t),i.d(t,{default:()=>p});var e=i(7905),r=i(7465),a=i(899),n=i(8312),o=i(6697),d=i(7645),l=i(1552),u=i(4041),h=i(6458),c=i(6352);class p extends a.Z{constructor(){super(...arguments),(0,e.Z)(this,"username",void 0),(0,e.Z)(this,"email",void 0),(0,e.Z)(this,"isEmailConfirmed",void 0),(0,e.Z)(this,"setPassword",void 0),(0,e.Z)(this,"password",void 0),(0,e.Z)(this,"groups",{})}oninit(s){super.oninit(s);const t=this.attrs.user;this.username=(0,h.Z)(t.username()||""),this.email=(0,h.Z)(t.email()||""),this.isEmailConfirmed=(0,h.Z)(t.isEmailConfirmed()||!1),this.setPassword=(0,h.Z)(!1),this.password=(0,h.Z)(t.password()||"");const i=t.groups()||[];r.Z.store.all("groups").filter((s=>![d.Z.GUEST_ID,d.Z.MEMBER_ID].includes(s.id()))).forEach((s=>this.groups[s.id()]=(0,h.Z)(i.includes(s))))}className(){return"EditUserModal Modal--small"}title(){return r.Z.translator.trans("core.lib.edit_user.title")}content(){const s=this.fields().toArray();return m("div",{className:"Modal-body"},s.length>1?m(c.Z,null,this.fields().toArray()):r.Z.translator.trans("core.lib.edit_user.nothing_available"))}fields(){const s=new u.Z;return this.attrs.user.canEditCredentials()&&(s.add("username",m("div",{className:"Form-group"},m("label",null,r.Z.translator.trans("core.lib.edit_user.username_heading")),m("input",{className:"FormControl",placeholder:(0,l.Z)(r.Z.translator.trans("core.lib.edit_user.username_label")),bidi:this.username,disabled:this.nonAdminEditingAdmin()})),40),r.Z.session.user!==this.attrs.user&&(s.add("email",m("div",{className:"Form-group"},m("label",null,r.Z.translator.trans("core.lib.edit_user.email_heading")),m("input",{className:"FormControl",placeholder:(0,l.Z)(r.Z.translator.trans("core.lib.edit_user.email_label")),bidi:this.email,disabled:this.nonAdminEditingAdmin()}),!this.isEmailConfirmed()&&this.userIsAdmin(r.Z.session.user)&&m(n.Z,{className:"Button Button--block",loading:this.loading,onclick:this.activate.bind(this)},r.Z.translator.trans("core.lib.edit_user.activate_button"))),30),s.add("password",m("div",{className:"Form-group"},m("label",null,r.Z.translator.trans("core.lib.edit_user.password_heading")),m("div",null,m("label",{className:"checkbox"},m("input",{type:"checkbox",onchange:s=>{const t=s.target;this.setPassword(t.checked),m.redraw.sync(),t.checked&&this.$("[name=password]").select(),s.redraw=!1},disabled:this.nonAdminEditingAdmin()}),r.Z.translator.trans("core.lib.edit_user.set_password_label"))),this.setPassword()&&m("input",{className:"FormControl",type:"password",name:"password",placeholder:(0,l.Z)(r.Z.translator.trans("core.lib.edit_user.password_label")),bidi:this.password,disabled:this.nonAdminEditingAdmin()})),20))),this.attrs.user.canEditGroups()&&s.add("groups",m("div",{className:"Form-group EditUserModal-groups"},m("label",null,r.Z.translator.trans("core.lib.edit_user.groups_heading")),m("div",null,Object.keys(this.groups).map((s=>r.Z.store.getById("groups",s))).filter(Boolean).map((s=>s&&m("label",{className:"checkbox"},m("input",{type:"checkbox",bidi:this.groups[s.id()],disabled:s.id()===d.Z.ADMINISTRATOR_ID&&(this.attrs.user===r.Z.session.user||!this.userIsAdmin(r.Z.session.user))}),m(o.Z,{group:s,label:null})," ",s.nameSingular()))))),10),s.add("submit",m("div",{className:"Form-group Form-controls"},m(n.Z,{className:"Button Button--primary",type:"submit",loading:this.loading},r.Z.translator.trans("core.lib.edit_user.submit_button"))),-10),s}activate(){this.loading=!0;const s={username:this.username(),isEmailConfirmed:!0};this.attrs.user.save(s,{errorHandler:this.onerror.bind(this)}).then((()=>{this.isEmailConfirmed(!0),this.loading=!1,m.redraw()})).catch((()=>{this.loading=!1,m.redraw()}))}data(){const s={},t={};return this.attrs.user.canEditCredentials()&&!this.nonAdminEditingAdmin()&&(s.username=this.username(),r.Z.session.user!==this.attrs.user&&(s.email=this.email()),this.setPassword()&&(s.password=this.password())),this.attrs.user.canEditGroups()&&(t.groups=Object.keys(this.groups).filter((s=>this.groups[s]())).map((s=>r.Z.store.getById("groups",s))).filter((s=>s instanceof d.Z))),s.relationships=t,s}onsubmit(s){s.preventDefault(),this.loading=!0,this.attrs.user.save(this.data(),{errorHandler:this.onerror.bind(this)}).then(this.hide.bind(this)).catch((()=>{this.loading=!1,m.redraw()}))}nonAdminEditingAdmin(){return this.userIsAdmin(this.attrs.user)&&!this.userIsAdmin(r.Z.session.user)}userIsAdmin(s){return!!((null==s?void 0:s.groups())||[]).some((s=>(null==s?void 0:s.id())===d.Z.ADMINISTRATOR_ID))}}flarum.reg.add("core","common/components/EditUserModal",p)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["common/components/EditUserModal"],{
|
||||
|
||||
/***/ "./src/common/components/EditUserModal.tsx":
|
||||
/*!*************************************************!*\
|
||||
!*** ./src/common/components/EditUserModal.tsx ***!
|
||||
\*************************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ EditUserModal)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/esm/defineProperty */ "../../../js-packages/webpack-config/node_modules/@babel/runtime/helpers/esm/defineProperty.js");
|
||||
/* harmony import */ var _common_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/app */ "./src/common/app.ts");
|
||||
/* harmony import */ var _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/FormModal */ "./src/common/components/FormModal.tsx");
|
||||
/* harmony import */ var _Button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Button */ "./src/common/components/Button.tsx");
|
||||
/* harmony import */ var _GroupBadge__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./GroupBadge */ "./src/common/components/GroupBadge.tsx");
|
||||
/* harmony import */ var _models_Group__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../models/Group */ "./src/common/models/Group.ts");
|
||||
/* harmony import */ var _utils_extractText__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils/extractText */ "./src/common/utils/extractText.ts");
|
||||
/* harmony import */ var _utils_ItemList__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
/* harmony import */ var _utils_Stream__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../utils/Stream */ "./src/common/utils/Stream.ts");
|
||||
/* harmony import */ var _Form__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./Form */ "./src/common/components/Form.tsx");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class EditUserModal extends _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__["default"] {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "username", void 0);
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "email", void 0);
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "isEmailConfirmed", void 0);
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "setPassword", void 0);
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "password", void 0);
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "groups", {});
|
||||
}
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
const user = this.attrs.user;
|
||||
this.username = (0,_utils_Stream__WEBPACK_IMPORTED_MODULE_8__["default"])(user.username() || '');
|
||||
this.email = (0,_utils_Stream__WEBPACK_IMPORTED_MODULE_8__["default"])(user.email() || '');
|
||||
this.isEmailConfirmed = (0,_utils_Stream__WEBPACK_IMPORTED_MODULE_8__["default"])(user.isEmailConfirmed() || false);
|
||||
this.setPassword = (0,_utils_Stream__WEBPACK_IMPORTED_MODULE_8__["default"])(false);
|
||||
this.password = (0,_utils_Stream__WEBPACK_IMPORTED_MODULE_8__["default"])(user.password() || '');
|
||||
const userGroups = user.groups() || [];
|
||||
_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].store.all('groups').filter(group => ![_models_Group__WEBPACK_IMPORTED_MODULE_5__["default"].GUEST_ID, _models_Group__WEBPACK_IMPORTED_MODULE_5__["default"].MEMBER_ID].includes(group.id())).forEach(group => this.groups[group.id()] = (0,_utils_Stream__WEBPACK_IMPORTED_MODULE_8__["default"])(userGroups.includes(group)));
|
||||
}
|
||||
className() {
|
||||
return 'EditUserModal Modal--small';
|
||||
}
|
||||
title() {
|
||||
return _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.title');
|
||||
}
|
||||
content() {
|
||||
const fields = this.fields().toArray();
|
||||
return m("div", {
|
||||
className: "Modal-body"
|
||||
}, fields.length > 1 ? m(_Form__WEBPACK_IMPORTED_MODULE_9__["default"], null, this.fields().toArray()) : _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.nothing_available'));
|
||||
}
|
||||
fields() {
|
||||
const items = new _utils_ItemList__WEBPACK_IMPORTED_MODULE_7__["default"]();
|
||||
if (this.attrs.user.canEditCredentials()) {
|
||||
items.add('username', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("label", null, _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.username_heading')), m("input", {
|
||||
className: "FormControl",
|
||||
placeholder: (0,_utils_extractText__WEBPACK_IMPORTED_MODULE_6__["default"])(_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.username_label')),
|
||||
bidi: this.username,
|
||||
disabled: this.nonAdminEditingAdmin()
|
||||
})), 40);
|
||||
if (_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].session.user !== this.attrs.user) {
|
||||
items.add('email', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("label", null, _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.email_heading')), m("input", {
|
||||
className: "FormControl",
|
||||
placeholder: (0,_utils_extractText__WEBPACK_IMPORTED_MODULE_6__["default"])(_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.email_label')),
|
||||
bidi: this.email,
|
||||
disabled: this.nonAdminEditingAdmin()
|
||||
}), !this.isEmailConfirmed() && this.userIsAdmin(_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].session.user) && m(_Button__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
className: "Button Button--block",
|
||||
loading: this.loading,
|
||||
onclick: this.activate.bind(this)
|
||||
}, _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.activate_button'))), 30);
|
||||
items.add('password', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("label", null, _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.password_heading')), m("div", null, m("label", {
|
||||
className: "checkbox"
|
||||
}, m("input", {
|
||||
type: "checkbox",
|
||||
onchange: e => {
|
||||
const target = e.target;
|
||||
this.setPassword(target.checked);
|
||||
m.redraw.sync();
|
||||
if (target.checked) this.$('[name=password]').select();
|
||||
e.redraw = false;
|
||||
},
|
||||
disabled: this.nonAdminEditingAdmin()
|
||||
}), _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.set_password_label'))), this.setPassword() && m("input", {
|
||||
className: "FormControl",
|
||||
type: "password",
|
||||
name: "password",
|
||||
placeholder: (0,_utils_extractText__WEBPACK_IMPORTED_MODULE_6__["default"])(_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.password_label')),
|
||||
bidi: this.password,
|
||||
disabled: this.nonAdminEditingAdmin()
|
||||
})), 20);
|
||||
}
|
||||
}
|
||||
if (this.attrs.user.canEditGroups()) {
|
||||
items.add('groups', m("div", {
|
||||
className: "Form-group EditUserModal-groups"
|
||||
}, m("label", null, _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.groups_heading')), m("div", null, Object.keys(this.groups).map(id => _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].store.getById('groups', id)).filter(Boolean).map(group =>
|
||||
// Necessary because filter(Boolean) doesn't narrow out falsy values.
|
||||
group && m("label", {
|
||||
className: "checkbox"
|
||||
}, m("input", {
|
||||
type: "checkbox",
|
||||
bidi: this.groups[group.id()],
|
||||
disabled: group.id() === _models_Group__WEBPACK_IMPORTED_MODULE_5__["default"].ADMINISTRATOR_ID && (this.attrs.user === _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].session.user || !this.userIsAdmin(_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].session.user))
|
||||
}), m(_GroupBadge__WEBPACK_IMPORTED_MODULE_4__["default"], {
|
||||
group: group,
|
||||
label: null
|
||||
}), " ", group.nameSingular())))), 10);
|
||||
}
|
||||
items.add('submit', m("div", {
|
||||
className: "Form-group Form-controls"
|
||||
}, m(_Button__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
className: "Button Button--primary",
|
||||
type: "submit",
|
||||
loading: this.loading
|
||||
}, _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.lib.edit_user.submit_button'))), -10);
|
||||
return items;
|
||||
}
|
||||
activate() {
|
||||
this.loading = true;
|
||||
const data = {
|
||||
username: this.username(),
|
||||
isEmailConfirmed: true
|
||||
};
|
||||
this.attrs.user.save(data, {
|
||||
errorHandler: this.onerror.bind(this)
|
||||
}).then(() => {
|
||||
this.isEmailConfirmed(true);
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
data() {
|
||||
const data = {};
|
||||
const relationships = {};
|
||||
if (this.attrs.user.canEditCredentials() && !this.nonAdminEditingAdmin()) {
|
||||
data.username = this.username();
|
||||
if (_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].session.user !== this.attrs.user) {
|
||||
data.email = this.email();
|
||||
}
|
||||
if (this.setPassword()) {
|
||||
data.password = this.password();
|
||||
}
|
||||
}
|
||||
if (this.attrs.user.canEditGroups()) {
|
||||
relationships.groups = Object.keys(this.groups).filter(id => this.groups[id]()).map(id => _common_app__WEBPACK_IMPORTED_MODULE_1__["default"].store.getById('groups', id)).filter(g => g instanceof _models_Group__WEBPACK_IMPORTED_MODULE_5__["default"]);
|
||||
}
|
||||
data.relationships = relationships;
|
||||
return data;
|
||||
}
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
this.loading = true;
|
||||
this.attrs.user.save(this.data(), {
|
||||
errorHandler: this.onerror.bind(this)
|
||||
}).then(this.hide.bind(this)).catch(() => {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
nonAdminEditingAdmin() {
|
||||
return this.userIsAdmin(this.attrs.user) && !this.userIsAdmin(_common_app__WEBPACK_IMPORTED_MODULE_1__["default"].session.user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
userIsAdmin(user) {
|
||||
return !!((user == null ? void 0 : user.groups()) || []).some(g => (g == null ? void 0 : g.id()) === _models_Group__WEBPACK_IMPORTED_MODULE_5__["default"].ADMINISTRATOR_ID);
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'common/components/EditUserModal', EditUserModal);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=EditUserModal.js.map
|
2
framework/core/js/dist/common/components/EditUserModal.js.map
generated
vendored
2
framework/core/js/dist/common/components/EditUserModal.js.map
generated
vendored
File diff suppressed because one or more lines are too long
63
framework/core/js/dist/forum.js.LICENSE.txt
generated
vendored
63
framework/core/js/dist/forum.js.LICENSE.txt
generated
vendored
@ -1,63 +0,0 @@
|
||||
/*!
|
||||
* quantize.js Copyright 2008 Nick Rabinowitz.
|
||||
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Sizzle CSS Selector Engine v2.3.6
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright JS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://js.foundation/
|
||||
*
|
||||
* Date: 2021-02-16
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Block below copied from Protovis: http://mbostock.github.com/protovis/
|
||||
* Copyright 2010 Stanford Visualization Group
|
||||
* Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Color Thief v2.0
|
||||
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||
*
|
||||
* Thanks
|
||||
* ------
|
||||
* Nick Rabinowitz - For creating quantize.js.
|
||||
* John Schulz - For clean up and optimization. @JFSIII
|
||||
* Nathan Spady - For adding drag and drop support to the demo page.
|
||||
*
|
||||
* License
|
||||
* -------
|
||||
* Copyright 2011, 2015 Lokesh Dhakar
|
||||
* Released under the MIT license
|
||||
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||
*
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.6.1
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2022-08-26T17:52Z
|
||||
*/
|
||||
|
||||
/*!
|
||||
* focus-trap 6.9.4
|
||||
* @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
/*!
|
||||
* tabbable 5.3.3
|
||||
* @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
|
||||
*/
|
418
framework/core/js/dist/forum/components/Composer.js
generated
vendored
418
framework/core/js/dist/forum/components/Composer.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum/components/Composer.js.map
generated
vendored
2
framework/core/js/dist/forum/components/Composer.js.map
generated
vendored
File diff suppressed because one or more lines are too long
288
framework/core/js/dist/forum/components/DiscussionComposer.js
generated
vendored
288
framework/core/js/dist/forum/components/DiscussionComposer.js
generated
vendored
@ -1,2 +1,288 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[437],{2140:(s,e,t)=>{t.d(e,{Z:()=>h});var o=t(2190),i=t(5226);class r extends o.Z{handler(){return this.attrs.when()||void 0}oncreate(s){super.oncreate(s),this.boundHandler=this.handler.bind(this),$(window).on("beforeunload",this.boundHandler)}onremove(s){super.onremove(s),$(window).off("beforeunload",this.boundHandler)}view(s){return m("[",null,s.children)}}flarum.reg.add("core","common/components/ConfirmDocumentUnload",r);var n=t(4944),a=t(1268),d=t(4041),l=t(3344),c=t(7323);class h extends o.Z{oninit(s){super.oninit(s),this.composer=this.attrs.composer,this.loading=!1,this.attrs.confirmExit&&this.composer.preventClosingWhen((()=>this.hasChanges()),this.attrs.confirmExit),this.composer.fields.content(this.attrs.originalContent||"")}view(){var s;return m(r,{when:this.hasChanges.bind(this)},m("div",{className:(0,l.Z)("ComposerBody",this.attrs.className)},m(c.Z,{user:this.attrs.user,className:"ComposerBody-avatar"}),m("div",{className:"ComposerBody-content"},m("ul",{className:"ComposerBody-header"},(0,a.Z)(this.headerItems().toArray())),m("div",{className:"ComposerBody-editor"},m(n.Z,{submitLabel:this.attrs.submitLabel,placeholder:this.attrs.placeholder,disabled:this.loading||this.attrs.disabled,composer:this.composer,preview:null==(s=this.jumpToPreview)?void 0:s.bind(this),onchange:this.composer.fields.content,onsubmit:this.onsubmit.bind(this),value:this.composer.fields.content()}))),m(i.Z,{display:"unset",containerClassName:(0,l.Z)("ComposerBody-loading",this.loading&&"active"),size:"large"})))}hasChanges(){const s=this.composer.fields.content();return s&&s!==this.attrs.originalContent}headerItems(){return new d.Z}onsubmit(){}loaded(){this.loading=!1,m.redraw()}}flarum.reg.add("core","forum/components/ComposerBody",h)},3829:(s,e,t)=>{t.r(e),t.d(e,{default:()=>a});var o=t(6789),i=t(2140),r=t(1552),n=t(6458);class a extends i.Z{static initAttrs(s){super.initAttrs(s),s.placeholder=s.placeholder||(0,r.Z)(o.Z.translator.trans("core.forum.composer_discussion.body_placeholder")),s.submitLabel=s.submitLabel||o.Z.translator.trans("core.forum.composer_discussion.submit_button"),s.confirmExit=s.confirmExit||(0,r.Z)(o.Z.translator.trans("core.forum.composer_discussion.discard_confirmation")),s.titlePlaceholder=s.titlePlaceholder||(0,r.Z)(o.Z.translator.trans("core.forum.composer_discussion.title_placeholder")),s.className="ComposerBody--discussion"}oninit(s){super.oninit(s),this.composer.fields.title=this.composer.fields.title||(0,n.Z)(""),this.title=this.composer.fields.title}headerItems(){const s=super.headerItems();return s.add("title",m("h3",null,o.Z.translator.trans("core.forum.composer_discussion.title")),100),s.add("discussionTitle",m("h3",null,m("input",{className:"FormControl",bidi:this.title,placeholder:this.attrs.titlePlaceholder,disabled:!!this.attrs.disabled,onkeydown:this.onkeydown.bind(this)}))),s}onkeydown(s){13===s.which&&(s.preventDefault(),this.composer.editor.moveCursorTo(0)),s.redraw=!1}hasChanges(){return this.title()||this.composer.fields.content()}data(){return{title:this.title(),content:this.composer.fields.content()}}onsubmit(){this.loading=!0;const s=this.data();o.Z.store.createRecord("discussions").save(s).then((s=>{this.composer.hide(),o.Z.discussions.refresh(),m.route.set(o.Z.route.discussion(s))}),this.loaded.bind(this))}}flarum.reg.add("core","forum/components/DiscussionComposer",a)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/DiscussionComposer"],{
|
||||
|
||||
/***/ "./src/common/components/ConfirmDocumentUnload.js":
|
||||
/*!********************************************************!*\
|
||||
!*** ./src/common/components/ConfirmDocumentUnload.js ***!
|
||||
\********************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ConfirmDocumentUnload)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Component */ "./src/common/Component.ts");
|
||||
|
||||
|
||||
/**
|
||||
* The `ConfirmDocumentUnload` component can be used to register a global
|
||||
* event handler that prevents closing the browser window/tab based on the
|
||||
* return value of a given callback prop.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - `when` - a callback returning true when the browser should prompt for
|
||||
* confirmation before closing the window/tab
|
||||
*/
|
||||
class ConfirmDocumentUnload extends _Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
handler() {
|
||||
return this.attrs.when() || undefined;
|
||||
}
|
||||
oncreate(vnode) {
|
||||
super.oncreate(vnode);
|
||||
this.boundHandler = this.handler.bind(this);
|
||||
$(window).on('beforeunload', this.boundHandler);
|
||||
}
|
||||
onremove(vnode) {
|
||||
super.onremove(vnode);
|
||||
$(window).off('beforeunload', this.boundHandler);
|
||||
}
|
||||
view(vnode) {
|
||||
return m('[', null, vnode.children);
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'common/components/ConfirmDocumentUnload', ConfirmDocumentUnload);
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/ComposerBody.js":
|
||||
/*!**********************************************!*\
|
||||
!*** ./src/forum/components/ComposerBody.js ***!
|
||||
\**********************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ComposerBody)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _common_Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/Component */ "./src/common/Component.ts");
|
||||
/* harmony import */ var _common_components_LoadingIndicator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/components/LoadingIndicator */ "./src/common/components/LoadingIndicator.tsx");
|
||||
/* harmony import */ var _common_components_ConfirmDocumentUnload__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/ConfirmDocumentUnload */ "./src/common/components/ConfirmDocumentUnload.js");
|
||||
/* harmony import */ var _common_components_TextEditor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/TextEditor */ "./src/common/components/TextEditor.js");
|
||||
/* harmony import */ var _common_helpers_listItems__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../common/helpers/listItems */ "./src/common/helpers/listItems.tsx");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
/* harmony import */ var _common_utils_classList__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../common/utils/classList */ "./src/common/utils/classList.ts");
|
||||
/* harmony import */ var _common_components_Avatar__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../common/components/Avatar */ "./src/common/components/Avatar.tsx");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `ComposerBody` component handles the body, or the content, of the
|
||||
* composer. Subclasses should implement the `onsubmit` method and override
|
||||
* `headerTimes`.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - `composer`
|
||||
* - `originalContent`
|
||||
* - `submitLabel`
|
||||
* - `placeholder`
|
||||
* - `user`
|
||||
* - `confirmExit`
|
||||
* - `disabled`
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
class ComposerBody extends _common_Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.composer = this.attrs.composer;
|
||||
|
||||
/**
|
||||
* Whether or not the component is loading.
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.loading = false;
|
||||
|
||||
// Let the composer state know to ask for confirmation under certain
|
||||
// circumstances, if the body supports / requires it and has a corresponding
|
||||
// confirmation question to ask.
|
||||
if (this.attrs.confirmExit) {
|
||||
this.composer.preventClosingWhen(() => this.hasChanges(), this.attrs.confirmExit);
|
||||
}
|
||||
this.composer.fields.content(this.attrs.originalContent || '');
|
||||
}
|
||||
view() {
|
||||
var _this$jumpToPreview;
|
||||
return m(_common_components_ConfirmDocumentUnload__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
when: this.hasChanges.bind(this)
|
||||
}, m("div", {
|
||||
className: (0,_common_utils_classList__WEBPACK_IMPORTED_MODULE_6__["default"])('ComposerBody', this.attrs.className)
|
||||
}, m(_common_components_Avatar__WEBPACK_IMPORTED_MODULE_7__["default"], {
|
||||
user: this.attrs.user,
|
||||
className: "ComposerBody-avatar"
|
||||
}), m("div", {
|
||||
className: "ComposerBody-content"
|
||||
}, m("ul", {
|
||||
className: "ComposerBody-header"
|
||||
}, (0,_common_helpers_listItems__WEBPACK_IMPORTED_MODULE_4__["default"])(this.headerItems().toArray())), m("div", {
|
||||
className: "ComposerBody-editor"
|
||||
}, m(_common_components_TextEditor__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
submitLabel: this.attrs.submitLabel,
|
||||
placeholder: this.attrs.placeholder,
|
||||
disabled: this.loading || this.attrs.disabled,
|
||||
composer: this.composer,
|
||||
preview: (_this$jumpToPreview = this.jumpToPreview) == null ? void 0 : _this$jumpToPreview.bind(this),
|
||||
onchange: this.composer.fields.content,
|
||||
onsubmit: this.onsubmit.bind(this),
|
||||
value: this.composer.fields.content()
|
||||
}))), m(_common_components_LoadingIndicator__WEBPACK_IMPORTED_MODULE_1__["default"], {
|
||||
display: "unset",
|
||||
containerClassName: (0,_common_utils_classList__WEBPACK_IMPORTED_MODULE_6__["default"])('ComposerBody-loading', this.loading && 'active'),
|
||||
size: "large"
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is any unsaved data.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasChanges() {
|
||||
const content = this.composer.fields.content();
|
||||
return content && content !== this.attrs.originalContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the composer's header.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
headerItems() {
|
||||
return new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_5__["default"]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the submit event of the text editor.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
onsubmit() {}
|
||||
|
||||
/**
|
||||
* Stop loading.
|
||||
*/
|
||||
loaded() {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/ComposerBody', ComposerBody);
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/DiscussionComposer.js":
|
||||
/*!****************************************************!*\
|
||||
!*** ./src/forum/components/DiscussionComposer.js ***!
|
||||
\****************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ DiscussionComposer)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _forum_app__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../forum/app */ "./src/forum/app.ts");
|
||||
/* harmony import */ var _ComposerBody__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ComposerBody */ "./src/forum/components/ComposerBody.js");
|
||||
/* harmony import */ var _common_utils_extractText__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/utils/extractText */ "./src/common/utils/extractText.ts");
|
||||
/* harmony import */ var _common_utils_Stream__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/utils/Stream */ "./src/common/utils/Stream.ts");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `DiscussionComposer` component displays the composer content for starting
|
||||
* a new discussion. It adds a text field as a header control so the user can
|
||||
* enter the title of their discussion. It also overrides the `submit` and
|
||||
* `willExit` actions to account for the title.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - All of the attrs for ComposerBody
|
||||
* - `titlePlaceholder`
|
||||
*/
|
||||
class DiscussionComposer extends _ComposerBody__WEBPACK_IMPORTED_MODULE_1__["default"] {
|
||||
static initAttrs(attrs) {
|
||||
super.initAttrs(attrs);
|
||||
attrs.placeholder = attrs.placeholder || (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_2__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_discussion.body_placeholder'));
|
||||
attrs.submitLabel = attrs.submitLabel || _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_discussion.submit_button');
|
||||
attrs.confirmExit = attrs.confirmExit || (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_2__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_discussion.discard_confirmation'));
|
||||
attrs.titlePlaceholder = attrs.titlePlaceholder || (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_2__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_discussion.title_placeholder'));
|
||||
attrs.className = 'ComposerBody--discussion';
|
||||
}
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.composer.fields.title = this.composer.fields.title || (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_3__["default"])('');
|
||||
|
||||
/**
|
||||
* The value of the title input.
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
this.title = this.composer.fields.title;
|
||||
}
|
||||
headerItems() {
|
||||
const items = super.headerItems();
|
||||
items.add('title', m("h3", null, _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_discussion.title')), 100);
|
||||
items.add('discussionTitle', m("h3", null, m("input", {
|
||||
className: "FormControl",
|
||||
bidi: this.title,
|
||||
placeholder: this.attrs.titlePlaceholder,
|
||||
disabled: !!this.attrs.disabled,
|
||||
onkeydown: this.onkeydown.bind(this)
|
||||
})));
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the title input's keydown event. When the return key is pressed,
|
||||
* move the focus to the start of the text editor.
|
||||
*
|
||||
* @param {KeyboardEvent} e
|
||||
*/
|
||||
onkeydown(e) {
|
||||
if (e.which === 13) {
|
||||
// Return
|
||||
e.preventDefault();
|
||||
this.composer.editor.moveCursorTo(0);
|
||||
}
|
||||
e.redraw = false;
|
||||
}
|
||||
hasChanges() {
|
||||
return this.title() || this.composer.fields.content();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data to submit to the server when the discussion is saved.
|
||||
*
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
title: this.title(),
|
||||
content: this.composer.fields.content()
|
||||
};
|
||||
}
|
||||
onsubmit() {
|
||||
this.loading = true;
|
||||
const data = this.data();
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].store.createRecord('discussions').save(data).then(discussion => {
|
||||
this.composer.hide();
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].discussions.refresh();
|
||||
m.route.set(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].route.discussion(discussion));
|
||||
}, this.loaded.bind(this));
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/DiscussionComposer', DiscussionComposer);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=DiscussionComposer.js.map
|
2
framework/core/js/dist/forum/components/DiscussionComposer.js.map
generated
vendored
2
framework/core/js/dist/forum/components/DiscussionComposer.js.map
generated
vendored
File diff suppressed because one or more lines are too long
52
framework/core/js/dist/forum/components/DiscussionsUserPage.js
generated
vendored
52
framework/core/js/dist/forum/components/DiscussionsUserPage.js
generated
vendored
@ -1,2 +1,52 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[799],{6466:(s,e,r)=>{r.r(e),r.d(e,{default:()=>u});var t=r(3390),a=r(8421),n=r(1654);class u extends t.Z{oninit(s){super.oninit(s),this.loadUser(m.route.param("username"))}show(s){super.show(s),this.state=new n.Z({filter:{author:s.username()},sort:"newest"}),this.state.refresh()}content(){return m("div",{className:"DiscussionsUserPage"},m(a.Z,{state:this.state}))}}flarum.reg.add("core","forum/components/DiscussionsUserPage",u)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/DiscussionsUserPage"],{
|
||||
|
||||
/***/ "./src/forum/components/DiscussionsUserPage.tsx":
|
||||
/*!******************************************************!*\
|
||||
!*** ./src/forum/components/DiscussionsUserPage.tsx ***!
|
||||
\******************************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ DiscussionsUserPage)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _UserPage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./UserPage */ "./src/forum/components/UserPage.tsx");
|
||||
/* harmony import */ var _DiscussionList__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./DiscussionList */ "./src/forum/components/DiscussionList.js");
|
||||
/* harmony import */ var _states_DiscussionListState__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../states/DiscussionListState */ "./src/forum/states/DiscussionListState.ts");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `DiscussionsUserPage` component shows a discussion list inside of a user
|
||||
* page.
|
||||
*/
|
||||
class DiscussionsUserPage extends _UserPage__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.loadUser(m.route.param('username'));
|
||||
}
|
||||
show(user) {
|
||||
super.show(user);
|
||||
this.state = new _states_DiscussionListState__WEBPACK_IMPORTED_MODULE_2__["default"]({
|
||||
filter: {
|
||||
author: user.username()
|
||||
},
|
||||
sort: 'newest'
|
||||
});
|
||||
this.state.refresh();
|
||||
}
|
||||
content() {
|
||||
return m("div", {
|
||||
className: "DiscussionsUserPage"
|
||||
}, m(_DiscussionList__WEBPACK_IMPORTED_MODULE_1__["default"], {
|
||||
state: this.state
|
||||
}));
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/DiscussionsUserPage', DiscussionsUserPage);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=DiscussionsUserPage.js.map
|
2
framework/core/js/dist/forum/components/DiscussionsUserPage.js.map
generated
vendored
2
framework/core/js/dist/forum/components/DiscussionsUserPage.js.map
generated
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"forum/components/DiscussionsUserPage.js","mappings":"yKAOe,MAAMA,UAA4B,IAC/CC,OAAOC,GACLC,MAAMF,OAAOC,GACbE,KAAKC,SAASC,EAAEC,MAAMC,MAAM,YAC9B,CACAC,KAAKC,GACHP,MAAMM,KAAKC,GACXN,KAAKO,MAAQ,IAAI,IAAoB,CACnCC,OAAQ,CACNC,OAAQH,EAAKI,YAEfC,KAAM,WAERX,KAAKO,MAAMK,SACb,CACAC,UACE,OAAOX,EAAE,MAAO,CACdY,UAAW,uBACVZ,EAAE,IAAgB,CACnBK,MAAOP,KAAKO,QAEhB,EAEFQ,OAAOC,IAAIC,IAAI,OAAQ,uCAAwCrB,E","sources":["webpack://@flarum/core/./src/forum/components/DiscussionsUserPage.tsx"],"sourcesContent":["import UserPage from './UserPage';\nimport DiscussionList from './DiscussionList';\nimport DiscussionListState from '../states/DiscussionListState';\n/**\n * The `DiscussionsUserPage` component shows a discussion list inside of a user\n * page.\n */\nexport default class DiscussionsUserPage extends UserPage {\n oninit(vnode) {\n super.oninit(vnode);\n this.loadUser(m.route.param('username'));\n }\n show(user) {\n super.show(user);\n this.state = new DiscussionListState({\n filter: {\n author: user.username()\n },\n sort: 'newest'\n });\n this.state.refresh();\n }\n content() {\n return m(\"div\", {\n className: \"DiscussionsUserPage\"\n }, m(DiscussionList, {\n state: this.state\n }));\n }\n}\nflarum.reg.add('core', 'forum/components/DiscussionsUserPage', DiscussionsUserPage);"],"names":["DiscussionsUserPage","oninit","vnode","super","this","loadUser","m","route","param","show","user","state","filter","author","username","sort","refresh","content","className","flarum","reg","add"],"sourceRoot":""}
|
||||
{"version":3,"file":"forum/components/DiscussionsUserPage.js","mappings":";;;;;;;;;;;;;;;;AAAkC;AACY;AACkB;AAChE;AACA;AACA;AACA;AACe,kCAAkC,iDAAQ;AACzD;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,mEAAmB;AACxC;AACA;AACA,OAAO;AACP;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,KAAK,IAAI,uDAAc;AACvB;AACA,KAAK;AACL;AACA;AACA","sources":["webpack://@flarum/core/./src/forum/components/DiscussionsUserPage.tsx"],"sourcesContent":["import UserPage from './UserPage';\nimport DiscussionList from './DiscussionList';\nimport DiscussionListState from '../states/DiscussionListState';\n/**\n * The `DiscussionsUserPage` component shows a discussion list inside of a user\n * page.\n */\nexport default class DiscussionsUserPage extends UserPage {\n oninit(vnode) {\n super.oninit(vnode);\n this.loadUser(m.route.param('username'));\n }\n show(user) {\n super.show(user);\n this.state = new DiscussionListState({\n filter: {\n author: user.username()\n },\n sort: 'newest'\n });\n this.state.refresh();\n }\n content() {\n return m(\"div\", {\n className: \"DiscussionsUserPage\"\n }, m(DiscussionList, {\n state: this.state\n }));\n }\n}\nflarum.reg.add('core', 'forum/components/DiscussionsUserPage', DiscussionsUserPage);"],"names":[],"sourceRoot":""}
|
293
framework/core/js/dist/forum/components/EditPostComposer.js
generated
vendored
293
framework/core/js/dist/forum/components/EditPostComposer.js
generated
vendored
@ -1,2 +1,293 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[293],{2140:(t,s,e)=>{e.d(s,{Z:()=>u});var o=e(2190),r=e(5226);class i extends o.Z{handler(){return this.attrs.when()||void 0}oncreate(t){super.oncreate(t),this.boundHandler=this.handler.bind(this),$(window).on("beforeunload",this.boundHandler)}onremove(t){super.onremove(t),$(window).off("beforeunload",this.boundHandler)}view(t){return m("[",null,t.children)}}flarum.reg.add("core","common/components/ConfirmDocumentUnload",i);var n=e(4944),a=e(1268),d=e(4041),c=e(3344),l=e(7323);class u extends o.Z{oninit(t){super.oninit(t),this.composer=this.attrs.composer,this.loading=!1,this.attrs.confirmExit&&this.composer.preventClosingWhen((()=>this.hasChanges()),this.attrs.confirmExit),this.composer.fields.content(this.attrs.originalContent||"")}view(){var t;return m(i,{when:this.hasChanges.bind(this)},m("div",{className:(0,c.Z)("ComposerBody",this.attrs.className)},m(l.Z,{user:this.attrs.user,className:"ComposerBody-avatar"}),m("div",{className:"ComposerBody-content"},m("ul",{className:"ComposerBody-header"},(0,a.Z)(this.headerItems().toArray())),m("div",{className:"ComposerBody-editor"},m(n.Z,{submitLabel:this.attrs.submitLabel,placeholder:this.attrs.placeholder,disabled:this.loading||this.attrs.disabled,composer:this.composer,preview:null==(t=this.jumpToPreview)?void 0:t.bind(this),onchange:this.composer.fields.content,onsubmit:this.onsubmit.bind(this),value:this.composer.fields.content()}))),m(r.Z,{display:"unset",containerClassName:(0,c.Z)("ComposerBody-loading",this.loading&&"active"),size:"large"})))}hasChanges(){const t=this.composer.fields.content();return t&&t!==this.attrs.originalContent}headerItems(){return new d.Z}onsubmit(){}loaded(){this.loading=!1,m.redraw()}}flarum.reg.add("core","forum/components/ComposerBody",u)},500:(t,s,e)=>{e.r(s),e.d(s,{default:()=>c});var o=e(6789),r=e(2140),i=e(8312),n=e(6597),a=e(9133);function d(t){o.Z.composer.isFullScreen()&&(o.Z.composer.minimize(),t.stopPropagation())}class c extends r.Z{static initAttrs(t){super.initAttrs(t),t.submitLabel=t.submitLabel||o.Z.translator.trans("core.forum.composer_edit.submit_button"),t.confirmExit=t.confirmExit||o.Z.translator.trans("core.forum.composer_edit.discard_confirmation"),t.originalContent=t.originalContent||t.post.content(),t.user=t.user||t.post.user(),t.post.editedContent=t.originalContent}headerItems(){const t=super.headerItems(),s=this.attrs.post;return t.add("title",m("h3",null,m(a.Z,{name:"fas fa-pencil-alt"})," ",m(n.Z,{href:o.Z.route.discussion(s.discussion(),s.number()),onclick:d},o.Z.translator.trans("core.forum.composer_edit.post_link",{number:s.number(),discussion:s.discussion().title()})))),t}jumpToPreview(t){d(t),m.route.set(o.Z.route.post(this.attrs.post))}data(){return{content:this.composer.fields.content()}}onsubmit(){const t=this.attrs.post.discussion();this.loading=!0;const s=this.data();this.attrs.post.save(s).then((s=>{if(o.Z.viewingDiscussion(t))o.Z.current.get("stream").goToNumber(s.number());else{const t=o.Z.alerts.show({type:"success",controls:[m(i.Z,{className:"Button Button--link",onclick:()=>{m.route.set(o.Z.route.post(s)),o.Z.alerts.dismiss(t)}},o.Z.translator.trans("core.forum.composer_edit.view_button"))]},o.Z.translator.trans("core.forum.composer_edit.edited_message"))}this.composer.hide()}),this.loaded.bind(this))}}flarum.reg.add("core","forum/components/EditPostComposer",c)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/EditPostComposer"],{
|
||||
|
||||
/***/ "./src/common/components/ConfirmDocumentUnload.js":
|
||||
/*!********************************************************!*\
|
||||
!*** ./src/common/components/ConfirmDocumentUnload.js ***!
|
||||
\********************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ConfirmDocumentUnload)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Component */ "./src/common/Component.ts");
|
||||
|
||||
|
||||
/**
|
||||
* The `ConfirmDocumentUnload` component can be used to register a global
|
||||
* event handler that prevents closing the browser window/tab based on the
|
||||
* return value of a given callback prop.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - `when` - a callback returning true when the browser should prompt for
|
||||
* confirmation before closing the window/tab
|
||||
*/
|
||||
class ConfirmDocumentUnload extends _Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
handler() {
|
||||
return this.attrs.when() || undefined;
|
||||
}
|
||||
oncreate(vnode) {
|
||||
super.oncreate(vnode);
|
||||
this.boundHandler = this.handler.bind(this);
|
||||
$(window).on('beforeunload', this.boundHandler);
|
||||
}
|
||||
onremove(vnode) {
|
||||
super.onremove(vnode);
|
||||
$(window).off('beforeunload', this.boundHandler);
|
||||
}
|
||||
view(vnode) {
|
||||
return m('[', null, vnode.children);
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'common/components/ConfirmDocumentUnload', ConfirmDocumentUnload);
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/ComposerBody.js":
|
||||
/*!**********************************************!*\
|
||||
!*** ./src/forum/components/ComposerBody.js ***!
|
||||
\**********************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ComposerBody)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _common_Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/Component */ "./src/common/Component.ts");
|
||||
/* harmony import */ var _common_components_LoadingIndicator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/components/LoadingIndicator */ "./src/common/components/LoadingIndicator.tsx");
|
||||
/* harmony import */ var _common_components_ConfirmDocumentUnload__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/ConfirmDocumentUnload */ "./src/common/components/ConfirmDocumentUnload.js");
|
||||
/* harmony import */ var _common_components_TextEditor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/TextEditor */ "./src/common/components/TextEditor.js");
|
||||
/* harmony import */ var _common_helpers_listItems__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../common/helpers/listItems */ "./src/common/helpers/listItems.tsx");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
/* harmony import */ var _common_utils_classList__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../common/utils/classList */ "./src/common/utils/classList.ts");
|
||||
/* harmony import */ var _common_components_Avatar__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../common/components/Avatar */ "./src/common/components/Avatar.tsx");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `ComposerBody` component handles the body, or the content, of the
|
||||
* composer. Subclasses should implement the `onsubmit` method and override
|
||||
* `headerTimes`.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - `composer`
|
||||
* - `originalContent`
|
||||
* - `submitLabel`
|
||||
* - `placeholder`
|
||||
* - `user`
|
||||
* - `confirmExit`
|
||||
* - `disabled`
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
class ComposerBody extends _common_Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.composer = this.attrs.composer;
|
||||
|
||||
/**
|
||||
* Whether or not the component is loading.
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.loading = false;
|
||||
|
||||
// Let the composer state know to ask for confirmation under certain
|
||||
// circumstances, if the body supports / requires it and has a corresponding
|
||||
// confirmation question to ask.
|
||||
if (this.attrs.confirmExit) {
|
||||
this.composer.preventClosingWhen(() => this.hasChanges(), this.attrs.confirmExit);
|
||||
}
|
||||
this.composer.fields.content(this.attrs.originalContent || '');
|
||||
}
|
||||
view() {
|
||||
var _this$jumpToPreview;
|
||||
return m(_common_components_ConfirmDocumentUnload__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
when: this.hasChanges.bind(this)
|
||||
}, m("div", {
|
||||
className: (0,_common_utils_classList__WEBPACK_IMPORTED_MODULE_6__["default"])('ComposerBody', this.attrs.className)
|
||||
}, m(_common_components_Avatar__WEBPACK_IMPORTED_MODULE_7__["default"], {
|
||||
user: this.attrs.user,
|
||||
className: "ComposerBody-avatar"
|
||||
}), m("div", {
|
||||
className: "ComposerBody-content"
|
||||
}, m("ul", {
|
||||
className: "ComposerBody-header"
|
||||
}, (0,_common_helpers_listItems__WEBPACK_IMPORTED_MODULE_4__["default"])(this.headerItems().toArray())), m("div", {
|
||||
className: "ComposerBody-editor"
|
||||
}, m(_common_components_TextEditor__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
submitLabel: this.attrs.submitLabel,
|
||||
placeholder: this.attrs.placeholder,
|
||||
disabled: this.loading || this.attrs.disabled,
|
||||
composer: this.composer,
|
||||
preview: (_this$jumpToPreview = this.jumpToPreview) == null ? void 0 : _this$jumpToPreview.bind(this),
|
||||
onchange: this.composer.fields.content,
|
||||
onsubmit: this.onsubmit.bind(this),
|
||||
value: this.composer.fields.content()
|
||||
}))), m(_common_components_LoadingIndicator__WEBPACK_IMPORTED_MODULE_1__["default"], {
|
||||
display: "unset",
|
||||
containerClassName: (0,_common_utils_classList__WEBPACK_IMPORTED_MODULE_6__["default"])('ComposerBody-loading', this.loading && 'active'),
|
||||
size: "large"
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is any unsaved data.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasChanges() {
|
||||
const content = this.composer.fields.content();
|
||||
return content && content !== this.attrs.originalContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the composer's header.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
headerItems() {
|
||||
return new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_5__["default"]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the submit event of the text editor.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
onsubmit() {}
|
||||
|
||||
/**
|
||||
* Stop loading.
|
||||
*/
|
||||
loaded() {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/ComposerBody', ComposerBody);
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/EditPostComposer.js":
|
||||
/*!**************************************************!*\
|
||||
!*** ./src/forum/components/EditPostComposer.js ***!
|
||||
\**************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ EditPostComposer)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _forum_app__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../forum/app */ "./src/forum/app.ts");
|
||||
/* harmony import */ var _ComposerBody__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ComposerBody */ "./src/forum/components/ComposerBody.js");
|
||||
/* harmony import */ var _common_components_Button__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/Button */ "./src/common/components/Button.tsx");
|
||||
/* harmony import */ var _common_components_Link__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/Link */ "./src/common/components/Link.js");
|
||||
/* harmony import */ var _common_components_Icon__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../common/components/Icon */ "./src/common/components/Icon.tsx");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function minimizeComposerIfFullScreen(e) {
|
||||
if (_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].composer.isFullScreen()) {
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].composer.minimize();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `EditPostComposer` component displays the composer content for editing a
|
||||
* post. It sets the initial content to the content of the post that is being
|
||||
* edited, and adds a header control to indicate which post is being edited.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - All of the attrs for ComposerBody
|
||||
* - `post`
|
||||
*/
|
||||
class EditPostComposer extends _ComposerBody__WEBPACK_IMPORTED_MODULE_1__["default"] {
|
||||
static initAttrs(attrs) {
|
||||
super.initAttrs(attrs);
|
||||
attrs.submitLabel = attrs.submitLabel || _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_edit.submit_button');
|
||||
attrs.confirmExit = attrs.confirmExit || _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_edit.discard_confirmation');
|
||||
attrs.originalContent = attrs.originalContent || attrs.post.content();
|
||||
attrs.user = attrs.user || attrs.post.user();
|
||||
attrs.post.editedContent = attrs.originalContent;
|
||||
}
|
||||
headerItems() {
|
||||
const items = super.headerItems();
|
||||
const post = this.attrs.post;
|
||||
items.add('title', m("h3", null, m(_common_components_Icon__WEBPACK_IMPORTED_MODULE_4__["default"], {
|
||||
name: 'fas fa-pencil-alt'
|
||||
}), ' ', m(_common_components_Link__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
href: _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].route.discussion(post.discussion(), post.number()),
|
||||
onclick: minimizeComposerIfFullScreen
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_edit.post_link', {
|
||||
number: post.number(),
|
||||
discussion: post.discussion().title()
|
||||
}))));
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump to the preview when triggered by the text editor.
|
||||
*/
|
||||
jumpToPreview(e) {
|
||||
minimizeComposerIfFullScreen(e);
|
||||
m.route.set(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].route.post(this.attrs.post));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data to submit to the server when the post is saved.
|
||||
*
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
content: this.composer.fields.content()
|
||||
};
|
||||
}
|
||||
onsubmit() {
|
||||
const discussion = this.attrs.post.discussion();
|
||||
this.loading = true;
|
||||
const data = this.data();
|
||||
this.attrs.post.save(data).then(post => {
|
||||
// If we're currently viewing the discussion which this edit was made
|
||||
// in, then we can scroll to the post.
|
||||
if (_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].viewingDiscussion(discussion)) {
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].current.get('stream').goToNumber(post.number());
|
||||
} else {
|
||||
// Otherwise, we'll create an alert message to inform the user that
|
||||
// their edit has been made, containing a button which will
|
||||
// transition to their edited post when clicked.
|
||||
const alert = _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].alerts.show({
|
||||
type: 'success',
|
||||
controls: [m(_common_components_Button__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
className: "Button Button--link",
|
||||
onclick: () => {
|
||||
m.route.set(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].route.post(post));
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].alerts.dismiss(alert);
|
||||
}
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_edit.view_button'))]
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_edit.edited_message'));
|
||||
}
|
||||
this.composer.hide();
|
||||
}, this.loaded.bind(this));
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/EditPostComposer', EditPostComposer);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=EditPostComposer.js.map
|
2
framework/core/js/dist/forum/components/EditPostComposer.js.map
generated
vendored
2
framework/core/js/dist/forum/components/EditPostComposer.js.map
generated
vendored
File diff suppressed because one or more lines are too long
129
framework/core/js/dist/forum/components/ForgotPasswordModal.js
generated
vendored
129
framework/core/js/dist/forum/components/ForgotPasswordModal.js
generated
vendored
@ -1,2 +1,129 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[502],{1839:(r,t,s)=>{s.r(t),s.d(t,{default:()=>c});var o=s(7905),a=s(6789),e=s(899),l=s(8312),i=s(1552),n=s(6458),d=s(4041),u=s(6352);class c extends e.Z{constructor(){super(...arguments),(0,o.Z)(this,"email",void 0),(0,o.Z)(this,"success",!1)}oninit(r){super.oninit(r),this.email=(0,n.Z)(this.attrs.email||"")}className(){return"ForgotPasswordModal Modal--small"}title(){return a.Z.translator.trans("core.forum.forgot_password.title")}content(){return this.success?m("div",{className:"Modal-body"},m(u.Z,{className:"Form--centered"},m("p",{className:"helpText"},a.Z.translator.trans("core.forum.forgot_password.email_sent_message")),m("div",{className:"Form-group Form-controls"},m(l.Z,{className:"Button Button--primary Button--block",onclick:this.hide.bind(this)},a.Z.translator.trans("core.forum.forgot_password.dismiss_button"))))):m("div",{className:"Modal-body"},m(u.Z,{className:"Form--centered",description:a.Z.translator.trans("core.forum.forgot_password.text")},this.fields().toArray()))}fields(){const r=new d.Z,t=(0,i.Z)(a.Z.translator.trans("core.forum.forgot_password.email_placeholder"));return r.add("email",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"email",type:"email",placeholder:t,"aria-label":t,bidi:this.email,disabled:this.loading})),50),r.add("submit",m("div",{className:"Form-group Form-controls"},m(l.Z,{className:"Button Button--primary Button--block",type:"submit",loading:this.loading},a.Z.translator.trans("core.forum.forgot_password.submit_button"))),-10),r}onsubmit(r){r.preventDefault(),this.loading=!0,a.Z.request({method:"POST",url:a.Z.forum.attribute("apiUrl")+"/forgot",body:this.requestParams(),errorHandler:this.onerror.bind(this)}).then((()=>{this.success=!0,this.alertAttrs=null})).catch((()=>{})).then(this.loaded.bind(this))}requestParams(){return{email:this.email()}}onerror(r){404===r.status&&r.alert&&(r.alert.content=a.Z.translator.trans("core.forum.forgot_password.not_found_message")),super.onerror(r)}}flarum.reg.add("core","forum/components/ForgotPasswordModal",c)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/ForgotPasswordModal"],{
|
||||
|
||||
/***/ "./src/forum/components/ForgotPasswordModal.tsx":
|
||||
/*!******************************************************!*\
|
||||
!*** ./src/forum/components/ForgotPasswordModal.tsx ***!
|
||||
\******************************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ForgotPasswordModal)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/esm/defineProperty */ "../../../js-packages/webpack-config/node_modules/@babel/runtime/helpers/esm/defineProperty.js");
|
||||
/* harmony import */ var _forum_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../forum/app */ "./src/forum/app.ts");
|
||||
/* harmony import */ var _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/FormModal */ "./src/common/components/FormModal.tsx");
|
||||
/* harmony import */ var _common_components_Button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/Button */ "./src/common/components/Button.tsx");
|
||||
/* harmony import */ var _common_utils_extractText__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../common/utils/extractText */ "./src/common/utils/extractText.ts");
|
||||
/* harmony import */ var _common_utils_Stream__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../common/utils/Stream */ "./src/common/utils/Stream.ts");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
/* harmony import */ var _common_components_Form__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../common/components/Form */ "./src/common/components/Form.tsx");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `ForgotPasswordModal` component displays a modal which allows the user to
|
||||
* enter their email address and request a link to reset their password.
|
||||
*/
|
||||
class ForgotPasswordModal extends _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__["default"] {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* The value of the email input.
|
||||
*/
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "email", void 0);
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "success", false);
|
||||
}
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.email = (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_5__["default"])(this.attrs.email || '');
|
||||
}
|
||||
className() {
|
||||
return 'ForgotPasswordModal Modal--small';
|
||||
}
|
||||
title() {
|
||||
return _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.forgot_password.title');
|
||||
}
|
||||
content() {
|
||||
if (this.success) {
|
||||
return m("div", {
|
||||
className: "Modal-body"
|
||||
}, m(_common_components_Form__WEBPACK_IMPORTED_MODULE_7__["default"], {
|
||||
className: "Form--centered"
|
||||
}, m("p", {
|
||||
className: "helpText"
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.forgot_password.email_sent_message')), m("div", {
|
||||
className: "Form-group Form-controls"
|
||||
}, m(_common_components_Button__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
className: "Button Button--primary Button--block",
|
||||
onclick: this.hide.bind(this)
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.forgot_password.dismiss_button')))));
|
||||
}
|
||||
return m("div", {
|
||||
className: "Modal-body"
|
||||
}, m(_common_components_Form__WEBPACK_IMPORTED_MODULE_7__["default"], {
|
||||
className: "Form--centered",
|
||||
description: _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.forgot_password.text')
|
||||
}, this.fields().toArray()));
|
||||
}
|
||||
fields() {
|
||||
const items = new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_6__["default"]();
|
||||
const emailLabel = (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_4__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.forgot_password.email_placeholder'));
|
||||
items.add('email', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("input", {
|
||||
className: "FormControl",
|
||||
name: "email",
|
||||
type: "email",
|
||||
placeholder: emailLabel,
|
||||
"aria-label": emailLabel,
|
||||
bidi: this.email,
|
||||
disabled: this.loading
|
||||
})), 50);
|
||||
items.add('submit', m("div", {
|
||||
className: "Form-group Form-controls"
|
||||
}, m(_common_components_Button__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
className: "Button Button--primary Button--block",
|
||||
type: "submit",
|
||||
loading: this.loading
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.forgot_password.submit_button'))), -10);
|
||||
return items;
|
||||
}
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
this.loading = true;
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].request({
|
||||
method: 'POST',
|
||||
url: _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].forum.attribute('apiUrl') + '/forgot',
|
||||
body: this.requestParams(),
|
||||
errorHandler: this.onerror.bind(this)
|
||||
}).then(() => {
|
||||
this.success = true;
|
||||
this.alertAttrs = null;
|
||||
}).catch(() => {}).then(this.loaded.bind(this));
|
||||
}
|
||||
requestParams() {
|
||||
const data = {
|
||||
email: this.email()
|
||||
};
|
||||
return data;
|
||||
}
|
||||
onerror(error) {
|
||||
if (error.status === 404 && error.alert) {
|
||||
error.alert.content = _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.forgot_password.not_found_message');
|
||||
}
|
||||
super.onerror(error);
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/ForgotPasswordModal', ForgotPasswordModal);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=ForgotPasswordModal.js.map
|
2
framework/core/js/dist/forum/components/ForgotPasswordModal.js.map
generated
vendored
2
framework/core/js/dist/forum/components/ForgotPasswordModal.js.map
generated
vendored
File diff suppressed because one or more lines are too long
222
framework/core/js/dist/forum/components/LogInModal.js
generated
vendored
222
framework/core/js/dist/forum/components/LogInModal.js
generated
vendored
@ -1,2 +1,222 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[460],{5049:(o,r,t)=>{t.r(r),t.d(r,{default:()=>u});var s=t(7905),e=t(6789),i=t(899),a=t(8312),n=t(6403),l=t(1552),d=t(4041),c=t(6458);class u extends i.Z{constructor(){super(...arguments),(0,s.Z)(this,"identification",void 0),(0,s.Z)(this,"password",void 0),(0,s.Z)(this,"remember",void 0)}oninit(o){super.oninit(o),this.identification=(0,c.Z)(this.attrs.identification||""),this.password=(0,c.Z)(this.attrs.password||""),this.remember=(0,c.Z)(!!this.attrs.remember)}className(){return"LogInModal Modal--small"}title(){return e.Z.translator.trans("core.forum.log_in.title")}content(){return[m("div",{className:"Modal-body"},this.body()),m("div",{className:"Modal-footer"},this.footer())]}body(){return[m(n.Z,null),m("div",{className:"Form Form--centered"},this.fields().toArray())]}fields(){const o=new d.Z,r=(0,l.Z)(e.Z.translator.trans("core.forum.log_in.username_or_email_placeholder")),t=(0,l.Z)(e.Z.translator.trans("core.forum.log_in.password_placeholder"));return o.add("identification",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"identification",type:"text",placeholder:r,"aria-label":r,bidi:this.identification,disabled:this.loading})),30),o.add("password",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"password",type:"password",autocomplete:"current-password",placeholder:t,"aria-label":t,bidi:this.password,disabled:this.loading})),20),o.add("remember",m("div",{className:"Form-group"},m("div",null,m("label",{className:"checkbox"},m("input",{type:"checkbox",bidi:this.remember,disabled:this.loading}),e.Z.translator.trans("core.forum.log_in.remember_me_label")))),10),o.add("submit",m("div",{className:"Form-group"},m(a.Z,{className:"Button Button--primary Button--block",type:"submit",loading:this.loading},e.Z.translator.trans("core.forum.log_in.submit_button"))),-10),o}footer(){return m("[",null,m("p",{className:"LogInModal-forgotPassword"},m("a",{onclick:this.forgotPassword.bind(this)},e.Z.translator.trans("core.forum.log_in.forgot_password_link"))),e.Z.forum.attribute("allowSignUp")&&m("p",{className:"LogInModal-signUp"},e.Z.translator.trans("core.forum.log_in.sign_up_text",{a:m("a",{onclick:this.signUp.bind(this)})})))}forgotPassword(){const o=this.identification(),r=o.includes("@")?{email:o}:void 0;e.Z.modal.show((()=>t.e(502).then(t.bind(t,1839))),r)}signUp(){const o=this.identification(),r={[o.includes("@")?"email":"username"]:o};e.Z.modal.show((()=>t.e(395).then(t.bind(t,8686))),r)}onready(){this.$("[name="+(this.identification()?"password":"identification")+"]").trigger("select")}onsubmit(o){o.preventDefault(),this.loading=!0,e.Z.session.login(this.loginParams(),{errorHandler:this.onerror.bind(this)}).then((()=>window.location.reload()),this.loaded.bind(this))}loginParams(){return{identification:this.identification(),password:this.password(),remember:this.remember()}}onerror(o){401===o.status&&o.alert&&(o.alert.content=e.Z.translator.trans("core.forum.log_in.invalid_login_message"),this.password("")),super.onerror(o)}}flarum.reg.add("core","forum/components/LogInModal",u),flarum.reg.addChunkModule("502","1839","core","forum/components/ForgotPasswordModal"),flarum.reg.addChunkModule("395","8686","core","forum/components/SignUpModal")},6403:(o,r,t)=>{t.d(r,{Z:()=>i});var s=t(2190),e=t(4041);class i extends s.Z{view(){return m("div",{className:"LogInButtons"},this.items().toArray())}items(){return new e.Z}}flarum.reg.add("core","forum/components/LogInButtons",i)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/LogInModal"],{
|
||||
|
||||
/***/ "./src/forum/components/LogInModal.tsx":
|
||||
/*!*********************************************!*\
|
||||
!*** ./src/forum/components/LogInModal.tsx ***!
|
||||
\*********************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ LogInModal)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/esm/defineProperty */ "../../../js-packages/webpack-config/node_modules/@babel/runtime/helpers/esm/defineProperty.js");
|
||||
/* harmony import */ var _forum_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../forum/app */ "./src/forum/app.ts");
|
||||
/* harmony import */ var _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/FormModal */ "./src/common/components/FormModal.tsx");
|
||||
/* harmony import */ var _common_components_Button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/Button */ "./src/common/components/Button.tsx");
|
||||
/* harmony import */ var _LogInButtons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./LogInButtons */ "./src/forum/components/LogInButtons.js");
|
||||
/* harmony import */ var _common_utils_extractText__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../common/utils/extractText */ "./src/common/utils/extractText.ts");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
/* harmony import */ var _common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../common/utils/Stream */ "./src/common/utils/Stream.ts");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class LogInModal extends _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__["default"] {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* The value of the identification input.
|
||||
*/
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "identification", void 0);
|
||||
/**
|
||||
* The value of the password input.
|
||||
*/
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "password", void 0);
|
||||
/**
|
||||
* The value of the remember me input.
|
||||
*/
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "remember", void 0);
|
||||
}
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.identification = (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__["default"])(this.attrs.identification || '');
|
||||
this.password = (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__["default"])(this.attrs.password || '');
|
||||
this.remember = (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__["default"])(!!this.attrs.remember);
|
||||
}
|
||||
className() {
|
||||
return 'LogInModal Modal--small';
|
||||
}
|
||||
title() {
|
||||
return _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.title');
|
||||
}
|
||||
content() {
|
||||
return [m("div", {
|
||||
className: "Modal-body"
|
||||
}, this.body()), m("div", {
|
||||
className: "Modal-footer"
|
||||
}, this.footer())];
|
||||
}
|
||||
body() {
|
||||
return [m(_LogInButtons__WEBPACK_IMPORTED_MODULE_4__["default"], null), m("div", {
|
||||
className: "Form Form--centered"
|
||||
}, this.fields().toArray())];
|
||||
}
|
||||
fields() {
|
||||
const items = new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_6__["default"]();
|
||||
const identificationLabel = (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_5__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.username_or_email_placeholder'));
|
||||
const passwordLabel = (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_5__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.password_placeholder'));
|
||||
items.add('identification', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("input", {
|
||||
className: "FormControl",
|
||||
name: "identification",
|
||||
type: "text",
|
||||
placeholder: identificationLabel,
|
||||
"aria-label": identificationLabel,
|
||||
bidi: this.identification,
|
||||
disabled: this.loading
|
||||
})), 30);
|
||||
items.add('password', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("input", {
|
||||
className: "FormControl",
|
||||
name: "password",
|
||||
type: "password",
|
||||
autocomplete: "current-password",
|
||||
placeholder: passwordLabel,
|
||||
"aria-label": passwordLabel,
|
||||
bidi: this.password,
|
||||
disabled: this.loading
|
||||
})), 20);
|
||||
items.add('remember', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("div", null, m("label", {
|
||||
className: "checkbox"
|
||||
}, m("input", {
|
||||
type: "checkbox",
|
||||
bidi: this.remember,
|
||||
disabled: this.loading
|
||||
}), _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.remember_me_label')))), 10);
|
||||
items.add('submit', m("div", {
|
||||
className: "Form-group"
|
||||
}, m(_common_components_Button__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
className: "Button Button--primary Button--block",
|
||||
type: "submit",
|
||||
loading: this.loading
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.submit_button'))), -10);
|
||||
return items;
|
||||
}
|
||||
footer() {
|
||||
return m('[', null, m("p", {
|
||||
className: "LogInModal-forgotPassword"
|
||||
}, m("a", {
|
||||
onclick: this.forgotPassword.bind(this)
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.forgot_password_link'))), _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].forum.attribute('allowSignUp') && m("p", {
|
||||
className: "LogInModal-signUp"
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.sign_up_text', {
|
||||
a: m("a", {
|
||||
onclick: this.signUp.bind(this)
|
||||
})
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the forgot password modal, prefilling it with an email if the user has
|
||||
* entered one.
|
||||
*/
|
||||
forgotPassword() {
|
||||
const email = this.identification();
|
||||
const attrs = email.includes('@') ? {
|
||||
email
|
||||
} : undefined;
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].modal.show(() => __webpack_require__.e(/*! import() | forum/components/ForgotPasswordModal */ "forum/components/ForgotPasswordModal").then(__webpack_require__.bind(__webpack_require__, /*! ./ForgotPasswordModal */ "./src/forum/components/ForgotPasswordModal.tsx")), attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the sign up modal, prefilling it with an email/username/password if
|
||||
* the user has entered one.
|
||||
*/
|
||||
signUp() {
|
||||
const identification = this.identification();
|
||||
const attrs = {
|
||||
[identification.includes('@') ? 'email' : 'username']: identification
|
||||
};
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].modal.show(() => __webpack_require__.e(/*! import() | forum/components/SignUpModal */ "forum/components/SignUpModal").then(__webpack_require__.bind(__webpack_require__, /*! ./SignUpModal */ "./src/forum/components/SignUpModal.tsx")), attrs);
|
||||
}
|
||||
onready() {
|
||||
this.$('[name=' + (this.identification() ? 'password' : 'identification') + ']').trigger('select');
|
||||
}
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
this.loading = true;
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].session.login(this.loginParams(), {
|
||||
errorHandler: this.onerror.bind(this)
|
||||
}).then(() => window.location.reload(), this.loaded.bind(this));
|
||||
}
|
||||
loginParams() {
|
||||
const data = {
|
||||
identification: this.identification(),
|
||||
password: this.password(),
|
||||
remember: this.remember()
|
||||
};
|
||||
return data;
|
||||
}
|
||||
onerror(error) {
|
||||
if (error.status === 401 && error.alert) {
|
||||
error.alert.content = _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.log_in.invalid_login_message');
|
||||
this.password('');
|
||||
}
|
||||
super.onerror(error);
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/LogInModal', LogInModal);flarum.reg.addChunkModule('forum/components/ForgotPasswordModal', './src/forum/components/ForgotPasswordModal.tsx', 'core', 'forum/components/ForgotPasswordModal');
|
||||
flarum.reg.addChunkModule('forum/components/SignUpModal', './src/forum/components/SignUpModal.tsx', 'core', 'forum/components/SignUpModal');
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/LogInButtons.js":
|
||||
/*!**********************************************!*\
|
||||
!*** ./src/forum/components/LogInButtons.js ***!
|
||||
\**********************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ LogInButtons)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _common_Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/Component */ "./src/common/Component.ts");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `LogInButtons` component displays a collection of social login buttons.
|
||||
*/
|
||||
class LogInButtons extends _common_Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
view() {
|
||||
return m("div", {
|
||||
className: "LogInButtons"
|
||||
}, this.items().toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of LogInButton components.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
return new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_1__["default"]();
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/LogInButtons', LogInButtons);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=LogInModal.js.map
|
2
framework/core/js/dist/forum/components/LogInModal.js.map
generated
vendored
2
framework/core/js/dist/forum/components/LogInModal.js.map
generated
vendored
File diff suppressed because one or more lines are too long
47
framework/core/js/dist/forum/components/NotificationsPage.js
generated
vendored
47
framework/core/js/dist/forum/components/NotificationsPage.js
generated
vendored
@ -1,2 +1,47 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[744],{8246:(i,t,o)=>{o.r(t),o.d(t,{default:()=>r});var s=o(6789),a=o(4661),n=o(7297),e=o(1552);class r extends a.Z{oninit(i){super.oninit(i),s.Z.history.push("notifications",(0,e.Z)(s.Z.translator.trans("core.forum.notifications.title"))),s.Z.notifications.load(),this.bodyClass="App--notifications"}view(){return m("div",{className:"NotificationsPage"},m(n.Z,{state:s.Z.notifications}))}}flarum.reg.add("core","forum/components/NotificationsPage",r)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/NotificationsPage"],{
|
||||
|
||||
/***/ "./src/forum/components/NotificationsPage.tsx":
|
||||
/*!****************************************************!*\
|
||||
!*** ./src/forum/components/NotificationsPage.tsx ***!
|
||||
\****************************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ NotificationsPage)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _forum_app__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../forum/app */ "./src/forum/app.ts");
|
||||
/* harmony import */ var _common_components_Page__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/components/Page */ "./src/common/components/Page.tsx");
|
||||
/* harmony import */ var _NotificationList__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./NotificationList */ "./src/forum/components/NotificationList.js");
|
||||
/* harmony import */ var _common_utils_extractText__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/utils/extractText */ "./src/common/utils/extractText.ts");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NotificationsPage` component shows the notifications list. It is only
|
||||
* used on mobile devices where the notifications dropdown is within the drawer.
|
||||
*/
|
||||
class NotificationsPage extends _common_components_Page__WEBPACK_IMPORTED_MODULE_1__["default"] {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].history.push('notifications', (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_3__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.notifications.title')));
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].notifications.load();
|
||||
this.bodyClass = 'App--notifications';
|
||||
}
|
||||
view() {
|
||||
return m("div", {
|
||||
className: "NotificationsPage"
|
||||
}, m(_NotificationList__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
state: _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].notifications
|
||||
}));
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/NotificationsPage', NotificationsPage);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=NotificationsPage.js.map
|
2
framework/core/js/dist/forum/components/NotificationsPage.js.map
generated
vendored
2
framework/core/js/dist/forum/components/NotificationsPage.js.map
generated
vendored
@ -1 +1 @@
|
||||
{"version":3,"file":"forum/components/NotificationsPage.js","mappings":"mLASe,MAAMA,UAA0B,IAC7CC,OAAOC,GACLC,MAAMF,OAAOC,GACb,iBAAiB,iBAAiB,OAAY,qBAAqB,oCACnE,yBACAE,KAAKC,UAAY,oBACnB,CACAC,OACE,OAAOC,EAAE,MAAO,CACdC,UAAW,qBACVD,EAAE,IAAkB,CACrBE,MAAO,oBAEX,EAEFC,OAAOC,IAAIC,IAAI,OAAQ,qCAAsCZ,E","sources":["webpack://@flarum/core/./src/forum/components/NotificationsPage.tsx"],"sourcesContent":["import app from '../../forum/app';\nimport Page from '../../common/components/Page';\nimport NotificationList from './NotificationList';\nimport extractText from '../../common/utils/extractText';\n\n/**\n * The `NotificationsPage` component shows the notifications list. It is only\n * used on mobile devices where the notifications dropdown is within the drawer.\n */\nexport default class NotificationsPage extends Page {\n oninit(vnode) {\n super.oninit(vnode);\n app.history.push('notifications', extractText(app.translator.trans('core.forum.notifications.title')));\n app.notifications.load();\n this.bodyClass = 'App--notifications';\n }\n view() {\n return m(\"div\", {\n className: \"NotificationsPage\"\n }, m(NotificationList, {\n state: app.notifications\n }));\n }\n}\nflarum.reg.add('core', 'forum/components/NotificationsPage', NotificationsPage);"],"names":["NotificationsPage","oninit","vnode","super","this","bodyClass","view","m","className","state","flarum","reg","add"],"sourceRoot":""}
|
||||
{"version":3,"file":"forum/components/NotificationsPage.js","mappings":";;;;;;;;;;;;;;;;;AAAkC;AACc;AACE;AACO;;AAEzD;AACA;AACA;AACA;AACe,gCAAgC,+DAAI;AACnD;AACA;AACA,IAAI,+DAAgB,kBAAkB,qEAAW,CAAC,mEAAoB;AACtE,IAAI,qEAAsB;AAC1B;AACA;AACA;AACA;AACA;AACA,KAAK,IAAI,yDAAgB;AACzB,aAAa,gEAAiB;AAC9B,KAAK;AACL;AACA;AACA","sources":["webpack://@flarum/core/./src/forum/components/NotificationsPage.tsx"],"sourcesContent":["import app from '../../forum/app';\nimport Page from '../../common/components/Page';\nimport NotificationList from './NotificationList';\nimport extractText from '../../common/utils/extractText';\n\n/**\n * The `NotificationsPage` component shows the notifications list. It is only\n * used on mobile devices where the notifications dropdown is within the drawer.\n */\nexport default class NotificationsPage extends Page {\n oninit(vnode) {\n super.oninit(vnode);\n app.history.push('notifications', extractText(app.translator.trans('core.forum.notifications.title')));\n app.notifications.load();\n this.bodyClass = 'App--notifications';\n }\n view() {\n return m(\"div\", {\n className: \"NotificationsPage\"\n }, m(NotificationList, {\n state: app.notifications\n }));\n }\n}\nflarum.reg.add('core', 'forum/components/NotificationsPage', NotificationsPage);"],"names":[],"sourceRoot":""}
|
590
framework/core/js/dist/forum/components/PostStream.js
generated
vendored
590
framework/core/js/dist/forum/components/PostStream.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum/components/PostStream.js.map
generated
vendored
2
framework/core/js/dist/forum/components/PostStream.js.map
generated
vendored
File diff suppressed because one or more lines are too long
339
framework/core/js/dist/forum/components/PostStreamScrubber.js
generated
vendored
339
framework/core/js/dist/forum/components/PostStreamScrubber.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum/components/PostStreamScrubber.js.map
generated
vendored
2
framework/core/js/dist/forum/components/PostStreamScrubber.js.map
generated
vendored
File diff suppressed because one or more lines are too long
296
framework/core/js/dist/forum/components/ReplyComposer.js
generated
vendored
296
framework/core/js/dist/forum/components/ReplyComposer.js
generated
vendored
@ -1,2 +1,296 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[630],{2140:(s,e,t)=>{t.d(e,{Z:()=>h});var o=t(2190),r=t(5226);class i extends o.Z{handler(){return this.attrs.when()||void 0}oncreate(s){super.oncreate(s),this.boundHandler=this.handler.bind(this),$(window).on("beforeunload",this.boundHandler)}onremove(s){super.onremove(s),$(window).off("beforeunload",this.boundHandler)}view(s){return m("[",null,s.children)}}flarum.reg.add("core","common/components/ConfirmDocumentUnload",i);var n=t(4944),a=t(1268),c=t(4041),l=t(3344),d=t(7323);class h extends o.Z{oninit(s){super.oninit(s),this.composer=this.attrs.composer,this.loading=!1,this.attrs.confirmExit&&this.composer.preventClosingWhen((()=>this.hasChanges()),this.attrs.confirmExit),this.composer.fields.content(this.attrs.originalContent||"")}view(){var s;return m(i,{when:this.hasChanges.bind(this)},m("div",{className:(0,l.Z)("ComposerBody",this.attrs.className)},m(d.Z,{user:this.attrs.user,className:"ComposerBody-avatar"}),m("div",{className:"ComposerBody-content"},m("ul",{className:"ComposerBody-header"},(0,a.Z)(this.headerItems().toArray())),m("div",{className:"ComposerBody-editor"},m(n.Z,{submitLabel:this.attrs.submitLabel,placeholder:this.attrs.placeholder,disabled:this.loading||this.attrs.disabled,composer:this.composer,preview:null==(s=this.jumpToPreview)?void 0:s.bind(this),onchange:this.composer.fields.content,onsubmit:this.onsubmit.bind(this),value:this.composer.fields.content()}))),m(r.Z,{display:"unset",containerClassName:(0,l.Z)("ComposerBody-loading",this.loading&&"active"),size:"large"})))}hasChanges(){const s=this.composer.fields.content();return s&&s!==this.attrs.originalContent}headerItems(){return new c.Z}onsubmit(){}loaded(){this.loading=!1,m.redraw()}}flarum.reg.add("core","forum/components/ComposerBody",h)},2925:(s,e,t)=>{t.r(e),t.d(e,{default:()=>d});var o=t(6789),r=t(2140),i=t(8312),n=t(6597),a=t(1552),c=t(9133);function l(s){o.Z.composer.isFullScreen()&&(o.Z.composer.minimize(),s.stopPropagation())}class d extends r.Z{static initAttrs(s){super.initAttrs(s),s.placeholder=s.placeholder||(0,a.Z)(o.Z.translator.trans("core.forum.composer_reply.body_placeholder")),s.submitLabel=s.submitLabel||o.Z.translator.trans("core.forum.composer_reply.submit_button"),s.confirmExit=s.confirmExit||(0,a.Z)(o.Z.translator.trans("core.forum.composer_reply.discard_confirmation"))}headerItems(){const s=super.headerItems(),e=this.attrs.discussion;return s.add("title",m("h3",null,m(c.Z,{name:"fas fa-reply"})," ",m(n.Z,{href:o.Z.route.discussion(e),onclick:l},e.title()))),s}jumpToPreview(s){l(s),m.route.set(o.Z.route.discussion(this.attrs.discussion,"reply"))}data(){return{content:this.composer.fields.content(),relationships:{discussion:this.attrs.discussion}}}onsubmit(){const s=this.attrs.discussion;this.loading=!0,m.redraw();const e=this.data();o.Z.store.createRecord("posts").save(e).then((e=>{if(o.Z.viewingDiscussion(s)){const s=o.Z.current.get("stream");s.update().then((()=>s.goToNumber(e.number())))}else{let s;const t=m(i.Z,{className:"Button Button--link",onclick:()=>{m.route.set(o.Z.route.post(e)),o.Z.alerts.dismiss(s)}},o.Z.translator.trans("core.forum.composer_reply.view_button"));s=o.Z.alerts.show({type:"success",controls:[t]},o.Z.translator.trans("core.forum.composer_reply.posted_message"))}this.composer.hide()}),this.loaded.bind(this))}}flarum.reg.add("core","forum/components/ReplyComposer",d)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/ReplyComposer"],{
|
||||
|
||||
/***/ "./src/common/components/ConfirmDocumentUnload.js":
|
||||
/*!********************************************************!*\
|
||||
!*** ./src/common/components/ConfirmDocumentUnload.js ***!
|
||||
\********************************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ConfirmDocumentUnload)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Component */ "./src/common/Component.ts");
|
||||
|
||||
|
||||
/**
|
||||
* The `ConfirmDocumentUnload` component can be used to register a global
|
||||
* event handler that prevents closing the browser window/tab based on the
|
||||
* return value of a given callback prop.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - `when` - a callback returning true when the browser should prompt for
|
||||
* confirmation before closing the window/tab
|
||||
*/
|
||||
class ConfirmDocumentUnload extends _Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
handler() {
|
||||
return this.attrs.when() || undefined;
|
||||
}
|
||||
oncreate(vnode) {
|
||||
super.oncreate(vnode);
|
||||
this.boundHandler = this.handler.bind(this);
|
||||
$(window).on('beforeunload', this.boundHandler);
|
||||
}
|
||||
onremove(vnode) {
|
||||
super.onremove(vnode);
|
||||
$(window).off('beforeunload', this.boundHandler);
|
||||
}
|
||||
view(vnode) {
|
||||
return m('[', null, vnode.children);
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'common/components/ConfirmDocumentUnload', ConfirmDocumentUnload);
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/ComposerBody.js":
|
||||
/*!**********************************************!*\
|
||||
!*** ./src/forum/components/ComposerBody.js ***!
|
||||
\**********************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ComposerBody)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _common_Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/Component */ "./src/common/Component.ts");
|
||||
/* harmony import */ var _common_components_LoadingIndicator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/components/LoadingIndicator */ "./src/common/components/LoadingIndicator.tsx");
|
||||
/* harmony import */ var _common_components_ConfirmDocumentUnload__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/ConfirmDocumentUnload */ "./src/common/components/ConfirmDocumentUnload.js");
|
||||
/* harmony import */ var _common_components_TextEditor__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/TextEditor */ "./src/common/components/TextEditor.js");
|
||||
/* harmony import */ var _common_helpers_listItems__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../common/helpers/listItems */ "./src/common/helpers/listItems.tsx");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
/* harmony import */ var _common_utils_classList__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../common/utils/classList */ "./src/common/utils/classList.ts");
|
||||
/* harmony import */ var _common_components_Avatar__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../common/components/Avatar */ "./src/common/components/Avatar.tsx");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `ComposerBody` component handles the body, or the content, of the
|
||||
* composer. Subclasses should implement the `onsubmit` method and override
|
||||
* `headerTimes`.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - `composer`
|
||||
* - `originalContent`
|
||||
* - `submitLabel`
|
||||
* - `placeholder`
|
||||
* - `user`
|
||||
* - `confirmExit`
|
||||
* - `disabled`
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
class ComposerBody extends _common_Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.composer = this.attrs.composer;
|
||||
|
||||
/**
|
||||
* Whether or not the component is loading.
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.loading = false;
|
||||
|
||||
// Let the composer state know to ask for confirmation under certain
|
||||
// circumstances, if the body supports / requires it and has a corresponding
|
||||
// confirmation question to ask.
|
||||
if (this.attrs.confirmExit) {
|
||||
this.composer.preventClosingWhen(() => this.hasChanges(), this.attrs.confirmExit);
|
||||
}
|
||||
this.composer.fields.content(this.attrs.originalContent || '');
|
||||
}
|
||||
view() {
|
||||
var _this$jumpToPreview;
|
||||
return m(_common_components_ConfirmDocumentUnload__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
when: this.hasChanges.bind(this)
|
||||
}, m("div", {
|
||||
className: (0,_common_utils_classList__WEBPACK_IMPORTED_MODULE_6__["default"])('ComposerBody', this.attrs.className)
|
||||
}, m(_common_components_Avatar__WEBPACK_IMPORTED_MODULE_7__["default"], {
|
||||
user: this.attrs.user,
|
||||
className: "ComposerBody-avatar"
|
||||
}), m("div", {
|
||||
className: "ComposerBody-content"
|
||||
}, m("ul", {
|
||||
className: "ComposerBody-header"
|
||||
}, (0,_common_helpers_listItems__WEBPACK_IMPORTED_MODULE_4__["default"])(this.headerItems().toArray())), m("div", {
|
||||
className: "ComposerBody-editor"
|
||||
}, m(_common_components_TextEditor__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
submitLabel: this.attrs.submitLabel,
|
||||
placeholder: this.attrs.placeholder,
|
||||
disabled: this.loading || this.attrs.disabled,
|
||||
composer: this.composer,
|
||||
preview: (_this$jumpToPreview = this.jumpToPreview) == null ? void 0 : _this$jumpToPreview.bind(this),
|
||||
onchange: this.composer.fields.content,
|
||||
onsubmit: this.onsubmit.bind(this),
|
||||
value: this.composer.fields.content()
|
||||
}))), m(_common_components_LoadingIndicator__WEBPACK_IMPORTED_MODULE_1__["default"], {
|
||||
display: "unset",
|
||||
containerClassName: (0,_common_utils_classList__WEBPACK_IMPORTED_MODULE_6__["default"])('ComposerBody-loading', this.loading && 'active'),
|
||||
size: "large"
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is any unsaved data.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
hasChanges() {
|
||||
const content = this.composer.fields.content();
|
||||
return content && content !== this.attrs.originalContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the composer's header.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
headerItems() {
|
||||
return new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_5__["default"]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the submit event of the text editor.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
onsubmit() {}
|
||||
|
||||
/**
|
||||
* Stop loading.
|
||||
*/
|
||||
loaded() {
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/ComposerBody', ComposerBody);
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/ReplyComposer.js":
|
||||
/*!***********************************************!*\
|
||||
!*** ./src/forum/components/ReplyComposer.js ***!
|
||||
\***********************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ ReplyComposer)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _forum_app__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../forum/app */ "./src/forum/app.ts");
|
||||
/* harmony import */ var _ComposerBody__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ComposerBody */ "./src/forum/components/ComposerBody.js");
|
||||
/* harmony import */ var _common_components_Button__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/Button */ "./src/common/components/Button.tsx");
|
||||
/* harmony import */ var _common_components_Link__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/Link */ "./src/common/components/Link.js");
|
||||
/* harmony import */ var _common_utils_extractText__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../common/utils/extractText */ "./src/common/utils/extractText.ts");
|
||||
/* harmony import */ var _common_components_Icon__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../common/components/Icon */ "./src/common/components/Icon.tsx");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function minimizeComposerIfFullScreen(e) {
|
||||
if (_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].composer.isFullScreen()) {
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].composer.minimize();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `ReplyComposer` component displays the composer content for replying to a
|
||||
* discussion.
|
||||
*
|
||||
* ### Attrs
|
||||
*
|
||||
* - All of the attrs of ComposerBody
|
||||
* - `discussion`
|
||||
*/
|
||||
class ReplyComposer extends _ComposerBody__WEBPACK_IMPORTED_MODULE_1__["default"] {
|
||||
static initAttrs(attrs) {
|
||||
super.initAttrs(attrs);
|
||||
attrs.placeholder = attrs.placeholder || (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_4__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_reply.body_placeholder'));
|
||||
attrs.submitLabel = attrs.submitLabel || _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_reply.submit_button');
|
||||
attrs.confirmExit = attrs.confirmExit || (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_4__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_reply.discard_confirmation'));
|
||||
}
|
||||
headerItems() {
|
||||
const items = super.headerItems();
|
||||
const discussion = this.attrs.discussion;
|
||||
items.add('title', m("h3", null, m(_common_components_Icon__WEBPACK_IMPORTED_MODULE_5__["default"], {
|
||||
name: 'fas fa-reply'
|
||||
}), ' ', m(_common_components_Link__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
href: _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].route.discussion(discussion),
|
||||
onclick: minimizeComposerIfFullScreen
|
||||
}, discussion.title())));
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump to the preview when triggered by the text editor.
|
||||
*/
|
||||
jumpToPreview(e) {
|
||||
minimizeComposerIfFullScreen(e);
|
||||
m.route.set(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].route.discussion(this.attrs.discussion, 'reply'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data to submit to the server when the reply is saved.
|
||||
*
|
||||
* @return {Record<string, unknown>}
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
content: this.composer.fields.content(),
|
||||
relationships: {
|
||||
discussion: this.attrs.discussion
|
||||
}
|
||||
};
|
||||
}
|
||||
onsubmit() {
|
||||
const discussion = this.attrs.discussion;
|
||||
this.loading = true;
|
||||
m.redraw();
|
||||
const data = this.data();
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].store.createRecord('posts').save(data).then(post => {
|
||||
// If we're currently viewing the discussion which this reply was made
|
||||
// in, then we can update the post stream and scroll to the post.
|
||||
if (_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].viewingDiscussion(discussion)) {
|
||||
const stream = _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].current.get('stream');
|
||||
stream.update().then(() => stream.goToNumber(post.number()));
|
||||
} else {
|
||||
// Otherwise, we'll create an alert message to inform the user that
|
||||
// their reply has been posted, containing a button which will
|
||||
// transition to their new post when clicked.
|
||||
let alert;
|
||||
const viewButton = m(_common_components_Button__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
className: "Button Button--link",
|
||||
onclick: () => {
|
||||
m.route.set(_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].route.post(post));
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].alerts.dismiss(alert);
|
||||
}
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_reply.view_button'));
|
||||
alert = _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].alerts.show({
|
||||
type: 'success',
|
||||
controls: [viewButton]
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_0__["default"].translator.trans('core.forum.composer_reply.posted_message'));
|
||||
}
|
||||
this.composer.hide();
|
||||
}, this.loaded.bind(this));
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/ReplyComposer', ReplyComposer);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=ReplyComposer.js.map
|
2
framework/core/js/dist/forum/components/ReplyComposer.js.map
generated
vendored
2
framework/core/js/dist/forum/components/ReplyComposer.js.map
generated
vendored
File diff suppressed because one or more lines are too long
386
framework/core/js/dist/forum/components/SearchModal.js
generated
vendored
386
framework/core/js/dist/forum/components/SearchModal.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum/components/SearchModal.js.map
generated
vendored
2
framework/core/js/dist/forum/components/SearchModal.js.map
generated
vendored
File diff suppressed because one or more lines are too long
551
framework/core/js/dist/forum/components/SettingsPage.js
generated
vendored
551
framework/core/js/dist/forum/components/SettingsPage.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum/components/SettingsPage.js.map
generated
vendored
2
framework/core/js/dist/forum/components/SettingsPage.js.map
generated
vendored
File diff suppressed because one or more lines are too long
223
framework/core/js/dist/forum/components/SignUpModal.js
generated
vendored
223
framework/core/js/dist/forum/components/SignUpModal.js
generated
vendored
@ -1,2 +1,223 @@
|
||||
"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[395],{8686:(s,t,e)=>{e.r(t),e.d(t,{default:()=>h});var a=e(7905),r=e(6789),o=e(899),i=e(8312),n=e(6403),l=e(1552),d=e(4041),u=e(6458);class h extends o.Z{constructor(){super(...arguments),(0,a.Z)(this,"username",void 0),(0,a.Z)(this,"email",void 0),(0,a.Z)(this,"password",void 0)}oninit(s){super.oninit(s),this.username=(0,u.Z)(this.attrs.username||""),this.email=(0,u.Z)(this.attrs.email||""),this.password=(0,u.Z)(this.attrs.password||"")}className(){return"Modal--small SignUpModal"}title(){return r.Z.translator.trans("core.forum.sign_up.title")}content(){return[m("div",{className:"Modal-body"},this.body()),m("div",{className:"Modal-footer"},this.footer())]}isProvided(s){var t,e;return null!=(t=null==(e=this.attrs.provided)?void 0:e.includes(s))&&t}body(){return[!this.attrs.token&&m(n.Z,null),m("div",{className:"Form Form--centered"},this.fields().toArray())]}fields(){const s=new d.Z,t=(0,l.Z)(r.Z.translator.trans("core.forum.sign_up.username_placeholder")),e=(0,l.Z)(r.Z.translator.trans("core.forum.sign_up.email_placeholder")),a=(0,l.Z)(r.Z.translator.trans("core.forum.sign_up.password_placeholder"));return s.add("username",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"username",type:"text",placeholder:t,"aria-label":t,bidi:this.username,disabled:this.loading||this.isProvided("username")})),30),s.add("email",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"email",type:"email",placeholder:e,"aria-label":e,bidi:this.email,disabled:this.loading||this.isProvided("email")})),20),this.attrs.token||s.add("password",m("div",{className:"Form-group"},m("input",{className:"FormControl",name:"password",type:"password",autocomplete:"new-password",placeholder:a,"aria-label":a,bidi:this.password,disabled:this.loading})),10),s.add("submit",m("div",{className:"Form-group"},m(i.Z,{className:"Button Button--primary Button--block",type:"submit",loading:this.loading},r.Z.translator.trans("core.forum.sign_up.submit_button"))),-10),s}footer(){return[m("p",{className:"SignUpModal-logIn"},r.Z.translator.trans("core.forum.sign_up.log_in_text",{a:m("a",{onclick:this.logIn.bind(this)})}))]}logIn(){const s={identification:this.email()||this.username()};r.Z.modal.show((()=>e.e(460).then(e.bind(e,5049))),s)}onready(){this.attrs.username&&!this.attrs.email?this.$("[name=email]").select():this.$("[name=username]").select()}onsubmit(s){s.preventDefault(),this.loading=!0;const t=this.submitData();r.Z.request({url:r.Z.forum.attribute("baseUrl")+"/register",method:"POST",body:t,errorHandler:this.onerror.bind(this)}).then((()=>window.location.reload()),this.loaded.bind(this))}submitData(){const s=this.attrs.token?{token:this.attrs.token}:{password:this.password()};return{username:this.username(),email:this.email(),...s}}}flarum.reg.add("core","forum/components/SignUpModal",h),flarum.reg.addChunkModule("460","5049","core","forum/components/LogInModal")},6403:(s,t,e)=>{e.d(t,{Z:()=>o});var a=e(2190),r=e(4041);class o extends a.Z{view(){return m("div",{className:"LogInButtons"},this.items().toArray())}items(){return new r.Z}}flarum.reg.add("core","forum/components/LogInButtons",o)}}]);
|
||||
"use strict";
|
||||
(self["webpackChunkflarum_core"] = self["webpackChunkflarum_core"] || []).push([["forum/components/SignUpModal"],{
|
||||
|
||||
/***/ "./src/forum/components/SignUpModal.tsx":
|
||||
/*!**********************************************!*\
|
||||
!*** ./src/forum/components/SignUpModal.tsx ***!
|
||||
\**********************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ SignUpModal)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/esm/defineProperty */ "../../../js-packages/webpack-config/node_modules/@babel/runtime/helpers/esm/defineProperty.js");
|
||||
/* harmony import */ var _forum_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../forum/app */ "./src/forum/app.ts");
|
||||
/* harmony import */ var _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../common/components/FormModal */ "./src/common/components/FormModal.tsx");
|
||||
/* harmony import */ var _common_components_Button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../common/components/Button */ "./src/common/components/Button.tsx");
|
||||
/* harmony import */ var _LogInButtons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./LogInButtons */ "./src/forum/components/LogInButtons.js");
|
||||
/* harmony import */ var _common_utils_extractText__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../common/utils/extractText */ "./src/common/utils/extractText.ts");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
/* harmony import */ var _common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../../common/utils/Stream */ "./src/common/utils/Stream.ts");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class SignUpModal extends _common_components_FormModal__WEBPACK_IMPORTED_MODULE_2__["default"] {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
/**
|
||||
* The value of the username input.
|
||||
*/
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "username", void 0);
|
||||
/**
|
||||
* The value of the email input.
|
||||
*/
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "email", void 0);
|
||||
/**
|
||||
* The value of the password input.
|
||||
*/
|
||||
(0,_babel_runtime_helpers_esm_defineProperty__WEBPACK_IMPORTED_MODULE_0__["default"])(this, "password", void 0);
|
||||
}
|
||||
oninit(vnode) {
|
||||
super.oninit(vnode);
|
||||
this.username = (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__["default"])(this.attrs.username || '');
|
||||
this.email = (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__["default"])(this.attrs.email || '');
|
||||
this.password = (0,_common_utils_Stream__WEBPACK_IMPORTED_MODULE_7__["default"])(this.attrs.password || '');
|
||||
}
|
||||
className() {
|
||||
return 'Modal--small SignUpModal';
|
||||
}
|
||||
title() {
|
||||
return _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.sign_up.title');
|
||||
}
|
||||
content() {
|
||||
return [m("div", {
|
||||
className: "Modal-body"
|
||||
}, this.body()), m("div", {
|
||||
className: "Modal-footer"
|
||||
}, this.footer())];
|
||||
}
|
||||
isProvided(field) {
|
||||
var _this$attrs$provided$, _this$attrs$provided;
|
||||
return (_this$attrs$provided$ = (_this$attrs$provided = this.attrs.provided) == null ? void 0 : _this$attrs$provided.includes(field)) != null ? _this$attrs$provided$ : false;
|
||||
}
|
||||
body() {
|
||||
return [!this.attrs.token && m(_LogInButtons__WEBPACK_IMPORTED_MODULE_4__["default"], null), m("div", {
|
||||
className: "Form Form--centered"
|
||||
}, this.fields().toArray())];
|
||||
}
|
||||
fields() {
|
||||
const items = new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_6__["default"]();
|
||||
const usernameLabel = (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_5__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.sign_up.username_placeholder'));
|
||||
const emailLabel = (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_5__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.sign_up.email_placeholder'));
|
||||
const passwordLabel = (0,_common_utils_extractText__WEBPACK_IMPORTED_MODULE_5__["default"])(_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.sign_up.password_placeholder'));
|
||||
items.add('username', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("input", {
|
||||
className: "FormControl",
|
||||
name: "username",
|
||||
type: "text",
|
||||
placeholder: usernameLabel,
|
||||
"aria-label": usernameLabel,
|
||||
bidi: this.username,
|
||||
disabled: this.loading || this.isProvided('username')
|
||||
})), 30);
|
||||
items.add('email', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("input", {
|
||||
className: "FormControl",
|
||||
name: "email",
|
||||
type: "email",
|
||||
placeholder: emailLabel,
|
||||
"aria-label": emailLabel,
|
||||
bidi: this.email,
|
||||
disabled: this.loading || this.isProvided('email')
|
||||
})), 20);
|
||||
if (!this.attrs.token) {
|
||||
items.add('password', m("div", {
|
||||
className: "Form-group"
|
||||
}, m("input", {
|
||||
className: "FormControl",
|
||||
name: "password",
|
||||
type: "password",
|
||||
autocomplete: "new-password",
|
||||
placeholder: passwordLabel,
|
||||
"aria-label": passwordLabel,
|
||||
bidi: this.password,
|
||||
disabled: this.loading
|
||||
})), 10);
|
||||
}
|
||||
items.add('submit', m("div", {
|
||||
className: "Form-group"
|
||||
}, m(_common_components_Button__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
className: "Button Button--primary Button--block",
|
||||
type: "submit",
|
||||
loading: this.loading
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.sign_up.submit_button'))), -10);
|
||||
return items;
|
||||
}
|
||||
footer() {
|
||||
return [m("p", {
|
||||
className: "SignUpModal-logIn"
|
||||
}, _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].translator.trans('core.forum.sign_up.log_in_text', {
|
||||
a: m("a", {
|
||||
onclick: this.logIn.bind(this)
|
||||
})
|
||||
}))];
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the log in modal, prefilling it with an email/username/password if
|
||||
* the user has entered one.
|
||||
*/
|
||||
logIn() {
|
||||
const attrs = {
|
||||
identification: this.email() || this.username()
|
||||
};
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].modal.show(() => __webpack_require__.e(/*! import() | forum/components/LogInModal */ "forum/components/LogInModal").then(__webpack_require__.bind(__webpack_require__, /*! ./LogInModal */ "./src/forum/components/LogInModal.tsx")), attrs);
|
||||
}
|
||||
onready() {
|
||||
if (this.attrs.username && !this.attrs.email) {
|
||||
this.$('[name=email]').select();
|
||||
} else {
|
||||
this.$('[name=username]').select();
|
||||
}
|
||||
}
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
this.loading = true;
|
||||
const body = this.submitData();
|
||||
_forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].request({
|
||||
url: _forum_app__WEBPACK_IMPORTED_MODULE_1__["default"].forum.attribute('baseUrl') + '/register',
|
||||
method: 'POST',
|
||||
body,
|
||||
errorHandler: this.onerror.bind(this)
|
||||
}).then(() => window.location.reload(), this.loaded.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data that should be submitted in the sign-up request.
|
||||
*/
|
||||
submitData() {
|
||||
const authData = this.attrs.token ? {
|
||||
token: this.attrs.token
|
||||
} : {
|
||||
password: this.password()
|
||||
};
|
||||
const data = {
|
||||
username: this.username(),
|
||||
email: this.email(),
|
||||
...authData
|
||||
};
|
||||
return data;
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/SignUpModal', SignUpModal);flarum.reg.addChunkModule('forum/components/LogInModal', './src/forum/components/LogInModal.tsx', 'core', 'forum/components/LogInModal');
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/forum/components/LogInButtons.js":
|
||||
/*!**********************************************!*\
|
||||
!*** ./src/forum/components/LogInButtons.js ***!
|
||||
\**********************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "default": () => (/* binding */ LogInButtons)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _common_Component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../common/Component */ "./src/common/Component.ts");
|
||||
/* harmony import */ var _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../common/utils/ItemList */ "./src/common/utils/ItemList.ts");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `LogInButtons` component displays a collection of social login buttons.
|
||||
*/
|
||||
class LogInButtons extends _common_Component__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
||||
view() {
|
||||
return m("div", {
|
||||
className: "LogInButtons"
|
||||
}, this.items().toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of LogInButton components.
|
||||
*
|
||||
* @return {ItemList<import('mithril').Children>}
|
||||
*/
|
||||
items() {
|
||||
return new _common_utils_ItemList__WEBPACK_IMPORTED_MODULE_1__["default"]();
|
||||
}
|
||||
}
|
||||
flarum.reg.add('core', 'forum/components/LogInButtons', LogInButtons);
|
||||
|
||||
/***/ })
|
||||
|
||||
}]);
|
||||
//# sourceMappingURL=SignUpModal.js.map
|
2
framework/core/js/dist/forum/components/SignUpModal.js.map
generated
vendored
2
framework/core/js/dist/forum/components/SignUpModal.js.map
generated
vendored
File diff suppressed because one or more lines are too long
561
framework/core/js/dist/forum/components/UserSecurityPage.js
generated
vendored
561
framework/core/js/dist/forum/components/UserSecurityPage.js
generated
vendored
File diff suppressed because one or more lines are too long
2
framework/core/js/dist/forum/components/UserSecurityPage.js.map
generated
vendored
2
framework/core/js/dist/forum/components/UserSecurityPage.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@ -34,6 +34,7 @@ export type Extension = {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
require?: Record<string, string>;
|
||||
};
|
||||
|
||||
export interface AdminApplicationData extends ApplicationData {
|
||||
@ -44,7 +45,9 @@ export interface AdminApplicationData extends ApplicationData {
|
||||
slugDrivers: Record<string, string[]>;
|
||||
searchDrivers: Record<string, string[]>;
|
||||
permissions: Record<string, string[]>;
|
||||
advancedPageEmpty: boolean;
|
||||
maintenanceByConfig: boolean;
|
||||
safeModeExtensions?: string[] | null;
|
||||
safeModeExtensionsConfig?: string[] | null;
|
||||
}
|
||||
|
||||
export default class AdminApplication extends Application {
|
||||
|
@ -112,7 +112,7 @@ export default class AdminNav extends Component {
|
||||
50
|
||||
);
|
||||
|
||||
if (app.data.settings.show_advanced_settings && !app.data.advancedPageEmpty) {
|
||||
if (app.data.settings.show_advanced_settings) {
|
||||
items.add(
|
||||
'advanced',
|
||||
<LinkButton href={app.route('advanced')} icon="fas fa-cog" title={app.translator.trans('core.admin.nav.advanced_title')}>
|
||||
|
@ -9,6 +9,7 @@ import saveSettings from '../utils/saveSettings';
|
||||
import AdminHeader from './AdminHeader';
|
||||
import FormGroup, { FieldComponentOptions } from '../../common/components/FormGroup';
|
||||
import extractText from '../../common/utils/extractText';
|
||||
import LoadingModal from './LoadingModal';
|
||||
|
||||
export interface AdminHeaderOptions {
|
||||
title: Mithril.Children;
|
||||
@ -24,6 +25,8 @@ export interface AdminHeaderOptions {
|
||||
|
||||
export type SettingsComponentOptions = FieldComponentOptions & {
|
||||
setting: string;
|
||||
json?: boolean;
|
||||
refreshAfterSaving?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -38,6 +41,7 @@ export type SaveSubmitEvent = SubmitEvent & { redraw: boolean };
|
||||
|
||||
export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> {
|
||||
settings: MutableSettings = {};
|
||||
refreshAfterSaving: string[] = [];
|
||||
loading: boolean = false;
|
||||
|
||||
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children {
|
||||
@ -137,9 +141,34 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
|
||||
return entry.call(this);
|
||||
}
|
||||
|
||||
const { setting, ...attrs } = entry;
|
||||
const { setting, json, refreshAfterSaving, ...attrs } = entry;
|
||||
|
||||
return <FormGroup bidi={this.setting(setting)} {...attrs} />;
|
||||
const originalBidi: (value?: string) => any = this.setting(setting);
|
||||
let bidi: (value?: string) => any;
|
||||
|
||||
if (json) {
|
||||
bidi = function (value?: string) {
|
||||
if (arguments.length) {
|
||||
originalBidi(JSON.stringify(value));
|
||||
}
|
||||
|
||||
const v = originalBidi();
|
||||
|
||||
if (v) {
|
||||
return JSON.parse(v);
|
||||
}
|
||||
|
||||
return v;
|
||||
};
|
||||
} else {
|
||||
bidi = originalBidi;
|
||||
}
|
||||
|
||||
if (refreshAfterSaving) {
|
||||
this.refreshAfterSaving.push(setting);
|
||||
}
|
||||
|
||||
return <FormGroup stream={bidi} {...attrs} />;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,7 +223,16 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
|
||||
|
||||
this.loading = true;
|
||||
|
||||
return saveSettings(this.dirty()).then(this.onsaved.bind(this));
|
||||
const dirty = this.dirty();
|
||||
|
||||
return saveSettings(dirty)
|
||||
.then(this.onsaved.bind(this))
|
||||
.then(() => {
|
||||
if (this.refreshAfterSaving.length && Object.keys(dirty).some((setting) => this.refreshAfterSaving.includes(setting))) {
|
||||
app.modal.show(LoadingModal);
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
modelLocale(): Record<string, string> {
|
||||
|
@ -5,6 +5,9 @@ import type Mithril from 'mithril';
|
||||
import Form from '../../common/components/Form';
|
||||
import extractText from '../../common/utils/extractText';
|
||||
import FormSectionGroup, { FormSection } from './FormSectionGroup';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import InfoTile from '../../common/components/InfoTile';
|
||||
import { MaintenanceMode } from '../../common/Application';
|
||||
|
||||
export default class AdvancedPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends AdminPage<CustomAttrs> {
|
||||
searchDriverOptions: Record<string, Record<string, string>> = {};
|
||||
@ -35,8 +38,36 @@ export default class AdvancedPage<CustomAttrs extends IPageAttrs = IPageAttrs> e
|
||||
content() {
|
||||
return [
|
||||
<Form className="AdvancedPage-container">
|
||||
<FormSectionGroup>
|
||||
<FormSectionGroup>{this.sectionItems().toArray()}</FormSectionGroup>
|
||||
<div className="Form-group Form-controls">{this.submitButton()}</div>
|
||||
</Form>,
|
||||
];
|
||||
}
|
||||
|
||||
driverLocale(): Record<string, Record<string, string>> {
|
||||
return {
|
||||
search: {
|
||||
default: extractText(app.translator.trans('core.admin.advanced.search.driver_options.default')),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
sectionItems() {
|
||||
const items = new ItemList<Mithril.Children>();
|
||||
|
||||
items.add('search', this.searchDrivers(), 100);
|
||||
|
||||
items.add('maintenance', this.maintenance(), 90);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
searchDrivers() {
|
||||
const hasOtherDrivers = Object.keys(this.searchDriverOptions).some((model) => Object.keys(this.searchDriverOptions[model]).length > 1);
|
||||
|
||||
return (
|
||||
<FormSection label={app.translator.trans('core.admin.advanced.search.section_label')}>
|
||||
{hasOtherDrivers ? (
|
||||
<Form>
|
||||
{Object.keys(this.searchDriverOptions).map((model) => {
|
||||
const options = this.searchDriverOptions[model];
|
||||
@ -55,19 +86,83 @@ export default class AdvancedPage<CustomAttrs extends IPageAttrs = IPageAttrs> e
|
||||
return null;
|
||||
})}
|
||||
</Form>
|
||||
) : (
|
||||
<InfoTile icon="fas fa-database" className="InfoTile--warning">
|
||||
{app.translator.trans('core.admin.advanced.search.no_other_drivers')}
|
||||
</InfoTile>
|
||||
)}
|
||||
</FormSection>
|
||||
</FormSectionGroup>
|
||||
|
||||
<div className="Form-group Form-controls">{this.submitButton()}</div>
|
||||
</Form>,
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
driverLocale(): Record<string, Record<string, string>> {
|
||||
return {
|
||||
search: {
|
||||
default: extractText(app.translator.trans('core.admin.advanced.search.driver_options.default')),
|
||||
maintenance() {
|
||||
return (
|
||||
<FormSection label={app.translator.trans('core.admin.advanced.maintenance.section_label')}>
|
||||
<Form>
|
||||
{this.buildSettingComponent({
|
||||
type: 'select',
|
||||
help: app.translator.trans('core.admin.advanced.maintenance.help'),
|
||||
setting: 'maintenance_mode',
|
||||
refreshAfterSaving: true,
|
||||
options: {
|
||||
[MaintenanceMode.NO_MAINTENANCE]: app.translator.trans('core.admin.advanced.maintenance.options.' + MaintenanceMode.NO_MAINTENANCE),
|
||||
[MaintenanceMode.HIGH_MAINTENANCE]: {
|
||||
label: app.translator.trans('core.admin.advanced.maintenance.options.' + MaintenanceMode.HIGH_MAINTENANCE),
|
||||
disabled: true,
|
||||
},
|
||||
[MaintenanceMode.LOW_MAINTENANCE]: app.translator.trans('core.admin.advanced.maintenance.options.' + MaintenanceMode.LOW_MAINTENANCE),
|
||||
[MaintenanceMode.SAFE_MODE]: app.translator.trans('core.admin.advanced.maintenance.options.' + MaintenanceMode.SAFE_MODE),
|
||||
},
|
||||
default: MaintenanceMode.NO_MAINTENANCE,
|
||||
})}
|
||||
{this.setting('maintenance_mode')() === MaintenanceMode.SAFE_MODE
|
||||
? this.buildSettingComponent({
|
||||
type: 'dropdown',
|
||||
label: app.translator.trans('core.admin.advanced.maintenance.safe_mode_extensions'),
|
||||
help: app.data.safeModeExtensionsConfig
|
||||
? app.translator.trans('core.admin.advanced.maintenance.safe_mode_extensions_override_help', {
|
||||
extensions: app.data.safeModeExtensionsConfig.map((id) => app.data.extensions[id].extra['flarum-extension'].title).join(', '),
|
||||
})
|
||||
: null,
|
||||
setting: 'safe_mode_extensions',
|
||||
json: true,
|
||||
refreshAfterSaving: true,
|
||||
multiple: true,
|
||||
disabled: app.data.safeModeExtensionsConfig,
|
||||
options: Object.entries(app.data.extensions).reduce((acc, [id, extension]) => {
|
||||
const requiredExtensions = extension.require
|
||||
? Object.entries(app.data.extensions).filter(([, e]) => extension.require![e.name])
|
||||
: [];
|
||||
|
||||
// @ts-ignore
|
||||
acc[id] = {
|
||||
label: extension.extra['flarum-extension'].title,
|
||||
disabled: (value: string[]) => {
|
||||
let dependenciesMet = true;
|
||||
|
||||
if (extension.require) {
|
||||
dependenciesMet = !requiredExtensions.length || requiredExtensions.every(([id]) => value.includes(id));
|
||||
}
|
||||
|
||||
return !dependenciesMet;
|
||||
},
|
||||
tooltip: requiredExtensions.length
|
||||
? `Requires: ${requiredExtensions.map(([, e]) => e.extra['flarum-extension'].title).join(', ')}`
|
||||
: undefined,
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
})
|
||||
: null}
|
||||
{app.data.maintenanceByConfig ? (
|
||||
<div className="Form-group">
|
||||
<label>{app.translator.trans('core.admin.advanced.maintenance.config_override.label')}</label>
|
||||
<p className="helpText">{app.translator.trans('core.admin.advanced.maintenance.config_override.help')}</p>
|
||||
<strong className="helpText">{app.translator.trans('core.admin.advanced.maintenance.options.' + app.data.maintenanceMode)}</strong>
|
||||
</div>
|
||||
) : null}
|
||||
</Form>
|
||||
</FormSection>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
18
framework/core/js/src/admin/components/AlertWidget.tsx
Normal file
18
framework/core/js/src/admin/components/AlertWidget.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import Alert, { AlertAttrs } from '../../common/components/Alert';
|
||||
import DashboardWidget, { type IDashboardWidgetAttrs } from './DashboardWidget';
|
||||
import classList from '../../common/utils/classList';
|
||||
import type Mithril from 'mithril';
|
||||
|
||||
export interface IAlertWidgetAttrs extends IDashboardWidgetAttrs {
|
||||
alert: AlertAttrs;
|
||||
}
|
||||
|
||||
export default class AlertWidget<CustomAttrs extends IAlertWidgetAttrs = IAlertWidgetAttrs> extends DashboardWidget<CustomAttrs> {
|
||||
className() {
|
||||
return classList('AlertWidget', this.attrs.className);
|
||||
}
|
||||
|
||||
content(vnode: Mithril.Vnode<CustomAttrs, this>) {
|
||||
return <Alert {...vnode.attrs.alert}>{vnode.children}</Alert>;
|
||||
}
|
||||
}
|
@ -4,7 +4,8 @@ import ExtensionsWidget from './ExtensionsWidget';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
import AdminPage from './AdminPage';
|
||||
import type { Children } from 'mithril';
|
||||
import DebugWarningWidget from './DebugWarningWidget';
|
||||
import AlertWidget from './AlertWidget';
|
||||
import Link from '../../common/components/Link';
|
||||
|
||||
export default class DashboardPage extends AdminPage {
|
||||
headerInfo() {
|
||||
@ -23,8 +24,39 @@ export default class DashboardPage extends AdminPage {
|
||||
availableWidgets(): ItemList<Children> {
|
||||
const items = new ItemList<Children>();
|
||||
|
||||
if (app.data.maintenanceMode) {
|
||||
items.add(
|
||||
'maintenanceMode',
|
||||
<AlertWidget
|
||||
alert={{
|
||||
type: 'error',
|
||||
dismissible: false,
|
||||
}}
|
||||
>
|
||||
{app.translator.trans('core.lib.notices.maintenance_mode_' + app.data.maintenanceMode)}
|
||||
</AlertWidget>,
|
||||
110
|
||||
);
|
||||
}
|
||||
|
||||
if (app.data.debugEnabled) {
|
||||
items.add('debug-warning', <DebugWarningWidget />, 100);
|
||||
items.add(
|
||||
'debug-warning',
|
||||
<AlertWidget
|
||||
className="DebugWarningWidget"
|
||||
alert={{
|
||||
type: 'warning',
|
||||
dismissible: false,
|
||||
title: app.translator.trans('core.admin.debug-warning.label'),
|
||||
icon: 'fas fa-exclamation-triangle',
|
||||
}}
|
||||
>
|
||||
{app.translator.trans('core.admin.debug-warning.detail', {
|
||||
link: <Link href="https://docs.flarum.org/troubleshoot/#step-0-activate-debug-mode" external={true} target="_blank" />,
|
||||
})}
|
||||
</AlertWidget>,
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
items.add('status', <StatusWidget />, 30);
|
||||
|
@ -1,18 +1,18 @@
|
||||
import type { Children, Vnode } from 'mithril';
|
||||
import type Mithril from 'mithril';
|
||||
import Component, { ComponentAttrs } from '../../common/Component';
|
||||
|
||||
export interface IDashboardWidgetAttrs extends ComponentAttrs {}
|
||||
|
||||
export default class DashboardWidget<CustomAttrs extends IDashboardWidgetAttrs = IDashboardWidgetAttrs> extends Component<CustomAttrs> {
|
||||
view(vnode: Vnode<CustomAttrs, this>): Children {
|
||||
return <div className={'DashboardWidget Widget ' + this.className()}>{this.content()}</div>;
|
||||
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children {
|
||||
return <div className={'DashboardWidget Widget ' + this.className()}>{this.content(vnode)}</div>;
|
||||
}
|
||||
|
||||
className() {
|
||||
return '';
|
||||
}
|
||||
|
||||
content(): Children {
|
||||
content(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
import app from '../../admin/app';
|
||||
import Alert from '../../common/components/Alert';
|
||||
import Link from '../../common/components/Link';
|
||||
import DashboardWidget from './DashboardWidget';
|
||||
|
||||
export default class DebugWarningWidget extends DashboardWidget {
|
||||
className() {
|
||||
return 'DebugWarningWidget';
|
||||
}
|
||||
|
||||
content() {
|
||||
return (
|
||||
<Alert type="warning" dismissible={false} title={app.translator.trans('core.admin.debug-warning.label')} icon="fas fa-exclamation-triangle">
|
||||
{app.translator.trans('core.admin.debug-warning.detail', {
|
||||
link: <Link href="https://docs.flarum.org/troubleshoot/#step-0-activate-debug-mode" external={true} target="_blank" />,
|
||||
})}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@ import type Mithril from 'mithril';
|
||||
import extractText from '../../common/utils/extractText';
|
||||
import Form from '../../common/components/Form';
|
||||
import Icon from '../../common/components/Icon';
|
||||
import { MaintenanceMode } from '../../common/Application';
|
||||
import InfoTile from '../../common/components/InfoTile';
|
||||
|
||||
export interface ExtensionPageAttrs extends IPageAttrs {
|
||||
id: string;
|
||||
@ -61,17 +63,31 @@ export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionP
|
||||
return (
|
||||
<div className={'ExtensionPage ' + this.className()}>
|
||||
{this.header()}
|
||||
{!this.isEnabled() ? (
|
||||
{app.data.maintenanceMode === MaintenanceMode.SAFE_MODE && !app.data.safeModeExtensions?.includes(this.extension.id) ? (
|
||||
<div className="container">
|
||||
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.enable_to_see')}</h3>
|
||||
<div className="ExtensionPage-body">
|
||||
<InfoTile icon="fas fa-exclamation-triangle" type="warning">
|
||||
{app.translator.trans('core.admin.extension.safe_mode_warning')}
|
||||
</InfoTile>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="ExtensionPage-body">{this.sections(vnode).toArray()}</div>
|
||||
this.body(vnode)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
body(vnode: Mithril.VnodeDOM<Attrs, this>) {
|
||||
return this.isEnabled() ? (
|
||||
<div className="ExtensionPage-body">{this.sections(vnode).toArray()}</div>
|
||||
) : (
|
||||
<div className="container">
|
||||
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.enable_to_see')}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
header() {
|
||||
const isEnabled = this.isEnabled();
|
||||
|
||||
|
@ -72,7 +72,6 @@ export default class StatusWidget extends DashboardWidget {
|
||||
<Button onclick={this.handleClearCache.bind(this)}>{app.translator.trans('core.admin.dashboard.clear_cache_button')}</Button>
|
||||
);
|
||||
|
||||
if (!app.data.advancedPageEmpty) {
|
||||
items.add(
|
||||
'toggleAdvancedPage',
|
||||
<Button
|
||||
@ -89,7 +88,6 @@ export default class StatusWidget extends DashboardWidget {
|
||||
{app.translator.trans('core.admin.dashboard.toggle_advanced_page_button')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
@ -124,12 +124,20 @@ export interface RouteResolver<
|
||||
render?(this: this, vnode: Mithril.Vnode<Attrs, Comp>): Mithril.Children;
|
||||
}
|
||||
|
||||
export enum MaintenanceMode {
|
||||
NO_MAINTENANCE = 'none',
|
||||
HIGH_MAINTENANCE = 'high',
|
||||
LOW_MAINTENANCE = 'low',
|
||||
SAFE_MODE = 'safe',
|
||||
}
|
||||
|
||||
export interface ApplicationData {
|
||||
apiDocument: ApiPayload | null;
|
||||
locale: string;
|
||||
locales: Record<string, string>;
|
||||
resources: SavedModelData[];
|
||||
session: { userId: number; csrfToken: string };
|
||||
maintenanceMode?: MaintenanceMode;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ import Icon from './Icon';
|
||||
export interface IDropdownAttrs extends ComponentAttrs {
|
||||
/** A class name to apply to the dropdown toggle button. */
|
||||
buttonClassName?: string;
|
||||
/** Additional attributes to apply to the dropdown toggle button. */
|
||||
buttonAttrs?: Record<string, string>;
|
||||
/** A class name to apply to the dropdown menu. */
|
||||
menuClassName?: string;
|
||||
/** The name of an icon to show in the dropdown toggle button. */
|
||||
@ -132,6 +134,7 @@ export default class Dropdown<CustomAttrs extends IDropdownAttrs = IDropdownAttr
|
||||
aria-label={this.attrs.accessibleToggleLabel}
|
||||
data-toggle="dropdown"
|
||||
onclick={this.attrs.onclick}
|
||||
{...this.attrs.buttonAttrs}
|
||||
>
|
||||
{this.getButtonContent(children)}
|
||||
</button>
|
||||
|
@ -10,6 +10,7 @@ import ItemList from '../utils/ItemList';
|
||||
import type { IUploadImageButtonAttrs } from './UploadImageButton';
|
||||
import type { ComponentAttrs } from '../Component';
|
||||
import type Mithril from 'mithril';
|
||||
import MultiSelect from './MultiSelect';
|
||||
|
||||
/**
|
||||
* A type that matches any valid value for the `type` attribute on an HTML `<input>` element.
|
||||
@ -81,8 +82,16 @@ export interface SelectFieldComponentOptions extends CommonFieldOptions {
|
||||
/**
|
||||
* Map of values to their labels
|
||||
*/
|
||||
options: { [value: string]: Mithril.Children };
|
||||
options: {
|
||||
[value: string]:
|
||||
| Mithril.Children
|
||||
| {
|
||||
label: Mithril.Children;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
default: string;
|
||||
multiple?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,7 +131,7 @@ export type FieldComponentOptions =
|
||||
|
||||
export type IFormGroupAttrs = ComponentAttrs &
|
||||
FieldComponentOptions & {
|
||||
bidi?: Stream<any>;
|
||||
stream?: Stream<any>;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -157,12 +166,12 @@ export default class FormGroup<CustomAttrs extends IFormGroupAttrs = IFormGroupA
|
||||
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children {
|
||||
const customFieldComponents = this.customFieldComponents();
|
||||
|
||||
const { help, type, label, bidi, ...componentAttrs } = this.attrs;
|
||||
const { help, type, label, stream, ...componentAttrs } = this.attrs;
|
||||
|
||||
// TypeScript being TypeScript
|
||||
const attrs = componentAttrs as unknown as Omit<IFormGroupAttrs, 'bidi' | 'label' | 'help' | 'type'>;
|
||||
const attrs = componentAttrs as unknown as Omit<IFormGroupAttrs, 'stream' | 'label' | 'help' | 'type'>;
|
||||
|
||||
const value = bidi ? bidi() : null;
|
||||
const value = stream ? stream() : null;
|
||||
|
||||
const [inputId, helpTextId] = [generateElementId(), generateElementId()];
|
||||
|
||||
@ -175,29 +184,31 @@ export default class FormGroup<CustomAttrs extends IFormGroupAttrs = IFormGroupA
|
||||
// TODO: Add aria-describedby for switch help text.
|
||||
//? Requires changes to Checkbox component to allow providing attrs directly for the element(s).
|
||||
<div className="Form-group">
|
||||
<Switch state={!!value && value !== '0'} onchange={bidi} {...attrs}>
|
||||
<Switch state={!!value && value !== '0'} onchange={stream} {...attrs}>
|
||||
{label}
|
||||
</Switch>
|
||||
{help ? <div className="helpText">{help}</div> : null}
|
||||
</div>
|
||||
);
|
||||
} else if ((SelectSettingTypes as readonly string[]).includes(type)) {
|
||||
const { default: defaultValue, options, ...otherAttrs } = attrs;
|
||||
const { default: defaultValue, options, multiple, ...otherAttrs } = attrs;
|
||||
|
||||
const Tag = multiple ? MultiSelect : Select;
|
||||
|
||||
settingElement = (
|
||||
<Select id={inputId} aria-describedby={helpTextId} value={value || defaultValue} options={options} onchange={bidi} {...otherAttrs} />
|
||||
<Tag id={inputId} aria-describedby={helpTextId} value={value || defaultValue} options={options} onchange={stream} {...otherAttrs} />
|
||||
);
|
||||
} else if (type === ImageUploadSettingType) {
|
||||
const { value, ...otherAttrs } = attrs;
|
||||
|
||||
settingElement = <UploadImageButton value={bidi} {...otherAttrs} />;
|
||||
settingElement = <UploadImageButton value={stream} {...otherAttrs} />;
|
||||
} else if (customFieldComponents.has(type)) {
|
||||
return customFieldComponents.get(type)(this.attrs);
|
||||
} else {
|
||||
attrs.className = classList('FormControl', attrs.className);
|
||||
|
||||
if ((TextareaSettingTypes as readonly string[]).includes(type)) {
|
||||
settingElement = <textarea id={inputId} aria-describedby={helpTextId} bidi={bidi} {...attrs} />;
|
||||
settingElement = <textarea id={inputId} aria-describedby={helpTextId} bidi={stream} {...attrs} />;
|
||||
} else {
|
||||
let Tag: VnodeElementTag = 'input';
|
||||
|
||||
@ -207,7 +218,7 @@ export default class FormGroup<CustomAttrs extends IFormGroupAttrs = IFormGroupA
|
||||
attrs.type = type;
|
||||
}
|
||||
|
||||
settingElement = <Tag id={inputId} aria-describedby={helpTextId} bidi={bidi} {...attrs} />;
|
||||
settingElement = <Tag id={inputId} aria-describedby={helpTextId} bidi={stream} {...attrs} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
126
framework/core/js/src/common/components/MultiSelect.tsx
Normal file
126
framework/core/js/src/common/components/MultiSelect.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import app from '../app';
|
||||
import Component, { type ComponentAttrs } from '../Component';
|
||||
import classList from '../utils/classList';
|
||||
import Dropdown from './Dropdown';
|
||||
import Mithril from 'mithril';
|
||||
import Button from './Button';
|
||||
import Tooltip from './Tooltip';
|
||||
|
||||
export type Option = {
|
||||
label: string;
|
||||
disabled?: boolean | ((value: string[]) => boolean);
|
||||
tooltip?: string;
|
||||
};
|
||||
|
||||
export interface IMultiSelectAttrs extends ComponentAttrs {
|
||||
options: Record<string, string | Option>;
|
||||
onchange?: (value: string[]) => void;
|
||||
value?: string[];
|
||||
disabled?: boolean;
|
||||
wrapperAttrs?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `MultiSelect` component displays an input with selected elements.
|
||||
* With a dropdown to select multiple options.
|
||||
*
|
||||
* - `options` A map of option values to labels.
|
||||
* - `onchange` A callback to run when the selected value is changed.
|
||||
* - `value` The value of the selected option.
|
||||
* - `disabled` Disabled state for the input.
|
||||
* - `wrapperAttrs` A map of attrs to be passed to the DOM element wrapping the input.
|
||||
*
|
||||
* Other attributes are passed directly to the input element rendered to the DOM.
|
||||
*/
|
||||
export default class MultiSelect<CustomAttrs extends IMultiSelectAttrs = IMultiSelectAttrs> extends Component<CustomAttrs> {
|
||||
protected selected: string[] = [];
|
||||
|
||||
oninit(vnode: Mithril.Vnode<CustomAttrs, this>) {
|
||||
super.oninit(vnode);
|
||||
|
||||
this.selected = this.attrs.value || [];
|
||||
}
|
||||
|
||||
view() {
|
||||
const {
|
||||
options,
|
||||
onchange,
|
||||
disabled,
|
||||
className,
|
||||
class: _class,
|
||||
|
||||
// Destructure the `wrapperAttrs` object to extract the `className` for passing to `classList()`
|
||||
// `= {}` prevents errors when `wrapperAttrs` is undefined
|
||||
wrapperAttrs: { className: wrapperClassName, class: wrapperClass, ...wrapperAttrs } = {},
|
||||
|
||||
...domAttrs
|
||||
} = this.attrs;
|
||||
|
||||
return (
|
||||
<span className={classList('Select MultiSelect', wrapperClassName, wrapperClass)} {...wrapperAttrs}>
|
||||
<Dropdown
|
||||
disabled={disabled}
|
||||
buttonClassName="Button"
|
||||
buttonAttrs={{ disabled }}
|
||||
label={
|
||||
Object.keys(options)
|
||||
.filter((key) => this.selected.includes(key))
|
||||
.map((key) => (typeof options[key] === 'string' ? options[key] : (options[key] as Option).label))
|
||||
.join(', ') || app.translator.trans('core.lib.multi_select.placeholder')
|
||||
}
|
||||
>
|
||||
{Object.keys(options).map((key) => {
|
||||
const option = options[key];
|
||||
const label = typeof option === 'string' ? option : option.label;
|
||||
const tooltip = typeof option !== 'string' && option.tooltip;
|
||||
let disabled = typeof option !== 'string' && option.disabled;
|
||||
|
||||
if (typeof disabled === 'function') {
|
||||
disabled = disabled(this.selected);
|
||||
}
|
||||
|
||||
const button = (
|
||||
<Button
|
||||
type="button"
|
||||
className={classList('Dropdown-item', { disabled })}
|
||||
onclick={this.toggle.bind(this, key)}
|
||||
disabled={disabled}
|
||||
icon={this.selected.includes(key) ? 'fas fa-check' : 'fas fa-empty'}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
|
||||
if (tooltip) {
|
||||
return <Tooltip text={tooltip}>{button}</Tooltip>;
|
||||
}
|
||||
|
||||
return button;
|
||||
})}
|
||||
</Dropdown>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
select(value: string) {
|
||||
this.selected.push(value);
|
||||
}
|
||||
|
||||
unselect(value: string) {
|
||||
this.selected = this.selected.filter((v) => v !== value);
|
||||
}
|
||||
|
||||
toggle(value: string, e: MouseEvent) {
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.selected.includes(value)) {
|
||||
this.unselect(value);
|
||||
} else {
|
||||
this.select(value);
|
||||
}
|
||||
|
||||
if (this.attrs.onchange) {
|
||||
this.attrs.onchange(this.selected);
|
||||
}
|
||||
}
|
||||
}
|
@ -41,9 +41,25 @@ export default class Select extends Component {
|
||||
disabled={disabled}
|
||||
{...domAttrs}
|
||||
>
|
||||
{Object.keys(options).map((key) => (
|
||||
<option value={key}>{options[key]}</option>
|
||||
))}
|
||||
{Object.keys(options).map((key) => {
|
||||
const option = options[key];
|
||||
|
||||
let label;
|
||||
let disabled = false;
|
||||
|
||||
if (typeof option === 'object' && option.label) {
|
||||
label = option.label;
|
||||
disabled = option.disabled ?? false;
|
||||
} else {
|
||||
label = option;
|
||||
}
|
||||
|
||||
return (
|
||||
<option value={key} disabled={disabled}>
|
||||
{label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<Icon name="fas fa-sort" className="Select-caret" />
|
||||
</span>
|
||||
|
@ -40,6 +40,15 @@ export default class Notices extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (app.data.maintenanceMode) {
|
||||
items.add(
|
||||
'maintenanceMode',
|
||||
<Alert type="error" dismissible={false} className="Alert--maintenanceMode" containerClassName="container">
|
||||
{app.translator.trans('core.lib.notices.maintenance_mode_' + app.data.maintenanceMode)}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
@import "admin/AdminNav";
|
||||
@import "admin/CreateUserModal";
|
||||
@import "admin/DashboardPage";
|
||||
@import "admin/DebugWarningWidget";
|
||||
@import "admin/AlertWidget";
|
||||
@import "admin/FormSectionGroup";
|
||||
@import "admin/BasicsPage";
|
||||
@import "admin/PermissionsPage";
|
||||
|
3
framework/core/less/admin/AlertWidget.less
Normal file
3
framework/core/less/admin/AlertWidget.less
Normal file
@ -0,0 +1,3 @@
|
||||
.AlertWidget {
|
||||
padding: 0;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
.DebugWarningWidget {
|
||||
padding: 0;
|
||||
}
|
@ -130,6 +130,12 @@
|
||||
padding: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-body {
|
||||
.InfoTile {
|
||||
margin-top: 4rem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ExtensionTitle {
|
||||
|
@ -16,6 +16,10 @@
|
||||
border-radius: var(--border-radius);
|
||||
flex: 1 1 160px;
|
||||
gap: var(--gap);
|
||||
|
||||
&-body {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.FormSection > label {
|
||||
|
@ -34,6 +34,10 @@
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&, & > .Button {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -214,6 +218,8 @@
|
||||
}
|
||||
.Button-label {
|
||||
line-height: inherit;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.Button-icon {
|
||||
line-height: inherit;
|
||||
|
@ -61,7 +61,7 @@
|
||||
|
||||
.FieldSet, .FieldSet-items, .Form, .Form-body {
|
||||
> * {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,16 @@
|
||||
font-size: 1.1rem;
|
||||
color: var(--control-color);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
|
||||
.icon {
|
||||
color: var(--control-muted-color);
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.FormSection & {
|
||||
font-size: 0.94rem;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,10 @@
|
||||
padding-right: 30px;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
|
||||
.FormSection & {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.Select-caret {
|
||||
@ -19,3 +23,7 @@
|
||||
text-align: center;
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
.MultiSelect {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
@ -10,12 +10,26 @@ core:
|
||||
# These translations are used in the Advanced page.
|
||||
advanced:
|
||||
description: "Configure advanced settings for your forum."
|
||||
maintenance:
|
||||
config_override:
|
||||
label: Your <code>config.php</code> file is overriding these settings.
|
||||
help: You can still change these settings here, but they will not take effect until you set <code>offline</code> to <code>0</code> in your <code>config.php</code> file.
|
||||
help: Put your forum in maintenance mode to prevent users from accessing it.
|
||||
options:
|
||||
none: No maintenance.
|
||||
high: High maintenance mode. No one can access the forum (can only be enabled through config.php)
|
||||
low: Low maintenance mode. Admins can access the forum.
|
||||
safe: Safe mode. No extensions are booted and only admins can access the forum.
|
||||
safe_mode_extensions: Extensions allowed to boot during safe mode
|
||||
safe_mode_extensions_override_help: "This setting is overridden by the <code>safe_mode_extensions</code> key in your <code>config.php</code> file. (<b>{extensions}</b>)"
|
||||
section_label: Maintenance
|
||||
search:
|
||||
section_label: Search Drivers
|
||||
driver_heading: "Search Driver: {model}"
|
||||
driver_text: Select a driver to be used for searching this model.
|
||||
driver_options:
|
||||
default: Default database search
|
||||
no_other_drivers: No search drivers are available yet. Install a search driver extension to be able to configure it.
|
||||
title: Advanced
|
||||
|
||||
# These translations are used in the Appearance page.
|
||||
@ -194,6 +208,7 @@ core:
|
||||
button_label: README
|
||||
no_readme: This extension does not appear to have a README file
|
||||
title: "{extName} documentation"
|
||||
safe_mode_warning: Safe mode is currently enabled. Extensions are not booted and their settings are therefore inaccessible.
|
||||
|
||||
# These translations are used in the secondary header.
|
||||
header:
|
||||
@ -463,13 +478,13 @@ core:
|
||||
# These translations are used in the Log In modal dialog.
|
||||
log_in:
|
||||
forgot_password_link: "Forgot password?"
|
||||
invalid_login_message: Your login details were incorrect.
|
||||
invalid_login_message: => core.ref.invalid_login_message
|
||||
password_placeholder: => core.ref.password
|
||||
remember_me_label: Remember Me
|
||||
remember_me_label: => core.ref.remember_me_label
|
||||
sign_up_text: "Don't have an account? <a>Sign Up</a>"
|
||||
submit_button: => core.ref.log_in
|
||||
title: => core.ref.log_in
|
||||
username_or_email_placeholder: Username or Email
|
||||
username_or_email_placeholder: => core.ref.username_or_email_placeholder
|
||||
|
||||
# These translations are used by the Notifications dropdown, a.k.a. "the bell".
|
||||
notifications:
|
||||
@ -689,10 +704,19 @@ core:
|
||||
modal:
|
||||
close: Close
|
||||
|
||||
# These translations are used in multi-select components.
|
||||
multi_select:
|
||||
placeholder: Select multiple options
|
||||
|
||||
# These translations are used in the navigation header.
|
||||
nav:
|
||||
drawer_button: Open Navigation Drawer
|
||||
|
||||
# These translations are used in forum & admin notices.
|
||||
notices:
|
||||
maintenance_mode_low: Down for maintenance. Only administrators can access the forum.
|
||||
maintenance_mode_safe: Down for maintenance with safe mode. Only administrators can access the forum and no extensions are booted.
|
||||
|
||||
# These translations are used as suffixes when abbreviating numbers.
|
||||
number_suffix:
|
||||
kilo_text: K
|
||||
@ -776,6 +800,9 @@ core:
|
||||
csrf_token_mismatch: You have been inactive for too long.
|
||||
csrf_token_mismatch_return_link: Go back, to try again
|
||||
invalid_confirmation_token: This confirmation link has already been used or is invalid.
|
||||
maintenance_mode_link: Administrator login
|
||||
maintenance_mode_message: This forum is currently down for maintenance. Please check back later.
|
||||
maintenance_mode_title: Maintenance
|
||||
not_authenticated: You do not have permission to access this page. Try again after logging in.
|
||||
not_found: The page you requested could not be found.
|
||||
not_found_return_link: "Return to {forum}"
|
||||
@ -794,6 +821,15 @@ core:
|
||||
log_out_confirmation: "Are you sure you want to log out of {forum}?"
|
||||
title: => core.ref.log_out
|
||||
|
||||
# Translations in this namespace are displayed on the login interface.
|
||||
log_in:
|
||||
invalid_login_message: => core.ref.invalid_login_message
|
||||
username_or_email_placeholder: => core.ref.username_or_email_placeholder
|
||||
password_placeholder: => core.ref.password
|
||||
remember_me_label: => core.ref.remember_me_label
|
||||
submit_button: => core.ref.log_in
|
||||
title: => core.ref.log_in
|
||||
|
||||
# Translations in this namespace are displayed by the Reset Password interface.
|
||||
reset_password:
|
||||
confirm_password_label: Confirm New Password
|
||||
@ -911,6 +947,7 @@ core:
|
||||
generic_confirmation_message: "Are you sure you want to proceed? This action cannot be undone."
|
||||
icon: Icon
|
||||
icon_text: "Enter the name of any <a>FontAwesome</a> icon class, <em>including</em> the <code>fas fa-</code> prefix."
|
||||
invalid_login_message: Your login details were incorrect.
|
||||
load_more: Load More
|
||||
loading: Loading...
|
||||
log_in: Log In
|
||||
@ -924,6 +961,7 @@ core:
|
||||
password: Password
|
||||
posts: Posts # Referenced by flarum-statistics.yml
|
||||
previous_page: Previous Page
|
||||
remember_me_label: Remember Me
|
||||
remove: Remove
|
||||
rename: Rename
|
||||
reply: Reply # Referenced by flarum-mentions.yml
|
||||
@ -937,6 +975,7 @@ core:
|
||||
some_others: "{count, plural, one {# other} other {# others}}" # Referenced by flarum-likes.yml, flarum-mentions.yml
|
||||
start_a_discussion: Start a Discussion
|
||||
username: Username
|
||||
username_or_email_placeholder: Username or Email
|
||||
users: Users # Referenced by flarum-statistics.yml
|
||||
view: View
|
||||
write_a_reply: Write a Reply...
|
||||
|
@ -19,6 +19,7 @@ use Flarum\Foundation\ErrorHandling\WhoopsFormatter;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Frontend\AddLocaleAssets;
|
||||
use Flarum\Frontend\AddTranslations;
|
||||
use Flarum\Frontend\AssetManager;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\RecompileFrontendAssets;
|
||||
use Flarum\Http\Middleware as HttpMiddleware;
|
||||
@ -26,7 +27,10 @@ use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Flarum\Settings\Event\Saving;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Laminas\Stratigility\MiddlewarePipe;
|
||||
|
||||
class AdminServiceProvider extends AbstractServiceProvider
|
||||
@ -104,6 +108,10 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
return $assets;
|
||||
});
|
||||
|
||||
$this->container->afterResolving(AssetManager::class, function (AssetManager $assets) {
|
||||
$assets->register('admin', 'flarum.assets.admin');
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.frontend.admin', function (Container $container) {
|
||||
/** @var \Flarum\Frontend\Frontend $frontend */
|
||||
$frontend = $container->make('flarum.frontend.factory')('admin');
|
||||
@ -114,22 +122,34 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
});
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
public function boot(Container $container, Dispatcher $events): void
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum.admin');
|
||||
|
||||
$events = $this->container->make('events');
|
||||
|
||||
$events->listen(
|
||||
[Enabled::class, Disabled::class, ClearingCache::class],
|
||||
function () {
|
||||
function () use ($container) {
|
||||
$recompile = new RecompileFrontendAssets(
|
||||
$this->container->make('flarum.assets.admin'),
|
||||
$this->container->make(LocaleManager::class)
|
||||
$container->make('flarum.assets.admin'),
|
||||
$container->make(LocaleManager::class)
|
||||
);
|
||||
$recompile->flush();
|
||||
}
|
||||
);
|
||||
|
||||
$events->listen(
|
||||
[Saved::class, Saving::class],
|
||||
function (Saved|Saving $event) use ($container) {
|
||||
/** @var WhenSavingSettings $listener */
|
||||
$listener = $container->make(WhenSavingSettings::class);
|
||||
|
||||
if ($event instanceof Saving) {
|
||||
$listener->beforeSave($event);
|
||||
} else {
|
||||
$listener->afterSave($event);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function populateRoutes(RouteCollection $routes): void
|
||||
|
@ -13,6 +13,7 @@ use Flarum\Database\AbstractModel;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Foundation\ApplicationInfoProvider;
|
||||
use Flarum\Foundation\Config;
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Group\Permission;
|
||||
use Flarum\Search\AbstractDriver;
|
||||
@ -35,7 +36,8 @@ class AdminPayload
|
||||
protected ConnectionInterface $db,
|
||||
protected Dispatcher $events,
|
||||
protected Config $config,
|
||||
protected ApplicationInfoProvider $appInfo
|
||||
protected ApplicationInfoProvider $appInfo,
|
||||
protected MaintenanceMode $maintenance
|
||||
) {
|
||||
}
|
||||
|
||||
@ -57,8 +59,6 @@ class AdminPayload
|
||||
}, $this->container->make('flarum.http.slugDrivers'));
|
||||
$document->payload['searchDrivers'] = $this->getSearchDrivers();
|
||||
|
||||
$document->payload['advancedPageEmpty'] = $this->checkAdvancedPageEmpty();
|
||||
|
||||
$document->payload['phpVersion'] = $this->appInfo->identifyPHPVersion();
|
||||
$document->payload['mysqlVersion'] = $this->appInfo->identifyDatabaseVersion();
|
||||
$document->payload['debugEnabled'] = Arr::get($this->config, 'debug');
|
||||
@ -82,6 +82,10 @@ class AdminPayload
|
||||
'total' => User::query()->count()
|
||||
]
|
||||
];
|
||||
|
||||
$document->payload['maintenanceByConfig'] = $this->maintenance->configOverride();
|
||||
$document->payload['safeModeExtensions'] = $this->maintenance->safeModeExtensions();
|
||||
$document->payload['safeModeExtensionsConfig'] = $this->config->safeModeExtensions();
|
||||
}
|
||||
|
||||
protected function getSearchDrivers(): array
|
||||
@ -98,9 +102,4 @@ class AdminPayload
|
||||
|
||||
return $searchDriversPerModel;
|
||||
}
|
||||
|
||||
protected function checkAdvancedPageEmpty(): bool
|
||||
{
|
||||
return count($this->container->make('flarum.search.drivers')) === 1;
|
||||
}
|
||||
}
|
||||
|
73
framework/core/src/Admin/WhenSavingSettings.php
Normal file
73
framework/core/src/Admin/WhenSavingSettings.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?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\Admin;
|
||||
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Frontend\AssetManager;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Flarum\Settings\Event\Saving;
|
||||
|
||||
class WhenSavingSettings
|
||||
{
|
||||
/**
|
||||
* Settings that should trigger JS cache clear when saved.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $resetJsCacheFor = ['maintenance_mode', 'safe_mode_extensions'];
|
||||
|
||||
public function __construct(
|
||||
protected AssetManager $assets,
|
||||
protected LocaleManager $locales,
|
||||
protected ExtensionManager $extensions,
|
||||
) {
|
||||
}
|
||||
|
||||
public function beforeSave(Saving $event): void
|
||||
{
|
||||
if (array_key_exists('safe_mode_extensions', $event->settings)) {
|
||||
$safeModeExtensions = json_decode($event->settings['safe_mode_extensions'] ?? '[]', true);
|
||||
|
||||
$extensions = $this->extensions->getExtensions()->filter(function ($extension) use ($safeModeExtensions) {
|
||||
return in_array($extension->getId(), $safeModeExtensions);
|
||||
});
|
||||
|
||||
$sorted = array_map(fn (Extension $e) => $e->getId(), $this->extensions->sortDependencies($extensions->all()));
|
||||
|
||||
$event->settings['safe_mode_extensions'] = json_encode(array_values($sorted));
|
||||
}
|
||||
}
|
||||
|
||||
public function afterSave(Saved $event): void
|
||||
{
|
||||
$this->resetCache($event);
|
||||
}
|
||||
|
||||
protected function resetCache(Saved $event): void
|
||||
{
|
||||
if (! $this->hasDirtySettings($event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assets->flushJs();
|
||||
}
|
||||
|
||||
public function resetJsCacheFor(string|array $setting): void
|
||||
{
|
||||
$this->resetJsCacheFor = array_merge($this->resetJsCacheFor, (array) $setting);
|
||||
}
|
||||
|
||||
protected function hasDirtySettings(Saved $event): bool
|
||||
{
|
||||
return array_intersect(array_keys($event->settings), $this->resetJsCacheFor) !== [];
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\ErrorHandling\JsonApiFormatter;
|
||||
use Flarum\Foundation\ErrorHandling\Registry;
|
||||
use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Http\Middleware as HttpMiddleware;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
@ -65,6 +66,7 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
HttpMiddleware\AuthenticateWithHeader::class,
|
||||
HttpMiddleware\SetLocale::class,
|
||||
'flarum.api.route_resolver',
|
||||
'flarum.api.check_for_maintenance',
|
||||
HttpMiddleware\CheckCsrfToken::class,
|
||||
Middleware\ThrottleApi::class
|
||||
];
|
||||
@ -82,6 +84,17 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
return new HttpMiddleware\ResolveRoute($container->make('flarum.api.routes'));
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.api.check_for_maintenance', function (Container $container) {
|
||||
return new HttpMiddleware\CheckForMaintenanceMode(
|
||||
$container->make(MaintenanceMode::class),
|
||||
$container->make('flarum.api.maintenance_route_exclusions')
|
||||
);
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.api.maintenance_route_exclusions', function () {
|
||||
return [];
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.api.handler', function (Container $container) {
|
||||
$pipe = new MiddlewarePipe;
|
||||
|
||||
@ -108,6 +121,7 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
HttpMiddleware\StartSession::class,
|
||||
HttpMiddleware\AuthenticateWithSession::class,
|
||||
HttpMiddleware\AuthenticateWithHeader::class,
|
||||
'flarum.api.check_for_maintenance',
|
||||
HttpMiddleware\CheckCsrfToken::class,
|
||||
HttpMiddleware\RememberFromCookie::class,
|
||||
];
|
||||
|
@ -29,7 +29,7 @@ class Schedule extends LaravelSchedule
|
||||
|
||||
public function isDownForMaintenance(): bool
|
||||
{
|
||||
return $this->config->inMaintenanceMode();
|
||||
return $this->config->inHighMaintenanceMode();
|
||||
}
|
||||
|
||||
public function environment(): string
|
||||
|
@ -46,8 +46,6 @@ class UserState extends AbstractModel
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = ['last_read_post_number'];
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
namespace Flarum\Extend;
|
||||
|
||||
use Flarum\Admin\WhenSavingSettings;
|
||||
use Flarum\Api\Serializer\AbstractSerializer;
|
||||
use Flarum\Api\Serializer\ForumSerializer;
|
||||
use Flarum\Extension\Extension;
|
||||
@ -22,6 +23,7 @@ class Settings implements ExtenderInterface
|
||||
private array $settings = [];
|
||||
private array $defaults = [];
|
||||
private array $lessConfigs = [];
|
||||
private array $resetJsCacheFor = [];
|
||||
|
||||
/**
|
||||
* Serialize a setting value to the ForumSerializer attributes.
|
||||
@ -82,6 +84,19 @@ class Settings implements ExtenderInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a setting that should trigger JS cache clear when saved.
|
||||
*
|
||||
* @param string $setting: The key of the setting.
|
||||
* @return self
|
||||
*/
|
||||
public function resetJsCacheFor(string $setting): self
|
||||
{
|
||||
$this->resetJsCacheFor[] = $setting;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function extend(Container $container, Extension $extension = null): void
|
||||
{
|
||||
if (! empty($this->defaults)) {
|
||||
@ -134,5 +149,11 @@ class Settings implements ExtenderInterface
|
||||
return array_merge($existingConfig, $config);
|
||||
});
|
||||
}
|
||||
|
||||
if (! empty($this->resetJsCacheFor)) {
|
||||
$container->afterResolving(WhenSavingSettings::class, function (WhenSavingSettings $whenSavingSettings) {
|
||||
$whenSavingSettings->resetJsCacheFor($this->resetJsCacheFor);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Extension\Event\Enabling;
|
||||
use Flarum\Extension\Event\Uninstalled;
|
||||
use Flarum\Extension\Exception\CircularDependenciesException;
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Foundation\Paths;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
@ -37,7 +38,8 @@ class ExtensionManager
|
||||
protected Container $container,
|
||||
protected Migrator $migrator,
|
||||
protected Dispatcher $dispatcher,
|
||||
protected Filesystem $filesystem
|
||||
protected Filesystem $filesystem,
|
||||
protected MaintenanceMode $maintenance,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -302,7 +304,19 @@ class ExtensionManager
|
||||
*/
|
||||
public function extend(Container $container): void
|
||||
{
|
||||
foreach ($this->getEnabledExtensions() as $extension) {
|
||||
$extensions = $this->getEnabledExtensions();
|
||||
|
||||
if ($this->maintenance->inSafeMode()) {
|
||||
$safeModeExtensions = $this->maintenance->safeModeExtensions();
|
||||
|
||||
$extensions = array_filter($extensions, function (Extension $extension) use ($safeModeExtensions) {
|
||||
return in_array($extension->getId(), $safeModeExtensions, true);
|
||||
});
|
||||
|
||||
$extensions = $this->sortDependencies($extensions);
|
||||
}
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
$extension->extend($container);
|
||||
}
|
||||
}
|
||||
@ -325,7 +339,21 @@ class ExtensionManager
|
||||
*/
|
||||
protected function setEnabledExtensions(array $enabledExtensions): void
|
||||
{
|
||||
$resolved = static::resolveExtensionOrder($enabledExtensions);
|
||||
$this->config->set('extensions_enabled', json_encode(array_map(function (Extension $extension) {
|
||||
return $extension->getId();
|
||||
}, $this->sortDependencies($enabledExtensions))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a topological sort to the extensions to ensure that they are in the correct order.
|
||||
*
|
||||
* @param Extension[] $extensions
|
||||
* @return Extension[]
|
||||
* @throws CircularDependenciesException
|
||||
*/
|
||||
public function sortDependencies(array $extensions): array
|
||||
{
|
||||
$resolved = static::resolveExtensionOrder($extensions);
|
||||
|
||||
if (! empty($resolved['circularDependencies'])) {
|
||||
throw new Exception\CircularDependenciesException(
|
||||
@ -333,13 +361,7 @@ class ExtensionManager
|
||||
);
|
||||
}
|
||||
|
||||
$sortedEnabled = $resolved['valid'];
|
||||
|
||||
$sortedEnabledIds = array_map(function (Extension $extension) {
|
||||
return $extension->getId();
|
||||
}, $sortedEnabled);
|
||||
|
||||
$this->config->set('extensions_enabled', json_encode($sortedEnabledIds));
|
||||
return $resolved['valid'];
|
||||
}
|
||||
|
||||
public function isEnabled(string $extension): bool
|
||||
|
@ -11,6 +11,7 @@ namespace Flarum\Extension;
|
||||
|
||||
use Flarum\Extension\Event\Disabling;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class ExtensionServiceProvider extends AbstractServiceProvider
|
||||
@ -24,8 +25,11 @@ class ExtensionServiceProvider extends AbstractServiceProvider
|
||||
// listener on the app rather than in the service provider's boot method
|
||||
// below, so that extensions have a chance to register things on the
|
||||
// container before the core boots up (and starts resolving services).
|
||||
$this->container['flarum']->booting(function () {
|
||||
$this->container->make('flarum.extensions')->extend($this->container);
|
||||
$this->container['flarum']->booting(function (Container $container) {
|
||||
/** @var ExtensionManager $manager */
|
||||
$manager = $container->make('flarum.extensions');
|
||||
|
||||
$manager->extend($container);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -14,11 +14,16 @@ use Flarum\Forum\LogInValidator;
|
||||
use Flarum\Http\AccessToken;
|
||||
use Flarum\Http\RememberAccessToken;
|
||||
use Flarum\Http\Rememberer;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Flarum\Http\SessionAuthenticator;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Locale\TranslatorInterface;
|
||||
use Flarum\User\Event\LoggedIn;
|
||||
use Flarum\User\UserRepository;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Laminas\Diactoros\Response\RedirectResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
@ -31,7 +36,9 @@ class LogInController implements RequestHandlerInterface
|
||||
protected SessionAuthenticator $authenticator,
|
||||
protected Dispatcher $events,
|
||||
protected Rememberer $rememberer,
|
||||
protected LogInValidator $validator
|
||||
protected LogInValidator $validator,
|
||||
protected UrlGenerator $url,
|
||||
protected TranslatorInterface $translator
|
||||
) {
|
||||
}
|
||||
|
||||
@ -39,8 +46,22 @@ class LogInController implements RequestHandlerInterface
|
||||
{
|
||||
$body = $request->getParsedBody();
|
||||
$params = Arr::only($body, ['identification', 'password', 'remember']);
|
||||
$isHtmlRequest = RequestUtil::isHtmlRequest($request);
|
||||
|
||||
$errors = null;
|
||||
|
||||
if ($isHtmlRequest) {
|
||||
$validator = $this->validator->basic()->prepare($body)->validator();
|
||||
|
||||
if (! $validator->passes()) {
|
||||
$errors = $validator->errors();
|
||||
$request->getAttribute('session')->put('errors', $errors);
|
||||
|
||||
return new RedirectResponse($this->url->to('forum')->route('maintenance.login'));
|
||||
}
|
||||
} else {
|
||||
$this->validator->assertValid($body);
|
||||
}
|
||||
|
||||
$response = $this->apiClient->withParentRequest($request)->withBody($params)->post('/token');
|
||||
|
||||
@ -59,6 +80,15 @@ class LogInController implements RequestHandlerInterface
|
||||
}
|
||||
}
|
||||
|
||||
if ($isHtmlRequest) {
|
||||
if ($response->getStatusCode() === 401) {
|
||||
$errors = new MessageBag(['identification' => $this->translator->trans('core.views.log_in.invalid_login_message')]);
|
||||
$request->getAttribute('session')->put('errors', $errors);
|
||||
}
|
||||
|
||||
return new RedirectResponse($this->url->to('forum')->route('maintenance.login'));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
51
framework/core/src/Forum/Controller/LogInViewController.php
Normal file
51
framework/core/src/Forum/Controller/LogInViewController.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?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\Forum\Controller;
|
||||
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Http\Controller\AbstractHtmlController;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Laminas\Diactoros\Response\RedirectResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
/**
|
||||
* Maintenance login view.
|
||||
*/
|
||||
class LogInViewController extends AbstractHtmlController
|
||||
{
|
||||
public function __construct(
|
||||
protected Factory $view,
|
||||
protected UrlGenerator $url,
|
||||
protected MaintenanceMode $maintenance
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(Request $request): ResponseInterface
|
||||
{
|
||||
$actor = RequestUtil::getActor($request);
|
||||
|
||||
if (! $actor->isGuest() || ! $this->maintenance->inMaintenanceMode()) {
|
||||
return new RedirectResponse($this->url->to('forum')->base());
|
||||
}
|
||||
|
||||
return parent::handle($request);
|
||||
}
|
||||
|
||||
public function render(Request $request): View
|
||||
{
|
||||
return $this->view
|
||||
->make('flarum.forum::log-in')
|
||||
->with('csrfToken', $request->getAttribute('session')->token());
|
||||
}
|
||||
}
|
@ -18,8 +18,10 @@ use Flarum\Foundation\ErrorHandling\Reporter;
|
||||
use Flarum\Foundation\ErrorHandling\ViewFormatter;
|
||||
use Flarum\Foundation\ErrorHandling\WhoopsFormatter;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Frontend\AddLocaleAssets;
|
||||
use Flarum\Frontend\AddTranslations;
|
||||
use Flarum\Frontend\AssetManager;
|
||||
use Flarum\Frontend\Assets;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\Frontend;
|
||||
@ -68,6 +70,7 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
HttpMiddleware\AuthenticateWithSession::class,
|
||||
HttpMiddleware\SetLocale::class,
|
||||
'flarum.forum.route_resolver',
|
||||
'flarum.forum.check_for_maintenance',
|
||||
HttpMiddleware\CheckCsrfToken::class,
|
||||
HttpMiddleware\ShareErrorsFromSession::class,
|
||||
HttpMiddleware\FlarumPromotionHeader::class,
|
||||
@ -88,6 +91,17 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
return new HttpMiddleware\ResolveRoute($container->make('flarum.forum.routes'));
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.forum.check_for_maintenance', function (Container $container) {
|
||||
return new HttpMiddleware\CheckForMaintenanceMode(
|
||||
$container->make(MaintenanceMode::class),
|
||||
$container->make('flarum.forum.maintenance_route_exclusions')
|
||||
);
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.forum.maintenance_route_exclusions', function () {
|
||||
return ['login', 'maintenance.login'];
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.forum.handler', function (Container $container) {
|
||||
$pipe = new MiddlewarePipe;
|
||||
|
||||
@ -128,6 +142,10 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
return $assets;
|
||||
});
|
||||
|
||||
$this->container->afterResolving(AssetManager::class, function (AssetManager $assets) {
|
||||
$assets->register('forum', 'flarum.assets.forum');
|
||||
});
|
||||
|
||||
$this->container->bind('flarum.frontend.forum', function (Container $container, array $parameters = []) {
|
||||
/** @var Frontend $frontend */
|
||||
$frontend = $container->make('flarum.frontend.factory')('forum');
|
||||
|
@ -13,5 +13,16 @@ use Flarum\Foundation\AbstractValidator;
|
||||
|
||||
class LogInValidator extends AbstractValidator
|
||||
{
|
||||
public bool $basic = false;
|
||||
protected array $rules = [];
|
||||
|
||||
public function basic(): static
|
||||
{
|
||||
$this->rules['identification'] = 'required';
|
||||
$this->rules['password'] = 'required';
|
||||
|
||||
$this->basic = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,12 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
||||
$route->toController(Controller\GlobalLogOutController::class)
|
||||
);
|
||||
|
||||
$map->get(
|
||||
'/maintenance/login',
|
||||
'maintenance.login',
|
||||
$route->toController(Controller\LogInViewController::class)
|
||||
);
|
||||
|
||||
$map->post(
|
||||
'/login',
|
||||
'login',
|
||||
|
@ -27,6 +27,8 @@ abstract class AbstractValidator
|
||||
*/
|
||||
protected array $rules = [];
|
||||
|
||||
protected ?Validator $laravelValidator = null;
|
||||
|
||||
public function __construct(
|
||||
protected Factory $validator,
|
||||
protected TranslatorInterface $translator
|
||||
@ -40,6 +42,8 @@ abstract class AbstractValidator
|
||||
|
||||
/**
|
||||
* Throw an exception if a model is not valid.
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function assertValid(array $attributes): void
|
||||
{
|
||||
@ -50,6 +54,18 @@ abstract class AbstractValidator
|
||||
}
|
||||
}
|
||||
|
||||
public function prepare(array $attributes): static
|
||||
{
|
||||
$this->laravelValidator ??= $this->makeValidator($attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function validator(): Validator
|
||||
{
|
||||
return $this->laravelValidator;
|
||||
}
|
||||
|
||||
protected function getRules(): array
|
||||
{
|
||||
return $this->rules;
|
||||
|
@ -45,9 +45,14 @@ class Application extends IlluminateContainer implements LaravelApplication
|
||||
$this->registerCoreContainerAliases();
|
||||
}
|
||||
|
||||
public function getConfig(): Config
|
||||
{
|
||||
return $this->make(Config::class);
|
||||
}
|
||||
|
||||
public function config(string $key, mixed $default = null): mixed
|
||||
{
|
||||
$config = $this->make('flarum.config');
|
||||
$config = $this->getConfig();
|
||||
|
||||
return $config[$key] ?? $default;
|
||||
}
|
||||
@ -211,6 +216,7 @@ class Application extends IlluminateContainer implements LaravelApplication
|
||||
'filesystem' => [\Illuminate\Filesystem\FilesystemManager::class, \Illuminate\Contracts\Filesystem\Factory::class],
|
||||
'filesystem.disk' => [\Illuminate\Contracts\Filesystem\Filesystem::class],
|
||||
'filesystem.cloud' => [\Illuminate\Contracts\Filesystem\Cloud::class],
|
||||
'flarum.assets' => [\Flarum\Frontend\AssetManager::class],
|
||||
'hash' => [\Illuminate\Contracts\Hashing\Hasher::class],
|
||||
'mailer' => [\Illuminate\Mail\Mailer::class, \Illuminate\Contracts\Mail\Mailer::class, \Illuminate\Contracts\Mail\MailQueue::class],
|
||||
'validator' => [\Illuminate\Validation\Factory::class, \Illuminate\Contracts\Validation\Factory::class],
|
||||
|
@ -139,13 +139,9 @@ trait InteractsWithLaravel
|
||||
return null; // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Not actually used/has no meaning in Flarum.
|
||||
*/
|
||||
public function isDownForMaintenance(): bool
|
||||
{
|
||||
// TODO: Implement isDownForMaintenance() method.
|
||||
return false;
|
||||
return $this->getConfig()->inHighMaintenanceMode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +36,36 @@ class Config implements ArrayAccess
|
||||
|
||||
public function inMaintenanceMode(): bool
|
||||
{
|
||||
return $this->data['offline'] ?? false;
|
||||
return $this->inHighMaintenanceMode() || $this->inLowMaintenanceMode() || $this->inSafeMode();
|
||||
}
|
||||
|
||||
public function inHighMaintenanceMode(): bool
|
||||
{
|
||||
return $this->maintenanceMode() === MaintenanceMode::HIGH;
|
||||
}
|
||||
|
||||
public function inLowMaintenanceMode(): bool
|
||||
{
|
||||
return $this->maintenanceMode() === MaintenanceMode::LOW;
|
||||
}
|
||||
|
||||
public function inSafeMode(): bool
|
||||
{
|
||||
return $this->maintenanceMode() === MaintenanceMode::SAFE;
|
||||
}
|
||||
|
||||
public function maintenanceMode(): string
|
||||
{
|
||||
return match ($mode = $this->data['offline'] ?? MaintenanceMode::NONE) {
|
||||
true => MaintenanceMode::HIGH,
|
||||
false => MaintenanceMode::NONE,
|
||||
default => $mode,
|
||||
};
|
||||
}
|
||||
|
||||
public function safeModeExtensions(): ?array
|
||||
{
|
||||
return $this->data['safe_mode_extensions'];
|
||||
}
|
||||
|
||||
private function requireKeys(mixed ...$keys): void
|
||||
|
@ -29,7 +29,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
*/
|
||||
class ViewFormatter implements HttpFormatter
|
||||
{
|
||||
const ERRORS_WITH_VIEWS = ['csrf_token_mismatch', 'not_found'];
|
||||
const ERRORS_WITH_VIEWS = ['csrf_token_mismatch', 'not_found', 'maintenance'];
|
||||
|
||||
public function __construct(
|
||||
protected ViewFactory $view,
|
||||
|
@ -24,8 +24,17 @@ use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
*/
|
||||
class WhoopsFormatter implements HttpFormatter
|
||||
{
|
||||
public function __construct(
|
||||
protected ViewFormatter $viewFormatter
|
||||
) {
|
||||
}
|
||||
|
||||
public function format(HandledError $error, Request $request): Response
|
||||
{
|
||||
if (! $error->shouldBeReported()) {
|
||||
return $this->viewFormatter->format($error, $request);
|
||||
}
|
||||
|
||||
return WhoopsRunner::handle($error->getException(), $request)
|
||||
->withStatus($error->getStatusCode());
|
||||
}
|
||||
|
@ -44,6 +44,9 @@ class ErrorServiceProvider extends AbstractServiceProvider
|
||||
|
||||
// 429 Too Many Requests
|
||||
'too_many_requests' => 429,
|
||||
|
||||
// 503 Service Unavailable
|
||||
'maintenance' => 503,
|
||||
];
|
||||
});
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace Flarum\Foundation;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Laminas\Diactoros\Response\HtmlResponse;
|
||||
use Laminas\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
@ -17,7 +17,7 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
class MaintenanceModeHandler implements RequestHandlerInterface
|
||||
class HighMaintenanceModeHandler implements RequestHandlerInterface
|
||||
{
|
||||
const MESSAGE = 'Currently down for maintenance. Please come back later.';
|
||||
|
||||
@ -27,7 +27,7 @@ class MaintenanceModeHandler implements RequestHandlerInterface
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
// Special handling for API requests: they get a proper API response
|
||||
if ($this->isApiRequest($request)) {
|
||||
if (RequestUtil::isApiRequest($request)) {
|
||||
return $this->apiResponse();
|
||||
}
|
||||
|
||||
@ -35,14 +35,6 @@ class MaintenanceModeHandler implements RequestHandlerInterface
|
||||
return new HtmlResponse(self::MESSAGE, 503);
|
||||
}
|
||||
|
||||
private function isApiRequest(ServerRequestInterface $request): bool
|
||||
{
|
||||
return Str::contains(
|
||||
$request->getHeaderLine('Accept'),
|
||||
'application/vnd.api+json'
|
||||
);
|
||||
}
|
||||
|
||||
private function apiResponse(): ResponseInterface
|
||||
{
|
||||
return new JsonResponse(
|
@ -35,7 +35,7 @@ class InstalledApp implements AppInterface
|
||||
|
||||
public function getRequestHandler(): RequestHandlerInterface
|
||||
{
|
||||
if ($this->config->inMaintenanceMode()) {
|
||||
if ($this->config->inHighMaintenanceMode()) {
|
||||
return $this->container->make('flarum.maintenance.handler');
|
||||
} elseif ($this->needsUpdate()) {
|
||||
return $this->getUpdaterHandler();
|
||||
|
@ -96,7 +96,7 @@ class InstalledSite implements SiteInterface
|
||||
$app->alias('flarum.config', Config::class);
|
||||
$app->instance('flarum.debug', $this->config->inDebugMode());
|
||||
$app->instance('config', $this->getIlluminateConfig());
|
||||
$app->instance('flarum.maintenance.handler', new MaintenanceModeHandler);
|
||||
$app->instance('flarum.maintenance.handler', new HighMaintenanceModeHandler);
|
||||
|
||||
$this->registerLogger($app);
|
||||
$this->registerCache($app);
|
||||
|
79
framework/core/src/Foundation/MaintenanceMode.php
Normal file
79
framework/core/src/Foundation/MaintenanceMode.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?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\Settings\SettingsRepositoryInterface;
|
||||
|
||||
class MaintenanceMode
|
||||
{
|
||||
public const NONE = 'none';
|
||||
public const HIGH = 'high';
|
||||
public const LOW = 'low';
|
||||
public const SAFE = 'safe';
|
||||
|
||||
public function __construct(
|
||||
protected readonly Config $config,
|
||||
protected readonly SettingsRepositoryInterface $settings
|
||||
) {
|
||||
}
|
||||
|
||||
public function inMaintenanceMode(): bool
|
||||
{
|
||||
return $this->inHighMaintenanceMode() || $this->inLowMaintenanceMode() || $this->inSafeMode();
|
||||
}
|
||||
|
||||
public function inHighMaintenanceMode(): bool
|
||||
{
|
||||
return $this->mode() === self::HIGH;
|
||||
}
|
||||
|
||||
public function inLowMaintenanceMode(): bool
|
||||
{
|
||||
return $this->mode() === self::LOW;
|
||||
}
|
||||
|
||||
public function inSafeMode(): bool
|
||||
{
|
||||
return $this->mode() === self::SAFE;
|
||||
}
|
||||
|
||||
public function mode(): string
|
||||
{
|
||||
$mode = $this->config->maintenanceMode();
|
||||
|
||||
if ($mode === self::NONE) {
|
||||
$mode = strval($this->settings->get('maintenance_mode', self::NONE));
|
||||
|
||||
// Cannot set high maintenance mode from the settings.
|
||||
if ($mode === self::HIGH) {
|
||||
$mode = self::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return $mode;
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function safeModeExtensions(): array
|
||||
{
|
||||
$extensions = $this->config->safeModeExtensions();
|
||||
|
||||
if ($extensions === null) {
|
||||
$extensions = json_decode($this->settings->get('safe_mode_extensions', '[]'), true);
|
||||
}
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
public function configOverride(): bool
|
||||
{
|
||||
return $this->config->maintenanceMode() !== self::NONE;
|
||||
}
|
||||
}
|
63
framework/core/src/Frontend/AssetManager.php
Normal file
63
framework/core/src/Frontend/AssetManager.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?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\Frontend;
|
||||
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class AssetManager
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected array $assets = [];
|
||||
|
||||
public function __construct(
|
||||
protected readonly Container $container,
|
||||
protected readonly LocaleManager $locales
|
||||
) {
|
||||
}
|
||||
|
||||
public function frontend(string $frontend): Assets
|
||||
{
|
||||
if (! in_array($frontend, $this->assets)) {
|
||||
throw new InvalidArgumentException("Unknown frontend: $frontend");
|
||||
}
|
||||
|
||||
return $this->container->make($this->assets[$frontend]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Assets>
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return array_map(fn (string $abstract) => $this->container->make($abstract), $this->assets);
|
||||
}
|
||||
|
||||
public function register(string $frontend, string $abstract): void
|
||||
{
|
||||
$this->assets[$frontend] = $abstract;
|
||||
}
|
||||
|
||||
public function flushJs(): void
|
||||
{
|
||||
foreach ($this->all() as $assets) {
|
||||
$assets->makeJs()->flush();
|
||||
|
||||
foreach ($this->locales->getLocales() as $locale => $name) {
|
||||
$assets->makeLocaleJs($locale)->flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
|
||||
namespace Flarum\Frontend\Content;
|
||||
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
@ -17,7 +18,8 @@ use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
class CorePayload
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LocaleManager $locales
|
||||
private readonly LocaleManager $locales,
|
||||
private readonly MaintenanceMode $maintenance,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -33,15 +35,21 @@ class CorePayload
|
||||
{
|
||||
$data = $this->getDataFromApiDocument($document->getForumApiDocument());
|
||||
|
||||
return [
|
||||
$payload = [
|
||||
'resources' => $data,
|
||||
'session' => [
|
||||
'userId' => RequestUtil::getActor($request)->id,
|
||||
'csrfToken' => $request->getAttribute('session')->token()
|
||||
],
|
||||
'locales' => $this->locales->getLocales(),
|
||||
'locale' => $request->getAttribute('locale')
|
||||
'locale' => $request->getAttribute('locale'),
|
||||
];
|
||||
|
||||
if ($this->maintenance->inMaintenanceMode()) {
|
||||
$payload['maintenanceMode'] = $this->maintenance->mode();
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
private function getDataFromApiDocument(array $apiDocument): array
|
||||
|
@ -29,6 +29,10 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->container->singleton('flarum.assets', function (Container $container) {
|
||||
return new AssetManager($container, $container->make(LocaleManager::class));
|
||||
});
|
||||
|
||||
$this->container->singleton('flarum.assets.factory', function (Container $container) {
|
||||
return function (string $name) use ($container) {
|
||||
$paths = $container[Paths::class];
|
||||
@ -180,6 +184,10 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
||||
|
||||
return $assets;
|
||||
});
|
||||
|
||||
$this->container->afterResolving(AssetManager::class, function (AssetManager $assets) {
|
||||
$assets->register('common', 'flarum.assets.common');
|
||||
});
|
||||
}
|
||||
|
||||
public function boot(Container $container, Dispatcher $events, ViewFactory $views): void
|
||||
|
@ -0,0 +1,21 @@
|
||||
<?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\Http\Exception;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\KnownError;
|
||||
|
||||
class MaintenanceModeException extends Exception implements KnownError
|
||||
{
|
||||
public function getType(): string
|
||||
{
|
||||
return 'maintenance';
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?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\Http\Middleware;
|
||||
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Http\Exception\MaintenanceModeException;
|
||||
use Flarum\Http\RequestUtil;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class CheckForMaintenanceMode implements MiddlewareInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MaintenanceMode $maintenance,
|
||||
private readonly array $exemptRoutes,
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$actor = RequestUtil::getActor($request);
|
||||
$isRouteExcluded = in_array($request->getAttribute('routeName'), $this->exemptRoutes, true);
|
||||
|
||||
if ($this->maintenance->inMaintenanceMode() && ! $actor->isAdmin() && ! $isRouteExcluded) {
|
||||
throw new MaintenanceModeException('The forum is currently in maintenance mode.');
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
@ -10,10 +10,27 @@
|
||||
namespace Flarum\Http;
|
||||
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Support\Str;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class RequestUtil
|
||||
{
|
||||
public static function isApiRequest(Request $request): bool
|
||||
{
|
||||
return Str::contains(
|
||||
$request->getHeaderLine('Accept'),
|
||||
'application/vnd.api+json'
|
||||
);
|
||||
}
|
||||
|
||||
public static function isHtmlRequest(Request $request): bool
|
||||
{
|
||||
return Str::contains(
|
||||
$request->getHeaderLine('Accept'),
|
||||
'text/html'
|
||||
);
|
||||
}
|
||||
|
||||
public static function getActor(Request $request): User
|
||||
{
|
||||
return $request->getAttribute('actorReference')->getActor();
|
||||
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Queue\Console;
|
||||
|
||||
use Flarum\Foundation\Config;
|
||||
|
||||
class WorkCommand extends \Illuminate\Queue\Console\WorkCommand
|
||||
{
|
||||
protected function downForMaintenance()
|
||||
{
|
||||
if ($this->option('force')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->laravel->make(Config::class);
|
||||
|
||||
return $config->inMaintenanceMode();
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ class QueueServiceProvider extends AbstractServiceProvider
|
||||
Commands\ListFailedCommand::class,
|
||||
Commands\RestartCommand::class,
|
||||
Commands\RetryCommand::class,
|
||||
Console\WorkCommand::class,
|
||||
Commands\WorkCommand::class,
|
||||
];
|
||||
|
||||
public function register(): void
|
||||
@ -74,7 +74,7 @@ class QueueServiceProvider extends AbstractServiceProvider
|
||||
$container['events'],
|
||||
$container[ExceptionHandling::class],
|
||||
function () use ($config) {
|
||||
return $config->inMaintenanceMode();
|
||||
return $config->inHighMaintenanceMode();
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -22,8 +22,6 @@ use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
class AvatarValidator extends AbstractValidator
|
||||
{
|
||||
protected Validator $laravelValidator;
|
||||
|
||||
public function __construct(
|
||||
Factory $validator,
|
||||
TranslatorInterface $translator,
|
||||
|
@ -10,6 +10,7 @@
|
||||
namespace Flarum\Tests\unit\Foundation;
|
||||
|
||||
use Flarum\Foundation\Config;
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Testing\unit\TestCase;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
@ -73,14 +74,32 @@ class ConfigTest extends TestCase
|
||||
'offline' => false,
|
||||
]);
|
||||
|
||||
$this->assertFalse($config->inMaintenanceMode());
|
||||
$this->assertFalse($config->inHighMaintenanceMode());
|
||||
|
||||
$config = new Config([
|
||||
'url' => 'https://flarum.localhost',
|
||||
'offline' => true,
|
||||
]);
|
||||
|
||||
$this->assertTrue($config->inMaintenanceMode());
|
||||
$this->assertTrue($config->inHighMaintenanceMode());
|
||||
|
||||
$config = new Config([
|
||||
'url' => 'https://flarum.localhost',
|
||||
'offline' => MaintenanceMode::LOW,
|
||||
]);
|
||||
|
||||
$this->assertFalse($config->inSafeMode());
|
||||
$this->assertTrue($config->inLowMaintenanceMode());
|
||||
$this->assertFalse($config->inHighMaintenanceMode());
|
||||
|
||||
$config = new Config([
|
||||
'url' => 'https://flarum.localhost',
|
||||
'offline' => MaintenanceMode::SAFE,
|
||||
]);
|
||||
|
||||
$this->assertTrue($config->inSafeMode());
|
||||
$this->assertFalse($config->inLowMaintenanceMode());
|
||||
$this->assertFalse($config->inHighMaintenanceMode());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
@ -90,7 +109,7 @@ class ConfigTest extends TestCase
|
||||
'url' => 'https://flarum.localhost',
|
||||
]);
|
||||
|
||||
$this->assertFalse($config->inMaintenanceMode());
|
||||
$this->assertFalse($config->inHighMaintenanceMode());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
|
14
framework/core/views/error/maintenance.blade.php
Normal file
14
framework/core/views/error/maintenance.blade.php
Normal file
@ -0,0 +1,14 @@
|
||||
@extends('flarum.forum::layouts.basic')
|
||||
|
||||
@section('title', $translator->trans('core.views.error.maintenance_mode_title'))
|
||||
|
||||
@section('content')
|
||||
<p>
|
||||
{{ $translator->trans('core.views.error.maintenance_mode_message') }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ $url->to('forum')->route('maintenance.login') }}">
|
||||
{{ $translator->trans('core.views.error.maintenance_mode_link') }}
|
||||
</a>
|
||||
</p>
|
||||
@endsection
|
38
framework/core/views/log-in.blade.php
Normal file
38
framework/core/views/log-in.blade.php
Normal file
@ -0,0 +1,38 @@
|
||||
@extends('flarum.forum::layouts.basic')
|
||||
|
||||
@section('title', $translator->trans('core.views.log_in.title'))
|
||||
|
||||
@section('content')
|
||||
@if ($errors->any())
|
||||
<div class="errors">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form class="form" method="POST" action="{{ $url->to('forum')->route('login') }}">
|
||||
<input type="hidden" name="csrfToken" value="{{ $csrfToken }}">
|
||||
|
||||
<p class="form-group">
|
||||
<input type="text" class="form-control" name="identification" placeholder="{{ $translator->trans('core.views.log_in.username_or_email_placeholder') }}" aria-label="{{ $translator->trans('core.views.log_in.username_or_email_placeholder') }}">
|
||||
</p>
|
||||
|
||||
<p class="form-group">
|
||||
<input type="password" class="form-control" name="password" autocomplete="current-password" placeholder="{{ $translator->trans('core.views.log_in.password_placeholder') }}" aria-label="{{ $translator->trans('core.views.log_in.password_placeholder') }}">
|
||||
</p>
|
||||
|
||||
<p class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="remember" value="1" tabindex="1">
|
||||
<span>{{ $translator->trans('core.views.log_in.remember_me_label') }}</span>
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p class="form-group">
|
||||
<button type="submit" class="button">{{ $translator->trans('core.views.log_in.submit_button') }}</button>
|
||||
</p>
|
||||
</form>
|
||||
@endsection
|
@ -12,6 +12,7 @@ namespace Flarum\Testing\integration\Extension;
|
||||
use Flarum\Database\Migrator;
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Foundation\MaintenanceMode;
|
||||
use Flarum\Foundation\Paths;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
@ -42,9 +43,10 @@ class ExtensionManagerIncludeCurrent extends ExtensionManager
|
||||
Migrator $migrator,
|
||||
Dispatcher $dispatcher,
|
||||
Filesystem $filesystem,
|
||||
MaintenanceMode $maintenance,
|
||||
array $enabledIds
|
||||
) {
|
||||
parent::__construct($config, $paths, $container, $migrator, $dispatcher, $filesystem);
|
||||
parent::__construct($config, $paths, $container, $migrator, $dispatcher, $filesystem, $maintenance);
|
||||
|
||||
$this->enabledIds = $enabledIds;
|
||||
}
|
||||
|
Reference in New Issue
Block a user