mirror of
https://github.com/flarum/core.git
synced 2025-08-16 21:34:08 +02:00
Compare commits
69 Commits
fl/fronten
...
v0.1.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
bbe62f400f | ||
|
fc5977f86f | ||
|
796b57753d | ||
|
88e43cc694 | ||
|
6370f7ecff | ||
|
d9d7027ed0 | ||
|
1106a2e3a3 | ||
|
b7f666525c | ||
|
40dc6ac604 | ||
|
15e1a154e5 | ||
|
57d5846b64 | ||
|
fb6b51b1cf | ||
|
57f73c9638 | ||
|
7705a2b7d7 | ||
|
f591585d02 | ||
|
45afc33eb0 | ||
|
213fd62be3 | ||
|
66607a5674 | ||
|
546b4f01ac | ||
|
96e282458b | ||
|
24ff8899a0 | ||
|
1b32c7cc51 | ||
|
6c2a4a5ff7 | ||
|
2d31a6f72e | ||
|
9115b9e28f | ||
|
3bff2e0f5c | ||
|
edaca3160e | ||
|
9e63f32105 | ||
|
f3a5a89e12 | ||
|
b074f47298 | ||
|
2ef66ac716 | ||
|
6654894da1 | ||
|
41544c8529 | ||
|
53d1b87daf | ||
|
c11e4720d9 | ||
|
c73d03a427 | ||
|
d0d6c52839 | ||
|
9585d448d2 | ||
|
04d46b9925 | ||
|
c3b2d8e7d8 | ||
|
62a40036d0 | ||
|
2764ad87cc | ||
|
2c1be86857 | ||
|
b26eb8e609 | ||
|
1f0bf33cfb | ||
|
6fadc0b653 | ||
|
0255393108 | ||
|
b474843cc2 | ||
|
7e95b80341 | ||
|
18b90d16e3 | ||
|
64a7e8ac3a | ||
|
e8b8cd0078 | ||
|
3c8262ccde | ||
|
68c6638fb5 | ||
|
105dd093fe | ||
|
920802e5ae | ||
|
882c4aa105 | ||
|
b826f9ce36 | ||
|
c13dfa2228 | ||
|
bf3934d16f | ||
|
a08068b112 | ||
|
60149fbe15 | ||
|
13c593cbaa | ||
|
f7a320bcca | ||
|
0f9118fe2b | ||
|
95f0edcd80 | ||
|
222e3c3fe2 | ||
|
903c1e329d | ||
|
295a007cd5 |
3
.github/CONTRIBUTING.md
vendored
3
.github/CONTRIBUTING.md
vendored
@@ -1,3 +0,0 @@
|
|||||||
# Contributing to Flarum
|
|
||||||
|
|
||||||
Howdy! We're really excited that you are interested in contributing to Flarum. Before submitting your contribution, please take a moment and read through the [Contributing Guidelines](https://github.com/flarum/flarum/blob/master/CONTRIBUTING.md).
|
|
@@ -1,7 +1,5 @@
|
|||||||
language: php
|
language: php
|
||||||
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.composer/cache
|
- $HOME/.composer/cache
|
||||||
|
13
CHANGELOG.md
Normal file
13
CHANGELOG.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [0.1.0-beta.8.1](https://github.com/flarum/core/compare/v0.1.0-beta.8...v0.1.0-beta.8.1)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix live output in `migrate:reset` command ([f591585](https://github.com/flarum/core/commit/f591585d02f8c4ff0211c5bf4413dd6baa724c05))
|
||||||
|
- Fix search with database prefix ([7705a2b](https://github.com/flarum/core/commit/7705a2b7d751943ef9d0c7379ec34f8530b99310))
|
||||||
|
- Fix invalid join time of admin user created by installer ([57f73c9](https://github.com/flarum/core/commit/57f73c9638eeb825f9e336ed3c443afccfd8995e))
|
||||||
|
- Ensure InnoDB engine is used for all tables ([fb6b51b](https://github.com/flarum/core/commit/fb6b51b1cfef0af399607fe038603c8240800b2b))
|
||||||
|
- Fix dropping foreign keys in `down` migrations ([57d5846](https://github.com/flarum/core/commit/57d5846b647881009d9e60f9ffca20b1bb77776e))
|
||||||
|
- Fix discussion list scroll position not being maintained when hero is not visible ([40dc6ac](https://github.com/flarum/core/commit/40dc6ac604c2a0973356b38217aa8d09352daae5))
|
||||||
|
- Fix empty meta description tag ([88e43cc](https://github.com/flarum/core/commit/88e43cc6940ee30d6529e9ce659471ec4fb1c474))
|
||||||
|
- Remove empty attributes on `<html>` tag ([796b577](https://github.com/flarum/core/commit/796b57753d34d4ea741dbebcbc550b17808f6c94))
|
3
CONTRIBUTING.md
Normal file
3
CONTRIBUTING.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Contributing to Flarum
|
||||||
|
|
||||||
|
Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://flarum.org/docs/contributing.html)** to learn how you can help.
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2018 Toby Zerner
|
Copyright (c) Toby Zerner
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
34
README.md
34
README.md
@@ -1,11 +1,35 @@
|
|||||||
# Flarum Core
|
<p align="center"><img src="https://flarum.org/img/logo.png"></p>
|
||||||
|
|
||||||
This repository contains Flarum's core code. If you want to set up a forum, visit the [main Flarum repository](https://github.com/flarum/flarum).
|
<p align="center">
|
||||||
|
<a href="https://travis-ci.org/flarum/core"><img src="https://travis-ci.org/flarum/core.svg" alt="Build Status"></a>
|
||||||
|
<a href="https://packagist.org/packages/flarum/core"><img src="https://poser.pugx.org/flarum/core/d/total.svg" alt="Total Downloads"></a>
|
||||||
|
<a href="https://packagist.org/packages/flarum/core"><img src="https://poser.pugx.org/flarum/core/v/stable.svg" alt="Latest Stable Version"></a>
|
||||||
|
<a href="https://packagist.org/packages/flarum/core"><img src="https://poser.pugx.org/flarum/core/license.svg" alt="License"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## About Flarum
|
||||||
|
|
||||||
|
**[Flarum](https://flarum.org/) is a delightfully simple discussion platform for your website.** It's fast and easy to use, with all the features you need to run a successful community. It is designed to be:
|
||||||
|
|
||||||
|
* **Fast and simple.** No clutter, no bloat, no complex dependencies. Flarum is built with PHP so it’s quick and easy to deploy. The interface is powered by Mithril, a performant JavaScript framework with a tiny footprint.
|
||||||
|
|
||||||
|
* **Beautiful and responsive.** This is forum software for humans. Flarum is carefully designed to be consistent and intuitive across platforms, out-of-the-box.
|
||||||
|
|
||||||
|
* **Powerful and extensible.** Customize, extend, and integrate Flarum to suit your community. Flarum’s architecture is amazingly flexible, with a powerful Extension API.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This repository contains Flarum's core code. If you want to set up a forum, visit the [Flarum skeleton repository](https://github.com/flarum/flarum).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Flarum is open-source and we would love your help building it! Please read the [Contributing Guide](https://github.com/flarum/flarum/blob/master/CONTRIBUTING.md) to learn how you can help.
|
Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://flarum.org/docs/contributing.html)** to learn how you can help.
|
||||||
|
|
||||||
### Security Vulnerabilities
|
## Security Vulnerabilities
|
||||||
|
|
||||||
|
If you discover a security vulnerability within Flarum, please send an e-mail to [security@flarum.org](mailto:security@flarum.org). All security vulnerabilities will be promptly addressed.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Flarum is open-source software licensed under the [MIT License](https://github.com/flarum/flarum/blob/master/LICENSE).
|
||||||
|
|
||||||
If you discover a security vulnerability within Flarum, please send an email to [security@flarum.org](mailto:security@flarum.org).
|
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.1",
|
"php": ">=7.1",
|
||||||
"axy/sourcemap": "^0.1.4",
|
"axy/sourcemap": "^0.1.4",
|
||||||
"components/font-awesome": "^5.0.6",
|
"components/font-awesome": "^5.4.2",
|
||||||
"dflydev/fig-cookies": "^1.0.2",
|
"dflydev/fig-cookies": "^1.0.2",
|
||||||
"doctrine/dbal": "^2.7",
|
"doctrine/dbal": "^2.7",
|
||||||
"franzl/whoops-middleware": "^0.4.0",
|
"franzl/whoops-middleware": "^0.4.0",
|
||||||
|
6
js/dist/admin.js
vendored
6
js/dist/admin.js
vendored
File diff suppressed because one or more lines are too long
2
js/dist/admin.js.map
vendored
2
js/dist/admin.js.map
vendored
File diff suppressed because one or more lines are too long
8
js/dist/forum.js
vendored
8
js/dist/forum.js
vendored
File diff suppressed because one or more lines are too long
2
js/dist/forum.js.map
vendored
2
js/dist/forum.js.map
vendored
File diff suppressed because one or more lines are too long
4991
js/package-lock.json
generated
4991
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,26 @@
|
|||||||
{
|
{
|
||||||
|
"private": true,
|
||||||
"name": "@flarum/core",
|
"name": "@flarum/core",
|
||||||
"version": "0.0.0",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^3.3.7",
|
"bootstrap": "^3.3.7",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"color-thief-browser": "^2.0.2",
|
"color-thief-browser": "^2.0.2",
|
||||||
"expose-loader": "^0.7.5",
|
"expose-loader": "^0.7.5",
|
||||||
"flarum-webpack-config": "^0.1.0-beta.9",
|
"flarum-webpack-config": "0.1.0-beta.10",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"jquery.hotkeys": "^0.1.0",
|
"jquery.hotkeys": "^0.1.0",
|
||||||
"lodash-es": "^4.17.10",
|
"lodash-es": "^4.17.11",
|
||||||
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
|
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
|
||||||
"mithril": "^0.2.8",
|
"mithril": "^0.2.8",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"punycode": "^2.1.1",
|
"punycode": "^2.1.1",
|
||||||
"spin.js": "^3.1.0",
|
"spin.js": "^3.1.0",
|
||||||
"webpack": "^4.17.1",
|
"webpack": "^4.26.0",
|
||||||
"webpack-cli": "^2.1.5",
|
"webpack-cli": "^3.1.2",
|
||||||
"webpack-merge": "^4.1.4"
|
"webpack-merge": "^4.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode production",
|
"dev": "webpack --mode development --watch",
|
||||||
"watch": "webpack --mode development --watch"
|
"build": "webpack --mode production"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,9 +8,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import DashboardWidget from './DashboardWidget';
|
import DashboardWidget from './DashboardWidget';
|
||||||
import icon from '../../common/helpers/icon';
|
|
||||||
import listItems from '../../common/helpers/listItems';
|
import listItems from '../../common/helpers/listItems';
|
||||||
import ItemList from '../../common/utils/ItemList';
|
import ItemList from '../../common/utils/ItemList';
|
||||||
|
import Dropdown from '../../common/components/Dropdown';
|
||||||
|
import Button from '../../common/components/Button';
|
||||||
|
import LoadingModal from './LoadingModal';
|
||||||
|
|
||||||
export default class StatusWidget extends DashboardWidget {
|
export default class StatusWidget extends DashboardWidget {
|
||||||
className() {
|
className() {
|
||||||
@@ -26,10 +28,16 @@ export default class StatusWidget extends DashboardWidget {
|
|||||||
items() {
|
items() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
items.add('help', (
|
items.add('tools', (
|
||||||
<a href="http://flarum.org/docs/troubleshooting" target="_blank">
|
<Dropdown
|
||||||
{icon('fas fa-question-circle')} {app.translator.trans('core.admin.dashboard.help_link')}
|
label={app.translator.trans('core.admin.dashboard.tools_button')}
|
||||||
</a>
|
icon="fas fa-cog"
|
||||||
|
buttonClassName="Button"
|
||||||
|
menuClassName="Dropdown-menu--right">
|
||||||
|
<Button onclick={this.handleClearCache.bind(this)}>
|
||||||
|
{app.translator.trans('core.admin.dashboard.clear_cache_button')}
|
||||||
|
</Button>
|
||||||
|
</Dropdown>
|
||||||
));
|
));
|
||||||
|
|
||||||
items.add('version-flarum', [<strong>Flarum</strong>, <br/>, app.forum.attribute('version')]);
|
items.add('version-flarum', [<strong>Flarum</strong>, <br/>, app.forum.attribute('version')]);
|
||||||
@@ -38,4 +46,13 @@ export default class StatusWidget extends DashboardWidget {
|
|||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleClearCache(e) {
|
||||||
|
app.modal.show(new LoadingModal());
|
||||||
|
|
||||||
|
app.request({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: app.forum.attribute('apiUrl') + '/cache'
|
||||||
|
}).then(() => window.location.reload());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@ import ComposerButton from './components/ComposerButton';
|
|||||||
import DiscussionList from './components/DiscussionList';
|
import DiscussionList from './components/DiscussionList';
|
||||||
import ReplyPlaceholder from './components/ReplyPlaceholder';
|
import ReplyPlaceholder from './components/ReplyPlaceholder';
|
||||||
import TextEditor from './components/TextEditor';
|
import TextEditor from './components/TextEditor';
|
||||||
|
import TextEditorButton from './components/TextEditorButton';
|
||||||
import AvatarEditor from './components/AvatarEditor';
|
import AvatarEditor from './components/AvatarEditor';
|
||||||
import Post from './components/Post';
|
import Post from './components/Post';
|
||||||
import SettingsPage from './components/SettingsPage';
|
import SettingsPage from './components/SettingsPage';
|
||||||
@@ -99,6 +100,7 @@ export default Object.assign(compat, {
|
|||||||
'components/DiscussionList': DiscussionList,
|
'components/DiscussionList': DiscussionList,
|
||||||
'components/ReplyPlaceholder': ReplyPlaceholder,
|
'components/ReplyPlaceholder': ReplyPlaceholder,
|
||||||
'components/TextEditor': TextEditor,
|
'components/TextEditor': TextEditor,
|
||||||
|
'components/TextEditorButton': TextEditorButton,
|
||||||
'components/AvatarEditor': AvatarEditor,
|
'components/AvatarEditor': AvatarEditor,
|
||||||
'components/Post': Post,
|
'components/Post': Post,
|
||||||
'components/SettingsPage': SettingsPage,
|
'components/SettingsPage': SettingsPage,
|
||||||
|
@@ -3,6 +3,7 @@ import Button from '../../common/components/Button';
|
|||||||
import GroupBadge from '../../common/components/GroupBadge';
|
import GroupBadge from '../../common/components/GroupBadge';
|
||||||
import Group from '../../common/models/Group';
|
import Group from '../../common/models/Group';
|
||||||
import extractText from '../../common/utils/extractText';
|
import extractText from '../../common/utils/extractText';
|
||||||
|
import ItemList from '../../common/utils/ItemList';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `EditUserModal` component displays a modal dialog with a login form.
|
* The `EditUserModal` component displays a modal dialog with a login form.
|
||||||
@@ -37,80 +38,91 @@ export default class EditUserModal extends Modal {
|
|||||||
return (
|
return (
|
||||||
<div className="Modal-body">
|
<div className="Modal-body">
|
||||||
<div className="Form">
|
<div className="Form">
|
||||||
<div className="Form-group">
|
{this.fields().toArray()}
|
||||||
<label>{app.translator.trans('core.forum.edit_user.username_heading')}</label>
|
|
||||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.username_label'))}
|
|
||||||
bidi={this.username} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{app.session.user !== this.props.user ? [
|
|
||||||
<div className="Form-group">
|
|
||||||
<label>{app.translator.trans('core.forum.edit_user.email_heading')}</label>
|
|
||||||
<div>
|
|
||||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))}
|
|
||||||
bidi={this.email} />
|
|
||||||
</div>
|
|
||||||
{!this.isEmailConfirmed() ? (
|
|
||||||
<div>
|
|
||||||
{Button.component({
|
|
||||||
className: 'Button Button--block',
|
|
||||||
children: app.translator.trans('core.forum.edit_user.activate_button'),
|
|
||||||
loading: this.loading,
|
|
||||||
onclick: this.activate.bind(this)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : ''}
|
|
||||||
</div>,
|
|
||||||
|
|
||||||
<div className="Form-group">
|
|
||||||
<label>{app.translator.trans('core.forum.edit_user.password_heading')}</label>
|
|
||||||
<div>
|
|
||||||
<label className="checkbox">
|
|
||||||
<input type="checkbox" checked={this.setPassword()} onchange={e => {
|
|
||||||
this.setPassword(e.target.checked);
|
|
||||||
m.redraw(true);
|
|
||||||
if (e.target.checked) this.$('[name=password]').select();
|
|
||||||
m.redraw.strategy('none');
|
|
||||||
}}/>
|
|
||||||
{app.translator.trans('core.forum.edit_user.set_password_label')}
|
|
||||||
</label>
|
|
||||||
{this.setPassword() ? (
|
|
||||||
<input className="FormControl" type="password" name="password" placeholder={extractText(app.translator.trans('core.forum.edit_user.password_label'))}
|
|
||||||
bidi={this.password} />
|
|
||||||
) : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
] : ''}
|
|
||||||
|
|
||||||
<div className="Form-group EditUserModal-groups">
|
|
||||||
<label>{app.translator.trans('core.forum.edit_user.groups_heading')}</label>
|
|
||||||
<div>
|
|
||||||
{Object.keys(this.groups)
|
|
||||||
.map(id => app.store.getById('groups', id))
|
|
||||||
.map(group => (
|
|
||||||
<label className="checkbox">
|
|
||||||
<input type="checkbox"
|
|
||||||
bidi={this.groups[group.id()]}
|
|
||||||
disabled={this.props.user.id() === '1' && group.id() === Group.ADMINISTRATOR_ID} />
|
|
||||||
{GroupBadge.component({group, label: ''})} {group.nameSingular()}
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="Form-group">
|
|
||||||
{Button.component({
|
|
||||||
className: 'Button Button--primary',
|
|
||||||
type: 'submit',
|
|
||||||
loading: this.loading,
|
|
||||||
children: app.translator.trans('core.forum.edit_user.submit_button')
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fields() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('username', <div className="Form-group">
|
||||||
|
<label>{app.translator.trans('core.forum.edit_user.username_heading')}</label>
|
||||||
|
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.username_label'))}
|
||||||
|
bidi={this.username} />
|
||||||
|
</div>, 40);
|
||||||
|
|
||||||
|
if (app.session.user !== this.props.user) {
|
||||||
|
items.add('email', <div className="Form-group">
|
||||||
|
<label>{app.translator.trans('core.forum.edit_user.email_heading')}</label>
|
||||||
|
<div>
|
||||||
|
<input className="FormControl"
|
||||||
|
placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))}
|
||||||
|
bidi={this.email}/>
|
||||||
|
</div>
|
||||||
|
{!this.isEmailConfirmed() ? (
|
||||||
|
<div>
|
||||||
|
{Button.component({
|
||||||
|
className: 'Button Button--block',
|
||||||
|
children: app.translator.trans('core.forum.edit_user.activate_button'),
|
||||||
|
loading: this.loading,
|
||||||
|
onclick: this.activate.bind(this)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : ''}
|
||||||
|
</div>, 30);
|
||||||
|
|
||||||
|
items.add('password', <div className="Form-group">
|
||||||
|
<label>{app.translator.trans('core.forum.edit_user.password_heading')}</label>
|
||||||
|
<div>
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="checkbox" checked={this.setPassword()} onChange={e => {
|
||||||
|
this.setPassword(e.target.checked);
|
||||||
|
m.redraw(true);
|
||||||
|
if (e.target.checked) this.$('[name=password]').select();
|
||||||
|
m.redraw.strategy('none');
|
||||||
|
}}/>
|
||||||
|
{app.translator.trans('core.forum.edit_user.set_password_label')}
|
||||||
|
</label>
|
||||||
|
{this.setPassword() ? (
|
||||||
|
<input className="FormControl" type="password" name="password"
|
||||||
|
placeholder={extractText(app.translator.trans('core.forum.edit_user.password_label'))}
|
||||||
|
bidi={this.password}/>
|
||||||
|
) : ''}
|
||||||
|
</div>
|
||||||
|
</div>, 20);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
items.add('groups', <div className="Form-group EditUserModal-groups">
|
||||||
|
<label>{app.translator.trans('core.forum.edit_user.groups_heading')}</label>
|
||||||
|
<div>
|
||||||
|
{Object.keys(this.groups)
|
||||||
|
.map(id => app.store.getById('groups', id))
|
||||||
|
.map(group => (
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
bidi={this.groups[group.id()]}
|
||||||
|
disabled={this.props.user.id() === '1' && group.id() === Group.ADMINISTRATOR_ID} />
|
||||||
|
{GroupBadge.component({group, label: ''})} {group.nameSingular()}
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>, 10);
|
||||||
|
|
||||||
|
items.add('submit', <div className="Form-group">
|
||||||
|
{Button.component({
|
||||||
|
className: 'Button Button--primary',
|
||||||
|
type: 'submit',
|
||||||
|
loading: this.loading,
|
||||||
|
children: app.translator.trans('core.forum.edit_user.submit_button')
|
||||||
|
})}
|
||||||
|
</div>, -10);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
activate() {
|
activate() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const data = {
|
const data = {
|
||||||
|
@@ -102,7 +102,7 @@ export default class IndexPage extends Page {
|
|||||||
// previous hero. Maintain the same scroll position relative to the bottom
|
// previous hero. Maintain the same scroll position relative to the bottom
|
||||||
// of the hero so that the sidebar doesn't jump around.
|
// of the hero so that the sidebar doesn't jump around.
|
||||||
const oldHeroHeight = app.cache.heroHeight;
|
const oldHeroHeight = app.cache.heroHeight;
|
||||||
const heroHeight = app.cache.heroHeight = this.$('.Hero').outerHeight();
|
const heroHeight = app.cache.heroHeight = this.$('.Hero').outerHeight() || 0;
|
||||||
const scrollTop = app.cache.scrollTop;
|
const scrollTop = app.cache.scrollTop;
|
||||||
|
|
||||||
$('#app').css('min-height', $(window).height() + heroHeight);
|
$('#app').css('min-height', $(window).height() + heroHeight);
|
||||||
|
@@ -99,7 +99,7 @@ export default class PostStreamScrubber extends Component {
|
|||||||
<div className="Scrubber-bar"/>
|
<div className="Scrubber-bar"/>
|
||||||
<div className="Scrubber-info">
|
<div className="Scrubber-info">
|
||||||
<strong>{viewing}</strong>
|
<strong>{viewing}</strong>
|
||||||
<span class="Scrubber-description">{retain || this.description}</span>
|
<span className="Scrubber-description">{retain || this.description}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="Scrubber-after"/>
|
<div className="Scrubber-after"/>
|
||||||
@@ -132,7 +132,7 @@ export default class PostStreamScrubber extends Component {
|
|||||||
*/
|
*/
|
||||||
goToLast() {
|
goToLast() {
|
||||||
this.props.stream.goToLast();
|
this.props.stream.goToLast();
|
||||||
this.index = this.props.stream.count();
|
this.index = this.count();
|
||||||
this.renderScrollbar(true);
|
this.renderScrollbar(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +190,6 @@ export default class PostStreamScrubber extends Component {
|
|||||||
const marginTop = stream.getMarginTop();
|
const marginTop = stream.getMarginTop();
|
||||||
const viewportTop = scrollTop + marginTop;
|
const viewportTop = scrollTop + marginTop;
|
||||||
const viewportHeight = $(window).height() - marginTop;
|
const viewportHeight = $(window).height() - marginTop;
|
||||||
const viewportBottom = viewportTop + viewportHeight;
|
|
||||||
|
|
||||||
// Before looping through all of the posts, we reset the scrollbar
|
// Before looping through all of the posts, we reset the scrollbar
|
||||||
// properties to a 'default' state. These values reflect what would be
|
// properties to a 'default' state. These values reflect what would be
|
||||||
@@ -315,7 +314,7 @@ export default class PostStreamScrubber extends Component {
|
|||||||
const visible = this.visible || 1;
|
const visible = this.visible || 1;
|
||||||
|
|
||||||
const $scrubber = this.$();
|
const $scrubber = this.$();
|
||||||
$scrubber.find('.Scrubber-index').text(formatNumber(Math.ceil(index + visible)));
|
$scrubber.find('.Scrubber-index').text(formatNumber(Math.min(Math.ceil(index + visible), count)));
|
||||||
$scrubber.find('.Scrubber-description').text(this.description);
|
$scrubber.find('.Scrubber-description').text(this.description);
|
||||||
$scrubber.toggleClass('disabled', this.disabled());
|
$scrubber.toggleClass('disabled', this.disabled());
|
||||||
|
|
||||||
|
@@ -160,7 +160,7 @@ export default class Search extends Component {
|
|||||||
search.loadingSources++;
|
search.loadingSources++;
|
||||||
|
|
||||||
source.search(query).then(() => {
|
source.search(query).then(() => {
|
||||||
search.loadingSources--;
|
search.loadingSources = Math.max(0, search.loadingSources - 1);
|
||||||
m.redraw();
|
m.redraw();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -189,6 +189,9 @@ export default class Search extends Component {
|
|||||||
* Navigate to the currently selected search result and close the list.
|
* Navigate to the currently selected search result and close the list.
|
||||||
*/
|
*/
|
||||||
selectResult() {
|
selectResult() {
|
||||||
|
clearTimeout(this.searchTimeout);
|
||||||
|
this.loadingSources = 0;
|
||||||
|
|
||||||
if (this.value()) {
|
if (this.value()) {
|
||||||
m.route(this.getItem(this.index).find('a').attr('href'));
|
m.route(this.getItem(this.index).find('a').attr('href'));
|
||||||
} else {
|
} else {
|
||||||
|
@@ -36,6 +36,9 @@ export default class TextEditor extends Component {
|
|||||||
|
|
||||||
<ul className="TextEditor-controls Composer-footer">
|
<ul className="TextEditor-controls Composer-footer">
|
||||||
{listItems(this.controlItems().toArray())}
|
{listItems(this.controlItems().toArray())}
|
||||||
|
<li className="TextEditor-toolbar">
|
||||||
|
{this.toolbarItems().toArray()}
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -70,7 +73,7 @@ export default class TextEditor extends Component {
|
|||||||
items.add('submit',
|
items.add('submit',
|
||||||
Button.component({
|
Button.component({
|
||||||
children: this.props.submitLabel,
|
children: this.props.submitLabel,
|
||||||
icon: 'fas fa-check',
|
icon: 'fas fa-paper-plane',
|
||||||
className: 'Button Button--primary',
|
className: 'Button Button--primary',
|
||||||
itemClassName: 'App-primaryControl',
|
itemClassName: 'App-primaryControl',
|
||||||
onclick: this.onsubmit.bind(this)
|
onclick: this.onsubmit.bind(this)
|
||||||
@@ -80,10 +83,11 @@ export default class TextEditor extends Component {
|
|||||||
if (this.props.preview) {
|
if (this.props.preview) {
|
||||||
items.add('preview',
|
items.add('preview',
|
||||||
Button.component({
|
Button.component({
|
||||||
icon: 'fas fa-eye',
|
icon: 'far fa-eye',
|
||||||
className: 'Button Button--icon',
|
className: 'Button Button--icon',
|
||||||
onclick: this.props.preview,
|
onclick: this.props.preview,
|
||||||
title: app.translator.trans('core.forum.composer.preview_tooltip')
|
title: app.translator.trans('core.forum.composer.preview_tooltip'),
|
||||||
|
config: elm => $(elm).tooltip()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -91,6 +95,15 @@ export default class TextEditor extends Component {
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an item list for the toolbar controls.
|
||||||
|
*
|
||||||
|
* @return {ItemList}
|
||||||
|
*/
|
||||||
|
toolbarItems() {
|
||||||
|
return new ItemList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of the text editor.
|
* Set the value of the text editor.
|
||||||
*
|
*
|
||||||
@@ -141,6 +154,8 @@ export default class TextEditor extends Component {
|
|||||||
const pos = index + insert.length;
|
const pos = index + insert.length;
|
||||||
this.setSelectionRange(pos, pos);
|
this.setSelectionRange(pos, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textarea.dispatchEvent(new CustomEvent('input', {bubbles: true, cancelable: true}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
21
js/src/forum/components/TextEditorButton.js
Normal file
21
js/src/forum/components/TextEditorButton.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Button from '../../common/components/Button';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `TextEditorButton` component displays a button suitable for the text
|
||||||
|
* editor toolbar.
|
||||||
|
*/
|
||||||
|
export default class TextEditorButton extends Button {
|
||||||
|
static initProps(props) {
|
||||||
|
super.initProps(props);
|
||||||
|
|
||||||
|
props.className = props.className || 'Button Button--icon Button--link';
|
||||||
|
}
|
||||||
|
|
||||||
|
config(isInitialized, context) {
|
||||||
|
super.config(isInitialized, context);
|
||||||
|
|
||||||
|
if (isInitialized) return;
|
||||||
|
|
||||||
|
this.$().tooltip();
|
||||||
|
}
|
||||||
|
}
|
@@ -38,7 +38,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
&.item-help {
|
&.item-tools {
|
||||||
float: right;
|
float: right;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
@@ -56,6 +56,9 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
> .Button--icon {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.App-primaryControl {
|
.App-primaryControl {
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -71,12 +74,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.App-primaryControl, .App-backControl {
|
.App-primaryControl, .App-backControl {
|
||||||
|
margin: 0 !important;
|
||||||
|
|
||||||
> .Button {
|
> .Button {
|
||||||
color: @header-control-color !important;
|
color: @header-control-color !important;
|
||||||
|
|
||||||
.Button-icon {
|
.Button-icon {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
.Button-label {
|
.Button-label {
|
||||||
display: none;
|
display: none;
|
||||||
|
@@ -51,7 +51,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
background: @primary-color;
|
background: @primary-color;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 3px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
@import "print";
|
@import "print";
|
||||||
@import "scaffolding";
|
@import "scaffolding";
|
||||||
@import "sideNav";
|
@import "sideNav";
|
||||||
@import "mixins";
|
|
||||||
|
|
||||||
@import "App";
|
@import "App";
|
||||||
@import "Alert";
|
@import "Alert";
|
||||||
@@ -27,5 +26,3 @@
|
|||||||
@import "Search";
|
@import "Search";
|
||||||
@import "Select";
|
@import "Select";
|
||||||
@import "Tooltip";
|
@import "Tooltip";
|
||||||
|
|
||||||
@import "variables";
|
|
||||||
|
@@ -343,11 +343,19 @@
|
|||||||
}
|
}
|
||||||
.TextEditor-controls {
|
.TextEditor-controls {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 15px 0;
|
padding: 10px 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
& li {
|
li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.TextEditor-toolbar {
|
||||||
|
.Button--icon {
|
||||||
|
width: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,10 +365,6 @@
|
|||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
border-top: 1px solid @control-bg;
|
border-top: 1px solid @control-bg;
|
||||||
|
|
||||||
li {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullScreen & {
|
.fullScreen & {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
|
@@ -147,26 +147,38 @@
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 160%;
|
font-size: 225%;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 120%;
|
font-size: 175%;
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 100%;
|
font-size: 150%;
|
||||||
font-weight: bold;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
}
|
||||||
h4, h5, h6 {
|
h4 {
|
||||||
|
font-size: 125%;
|
||||||
|
}
|
||||||
|
h5, h6 {
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
font-weight: bold;
|
}
|
||||||
|
h6 {
|
||||||
|
color: @muted-more-color;
|
||||||
}
|
}
|
||||||
img, iframe {
|
img, iframe {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.Post-body, .Post-preview {
|
||||||
|
> *:first-child {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.Post--hidden {
|
.Post--hidden {
|
||||||
.Post-header, .Post-header a, .PostUser h3, .PostUser h3 a {
|
.Post-header, .Post-header a, .PostUser h3, .PostUser h3 a {
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
@@ -23,14 +24,18 @@ return [
|
|||||||
})
|
})
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
$schema->table('access_tokens', function (Blueprint $table) {
|
$schema->table('access_tokens', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('access_tokens', function (Blueprint $table) {
|
$schema->table('access_tokens', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['user_id']);
|
$table->dropForeign(['user_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,18 +9,21 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) {
|
'up' => function (Builder $schema) {
|
||||||
$schema->table('api_keys', function (Blueprint $table) {
|
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropPrimary(['id']);
|
$table->dropPrimary(['id']);
|
||||||
$table->renameColumn('id', 'key');
|
$table->renameColumn('id', 'key');
|
||||||
$table->unique('key');
|
$table->unique('key');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
|
|
||||||
$schema->table('api_keys', function (Blueprint $table) {
|
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
|
||||||
$table->increments('id');
|
$table->increments('id');
|
||||||
$table->string('allowed_ips')->nullable();
|
$table->string('allowed_ips')->nullable();
|
||||||
$table->string('scopes')->nullable();
|
$table->string('scopes')->nullable();
|
||||||
@@ -29,19 +32,25 @@ return [
|
|||||||
$table->dateTime('last_activity_at')->nullable();
|
$table->dateTime('last_activity_at')->nullable();
|
||||||
|
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('api_keys', function (Blueprint $table) {
|
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['user_id']);
|
$table->dropForeign(['user_id']);
|
||||||
$table->dropColumn('id', 'allowed_ips', 'user_id', 'scopes', 'created_at');
|
$table->dropColumn('id', 'allowed_ips', 'user_id', 'scopes', 'created_at');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
|
|
||||||
$schema->table('api_keys', function (Blueprint $table) {
|
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropUnique(['key']);
|
$table->dropUnique(['key']);
|
||||||
$table->renameColumn('key', 'id');
|
$table->renameColumn('key', 'id');
|
||||||
$table->primary('id');
|
$table->primary('id');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Query\Expression;
|
use Illuminate\Database\Query\Expression;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
@@ -33,21 +34,26 @@ return [
|
|||||||
'last_post_id' => $selectId('posts', 'last_post_id'),
|
'last_post_id' => $selectId('posts', 'last_post_id'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema->table('discussions', function (Blueprint $table) {
|
$schema->table('discussions', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
|
||||||
$table->foreign('last_posted_user_id')->references('id')->on('users')->onDelete('set null');
|
$table->foreign('last_posted_user_id')->references('id')->on('users')->onDelete('set null');
|
||||||
$table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
|
$table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
|
||||||
$table->foreign('first_post_id')->references('id')->on('posts')->onDelete('set null');
|
$table->foreign('first_post_id')->references('id')->on('posts')->onDelete('set null');
|
||||||
$table->foreign('last_post_id')->references('id')->on('posts')->onDelete('set null');
|
$table->foreign('last_post_id')->references('id')->on('posts')->onDelete('set null');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('discussions', function (Blueprint $table) {
|
$schema->table('discussions', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign([
|
$table->dropForeign(['user_id']);
|
||||||
'user_id', 'last_posted_user_id', 'hidden_user_id',
|
$table->dropForeign(['last_posted_user_id']);
|
||||||
'first_post_id', 'last_post_id'
|
$table->dropForeign(['hidden_user_id']);
|
||||||
]);
|
$table->dropForeign(['first_post_id']);
|
||||||
|
$table->dropForeign(['last_post_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
@@ -26,15 +27,20 @@ return [
|
|||||||
})
|
})
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
$schema->table('discussion_user', function (Blueprint $table) {
|
$schema->table('discussion_user', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
$table->foreign('discussion_id')->references('id')->on('discussions')->onDelete('cascade');
|
$table->foreign('discussion_id')->references('id')->on('discussions')->onDelete('cascade');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('discussion_user', function (Blueprint $table) {
|
$schema->table('discussion_user', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['user_id', 'discussion_id']);
|
$table->dropForeign(['user_id']);
|
||||||
|
$table->dropForeign(['discussion_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
@@ -23,14 +24,18 @@ return [
|
|||||||
})
|
})
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
$schema->table('email_tokens', function (Blueprint $table) {
|
$schema->table('email_tokens', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('email_tokens', function (Blueprint $table) {
|
$schema->table('email_tokens', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['user_id']);
|
$table->dropForeign(['user_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
@@ -23,14 +24,18 @@ return [
|
|||||||
})
|
})
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
$schema->table('group_permission', function (Blueprint $table) {
|
$schema->table('group_permission', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
|
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('group_permission', function (Blueprint $table) {
|
$schema->table('group_permission', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['group_id']);
|
$table->dropForeign(['group_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
@@ -26,15 +27,20 @@ return [
|
|||||||
})
|
})
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
$schema->table('group_user', function (Blueprint $table) {
|
$schema->table('group_user', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
|
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('group_user', function (Blueprint $table) {
|
$schema->table('group_user', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['user_id', 'group_id']);
|
$table->dropForeign(['user_id']);
|
||||||
|
$table->dropForeign(['group_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
@@ -30,15 +31,20 @@ return [
|
|||||||
})
|
})
|
||||||
->update(['from_user_id' => null]);
|
->update(['from_user_id' => null]);
|
||||||
|
|
||||||
$schema->table('notifications', function (Blueprint $table) {
|
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
$table->foreign('from_user_id')->references('id')->on('users')->onDelete('set null');
|
$table->foreign('from_user_id')->references('id')->on('users')->onDelete('set null');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('notifications', function (Blueprint $table) {
|
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['user_id', 'from_user_id']);
|
$table->dropForeign(['user_id']);
|
||||||
|
$table->dropForeign(['from_user_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
@@ -23,14 +24,18 @@ return [
|
|||||||
})
|
})
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
$schema->table('password_tokens', function (Blueprint $table) {
|
$schema->table('password_tokens', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('password_tokens', function (Blueprint $table) {
|
$schema->table('password_tokens', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign(['user_id']);
|
$table->dropForeign(['user_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Query\Expression;
|
use Illuminate\Database\Query\Expression;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
@@ -31,19 +32,23 @@ return [
|
|||||||
'hidden_user_id' => $selectId('users', 'hidden_user_id'),
|
'hidden_user_id' => $selectId('users', 'hidden_user_id'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$schema->table('posts', function (Blueprint $table) {
|
$schema->table('posts', function (Blueprint $table) use ($schema) {
|
||||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
|
||||||
$table->foreign('edited_user_id')->references('id')->on('users')->onDelete('set null');
|
$table->foreign('edited_user_id')->references('id')->on('users')->onDelete('set null');
|
||||||
$table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
|
$table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('posts', function (Blueprint $table) {
|
$schema->table('posts', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropForeign([
|
$table->dropForeign(['user_id']);
|
||||||
'user_id', 'discussion_id',
|
$table->dropForeign(['discussion_id']);
|
||||||
'edited_user_id', 'hidden_user_id'
|
$table->dropForeign(['edited_user_id']);
|
||||||
]);
|
$table->dropForeign(['hidden_user_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,25 +9,30 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) {
|
'up' => function (Builder $schema) {
|
||||||
$schema->table('users', function (Blueprint $table) {
|
$schema->table('users', function (Blueprint $table) use ($schema) {
|
||||||
$table->index('joined_at');
|
$table->index('joined_at');
|
||||||
$table->index('last_seen_at');
|
$table->index('last_seen_at');
|
||||||
$table->index('discussion_count');
|
$table->index('discussion_count');
|
||||||
$table->index('comment_count');
|
$table->index('comment_count');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('users', function (Blueprint $table) {
|
$schema->table('users', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropIndex(['joined_at']);
|
$table->dropIndex(['joined_at']);
|
||||||
$table->dropIndex(['last_seen_at']);
|
$table->dropIndex(['last_seen_at']);
|
||||||
$table->dropIndex(['discussion_count']);
|
$table->dropIndex(['discussion_count']);
|
||||||
$table->dropIndex(['comment_count']);
|
$table->dropIndex(['comment_count']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,12 +9,13 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) {
|
'up' => function (Builder $schema) {
|
||||||
$schema->table('discussions', function (Blueprint $table) {
|
$schema->table('discussions', function (Blueprint $table) use ($schema) {
|
||||||
$table->index('last_posted_at');
|
$table->index('last_posted_at');
|
||||||
$table->index('last_posted_user_id');
|
$table->index('last_posted_user_id');
|
||||||
$table->index('created_at');
|
$table->index('created_at');
|
||||||
@@ -22,11 +23,13 @@ return [
|
|||||||
$table->index('comment_count');
|
$table->index('comment_count');
|
||||||
$table->index('participant_count');
|
$table->index('participant_count');
|
||||||
$table->index('hidden_at');
|
$table->index('hidden_at');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('discussions', function (Blueprint $table) {
|
$schema->table('discussions', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropIndex(['last_posted_at']);
|
$table->dropIndex(['last_posted_at']);
|
||||||
$table->dropIndex(['last_posted_user_id']);
|
$table->dropIndex(['last_posted_user_id']);
|
||||||
$table->dropIndex(['created_at']);
|
$table->dropIndex(['created_at']);
|
||||||
@@ -34,6 +37,8 @@ return [
|
|||||||
$table->dropIndex(['comment_count']);
|
$table->dropIndex(['comment_count']);
|
||||||
$table->dropIndex(['participant_count']);
|
$table->dropIndex(['participant_count']);
|
||||||
$table->dropIndex(['hidden_at']);
|
$table->dropIndex(['hidden_at']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,19 +9,24 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) {
|
'up' => function (Builder $schema) {
|
||||||
$schema->table('notifications', function (Blueprint $table) {
|
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||||
$table->index('user_id');
|
$table->index('user_id');
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('notifications', function (Blueprint $table) {
|
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropIndex(['user_id']);
|
$table->dropIndex(['user_id']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -9,23 +9,28 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Database\Schema\Builder;
|
use Illuminate\Database\Schema\Builder;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) {
|
'up' => function (Builder $schema) {
|
||||||
$schema->table('posts', function (Blueprint $table) {
|
$schema->table('posts', function (Blueprint $table) use ($schema) {
|
||||||
$table->index(['discussion_id', 'number']);
|
$table->index(['discussion_id', 'number']);
|
||||||
$table->index(['discussion_id', 'created_at']);
|
$table->index(['discussion_id', 'created_at']);
|
||||||
$table->index(['user_id', 'created_at']);
|
$table->index(['user_id', 'created_at']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
'down' => function (Builder $schema) {
|
'down' => function (Builder $schema) {
|
||||||
$schema->table('posts', function (Blueprint $table) {
|
$schema->table('posts', function (Blueprint $table) use ($schema) {
|
||||||
$table->dropIndex(['discussion_id', 'number']);
|
$table->dropIndex(['discussion_id', 'number']);
|
||||||
$table->dropIndex(['discussion_id', 'created_at']);
|
$table->dropIndex(['discussion_id', 'created_at']);
|
||||||
$table->dropIndex(['user_id', 'created_at']);
|
$table->dropIndex(['user_id', 'created_at']);
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@@ -14,6 +14,9 @@ namespace Flarum\Admin;
|
|||||||
use Flarum\Event\ConfigureMiddleware;
|
use Flarum\Event\ConfigureMiddleware;
|
||||||
use Flarum\Foundation\AbstractServiceProvider;
|
use Flarum\Foundation\AbstractServiceProvider;
|
||||||
use Flarum\Foundation\Application;
|
use Flarum\Foundation\Application;
|
||||||
|
use Flarum\Frontend\AddLocaleAssets;
|
||||||
|
use Flarum\Frontend\AddTranslations;
|
||||||
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
use Flarum\Frontend\RecompileFrontendAssets;
|
use Flarum\Frontend\RecompileFrontendAssets;
|
||||||
use Flarum\Http\Middleware as HttpMiddleware;
|
use Flarum\Http\Middleware as HttpMiddleware;
|
||||||
use Flarum\Http\RouteCollection;
|
use Flarum\Http\RouteCollection;
|
||||||
@@ -62,17 +65,31 @@ class AdminServiceProvider extends AbstractServiceProvider
|
|||||||
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.admin.routes')));
|
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.admin.routes')));
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->app->bind('flarum.admin.assets', function () {
|
$this->app->bind('flarum.assets.admin', function () {
|
||||||
return $this->app->make('flarum.frontend.assets.defaults')('admin');
|
/** @var \Flarum\Frontend\Assets $assets */
|
||||||
|
$assets = $this->app->make('flarum.assets.factory')('admin');
|
||||||
|
|
||||||
|
$assets->js(function (SourceCollector $sources) {
|
||||||
|
$sources->addFile(__DIR__.'/../../js/dist/admin.js');
|
||||||
|
});
|
||||||
|
|
||||||
|
$assets->css(function (SourceCollector $sources) {
|
||||||
|
$sources->addFile(__DIR__.'/../../less/admin.less');
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->make(AddTranslations::class)->forFrontend('admin')->to($assets);
|
||||||
|
$this->app->make(AddLocaleAssets::class)->to($assets);
|
||||||
|
|
||||||
|
return $assets;
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->app->bind('flarum.admin.frontend', function () {
|
$this->app->bind('flarum.frontend.admin', function () {
|
||||||
$view = $this->app->make('flarum.frontend.view.defaults')('admin');
|
/** @var \Flarum\Frontend\Frontend $frontend */
|
||||||
|
$frontend = $this->app->make('flarum.frontend.factory')('admin');
|
||||||
|
|
||||||
$view->setAssets($this->app->make('flarum.admin.assets'));
|
$frontend->content($this->app->make(Content\AdminPayload::class));
|
||||||
$view->add($this->app->make(Content\AdminPayload::class));
|
|
||||||
|
|
||||||
return $view;
|
return $frontend;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +104,7 @@ class AdminServiceProvider extends AbstractServiceProvider
|
|||||||
|
|
||||||
$this->app->make('events')->subscribe(
|
$this->app->make('events')->subscribe(
|
||||||
new RecompileFrontendAssets(
|
new RecompileFrontendAssets(
|
||||||
$this->app->make('flarum.admin.assets'),
|
$this->app->make('flarum.assets.admin'),
|
||||||
$this->app->make('flarum.locales')
|
$this->app->make('flarum.locales')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@@ -12,8 +12,7 @@
|
|||||||
namespace Flarum\Admin\Content;
|
namespace Flarum\Admin\Content;
|
||||||
|
|
||||||
use Flarum\Extension\ExtensionManager;
|
use Flarum\Extension\ExtensionManager;
|
||||||
use Flarum\Frontend\Content\ContentInterface;
|
use Flarum\Frontend\Document;
|
||||||
use Flarum\Frontend\HtmlDocument;
|
|
||||||
use Flarum\Group\Permission;
|
use Flarum\Group\Permission;
|
||||||
use Flarum\Settings\Event\Deserializing;
|
use Flarum\Settings\Event\Deserializing;
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
@@ -21,7 +20,7 @@ use Illuminate\Contracts\Events\Dispatcher;
|
|||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
class AdminPayload implements ContentInterface
|
class AdminPayload
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var SettingsRepositoryInterface
|
* @var SettingsRepositoryInterface
|
||||||
@@ -52,7 +51,7 @@ class AdminPayload implements ContentInterface
|
|||||||
$this->events = $events;
|
$this->events = $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(HtmlDocument $document, Request $request)
|
public function __invoke(Document $document, Request $request)
|
||||||
{
|
{
|
||||||
$settings = $this->settings->all();
|
$settings = $this->settings->all();
|
||||||
|
|
||||||
|
@@ -25,7 +25,6 @@ use Flarum\Http\RouteCollection;
|
|||||||
use Flarum\Http\RouteHandlerFactory;
|
use Flarum\Http\RouteHandlerFactory;
|
||||||
use Flarum\Http\UrlGenerator;
|
use Flarum\Http\UrlGenerator;
|
||||||
use Tobscure\JsonApi\ErrorHandler;
|
use Tobscure\JsonApi\ErrorHandler;
|
||||||
use Tobscure\JsonApi\Exception\Handler\FallbackExceptionHandler;
|
|
||||||
use Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler;
|
use Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler;
|
||||||
use Zend\Stratigility\MiddlewarePipe;
|
use Zend\Stratigility\MiddlewarePipe;
|
||||||
|
|
||||||
@@ -80,7 +79,7 @@ class ApiServiceProvider extends AbstractServiceProvider
|
|||||||
$handler->registerHandler(new ExceptionHandler\TokenMismatchExceptionHandler);
|
$handler->registerHandler(new ExceptionHandler\TokenMismatchExceptionHandler);
|
||||||
$handler->registerHandler(new ExceptionHandler\ValidationExceptionHandler);
|
$handler->registerHandler(new ExceptionHandler\ValidationExceptionHandler);
|
||||||
$handler->registerHandler(new InvalidParameterExceptionHandler);
|
$handler->registerHandler(new InvalidParameterExceptionHandler);
|
||||||
$handler->registerHandler(new FallbackExceptionHandler($this->app->inDebugMode()));
|
$handler->registerHandler(new ExceptionHandler\FallbackExceptionHandler($this->app->inDebugMode(), $this->app->make('log')));
|
||||||
|
|
||||||
return $handler;
|
return $handler;
|
||||||
});
|
});
|
||||||
|
52
src/Api/Controller/ClearCacheController.php
Normal file
52
src/Api/Controller/ClearCacheController.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Api\Controller;
|
||||||
|
|
||||||
|
use Flarum\Foundation\Console\CacheClearCommand;
|
||||||
|
use Flarum\User\AssertPermissionTrait;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
|
use Zend\Diactoros\Response\EmptyResponse;
|
||||||
|
|
||||||
|
class ClearCacheController extends AbstractDeleteController
|
||||||
|
{
|
||||||
|
use AssertPermissionTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheClearCommand
|
||||||
|
*/
|
||||||
|
protected $command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheClearCommand $command
|
||||||
|
*/
|
||||||
|
public function __construct(CacheClearCommand $command)
|
||||||
|
{
|
||||||
|
$this->command = $command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function delete(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$this->assertAdmin($request->getAttribute('actor'));
|
||||||
|
|
||||||
|
$this->command->run(
|
||||||
|
new ArrayInput([]),
|
||||||
|
new NullOutput()
|
||||||
|
);
|
||||||
|
|
||||||
|
return new EmptyResponse(204);
|
||||||
|
}
|
||||||
|
}
|
@@ -12,6 +12,7 @@
|
|||||||
namespace Flarum\Api;
|
namespace Flarum\Api;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Throwable;
|
||||||
use Tobscure\JsonApi\Document;
|
use Tobscure\JsonApi\Document;
|
||||||
use Tobscure\JsonApi\ErrorHandler as JsonApiErrorHandler;
|
use Tobscure\JsonApi\ErrorHandler as JsonApiErrorHandler;
|
||||||
|
|
||||||
@@ -34,8 +35,12 @@ class ErrorHandler
|
|||||||
* @param Exception $e
|
* @param Exception $e
|
||||||
* @return JsonApiResponse
|
* @return JsonApiResponse
|
||||||
*/
|
*/
|
||||||
public function handle(Exception $e)
|
public function handle(Throwable $e)
|
||||||
{
|
{
|
||||||
|
if (! $e instanceof Exception) {
|
||||||
|
$e = new Exception($e->getMessage(), $e->getCode(), $e);
|
||||||
|
}
|
||||||
|
|
||||||
$response = $this->errorHandler->handle($e);
|
$response = $this->errorHandler->handle($e);
|
||||||
|
|
||||||
$document = new Document;
|
$document = new Document;
|
||||||
|
77
src/Api/ExceptionHandler/FallbackExceptionHandler.php
Normal file
77
src/Api/ExceptionHandler/FallbackExceptionHandler.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Api\ExceptionHandler;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Tobscure\JsonApi\Exception\Handler\ExceptionHandlerInterface;
|
||||||
|
use Tobscure\JsonApi\Exception\Handler\ResponseBag;
|
||||||
|
|
||||||
|
class FallbackExceptionHandler implements ExceptionHandlerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $debug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $debug
|
||||||
|
* @param LoggerInterface $logger
|
||||||
|
*/
|
||||||
|
public function __construct($debug, LoggerInterface $logger)
|
||||||
|
{
|
||||||
|
$this->debug = $debug;
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function manages(Exception $e)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function handle(Exception $e)
|
||||||
|
{
|
||||||
|
$status = 500;
|
||||||
|
$error = $this->constructError($e, $status);
|
||||||
|
|
||||||
|
$this->logger->error($e);
|
||||||
|
|
||||||
|
return new ResponseBag($status, [$error]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Exception $e
|
||||||
|
* @param $status
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function constructError(Exception $e, $status)
|
||||||
|
{
|
||||||
|
$error = ['code' => $status, 'title' => 'Internal server error'];
|
||||||
|
|
||||||
|
if ($this->debug) {
|
||||||
|
$error['detail'] = (string) $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $error;
|
||||||
|
}
|
||||||
|
}
|
@@ -11,12 +11,12 @@
|
|||||||
|
|
||||||
namespace Flarum\Api\Middleware;
|
namespace Flarum\Api\Middleware;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Flarum\Api\ErrorHandler;
|
use Flarum\Api\ErrorHandler;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class HandleErrors implements Middleware
|
class HandleErrors implements Middleware
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ class HandleErrors implements Middleware
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $handler->handle($request);
|
return $handler->handle($request);
|
||||||
} catch (Exception $e) {
|
} catch (Throwable $e) {
|
||||||
return $this->errorHandler->handle($e);
|
return $this->errorHandler->handle($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -301,4 +301,11 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
|||||||
'favicon.delete',
|
'favicon.delete',
|
||||||
$route->toController(Controller\DeleteFaviconController::class)
|
$route->toController(Controller\DeleteFaviconController::class)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Clear the cache
|
||||||
|
$map->delete(
|
||||||
|
'/cache',
|
||||||
|
'cache.clear',
|
||||||
|
$route->toController(Controller\ClearCacheController::class)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@@ -68,11 +68,20 @@ class MigrateCommand extends AbstractCommand
|
|||||||
$extensions->getMigrator()->setOutput($this->output);
|
$extensions->getMigrator()->setOutput($this->output);
|
||||||
|
|
||||||
foreach ($extensions->getEnabledExtensions() as $name => $extension) {
|
foreach ($extensions->getEnabledExtensions() as $name => $extension) {
|
||||||
$this->info('Migrating extension: '.$name);
|
if ($extension->hasMigrations()) {
|
||||||
|
$this->info('Migrating extension: '.$name);
|
||||||
|
|
||||||
$extensions->migrate($extension);
|
$extensions->migrate($extension);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->container->make('Flarum\Settings\SettingsRepositoryInterface')->set('version', $this->container->version());
|
$this->container->make('Flarum\Settings\SettingsRepositoryInterface')->set('version', $this->container->version());
|
||||||
|
|
||||||
|
$this->info('Publishing assets...');
|
||||||
|
|
||||||
|
$this->container->make('files')->copyDirectory(
|
||||||
|
base_path().'/vendor/components/font-awesome/webfonts',
|
||||||
|
public_path().'/assets/fonts'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -71,11 +71,8 @@ class ResetCommand extends AbstractCommand
|
|||||||
|
|
||||||
$this->info('Rolling back extension: '.$extensionName);
|
$this->info('Rolling back extension: '.$extensionName);
|
||||||
|
|
||||||
$notes = $this->manager->migrateDown($extension);
|
$this->manager->getMigrator()->setOutput($this->output);
|
||||||
|
$this->manager->migrateDown($extension);
|
||||||
foreach ($notes as $note) {
|
|
||||||
$this->info($note);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('DONE.');
|
$this->info('DONE.');
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,9 @@ class DatabaseServiceProvider extends AbstractServiceProvider
|
|||||||
$this->app->singleton('flarum.db', function () {
|
$this->app->singleton('flarum.db', function () {
|
||||||
$factory = new ConnectionFactory($this->app);
|
$factory = new ConnectionFactory($this->app);
|
||||||
|
|
||||||
$connection = $factory->make($this->app->config('database'));
|
$dbConfig = $this->app->config('database');
|
||||||
|
$dbConfig['engine'] = 'InnoDB';
|
||||||
|
$connection = $factory->make($dbConfig);
|
||||||
$connection->setEventDispatcher($this->app->make('Illuminate\Contracts\Events\Dispatcher'));
|
$connection->setEventDispatcher($this->app->make('Illuminate\Contracts\Events\Dispatcher'));
|
||||||
|
|
||||||
return $connection;
|
return $connection;
|
||||||
|
@@ -29,7 +29,11 @@ abstract class Migration
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) use ($name, $definition) {
|
'up' => function (Builder $schema) use ($name, $definition) {
|
||||||
$schema->create($name, $definition);
|
$schema->create($name, function (Blueprint $table) use ($schema, $definition) {
|
||||||
|
$definition($table);
|
||||||
|
|
||||||
|
static::fixIndexNames($schema, $table);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
'down' => function (Builder $schema) use ($name) {
|
'down' => function (Builder $schema) use ($name) {
|
||||||
$schema->drop($name);
|
$schema->drop($name);
|
||||||
@@ -59,11 +63,13 @@ abstract class Migration
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) use ($tableName, $columnDefinitions) {
|
'up' => function (Builder $schema) use ($tableName, $columnDefinitions) {
|
||||||
$schema->table($tableName, function (Blueprint $table) use ($columnDefinitions) {
|
$schema->table($tableName, function (Blueprint $table) use ($schema, $columnDefinitions) {
|
||||||
foreach ($columnDefinitions as $columnName => $options) {
|
foreach ($columnDefinitions as $columnName => $options) {
|
||||||
$type = array_shift($options);
|
$type = array_shift($options);
|
||||||
$table->addColumn($type, $columnName, $options);
|
$table->addColumn($type, $columnName, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Migration::fixIndexNames($schema, $table);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'down' => function (Builder $schema) use ($tableName, $columnDefinitions) {
|
'down' => function (Builder $schema) use ($tableName, $columnDefinitions) {
|
||||||
@@ -187,4 +193,27 @@ abstract class Migration
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a prefix to index names on the given table blueprint.
|
||||||
|
*
|
||||||
|
* Laravel 5.5 doesn't automatically add the table prefix to index
|
||||||
|
* names, but this has been fixed in 5.7. We will manually fix the
|
||||||
|
* names for now, and we can remove this when we upgrade to 5.7.
|
||||||
|
*/
|
||||||
|
public static function fixIndexNames(Builder $schema, Blueprint $table)
|
||||||
|
{
|
||||||
|
$indexCommands = [
|
||||||
|
'unique', 'index', 'spatialIndex', 'foreign',
|
||||||
|
'dropUnique', 'dropIndex', 'dropSpatialIndex', 'dropForeign'
|
||||||
|
];
|
||||||
|
|
||||||
|
$prefix = $schema->getConnection()->getTablePrefix();
|
||||||
|
|
||||||
|
foreach ($table->getCommands() as $command) {
|
||||||
|
if (in_array($command->name, $indexCommands)) {
|
||||||
|
$command->index = $prefix.$command->index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,10 +12,10 @@
|
|||||||
namespace Flarum\Discussion\Search\Gambit;
|
namespace Flarum\Discussion\Search\Gambit;
|
||||||
|
|
||||||
use Flarum\Discussion\Search\DiscussionSearch;
|
use Flarum\Discussion\Search\DiscussionSearch;
|
||||||
use Flarum\Event\ScopeModelVisibility;
|
|
||||||
use Flarum\Post\Post;
|
use Flarum\Post\Post;
|
||||||
use Flarum\Search\AbstractSearch;
|
use Flarum\Search\AbstractSearch;
|
||||||
use Flarum\Search\GambitInterface;
|
use Flarum\Search\GambitInterface;
|
||||||
|
use Illuminate\Database\Query\Expression;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
|
|
||||||
class FulltextGambit implements GambitInterface
|
class FulltextGambit implements GambitInterface
|
||||||
@@ -36,26 +36,35 @@ class FulltextGambit implements GambitInterface
|
|||||||
$query = $search->getQuery();
|
$query = $search->getQuery();
|
||||||
$grammar = $query->getGrammar();
|
$grammar = $query->getGrammar();
|
||||||
|
|
||||||
$query
|
// Construct a subquery to fetch discussions which contain relevant
|
||||||
->selectRaw('SUBSTRING_INDEX(GROUP_CONCAT('.$grammar->wrap('posts.id').' ORDER BY MATCH('.$grammar->wrap('posts.content').') AGAINST (?) DESC), \',\', 1) as most_relevant_post_id', [$bit])
|
// posts. Retrieve the collective relevance of each discussion's posts,
|
||||||
->leftJoin('posts', 'posts.discussion_id', '=', 'discussions.id')
|
// which we will use later in the order by clause, and also retrieve
|
||||||
|
// the ID of the most relevant post.
|
||||||
|
$subquery = Post::whereVisibleTo($search->getActor())
|
||||||
|
->select('posts.discussion_id')
|
||||||
|
->selectRaw('SUM(MATCH('.$grammar->wrap('posts.content').') AGAINST (?)) as score', [$bit])
|
||||||
|
->selectRaw('SUBSTRING_INDEX(GROUP_CONCAT('.$grammar->wrap('posts.id').' ORDER BY MATCH('.$grammar->wrap('posts.content').') AGAINST (?) DESC, '.$grammar->wrap('posts.number').'), \',\', 1) as most_relevant_post_id', [$bit])
|
||||||
->where('posts.type', 'comment')
|
->where('posts.type', 'comment')
|
||||||
->where(function ($query) use ($search) {
|
->whereRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (? IN BOOLEAN MODE)', [$bit])
|
||||||
event(new ScopeModelVisibility(Post::query()->setQuery($query), $search->getActor(), 'view'));
|
|
||||||
})
|
|
||||||
->where(function ($query) use ($bit) {
|
|
||||||
$grammar = $query->getGrammar();
|
|
||||||
|
|
||||||
$query->whereRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (? IN BOOLEAN MODE)', [$bit])
|
|
||||||
->orWhereRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (? IN BOOLEAN MODE)', [$bit]);
|
|
||||||
})
|
|
||||||
->groupBy('posts.discussion_id');
|
->groupBy('posts.discussion_id');
|
||||||
|
|
||||||
$search->setDefaultSort(function ($query) use ($bit) {
|
// Join the subquery into the main search query and scope results to
|
||||||
$grammar = $query->getGrammar();
|
// discussions that have a relevant title or that contain relevant posts.
|
||||||
|
$query
|
||||||
|
->addSelect('posts_ft.most_relevant_post_id')
|
||||||
|
->join(
|
||||||
|
new Expression('('.$subquery->toSql().') '.$grammar->wrapTable('posts_ft')),
|
||||||
|
'posts_ft.discussion_id', '=', 'discussions.id'
|
||||||
|
)
|
||||||
|
->addBinding($subquery->getBindings(), 'join')
|
||||||
|
->where(function ($query) use ($grammar, $bit) {
|
||||||
|
$query->whereRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (? IN BOOLEAN MODE)', [$bit])
|
||||||
|
->orWhereNotNull('posts_ft.score');
|
||||||
|
});
|
||||||
|
|
||||||
|
$search->setDefaultSort(function ($query) use ($grammar, $bit) {
|
||||||
$query->orderByRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) desc', [$bit]);
|
$query->orderByRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) desc', [$bit]);
|
||||||
$query->orderByRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (?) desc', [$bit]);
|
$query->orderBy('posts_ft.score', 'desc');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,72 +11,49 @@
|
|||||||
|
|
||||||
namespace Flarum\Extend;
|
namespace Flarum\Extend;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Flarum\Extension\Extension;
|
use Flarum\Extension\Extension;
|
||||||
use Flarum\Frontend\Asset\ExtensionAssets;
|
use Flarum\Frontend\Assets;
|
||||||
use Flarum\Frontend\CompilerFactory;
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
use Flarum\Frontend\HtmlDocumentFactory;
|
use Flarum\Frontend\Frontend as ActualFrontend;
|
||||||
|
use Flarum\Frontend\RecompileFrontendAssets;
|
||||||
use Flarum\Http\RouteHandlerFactory;
|
use Flarum\Http\RouteHandlerFactory;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
|
|
||||||
class Frontend implements ExtenderInterface
|
class Frontend implements ExtenderInterface
|
||||||
{
|
{
|
||||||
protected $frontend;
|
protected $frontend;
|
||||||
protected $inheritFrom;
|
|
||||||
|
|
||||||
protected $css = [];
|
protected $css = [];
|
||||||
protected $js;
|
protected $js;
|
||||||
protected $routes = [];
|
protected $routes = [];
|
||||||
protected $content = [];
|
protected $content = [];
|
||||||
|
|
||||||
public function __construct($frontend)
|
public function __construct(string $frontend)
|
||||||
{
|
{
|
||||||
$this->frontend = $frontend;
|
$this->frontend = $frontend;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function inherits($from)
|
public function css(string $path)
|
||||||
{
|
|
||||||
$this->inheritFrom = $from;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css($path)
|
|
||||||
{
|
{
|
||||||
$this->css[] = $path;
|
$this->css[] = $path;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function js($path)
|
public function js(string $path)
|
||||||
{
|
{
|
||||||
$this->js = $path;
|
$this->js = $path;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function route($path, $name, $content = null)
|
public function route(string $path, string $name, $content = null)
|
||||||
{
|
{
|
||||||
$this->ensureCanRegisterRoutes();
|
|
||||||
|
|
||||||
$this->routes[] = compact('path', 'name', 'content');
|
$this->routes[] = compact('path', 'name', 'content');
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function ensureCanRegisterRoutes()
|
|
||||||
{
|
|
||||||
if (in_array($this->frontend, ['forum', 'admin'])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception(
|
|
||||||
'The Frontend extender can only handle routes for the forum and '.
|
|
||||||
'admin frontends. Other routes (e.g. for the API) need to be '.
|
|
||||||
'registered through the Routes extender.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callable|string $callback
|
* @param callable|string $callback
|
||||||
* @return $this
|
* @return $this
|
||||||
@@ -90,56 +67,53 @@ class Frontend implements ExtenderInterface
|
|||||||
|
|
||||||
public function extend(Container $container, Extension $extension = null)
|
public function extend(Container $container, Extension $extension = null)
|
||||||
{
|
{
|
||||||
$this->registerFrontend($container);
|
|
||||||
$this->registerAssets($container, $this->getModuleName($extension));
|
$this->registerAssets($container, $this->getModuleName($extension));
|
||||||
$this->registerRoutes($container);
|
$this->registerRoutes($container);
|
||||||
$this->registerContent($container);
|
$this->registerContent($container);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerFrontend(Container $container)
|
|
||||||
{
|
|
||||||
if ($container->bound("flarum.$this->frontend.frontend")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$container->bind(
|
|
||||||
"flarum.$this->frontend.frontend",
|
|
||||||
function ($c) {
|
|
||||||
$view = $c->make('flarum.frontend.view.defaults')($this->frontend);
|
|
||||||
|
|
||||||
$view->setAssets($c->make("flarum.$this->frontend.assets"));
|
|
||||||
|
|
||||||
return $view;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
$container->bind("flarum.$this->frontend.assets", function ($c) {
|
|
||||||
if ($this->inheritFrom) {
|
|
||||||
// FIXME: will contain Assets\CoreAssets instance with wrong name
|
|
||||||
return $c->make("flarum.$this->inheritFrom.assets")
|
|
||||||
->inherit($this->frontend);
|
|
||||||
} else {
|
|
||||||
return $c->make('flarum.frontend.assets.defaults')($this->frontend);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function registerAssets(Container $container, string $moduleName)
|
private function registerAssets(Container $container, string $moduleName)
|
||||||
{
|
{
|
||||||
if (empty($this->css) && empty($this->js)) {
|
if (empty($this->css) && empty($this->js)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$container->resolving(
|
$abstract = 'flarum.assets.'.$this->frontend;
|
||||||
"flarum.$this->frontend.assets",
|
|
||||||
function (CompilerFactory $assets) use ($moduleName) {
|
$container->resolving($abstract, function (Assets $assets) use ($moduleName) {
|
||||||
$assets->add(function () use ($moduleName) {
|
if ($this->js) {
|
||||||
return new ExtensionAssets(
|
$assets->js(function (SourceCollector $sources) use ($moduleName) {
|
||||||
$moduleName, $this->css, $this->js
|
$sources->addString(function () {
|
||||||
);
|
return 'var module={}';
|
||||||
|
});
|
||||||
|
$sources->addFile($this->js);
|
||||||
|
$sources->addString(function () use ($moduleName) {
|
||||||
|
return "flarum.extensions['$moduleName']=module.exports";
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
if ($this->css) {
|
||||||
|
$assets->css(function (SourceCollector $sources) {
|
||||||
|
foreach ($this->css as $path) {
|
||||||
|
$sources->addFile($path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (! $container->bound($abstract)) {
|
||||||
|
$container->bind($abstract, function (Container $container) {
|
||||||
|
return $container->make('flarum.assets.factory')($this->frontend);
|
||||||
|
});
|
||||||
|
|
||||||
|
$container->make('events')->subscribe(
|
||||||
|
new RecompileFrontendAssets(
|
||||||
|
$container->make($abstract),
|
||||||
|
$container->make('flarum.locales')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerRoutes(Container $container)
|
private function registerRoutes(Container $container)
|
||||||
@@ -166,14 +140,14 @@ class Frontend implements ExtenderInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$container->resolving(
|
$container->resolving(
|
||||||
"flarum.$this->frontend.frontend",
|
"flarum.frontend.$this->frontend",
|
||||||
function (HtmlDocumentFactory $view, Container $container) {
|
function (ActualFrontend $frontend, Container $container) {
|
||||||
foreach ($this->content as $content) {
|
foreach ($this->content as $content) {
|
||||||
if (is_string($content)) {
|
if (is_string($content)) {
|
||||||
$content = $container->make($content);
|
$content = $container->make($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
$view->add($content);
|
$frontend->content($content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -223,22 +223,24 @@ class ExtensionManager
|
|||||||
*
|
*
|
||||||
* @param Extension $extension
|
* @param Extension $extension
|
||||||
* @param bool|true $up
|
* @param bool|true $up
|
||||||
* @return array Notes from the migrator.
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function migrate(Extension $extension, $up = true)
|
public function migrate(Extension $extension, $up = true)
|
||||||
{
|
{
|
||||||
if ($extension->hasMigrations()) {
|
if (! $extension->hasMigrations()) {
|
||||||
$migrationDir = $extension->getPath().'/migrations';
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
|
$migrationDir = $extension->getPath().'/migrations';
|
||||||
return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
|
|
||||||
});
|
|
||||||
|
|
||||||
if ($up) {
|
$this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
|
||||||
$this->migrator->run($migrationDir, $extension);
|
return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
|
||||||
} else {
|
});
|
||||||
$this->migrator->reset($migrationDir, $extension);
|
|
||||||
}
|
if ($up) {
|
||||||
|
$this->migrator->run($migrationDir, $extension);
|
||||||
|
} else {
|
||||||
|
$this->migrator->reset($migrationDir, $extension);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,11 +272,16 @@ class ExtensionManager
|
|||||||
*/
|
*/
|
||||||
public function getEnabledExtensions()
|
public function getEnabledExtensions()
|
||||||
{
|
{
|
||||||
|
$enabled = [];
|
||||||
$extensions = $this->getExtensions();
|
$extensions = $this->getExtensions();
|
||||||
|
|
||||||
return array_filter(array_map(function ($id) use ($extensions) {
|
foreach ($this->getEnabled() as $id) {
|
||||||
return $extensions[$id] ?? null;
|
if (isset($extensions[$id])) {
|
||||||
}, $this->getEnabled()));
|
$enabled[$id] = $extensions[$id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -122,10 +122,10 @@ class Formatter
|
|||||||
$configurator->Autolink;
|
$configurator->Autolink;
|
||||||
$configurator->tags->onDuplicate('replace');
|
$configurator->tags->onDuplicate('replace');
|
||||||
|
|
||||||
$this->configureExternalLinks($configurator);
|
|
||||||
|
|
||||||
$this->events->dispatch(new Configuring($configurator));
|
$this->events->dispatch(new Configuring($configurator));
|
||||||
|
|
||||||
|
$this->configureExternalLinks($configurator);
|
||||||
|
|
||||||
return $configurator;
|
return $configurator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Forum\Asset;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Asset\AssetInterface;
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
|
||||||
|
|
||||||
class CustomCss implements AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var SettingsRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $settings;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SettingsRepositoryInterface $settings
|
|
||||||
*/
|
|
||||||
public function __construct(SettingsRepositoryInterface $settings)
|
|
||||||
{
|
|
||||||
$this->settings = $settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
$sources->addString(function () {
|
|
||||||
return $this->settings->get('custom_less');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function js(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Forum\Asset;
|
|
||||||
|
|
||||||
use Flarum\Formatter\Formatter;
|
|
||||||
use Flarum\Frontend\Asset\AssetInterface;
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
|
|
||||||
class FormatterJs implements AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Formatter
|
|
||||||
*/
|
|
||||||
protected $formatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Formatter $formatter
|
|
||||||
*/
|
|
||||||
public function __construct(Formatter $formatter)
|
|
||||||
{
|
|
||||||
$this->formatter = $formatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function js(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
$sources->addString(function () {
|
|
||||||
return $this->formatter->getJs();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -11,16 +11,15 @@
|
|||||||
|
|
||||||
namespace Flarum\Forum\Content;
|
namespace Flarum\Forum\Content;
|
||||||
|
|
||||||
use Flarum\Frontend\Content\ContentInterface;
|
use Flarum\Frontend\Document;
|
||||||
use Flarum\Frontend\HtmlDocument;
|
|
||||||
use Flarum\User\AssertPermissionTrait;
|
use Flarum\User\AssertPermissionTrait;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
class AssertRegistered implements ContentInterface
|
class AssertRegistered
|
||||||
{
|
{
|
||||||
use AssertPermissionTrait;
|
use AssertPermissionTrait;
|
||||||
|
|
||||||
public function __invoke(HtmlDocument $document, Request $request)
|
public function __invoke(Document $document, Request $request)
|
||||||
{
|
{
|
||||||
$this->assertRegistered($request->getAttribute('actor'));
|
$this->assertRegistered($request->getAttribute('actor'));
|
||||||
}
|
}
|
||||||
|
@@ -12,15 +12,14 @@
|
|||||||
namespace Flarum\Forum\Content;
|
namespace Flarum\Forum\Content;
|
||||||
|
|
||||||
use Flarum\Api\Client;
|
use Flarum\Api\Client;
|
||||||
use Flarum\Frontend\Content\ContentInterface;
|
use Flarum\Frontend\Document;
|
||||||
use Flarum\Frontend\HtmlDocument;
|
|
||||||
use Flarum\Http\Exception\RouteNotFoundException;
|
use Flarum\Http\Exception\RouteNotFoundException;
|
||||||
use Flarum\Http\UrlGenerator;
|
use Flarum\Http\UrlGenerator;
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
class Discussion implements ContentInterface
|
class Discussion
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Client
|
* @var Client
|
||||||
@@ -49,7 +48,7 @@ class Discussion implements ContentInterface
|
|||||||
$this->view = $view;
|
$this->view = $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(HtmlDocument $document, Request $request)
|
public function __invoke(Document $document, Request $request)
|
||||||
{
|
{
|
||||||
$queryParams = $request->getQueryParams();
|
$queryParams = $request->getQueryParams();
|
||||||
$page = max(1, array_get($queryParams, 'page'));
|
$page = max(1, array_get($queryParams, 'page'));
|
||||||
|
@@ -13,13 +13,12 @@ namespace Flarum\Forum\Content;
|
|||||||
|
|
||||||
use Flarum\Api\Client;
|
use Flarum\Api\Client;
|
||||||
use Flarum\Api\Controller\ListDiscussionsController;
|
use Flarum\Api\Controller\ListDiscussionsController;
|
||||||
use Flarum\Frontend\Content\ContentInterface;
|
use Flarum\Frontend\Document;
|
||||||
use Flarum\Frontend\HtmlDocument;
|
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
class Index implements ContentInterface
|
class Index
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Client
|
* @var Client
|
||||||
@@ -41,10 +40,7 @@ class Index implements ContentInterface
|
|||||||
$this->view = $view;
|
$this->view = $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function __invoke(Document $document, Request $request)
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function __invoke(HtmlDocument $document, Request $request)
|
|
||||||
{
|
{
|
||||||
$queryParams = $request->getQueryParams();
|
$queryParams = $request->getQueryParams();
|
||||||
|
|
||||||
|
@@ -86,7 +86,7 @@ class SavePasswordController implements RequestHandlerInterface
|
|||||||
throw new ValidationException($validator);
|
throw new ValidationException($validator);
|
||||||
}
|
}
|
||||||
} catch (ValidationException $e) {
|
} catch (ValidationException $e) {
|
||||||
$request->getAttribute('session')->set('errors', $e->errors());
|
$request->getAttribute('session')->put('errors', $e->errors());
|
||||||
|
|
||||||
return new RedirectResponse($this->url->to('forum')->route('resetPassword', ['token' => $token->id]));
|
return new RedirectResponse($this->url->to('forum')->route('resetPassword', ['token' => $token->id]));
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,13 @@ namespace Flarum\Forum;
|
|||||||
|
|
||||||
use Flarum\Event\ConfigureForumRoutes;
|
use Flarum\Event\ConfigureForumRoutes;
|
||||||
use Flarum\Event\ConfigureMiddleware;
|
use Flarum\Event\ConfigureMiddleware;
|
||||||
|
use Flarum\Formatter\Formatter;
|
||||||
use Flarum\Foundation\AbstractServiceProvider;
|
use Flarum\Foundation\AbstractServiceProvider;
|
||||||
use Flarum\Foundation\Application;
|
use Flarum\Foundation\Application;
|
||||||
|
use Flarum\Frontend\AddLocaleAssets;
|
||||||
|
use Flarum\Frontend\AddTranslations;
|
||||||
|
use Flarum\Frontend\Assets;
|
||||||
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
use Flarum\Frontend\RecompileFrontendAssets;
|
use Flarum\Frontend\RecompileFrontendAssets;
|
||||||
use Flarum\Http\Middleware as HttpMiddleware;
|
use Flarum\Http\Middleware as HttpMiddleware;
|
||||||
use Flarum\Http\RouteCollection;
|
use Flarum\Http\RouteCollection;
|
||||||
@@ -66,25 +71,32 @@ class ForumServiceProvider extends AbstractServiceProvider
|
|||||||
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.forum.routes')));
|
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.forum.routes')));
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->app->bind('flarum.forum.assets', function () {
|
$this->app->bind('flarum.assets.forum', function () {
|
||||||
$assets = $this->app->make('flarum.frontend.assets.defaults')('forum');
|
/** @var Assets $assets */
|
||||||
|
$assets = $this->app->make('flarum.assets.factory')('forum');
|
||||||
|
|
||||||
$assets->add(function () {
|
$assets->js(function (SourceCollector $sources) {
|
||||||
return [
|
$sources->addFile(__DIR__.'/../../js/dist/forum.js');
|
||||||
$this->app->make(Asset\FormatterJs::class),
|
$sources->addString(function () {
|
||||||
$this->app->make(Asset\CustomCss::class)
|
return $this->app->make(Formatter::class)->getJs();
|
||||||
];
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$assets->css(function (SourceCollector $sources) {
|
||||||
|
$sources->addFile(__DIR__.'/../../less/forum.less');
|
||||||
|
$sources->addString(function () {
|
||||||
|
return $this->app->make(SettingsRepositoryInterface::class)->get('custom_less');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->make(AddTranslations::class)->forFrontend('forum')->to($assets);
|
||||||
|
$this->app->make(AddLocaleAssets::class)->to($assets);
|
||||||
|
|
||||||
return $assets;
|
return $assets;
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->app->bind('flarum.forum.frontend', function () {
|
$this->app->bind('flarum.frontend.forum', function () {
|
||||||
$view = $this->app->make('flarum.frontend.view.defaults')('forum');
|
return $this->app->make('flarum.frontend.factory')('forum');
|
||||||
|
|
||||||
$view->setAssets($this->app->make('flarum.forum.assets'));
|
|
||||||
|
|
||||||
return $view;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,9 +114,18 @@ class ForumServiceProvider extends AbstractServiceProvider
|
|||||||
'settings' => $this->app->make(SettingsRepositoryInterface::class)
|
'settings' => $this->app->make(SettingsRepositoryInterface::class)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->app->make('events')->subscribe(
|
$events = $this->app->make('events');
|
||||||
|
|
||||||
|
$events->subscribe(
|
||||||
new RecompileFrontendAssets(
|
new RecompileFrontendAssets(
|
||||||
$this->app->make('flarum.forum.assets'),
|
$this->app->make('flarum.assets.forum'),
|
||||||
|
$this->app->make('flarum.locales')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$events->subscribe(
|
||||||
|
new ValidateCustomLess(
|
||||||
|
$this->app->make('flarum.assets.forum'),
|
||||||
$this->app->make('flarum.locales'),
|
$this->app->make('flarum.locales'),
|
||||||
$this->app
|
$this->app
|
||||||
)
|
)
|
||||||
|
@@ -12,8 +12,7 @@
|
|||||||
namespace Flarum\Forum;
|
namespace Flarum\Forum;
|
||||||
|
|
||||||
use Flarum\Foundation\ValidationException;
|
use Flarum\Foundation\ValidationException;
|
||||||
use Flarum\Frontend\CompilerFactory;
|
use Flarum\Frontend\Assets;
|
||||||
use Flarum\Frontend\RecompileFrontendAssets as BaseListener;
|
|
||||||
use Flarum\Locale\LocaleManager;
|
use Flarum\Locale\LocaleManager;
|
||||||
use Flarum\Settings\Event\Saved;
|
use Flarum\Settings\Event\Saved;
|
||||||
use Flarum\Settings\Event\Saving;
|
use Flarum\Settings\Event\Saving;
|
||||||
@@ -26,22 +25,32 @@ use League\Flysystem\Adapter\NullAdapter;
|
|||||||
use League\Flysystem\Filesystem;
|
use League\Flysystem\Filesystem;
|
||||||
use Less_Exception_Parser;
|
use Less_Exception_Parser;
|
||||||
|
|
||||||
class RecompileFrontendAssets extends BaseListener
|
class ValidateCustomLess
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var Assets
|
||||||
|
*/
|
||||||
|
protected $assets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LocaleManager
|
||||||
|
*/
|
||||||
|
protected $locales;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Container
|
* @var Container
|
||||||
*/
|
*/
|
||||||
protected $container;
|
protected $container;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CompilerFactory $assets
|
* @param Assets $assets
|
||||||
* @param LocaleManager $locales
|
* @param LocaleManager $locales
|
||||||
* @param Container $container
|
* @param Container $container
|
||||||
*/
|
*/
|
||||||
public function __construct(CompilerFactory $assets, LocaleManager $locales, Container $container)
|
public function __construct(Assets $assets, LocaleManager $locales, Container $container)
|
||||||
{
|
{
|
||||||
parent::__construct($assets, $locales);
|
$this->assets = $assets;
|
||||||
|
$this->locales = $locales;
|
||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +59,8 @@ class RecompileFrontendAssets extends BaseListener
|
|||||||
*/
|
*/
|
||||||
public function subscribe(Dispatcher $events)
|
public function subscribe(Dispatcher $events)
|
||||||
{
|
{
|
||||||
parent::subscribe($events);
|
|
||||||
|
|
||||||
$events->listen(Saving::class, [$this, 'whenSettingsSaving']);
|
$events->listen(Saving::class, [$this, 'whenSettingsSaving']);
|
||||||
|
$events->listen(Saved::class, [$this, 'whenSettingsSaved']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,10 +109,12 @@ class RecompileFrontendAssets extends BaseListener
|
|||||||
*/
|
*/
|
||||||
public function whenSettingsSaved(Saved $event)
|
public function whenSettingsSaved(Saved $event)
|
||||||
{
|
{
|
||||||
parent::whenSettingsSaved($event);
|
|
||||||
|
|
||||||
if (isset($event->settings['custom_less'])) {
|
if (isset($event->settings['custom_less'])) {
|
||||||
$this->flushCss();
|
$this->assets->makeCss()->flush();
|
||||||
|
|
||||||
|
foreach ($this->locales->getLocales() as $locale => $name) {
|
||||||
|
$this->assets->makeLocaleCss($locale)->flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -25,7 +25,7 @@ class Application extends Container implements ApplicationContract
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
const VERSION = '0.1.0-beta.7';
|
const VERSION = '0.1.0-beta.8.1';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base path for the Flarum installation.
|
* The base path for the Flarum installation.
|
||||||
|
46
src/Frontend/AddLocaleAssets.php
Normal file
46
src/Frontend/AddLocaleAssets.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Frontend;
|
||||||
|
|
||||||
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
|
use Flarum\Locale\LocaleManager;
|
||||||
|
|
||||||
|
class AddLocaleAssets
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var LocaleManager
|
||||||
|
*/
|
||||||
|
protected $locales;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LocaleManager $locales
|
||||||
|
*/
|
||||||
|
public function __construct(LocaleManager $locales)
|
||||||
|
{
|
||||||
|
$this->locales = $locales;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function to(Assets $assets)
|
||||||
|
{
|
||||||
|
$assets->localeJs(function (SourceCollector $sources, string $locale) {
|
||||||
|
foreach ($this->locales->getJsFiles($locale) as $file) {
|
||||||
|
$sources->addFile($file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$assets->localeCss(function (SourceCollector $sources, string $locale) {
|
||||||
|
foreach ($this->locales->getCssFiles($locale) as $file) {
|
||||||
|
$sources->addFile($file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
64
src/Frontend/AddTranslations.php
Normal file
64
src/Frontend/AddTranslations.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Frontend;
|
||||||
|
|
||||||
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
|
use Flarum\Locale\LocaleManager;
|
||||||
|
|
||||||
|
class AddTranslations
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var LocaleManager
|
||||||
|
*/
|
||||||
|
protected $locales;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable
|
||||||
|
*/
|
||||||
|
protected $filter;
|
||||||
|
|
||||||
|
public function __construct(LocaleManager $locales, callable $filter = null)
|
||||||
|
{
|
||||||
|
$this->locales = $locales;
|
||||||
|
$this->filter = $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function forFrontend(string $name)
|
||||||
|
{
|
||||||
|
$this->filter = function (string $id) use ($name) {
|
||||||
|
return preg_match('/^.+(?:\.|::)(?:'.$name.'|lib)\./', $id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function to(Assets $assets)
|
||||||
|
{
|
||||||
|
$assets->localeJs(function (SourceCollector $sources, string $locale) {
|
||||||
|
$sources->addString(function () use ($locale) {
|
||||||
|
$translations = $this->getTranslations($locale);
|
||||||
|
|
||||||
|
return 'flarum.core.app.translator.addTranslations('.json_encode($translations).')';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTranslations(string $locale)
|
||||||
|
{
|
||||||
|
$translations = $this->locales->getTranslator()->getCatalogue($locale)->all('messages');
|
||||||
|
|
||||||
|
return array_only(
|
||||||
|
$translations,
|
||||||
|
array_filter(array_keys($translations), $this->filter)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Asset;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
|
|
||||||
interface AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param SourceCollector $sources
|
|
||||||
*/
|
|
||||||
public function js(SourceCollector $sources);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SourceCollector $sources
|
|
||||||
*/
|
|
||||||
public function css(SourceCollector $sources);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SourceCollector $sources
|
|
||||||
* @param string $locale
|
|
||||||
*/
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SourceCollector $sources
|
|
||||||
* @param string $locale
|
|
||||||
*/
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale);
|
|
||||||
}
|
|
@@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Asset;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
|
|
||||||
class CoreAssets implements AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public function __construct(string $name)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function js(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
$sources->addFile(__DIR__."/../../../js/dist/$this->name.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
$sources->addFile(__DIR__."/../../../less/$this->name.less");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,82 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Asset;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
|
|
||||||
class ExtensionAssets implements AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $moduleName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $css;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|callable|null
|
|
||||||
*/
|
|
||||||
protected $js;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $moduleName
|
|
||||||
* @param array $css
|
|
||||||
* @param string|callable|null $js
|
|
||||||
*/
|
|
||||||
public function __construct(string $moduleName, array $css, $js = null)
|
|
||||||
{
|
|
||||||
$this->moduleName = $moduleName;
|
|
||||||
$this->css = $css;
|
|
||||||
$this->js = $js;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function js(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
if ($this->js) {
|
|
||||||
$sources->addString(function () {
|
|
||||||
return 'var module={}';
|
|
||||||
});
|
|
||||||
|
|
||||||
if (is_callable($this->js)) {
|
|
||||||
$sources->addString($this->js);
|
|
||||||
} else {
|
|
||||||
$sources->addFile($this->js);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sources->addString(function () {
|
|
||||||
return "flarum.extensions['$this->moduleName']=module.exports";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
foreach ($this->css as $asset) {
|
|
||||||
if (is_callable($asset)) {
|
|
||||||
$sources->addString($asset);
|
|
||||||
} else {
|
|
||||||
$sources->addFile($asset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Asset;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
|
||||||
|
|
||||||
class LessVariables implements AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var SettingsRepositoryInterface
|
|
||||||
*/
|
|
||||||
protected $settings;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SettingsRepositoryInterface $settings
|
|
||||||
*/
|
|
||||||
public function __construct(SettingsRepositoryInterface $settings)
|
|
||||||
{
|
|
||||||
$this->settings = $settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
$this->addLessVariables($sources);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
$this->addLessVariables($sources);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addLessVariables(SourceCollector $compiler)
|
|
||||||
{
|
|
||||||
$vars = [
|
|
||||||
'config-primary-color' => $this->settings->get('theme_primary_color', '#000'),
|
|
||||||
'config-secondary-color' => $this->settings->get('theme_secondary_color', '#000'),
|
|
||||||
'config-dark-mode' => $this->settings->get('theme_dark_mode') ? 'true' : 'false',
|
|
||||||
'config-colored-header' => $this->settings->get('theme_colored_header') ? 'true' : 'false'
|
|
||||||
];
|
|
||||||
|
|
||||||
$compiler->addString(function () use ($vars) {
|
|
||||||
return array_reduce(array_keys($vars), function ($string, $name) use ($vars) {
|
|
||||||
return $string."@$name: {$vars[$name]};";
|
|
||||||
}, '');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function js(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Asset;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
use Flarum\Locale\LocaleManager;
|
|
||||||
|
|
||||||
class LocaleAssets implements AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var LocaleManager
|
|
||||||
*/
|
|
||||||
protected $locales;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param LocaleManager $locales
|
|
||||||
*/
|
|
||||||
public function __construct(LocaleManager $locales)
|
|
||||||
{
|
|
||||||
$this->locales = $locales;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
foreach ($this->locales->getJsFiles($locale) as $file) {
|
|
||||||
$sources->addFile($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
foreach ($this->locales->getCssFiles($locale) as $file) {
|
|
||||||
$sources->addFile($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function js(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,87 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Asset;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
use Flarum\Locale\LocaleManager;
|
|
||||||
|
|
||||||
class Translations implements AssetInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var LocaleManager
|
|
||||||
*/
|
|
||||||
protected $locales;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var callable
|
|
||||||
*/
|
|
||||||
protected $filter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param LocaleManager $locales
|
|
||||||
*/
|
|
||||||
public function __construct(LocaleManager $locales)
|
|
||||||
{
|
|
||||||
$this->locales = $locales;
|
|
||||||
|
|
||||||
$this->filter = function () {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeJs(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
$sources->addString(function () use ($locale) {
|
|
||||||
$translations = $this->getTranslations($locale);
|
|
||||||
|
|
||||||
return 'flarum.core.app.translator.addTranslations('.json_encode($translations).')';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getTranslations(string $locale)
|
|
||||||
{
|
|
||||||
$translations = $this->locales->getTranslator()->getCatalogue($locale)->all('messages');
|
|
||||||
|
|
||||||
return array_only(
|
|
||||||
$translations,
|
|
||||||
array_filter(array_keys($translations), $this->filter)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return callable
|
|
||||||
*/
|
|
||||||
public function getFilter(): callable
|
|
||||||
{
|
|
||||||
return $this->filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param callable $filter
|
|
||||||
*/
|
|
||||||
public function setFilter(callable $filter)
|
|
||||||
{
|
|
||||||
$this->filter = $filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function js(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function css(SourceCollector $sources)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function localeCss(SourceCollector $sources, string $locale)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
195
src/Frontend/Assets.php
Normal file
195
src/Frontend/Assets.php
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Frontend;
|
||||||
|
|
||||||
|
use Flarum\Frontend\Compiler\CompilerInterface;
|
||||||
|
use Flarum\Frontend\Compiler\JsCompiler;
|
||||||
|
use Flarum\Frontend\Compiler\LessCompiler;
|
||||||
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
|
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory class for creating frontend asset compilers.
|
||||||
|
*/
|
||||||
|
class Assets
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $sources = [
|
||||||
|
'js' => [],
|
||||||
|
'css' => [],
|
||||||
|
'localeJs' => [],
|
||||||
|
'localeCss' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Filesystem
|
||||||
|
*/
|
||||||
|
protected $assetsDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $cacheDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $lessImportDirs;
|
||||||
|
|
||||||
|
public function __construct(string $name, Filesystem $assetsDir, string $cacheDir = null, array $lessImportDirs = null)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->assetsDir = $assetsDir;
|
||||||
|
$this->cacheDir = $cacheDir;
|
||||||
|
$this->lessImportDirs = $lessImportDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function js($sources)
|
||||||
|
{
|
||||||
|
$this->addSources('js', $sources);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function css($callback)
|
||||||
|
{
|
||||||
|
$this->addSources('css', $callback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function localeJs($callback)
|
||||||
|
{
|
||||||
|
$this->addSources('localeJs', $callback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function localeCss($callback)
|
||||||
|
{
|
||||||
|
$this->addSources('localeCss', $callback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addSources($type, $callback)
|
||||||
|
{
|
||||||
|
$this->sources[$type][] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function populate(CompilerInterface $compiler, string $type, string $locale = null)
|
||||||
|
{
|
||||||
|
$compiler->addSources(function (SourceCollector $sources) use ($type, $locale) {
|
||||||
|
foreach ($this->sources[$type] as $callback) {
|
||||||
|
$callback($sources, $locale);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeJs(): JsCompiler
|
||||||
|
{
|
||||||
|
$compiler = new JsCompiler($this->assetsDir, $this->name.'.js');
|
||||||
|
|
||||||
|
$this->populate($compiler, 'js');
|
||||||
|
|
||||||
|
return $compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeCss(): LessCompiler
|
||||||
|
{
|
||||||
|
$compiler = $this->makeLessCompiler($this->name.'.css');
|
||||||
|
|
||||||
|
$this->populate($compiler, 'css');
|
||||||
|
|
||||||
|
return $compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeLocaleJs(string $locale): JsCompiler
|
||||||
|
{
|
||||||
|
$compiler = new JsCompiler($this->assetsDir, $this->name.'-'.$locale.'.js');
|
||||||
|
|
||||||
|
$this->populate($compiler, 'localeJs', $locale);
|
||||||
|
|
||||||
|
return $compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function makeLocaleCss(string $locale): LessCompiler
|
||||||
|
{
|
||||||
|
$compiler = $this->makeLessCompiler($this->name.'-'.$locale.'.css');
|
||||||
|
|
||||||
|
$this->populate($compiler, 'localeCss', $locale);
|
||||||
|
|
||||||
|
return $compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function makeLessCompiler(string $filename): LessCompiler
|
||||||
|
{
|
||||||
|
$compiler = new LessCompiler($this->assetsDir, $filename);
|
||||||
|
|
||||||
|
if ($this->cacheDir) {
|
||||||
|
$compiler->setCacheDir($this->cacheDir.'/less');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->lessImportDirs) {
|
||||||
|
$compiler->setImportDirs($this->lessImportDirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $compiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(string $name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAssetsDir(): Filesystem
|
||||||
|
{
|
||||||
|
return $this->assetsDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAssetsDir(Filesystem $assetsDir)
|
||||||
|
{
|
||||||
|
$this->assetsDir = $assetsDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCacheDir(): ?string
|
||||||
|
{
|
||||||
|
return $this->cacheDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCacheDir(?string $cacheDir)
|
||||||
|
{
|
||||||
|
$this->cacheDir = $cacheDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLessImportDirs(): array
|
||||||
|
{
|
||||||
|
return $this->lessImportDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLessImportDirs(array $lessImportDirs)
|
||||||
|
{
|
||||||
|
$this->lessImportDirs = $lessImportDirs;
|
||||||
|
}
|
||||||
|
}
|
@@ -21,6 +21,10 @@ class JsCompiler extends RevisionCompiler
|
|||||||
*/
|
*/
|
||||||
protected function save(string $file, array $sources): bool
|
protected function save(string $file, array $sources): bool
|
||||||
{
|
{
|
||||||
|
if (empty($sources)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$mapFile = $file.'.map';
|
$mapFile = $file.'.map';
|
||||||
|
|
||||||
$map = new SourceMap();
|
$map = new SourceMap();
|
||||||
@@ -67,7 +71,7 @@ class JsCompiler extends RevisionCompiler
|
|||||||
*/
|
*/
|
||||||
protected function format(string $string): string
|
protected function format(string $string): string
|
||||||
{
|
{
|
||||||
return preg_replace('~//# sourceMappingURL.*$~s', '', $string).";\n";
|
return preg_replace('~//# sourceMappingURL.*$~m', '', $string).";\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -59,7 +59,7 @@ class LessCompiler extends RevisionCompiler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* @throws \Less_Exception_Parser
|
||||||
*/
|
*/
|
||||||
protected function compile(array $sources): string
|
protected function compile(array $sources): string
|
||||||
{
|
{
|
||||||
|
@@ -13,7 +13,7 @@ namespace Flarum\Frontend\Compiler;
|
|||||||
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
use Flarum\Frontend\Compiler\Source\SourceInterface;
|
use Flarum\Frontend\Compiler\Source\SourceInterface;
|
||||||
use Illuminate\Filesystem\FilesystemAdapter;
|
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||||
|
|
||||||
class RevisionCompiler implements CompilerInterface
|
class RevisionCompiler implements CompilerInterface
|
||||||
{
|
{
|
||||||
@@ -22,7 +22,7 @@ class RevisionCompiler implements CompilerInterface
|
|||||||
const EMPTY_REVISION = 'empty';
|
const EMPTY_REVISION = 'empty';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var FilesystemAdapter
|
* @var Filesystem
|
||||||
*/
|
*/
|
||||||
protected $assetsDir;
|
protected $assetsDir;
|
||||||
|
|
||||||
@@ -37,10 +37,10 @@ class RevisionCompiler implements CompilerInterface
|
|||||||
protected $sourcesCallbacks = [];
|
protected $sourcesCallbacks = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param FilesystemAdapter $assetsDir
|
* @param Filesystem $assetsDir
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
*/
|
*/
|
||||||
public function __construct(FilesystemAdapter $assetsDir, string $filename)
|
public function __construct(Filesystem $assetsDir, string $filename)
|
||||||
{
|
{
|
||||||
$this->assetsDir = $assetsDir;
|
$this->assetsDir = $assetsDir;
|
||||||
$this->filename = $filename;
|
$this->filename = $filename;
|
||||||
|
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace Flarum\Frontend\Compiler\Source;
|
namespace Flarum\Frontend\Compiler\Source;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
class FileSource implements SourceInterface
|
class FileSource implements SourceInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -23,6 +25,10 @@ class FileSource implements SourceInterface
|
|||||||
*/
|
*/
|
||||||
public function __construct(string $path)
|
public function __construct(string $path)
|
||||||
{
|
{
|
||||||
|
if (! file_exists($path)) {
|
||||||
|
throw new InvalidArgumentException("File not found at path: $path");
|
||||||
|
}
|
||||||
|
|
||||||
$this->path = $path;
|
$this->path = $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,248 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend;
|
|
||||||
|
|
||||||
use Flarum\Frontend\Asset\AssetInterface;
|
|
||||||
use Flarum\Frontend\Compiler\CompilerInterface;
|
|
||||||
use Flarum\Frontend\Compiler\JsCompiler;
|
|
||||||
use Flarum\Frontend\Compiler\LessCompiler;
|
|
||||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
|
||||||
use Illuminate\Filesystem\FilesystemAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory class for creating frontend asset compilers.
|
|
||||||
*/
|
|
||||||
class CompilerFactory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var FilesystemAdapter
|
|
||||||
*/
|
|
||||||
protected $assetsDir;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $cacheDir;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $lessImportDirs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var AssetInterface[]
|
|
||||||
*/
|
|
||||||
protected $assets = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var callable[]
|
|
||||||
*/
|
|
||||||
protected $addCallbacks = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
* @param FilesystemAdapter $assetsDir
|
|
||||||
* @param string $cacheDir
|
|
||||||
* @param array|null $lessImportDirs
|
|
||||||
*/
|
|
||||||
public function __construct(string $name, FilesystemAdapter $assetsDir, string $cacheDir = null, array $lessImportDirs = null)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->assetsDir = $assetsDir;
|
|
||||||
$this->cacheDir = $cacheDir;
|
|
||||||
$this->lessImportDirs = $lessImportDirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function inherit($childName): self
|
|
||||||
{
|
|
||||||
// TODO: Probably some more deep cloning necessary here
|
|
||||||
$child = clone $this;
|
|
||||||
$child->name = $childName;
|
|
||||||
|
|
||||||
return $child;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param callable $callback
|
|
||||||
*/
|
|
||||||
public function add(callable $callback)
|
|
||||||
{
|
|
||||||
$this->addCallbacks[] = $callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return JsCompiler
|
|
||||||
*/
|
|
||||||
public function makeJs(): JsCompiler
|
|
||||||
{
|
|
||||||
$compiler = new JsCompiler($this->assetsDir, $this->name.'.js');
|
|
||||||
|
|
||||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) {
|
|
||||||
$asset->js($sources);
|
|
||||||
});
|
|
||||||
|
|
||||||
return $compiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return LessCompiler
|
|
||||||
*/
|
|
||||||
public function makeCss(): LessCompiler
|
|
||||||
{
|
|
||||||
$compiler = $this->makeLessCompiler($this->name.'.css');
|
|
||||||
|
|
||||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) {
|
|
||||||
$asset->css($sources);
|
|
||||||
});
|
|
||||||
|
|
||||||
return $compiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $locale
|
|
||||||
* @return JsCompiler
|
|
||||||
*/
|
|
||||||
public function makeLocaleJs(string $locale): JsCompiler
|
|
||||||
{
|
|
||||||
$compiler = new JsCompiler($this->assetsDir, $this->name.'-'.$locale.'.js');
|
|
||||||
|
|
||||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) use ($locale) {
|
|
||||||
$asset->localeJs($sources, $locale);
|
|
||||||
});
|
|
||||||
|
|
||||||
return $compiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $locale
|
|
||||||
* @return LessCompiler
|
|
||||||
*/
|
|
||||||
public function makeLocaleCss(string $locale): LessCompiler
|
|
||||||
{
|
|
||||||
$compiler = $this->makeLessCompiler($this->name.'-'.$locale.'.css');
|
|
||||||
|
|
||||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) use ($locale) {
|
|
||||||
$asset->localeCss($sources, $locale);
|
|
||||||
});
|
|
||||||
|
|
||||||
return $compiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filename
|
|
||||||
* @return LessCompiler
|
|
||||||
*/
|
|
||||||
protected function makeLessCompiler(string $filename): LessCompiler
|
|
||||||
{
|
|
||||||
$compiler = new LessCompiler($this->assetsDir, $filename);
|
|
||||||
|
|
||||||
if ($this->cacheDir) {
|
|
||||||
$compiler->setCacheDir($this->cacheDir.'/less');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->lessImportDirs) {
|
|
||||||
$compiler->setImportDirs($this->lessImportDirs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $compiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function fireAddCallbacks()
|
|
||||||
{
|
|
||||||
foreach ($this->addCallbacks as $callback) {
|
|
||||||
$assets = $callback($this);
|
|
||||||
$this->assets = array_merge($this->assets, is_array($assets) ? $assets : [$assets]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addCallbacks = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addSources(CompilerInterface $compiler, callable $callback)
|
|
||||||
{
|
|
||||||
$compiler->addSources(function ($sources) use ($callback) {
|
|
||||||
$this->fireAddCallbacks();
|
|
||||||
|
|
||||||
foreach ($this->assets as $asset) {
|
|
||||||
$callback($asset, $sources);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName(): string
|
|
||||||
{
|
|
||||||
return $this->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public function setName(string $name)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return FilesystemAdapter
|
|
||||||
*/
|
|
||||||
public function getAssetsDir(): FilesystemAdapter
|
|
||||||
{
|
|
||||||
return $this->assetsDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param FilesystemAdapter $assetsDir
|
|
||||||
*/
|
|
||||||
public function setAssetsDir(FilesystemAdapter $assetsDir)
|
|
||||||
{
|
|
||||||
$this->assetsDir = $assetsDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getCacheDir(): ?string
|
|
||||||
{
|
|
||||||
return $this->cacheDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $cacheDir
|
|
||||||
*/
|
|
||||||
public function setCacheDir(?string $cacheDir)
|
|
||||||
{
|
|
||||||
$this->cacheDir = $cacheDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getLessImportDirs(): array
|
|
||||||
{
|
|
||||||
return $this->lessImportDirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $lessImportDirs
|
|
||||||
*/
|
|
||||||
public function setLessImportDirs(array $lessImportDirs)
|
|
||||||
{
|
|
||||||
$this->lessImportDirs = $lessImportDirs;
|
|
||||||
}
|
|
||||||
}
|
|
74
src/Frontend/Content/Assets.php
Normal file
74
src/Frontend/Content/Assets.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Frontend\Content;
|
||||||
|
|
||||||
|
use Flarum\Foundation\Application;
|
||||||
|
use Flarum\Frontend\Compiler\CompilerInterface;
|
||||||
|
use Flarum\Frontend\Document;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
|
class Assets
|
||||||
|
{
|
||||||
|
protected $app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Flarum\Frontend\Assets
|
||||||
|
*/
|
||||||
|
protected $assets;
|
||||||
|
|
||||||
|
public function __construct(Application $app)
|
||||||
|
{
|
||||||
|
$this->app = $app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function forFrontend(string $name)
|
||||||
|
{
|
||||||
|
$this->assets = $this->app->make('flarum.assets.'.$name);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(Document $document, Request $request)
|
||||||
|
{
|
||||||
|
$locale = $request->getAttribute('locale');
|
||||||
|
|
||||||
|
$compilers = [
|
||||||
|
'js' => [$this->assets->makeJs(), $this->assets->makeLocaleJs($locale)],
|
||||||
|
'css' => [$this->assets->makeCss(), $this->assets->makeLocaleCss($locale)]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->app->inDebugMode()) {
|
||||||
|
$this->commit(array_flatten($compilers));
|
||||||
|
}
|
||||||
|
|
||||||
|
$document->js = array_merge($document->js, $this->getUrls($compilers['js']));
|
||||||
|
$document->css = array_merge($document->css, $this->getUrls($compilers['css']));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function commit(array $compilers)
|
||||||
|
{
|
||||||
|
foreach ($compilers as $compiler) {
|
||||||
|
$compiler->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CompilerInterface[] $compilers
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private function getUrls(array $compilers)
|
||||||
|
{
|
||||||
|
return array_filter(array_map(function (CompilerInterface $compiler) {
|
||||||
|
return $compiler->getUrl();
|
||||||
|
}, $compilers));
|
||||||
|
}
|
||||||
|
}
|
@@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Content;
|
|
||||||
|
|
||||||
use Flarum\Frontend\HtmlDocument;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
|
|
||||||
interface ContentInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param HtmlDocument $document
|
|
||||||
* @param Request $request
|
|
||||||
*/
|
|
||||||
public function __invoke(HtmlDocument $document, Request $request);
|
|
||||||
}
|
|
@@ -13,13 +13,13 @@ namespace Flarum\Frontend\Content;
|
|||||||
|
|
||||||
use Flarum\Api\Client;
|
use Flarum\Api\Client;
|
||||||
use Flarum\Api\Controller\ShowUserController;
|
use Flarum\Api\Controller\ShowUserController;
|
||||||
use Flarum\Frontend\HtmlDocument;
|
use Flarum\Frontend\Document;
|
||||||
use Flarum\Locale\LocaleManager;
|
use Flarum\Locale\LocaleManager;
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
class CorePayload implements ContentInterface
|
class CorePayload
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var LocaleManager
|
* @var LocaleManager
|
||||||
@@ -41,7 +41,7 @@ class CorePayload implements ContentInterface
|
|||||||
$this->api = $api;
|
$this->api = $api;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(HtmlDocument $document, Request $request)
|
public function __invoke(Document $document, Request $request)
|
||||||
{
|
{
|
||||||
$document->payload = array_merge(
|
$document->payload = array_merge(
|
||||||
$document->payload,
|
$document->payload,
|
||||||
@@ -49,7 +49,7 @@ class CorePayload implements ContentInterface
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildPayload(HtmlDocument $document, Request $request)
|
private function buildPayload(Document $document, Request $request)
|
||||||
{
|
{
|
||||||
$data = $this->getDataFromApiDocument($document->getForumApiDocument());
|
$data = $this->getDataFromApiDocument($document->getForumApiDocument());
|
||||||
|
|
||||||
|
@@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend\Content;
|
|
||||||
|
|
||||||
use Flarum\Frontend\HtmlDocument;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
|
|
||||||
class Layout implements ContentInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $layoutView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $layoutView
|
|
||||||
*/
|
|
||||||
public function __construct(string $layoutView)
|
|
||||||
{
|
|
||||||
$this->layoutView = $layoutView;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke(HtmlDocument $document, Request $request)
|
|
||||||
{
|
|
||||||
$document->layoutView = $this->layoutView;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -11,31 +11,31 @@
|
|||||||
|
|
||||||
namespace Flarum\Frontend\Content;
|
namespace Flarum\Frontend\Content;
|
||||||
|
|
||||||
use Flarum\Frontend\HtmlDocument;
|
use Flarum\Frontend\Document;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
class Meta implements ContentInterface
|
class Meta
|
||||||
{
|
{
|
||||||
public function __invoke(HtmlDocument $document, Request $request)
|
public function __invoke(Document $document, Request $request)
|
||||||
{
|
{
|
||||||
$document->meta = array_merge($document->meta, $this->buildMeta($document));
|
$document->meta = array_merge($document->meta, $this->buildMeta($document));
|
||||||
$document->head = array_merge($document->head, $this->buildHead($document));
|
$document->head = array_merge($document->head, $this->buildHead($document));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildMeta(HtmlDocument $document)
|
private function buildMeta(Document $document)
|
||||||
{
|
{
|
||||||
$forumApiDocument = $document->getForumApiDocument();
|
$forumApiDocument = $document->getForumApiDocument();
|
||||||
|
|
||||||
$meta = [
|
$meta = [
|
||||||
'viewport' => 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1',
|
'viewport' => 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1',
|
||||||
'description' => array_get($forumApiDocument, 'data.attributes.forumDescription'),
|
'description' => array_get($forumApiDocument, 'data.attributes.description'),
|
||||||
'theme-color' => array_get($forumApiDocument, 'data.attributes.themePrimaryColor')
|
'theme-color' => array_get($forumApiDocument, 'data.attributes.themePrimaryColor')
|
||||||
];
|
];
|
||||||
|
|
||||||
return $meta;
|
return $meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildHead(HtmlDocument $document)
|
private function buildHead(Document $document)
|
||||||
{
|
{
|
||||||
$head = [];
|
$head = [];
|
||||||
|
|
||||||
|
@@ -19,25 +19,19 @@ use Zend\Diactoros\Response\HtmlResponse;
|
|||||||
class Controller implements RequestHandlerInterface
|
class Controller implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var HtmlDocumentFactory
|
* @var Frontend
|
||||||
*/
|
*/
|
||||||
protected $document;
|
protected $frontend;
|
||||||
|
|
||||||
/**
|
public function __construct(Frontend $frontend)
|
||||||
* @param HtmlDocumentFactory $document
|
|
||||||
*/
|
|
||||||
public function __construct(HtmlDocumentFactory $document)
|
|
||||||
{
|
{
|
||||||
$this->document = $document;
|
$this->frontend = $frontend;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function handle(Request $request): Response
|
public function handle(Request $request): Response
|
||||||
{
|
{
|
||||||
return new HtmlResponse(
|
return new HtmlResponse(
|
||||||
$this->document->make($request)->render()
|
$this->frontend->document($request)->render()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ use Illuminate\Contracts\View\View;
|
|||||||
/**
|
/**
|
||||||
* A view which renders a HTML skeleton for Flarum's frontend app.
|
* A view which renders a HTML skeleton for Flarum's frontend app.
|
||||||
*/
|
*/
|
||||||
class HtmlDocument implements Renderable
|
class Document implements Renderable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The title of the document, displayed in the <title> tag.
|
* The title of the document, displayed in the <title> tag.
|
84
src/Frontend/Frontend.php
Normal file
84
src/Frontend/Frontend.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Frontend;
|
||||||
|
|
||||||
|
use Flarum\Api\Client;
|
||||||
|
use Flarum\Api\Controller\ShowForumController;
|
||||||
|
use Illuminate\Contracts\View\Factory;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
|
class Frontend
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Factory
|
||||||
|
*/
|
||||||
|
protected $view;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
protected $api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable[]
|
||||||
|
*/
|
||||||
|
protected $content = [];
|
||||||
|
|
||||||
|
public function __construct(Factory $view, Client $api)
|
||||||
|
{
|
||||||
|
$this->view = $view;
|
||||||
|
$this->api = $api;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $content
|
||||||
|
*/
|
||||||
|
public function content(callable $content)
|
||||||
|
{
|
||||||
|
$this->content[] = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function document(Request $request): Document
|
||||||
|
{
|
||||||
|
$forumDocument = $this->getForumDocument($request);
|
||||||
|
|
||||||
|
$document = new Document($this->view, $forumDocument);
|
||||||
|
|
||||||
|
$this->populate($document, $request);
|
||||||
|
|
||||||
|
return $document;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function populate(Document $document, Request $request)
|
||||||
|
{
|
||||||
|
foreach ($this->content as $content) {
|
||||||
|
$content($document, $request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getForumDocument(Request $request): array
|
||||||
|
{
|
||||||
|
$actor = $request->getAttribute('actor');
|
||||||
|
|
||||||
|
$this->api->setErrorHandler(null);
|
||||||
|
|
||||||
|
return $this->getResponseBody(
|
||||||
|
$this->api->send(ShowForumController::class, $actor)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getResponseBody(Response $response): array
|
||||||
|
{
|
||||||
|
return json_decode($response->getBody(), true);
|
||||||
|
}
|
||||||
|
}
|
@@ -12,21 +12,18 @@
|
|||||||
namespace Flarum\Frontend;
|
namespace Flarum\Frontend;
|
||||||
|
|
||||||
use Flarum\Foundation\AbstractServiceProvider;
|
use Flarum\Foundation\AbstractServiceProvider;
|
||||||
|
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||||
use Flarum\Http\UrlGenerator;
|
use Flarum\Http\UrlGenerator;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||||
|
|
||||||
class FrontendServiceProvider extends AbstractServiceProvider
|
class FrontendServiceProvider extends AbstractServiceProvider
|
||||||
{
|
{
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
// Yo dawg, I heard you like factories, so I made you a factory to
|
$this->app->singleton('flarum.assets.factory', function () {
|
||||||
// create your factory. We expose a couple of factory functions that
|
|
||||||
// will create frontend factories and configure them with some default
|
|
||||||
// settings common to both the forum and admin frontends.
|
|
||||||
|
|
||||||
$this->app->singleton('flarum.frontend.assets.defaults', function () {
|
|
||||||
return function (string $name) {
|
return function (string $name) {
|
||||||
$assets = new CompilerFactory(
|
$assets = new Assets(
|
||||||
$name,
|
$name,
|
||||||
$this->app->make('filesystem')->disk('flarum-assets'),
|
$this->app->make('filesystem')->disk('flarum-assets'),
|
||||||
$this->app->storagePath()
|
$this->app->storagePath()
|
||||||
@@ -36,35 +33,26 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
|||||||
$this->app->basePath().'/vendor/components/font-awesome/less' => ''
|
$this->app->basePath().'/vendor/components/font-awesome/less' => ''
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$assets->add(function () use ($name) {
|
$assets->css([$this, 'addBaseCss']);
|
||||||
$translations = $this->app->make(Asset\Translations::class);
|
$assets->localeCss([$this, 'addBaseCss']);
|
||||||
$translations->setFilter(function (string $id) use ($name) {
|
|
||||||
return preg_match('/^.+(?:\.|::)(?:'.$name.'|lib)\./', $id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
new Asset\CoreAssets($name),
|
|
||||||
$this->app->make(Asset\LessVariables::class),
|
|
||||||
$translations,
|
|
||||||
$this->app->make(Asset\LocaleAssets::class)
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
return $assets;
|
return $assets;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->app->singleton('flarum.frontend.view.defaults', function () {
|
$this->app->singleton('flarum.frontend.factory', function () {
|
||||||
return function (string $name) {
|
return function (string $name) {
|
||||||
$view = $this->app->make(HtmlDocumentFactory::class);
|
$frontend = $this->app->make(Frontend::class);
|
||||||
|
|
||||||
$view->setCommitAssets($this->app->inDebugMode());
|
$frontend->content(function (Document $document) use ($name) {
|
||||||
|
$document->layoutView = 'flarum::frontend.'.$name;
|
||||||
|
});
|
||||||
|
|
||||||
$view->add(new Content\Layout('flarum::frontend.'.$name));
|
$frontend->content($this->app->make(Content\Assets::class)->forFrontend($name));
|
||||||
$view->add($this->app->make(Content\CorePayload::class));
|
$frontend->content($this->app->make(Content\CorePayload::class));
|
||||||
$view->add($this->app->make(Content\Meta::class));
|
$frontend->content($this->app->make(Content\Meta::class));
|
||||||
|
|
||||||
return $view;
|
return $frontend;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -81,4 +69,30 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
|||||||
'url' => $this->app->make(UrlGenerator::class)
|
'url' => $this->app->make(UrlGenerator::class)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addBaseCss(SourceCollector $sources)
|
||||||
|
{
|
||||||
|
$sources->addFile(base_path().'/vendor/flarum/core/less/common/variables.less');
|
||||||
|
$sources->addFile(base_path().'/vendor/flarum/core/less/common/mixins.less');
|
||||||
|
|
||||||
|
$this->addLessVariables($sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addLessVariables(SourceCollector $sources)
|
||||||
|
{
|
||||||
|
$sources->addString(function () {
|
||||||
|
$settings = $this->app->make(SettingsRepositoryInterface::class);
|
||||||
|
|
||||||
|
$vars = [
|
||||||
|
'config-primary-color' => $settings->get('theme_primary_color', '#000'),
|
||||||
|
'config-secondary-color' => $settings->get('theme_secondary_color', '#000'),
|
||||||
|
'config-dark-mode' => $settings->get('theme_dark_mode') ? 'true' : 'false',
|
||||||
|
'config-colored-header' => $settings->get('theme_colored_header') ? 'true' : 'false'
|
||||||
|
];
|
||||||
|
|
||||||
|
return array_reduce(array_keys($vars), function ($string, $name) use ($vars) {
|
||||||
|
return $string."@$name: {$vars[$name]};";
|
||||||
|
}, '');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,182 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Frontend;
|
|
||||||
|
|
||||||
use Flarum\Api\Client;
|
|
||||||
use Flarum\Api\Controller\ShowForumController;
|
|
||||||
use Flarum\Frontend\Compiler\CompilerInterface;
|
|
||||||
use Flarum\Frontend\Content\ContentInterface;
|
|
||||||
use Illuminate\Contracts\View\Factory;
|
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
||||||
|
|
||||||
class HtmlDocumentFactory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Factory
|
|
||||||
*/
|
|
||||||
protected $view;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Client
|
|
||||||
*/
|
|
||||||
protected $api;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var CompilerFactory
|
|
||||||
*/
|
|
||||||
protected $assets;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $commitAssets;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ContentInterface[]
|
|
||||||
*/
|
|
||||||
protected $content = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Factory $view
|
|
||||||
* @param Client $api
|
|
||||||
* @param CompilerFactory|null $assets
|
|
||||||
* @param bool $commitAssets
|
|
||||||
*/
|
|
||||||
public function __construct(Factory $view, Client $api, CompilerFactory $assets = null, bool $commitAssets = false)
|
|
||||||
{
|
|
||||||
$this->view = $view;
|
|
||||||
$this->api = $api;
|
|
||||||
$this->assets = $assets;
|
|
||||||
$this->commitAssets = $commitAssets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ContentInterface|callable $content
|
|
||||||
*/
|
|
||||||
public function add($content)
|
|
||||||
{
|
|
||||||
$this->content[] = $content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @return HtmlDocument
|
|
||||||
*/
|
|
||||||
public function make(Request $request): HtmlDocument
|
|
||||||
{
|
|
||||||
$forumDocument = $this->getForumDocument($request);
|
|
||||||
|
|
||||||
$view = new HtmlDocument($this->view, $forumDocument);
|
|
||||||
|
|
||||||
$locale = $request->getAttribute('locale');
|
|
||||||
|
|
||||||
$js = [$this->assets->makeJs(), $this->assets->makeLocaleJs($locale)];
|
|
||||||
$css = [$this->assets->makeCss(), $this->assets->makeLocaleCss($locale)];
|
|
||||||
|
|
||||||
$this->maybeCommitAssets(array_merge($js, $css));
|
|
||||||
|
|
||||||
$view->js = array_merge($view->js, $this->getUrls($js));
|
|
||||||
$view->css = array_merge($view->css, $this->getUrls($css));
|
|
||||||
|
|
||||||
$this->populate($view, $request);
|
|
||||||
|
|
||||||
return $view;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return CompilerFactory
|
|
||||||
*/
|
|
||||||
public function getAssets(): CompilerFactory
|
|
||||||
{
|
|
||||||
return $this->assets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param CompilerFactory $assets
|
|
||||||
*/
|
|
||||||
public function setAssets(CompilerFactory $assets)
|
|
||||||
{
|
|
||||||
$this->assets = $assets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param HtmlDocument $view
|
|
||||||
* @param Request $request
|
|
||||||
*/
|
|
||||||
protected function populate(HtmlDocument $view, Request $request)
|
|
||||||
{
|
|
||||||
foreach ($this->content as $content) {
|
|
||||||
$content($view, $request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getForumDocument(Request $request): array
|
|
||||||
{
|
|
||||||
$actor = $request->getAttribute('actor');
|
|
||||||
|
|
||||||
$this->api->setErrorHandler(null);
|
|
||||||
|
|
||||||
return $this->getResponseBody(
|
|
||||||
$this->api->send(ShowForumController::class, $actor)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Response $response
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getResponseBody(Response $response)
|
|
||||||
{
|
|
||||||
return json_decode($response->getBody(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function maybeCommitAssets(array $compilers)
|
|
||||||
{
|
|
||||||
if ($this->commitAssets) {
|
|
||||||
foreach ($compilers as $compiler) {
|
|
||||||
$compiler->commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param CompilerInterface[] $compilers
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
private function getUrls(array $compilers)
|
|
||||||
{
|
|
||||||
return array_filter(array_map(function (CompilerInterface $compiler) {
|
|
||||||
return $compiler->getUrl();
|
|
||||||
}, $compilers));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function getCommitAssets(): bool
|
|
||||||
{
|
|
||||||
return $this->commitAssets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bool $commitAssets
|
|
||||||
*/
|
|
||||||
public function setCommitAssets(bool $commitAssets)
|
|
||||||
{
|
|
||||||
$this->commitAssets = $commitAssets;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -21,7 +21,7 @@ use Illuminate\Contracts\Events\Dispatcher;
|
|||||||
class RecompileFrontendAssets
|
class RecompileFrontendAssets
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var CompilerFactory
|
* @var Assets
|
||||||
*/
|
*/
|
||||||
protected $assets;
|
protected $assets;
|
||||||
|
|
||||||
@@ -31,10 +31,10 @@ class RecompileFrontendAssets
|
|||||||
protected $locales;
|
protected $locales;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param CompilerFactory $assets
|
* @param Assets $assets
|
||||||
* @param LocaleManager $locales
|
* @param LocaleManager $locales
|
||||||
*/
|
*/
|
||||||
public function __construct(CompilerFactory $assets, LocaleManager $locales)
|
public function __construct(Assets $assets, LocaleManager $locales)
|
||||||
{
|
{
|
||||||
$this->assets = $assets;
|
$this->assets = $assets;
|
||||||
$this->locales = $locales;
|
$this->locales = $locales;
|
||||||
|
@@ -1,75 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Flarum\Http;
|
|
||||||
|
|
||||||
use Illuminate\Contracts\Container\Container;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
use Psr\Http\Server\RequestHandlerInterface;
|
|
||||||
|
|
||||||
class ControllerRouteHandler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Container
|
|
||||||
*/
|
|
||||||
protected $container;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|callable
|
|
||||||
*/
|
|
||||||
protected $controller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Container $container
|
|
||||||
* @param string|callable $controller
|
|
||||||
*/
|
|
||||||
public function __construct(Container $container, $controller)
|
|
||||||
{
|
|
||||||
$this->container = $container;
|
|
||||||
$this->controller = $controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ServerRequestInterface $request
|
|
||||||
* @param array $routeParams
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
public function __invoke(ServerRequestInterface $request, array $routeParams)
|
|
||||||
{
|
|
||||||
$controller = $this->resolveController();
|
|
||||||
|
|
||||||
$request = $request->withQueryParams(array_merge($request->getQueryParams(), $routeParams));
|
|
||||||
|
|
||||||
return $controller->handle($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return RequestHandlerInterface
|
|
||||||
*/
|
|
||||||
protected function resolveController()
|
|
||||||
{
|
|
||||||
if (is_callable($this->controller)) {
|
|
||||||
$controller = $this->container->call($this->controller);
|
|
||||||
} else {
|
|
||||||
$controller = $this->container->make($this->controller);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! ($controller instanceof RequestHandlerInterface)) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'Controller must be an instance of '.RequestHandlerInterface::class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $controller;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
namespace Flarum\Http\Middleware;
|
namespace Flarum\Http\Middleware;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
@@ -20,6 +19,7 @@ use Psr\Http\Server\MiddlewareInterface as Middleware;
|
|||||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Translation\TranslatorInterface;
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
use Throwable;
|
||||||
use Zend\Diactoros\Response\HtmlResponse;
|
use Zend\Diactoros\Response\HtmlResponse;
|
||||||
|
|
||||||
class HandleErrorsWithView implements Middleware
|
class HandleErrorsWithView implements Middleware
|
||||||
@@ -65,12 +65,12 @@ class HandleErrorsWithView implements Middleware
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $handler->handle($request);
|
return $handler->handle($request);
|
||||||
} catch (Exception $e) {
|
} catch (Throwable $e) {
|
||||||
return $this->formatException($e);
|
return $this->formatException($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatException(Exception $error)
|
protected function formatException(Throwable $error)
|
||||||
{
|
{
|
||||||
$status = 500;
|
$status = 500;
|
||||||
$errorCode = $error->getCode();
|
$errorCode = $error->getCode();
|
||||||
@@ -81,11 +81,10 @@ class HandleErrorsWithView implements Middleware
|
|||||||
$status = $errorCode;
|
$status = $errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the exception (with trace)
|
|
||||||
$this->logger->debug($error);
|
|
||||||
|
|
||||||
if (! $this->view->exists($name = "flarum.forum::error.$status")) {
|
if (! $this->view->exists($name = "flarum.forum::error.$status")) {
|
||||||
$name = 'flarum.forum::error.default';
|
$name = 'flarum.forum::error.default';
|
||||||
|
|
||||||
|
$this->logger->error($error);
|
||||||
}
|
}
|
||||||
|
|
||||||
$view = $this->view->make($name)
|
$view = $this->view->make($name)
|
||||||
|
@@ -11,15 +11,29 @@
|
|||||||
|
|
||||||
namespace Flarum\Http\Middleware;
|
namespace Flarum\Http\Middleware;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Franzl\Middleware\Whoops\WhoopsRunner;
|
use Franzl\Middleware\Whoops\WhoopsRunner;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class HandleErrorsWithWhoops implements Middleware
|
class HandleErrorsWithWhoops implements Middleware
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
|
protected $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param LoggerInterface $logger
|
||||||
|
*/
|
||||||
|
public function __construct(LoggerInterface $logger)
|
||||||
|
{
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Catch all errors that happen during further middleware execution.
|
* Catch all errors that happen during further middleware execution.
|
||||||
*/
|
*/
|
||||||
@@ -27,7 +41,11 @@ class HandleErrorsWithWhoops implements Middleware
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $handler->handle($request);
|
return $handler->handle($request);
|
||||||
} catch (Exception $e) {
|
} catch (Throwable $e) {
|
||||||
|
if ($e->getCode() !== 404) {
|
||||||
|
$this->logger->error($e);
|
||||||
|
}
|
||||||
|
|
||||||
return WhoopsRunner::handle($e, $request);
|
return WhoopsRunner::handle($e, $request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,8 +11,12 @@
|
|||||||
|
|
||||||
namespace Flarum\Http;
|
namespace Flarum\Http;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
use Flarum\Frontend\Controller as FrontendController;
|
use Flarum\Frontend\Controller as FrontendController;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||||
|
|
||||||
class RouteHandlerFactory
|
class RouteHandlerFactory
|
||||||
{
|
{
|
||||||
@@ -21,56 +25,61 @@ class RouteHandlerFactory
|
|||||||
*/
|
*/
|
||||||
protected $container;
|
protected $container;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Container $container
|
|
||||||
*/
|
|
||||||
public function __construct(Container $container)
|
public function __construct(Container $container)
|
||||||
{
|
{
|
||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function toController($controller): Closure
|
||||||
* @param string|callable $controller
|
|
||||||
* @return ControllerRouteHandler
|
|
||||||
*/
|
|
||||||
public function toController($controller)
|
|
||||||
{
|
{
|
||||||
return new ControllerRouteHandler($this->container, $controller);
|
return function (Request $request, array $routeParams) use ($controller) {
|
||||||
|
$controller = $this->resolveController($controller);
|
||||||
|
|
||||||
|
$request = $request->withQueryParams(array_merge($request->getQueryParams(), $routeParams));
|
||||||
|
|
||||||
|
return $controller->handle($request);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $frontend
|
* @param string $frontend
|
||||||
* @param string|null $content
|
* @param string|callable|null $content
|
||||||
* @return ControllerRouteHandler
|
|
||||||
*/
|
*/
|
||||||
public function toFrontend(string $frontend, string $content = null)
|
public function toFrontend(string $frontend, $content = null): Closure
|
||||||
{
|
{
|
||||||
return $this->toController(function (Container $container) use ($frontend, $content) {
|
return $this->toController(function (Container $container) use ($frontend, $content) {
|
||||||
$frontend = $container->make("flarum.$frontend.frontend");
|
$frontend = $container->make("flarum.frontend.$frontend");
|
||||||
|
|
||||||
if ($content) {
|
if ($content) {
|
||||||
$frontend->add($container->make($content));
|
$frontend->content(is_callable($content) ? $content : $container->make($content));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FrontendController($frontend);
|
return new FrontendController($frontend);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function toForum(string $content = null): Closure
|
||||||
* @param string|null $content
|
|
||||||
* @return ControllerRouteHandler
|
|
||||||
*/
|
|
||||||
public function toForum(string $content = null)
|
|
||||||
{
|
{
|
||||||
return $this->toFrontend('forum', $content);
|
return $this->toFrontend('forum', $content);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function toAdmin(string $content = null): Closure
|
||||||
* @param string|null $content
|
|
||||||
* @return ControllerRouteHandler
|
|
||||||
*/
|
|
||||||
public function toAdmin(string $content = null)
|
|
||||||
{
|
{
|
||||||
return $this->toFrontend('admin', $content);
|
return $this->toFrontend('admin', $content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveController($controller): Handler
|
||||||
|
{
|
||||||
|
if (is_callable($controller)) {
|
||||||
|
$controller = $this->container->call($controller);
|
||||||
|
} else {
|
||||||
|
$controller = $this->container->make($controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $controller instanceof Handler) {
|
||||||
|
throw new InvalidArgumentException('Controller must be an instance of '.Handler::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $controller;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,11 +11,14 @@
|
|||||||
|
|
||||||
namespace Flarum\Install\Console;
|
namespace Flarum\Install\Console;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Flarum\Console\AbstractCommand;
|
use Flarum\Console\AbstractCommand;
|
||||||
use Flarum\Database\DatabaseMigrationRepository;
|
use Flarum\Database\DatabaseMigrationRepository;
|
||||||
use Flarum\Database\Migrator;
|
use Flarum\Database\Migrator;
|
||||||
use Flarum\Extension\ExtensionManager;
|
use Flarum\Extension\ExtensionManager;
|
||||||
|
use Flarum\Foundation\Application as FlarumApplication;
|
||||||
|
use Flarum\Foundation\Site;
|
||||||
use Flarum\Group\Group;
|
use Flarum\Group\Group;
|
||||||
use Flarum\Install\Prerequisite\PrerequisiteInterface;
|
use Flarum\Install\Prerequisite\PrerequisiteInterface;
|
||||||
use Flarum\Settings\DatabaseSettingsRepository;
|
use Flarum\Settings\DatabaseSettingsRepository;
|
||||||
@@ -186,9 +189,20 @@ class InstallCommand extends AbstractCommand
|
|||||||
|
|
||||||
$this->createAdminUser();
|
$this->createAdminUser();
|
||||||
|
|
||||||
$this->enableBundledExtensions();
|
|
||||||
|
|
||||||
$this->publishAssets();
|
$this->publishAssets();
|
||||||
|
|
||||||
|
// Now that the installation of core is complete, boot up a new
|
||||||
|
// application instance before enabling extensions so that all of
|
||||||
|
// the application services are available.
|
||||||
|
Site::fromPaths([
|
||||||
|
'base' => $this->application->basePath(),
|
||||||
|
'public' => $this->application->publicPath(),
|
||||||
|
'storage' => $this->application->storagePath(),
|
||||||
|
])->bootApp();
|
||||||
|
|
||||||
|
$this->application = FlarumApplication::getInstance();
|
||||||
|
|
||||||
|
$this->enableBundledExtensions();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@unlink($this->getConfigFile());
|
@unlink($this->getConfigFile());
|
||||||
|
|
||||||
@@ -225,6 +239,8 @@ class InstallCommand extends AbstractCommand
|
|||||||
|
|
||||||
$factory = new ConnectionFactory($this->application);
|
$factory = new ConnectionFactory($this->application);
|
||||||
|
|
||||||
|
$laravelDbConfig['engine'] = 'InnoDB';
|
||||||
|
|
||||||
$this->db = $factory->make($laravelDbConfig);
|
$this->db = $factory->make($laravelDbConfig);
|
||||||
$version = $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
$version = $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||||
|
|
||||||
@@ -281,7 +297,7 @@ class InstallCommand extends AbstractCommand
|
|||||||
'username' => $admin['username'],
|
'username' => $admin['username'],
|
||||||
'email' => $admin['email'],
|
'email' => $admin['email'],
|
||||||
'password' => (new BcryptHasher)->make($admin['password']),
|
'password' => (new BcryptHasher)->make($admin['password']),
|
||||||
'joined_at' => time(),
|
'joined_at' => Carbon::now(),
|
||||||
'is_email_confirmed' => 1,
|
'is_email_confirmed' => 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -34,7 +34,6 @@ class InstallServiceProvider extends AbstractServiceProvider
|
|||||||
new PhpVersion('7.1.0'),
|
new PhpVersion('7.1.0'),
|
||||||
new PhpExtensions([
|
new PhpExtensions([
|
||||||
'dom',
|
'dom',
|
||||||
'fileinfo',
|
|
||||||
'gd',
|
'gd',
|
||||||
'json',
|
'json',
|
||||||
'mbstring',
|
'mbstring',
|
||||||
|
@@ -39,7 +39,7 @@ class ReadNotificationHandler
|
|||||||
])
|
])
|
||||||
->update(['read_at' => Carbon::now()]);
|
->update(['read_at' => Carbon::now()]);
|
||||||
|
|
||||||
$notification->is_read = true;
|
$notification->read_at = Carbon::now();
|
||||||
|
|
||||||
return $notification;
|
return $notification;
|
||||||
}
|
}
|
||||||
|
@@ -115,9 +115,9 @@ class PostPolicy extends AbstractPolicy
|
|||||||
*/
|
*/
|
||||||
public function edit(User $actor, Post $post)
|
public function edit(User $actor, Post $post)
|
||||||
{
|
{
|
||||||
// A post is allowed to be edited if the user has permission to moderate
|
// A post is allowed to be edited if the user is the author, the post
|
||||||
// the discussion which it's in, or if they are the author and the post
|
// hasn't been deleted by someone else, and the user is allowed to
|
||||||
// hasn't been deleted by someone else.
|
// create new replies in the discussion.
|
||||||
if ($post->user_id == $actor->id && (! $post->hidden_at || $post->hidden_user_id == $actor->id) && $actor->can('reply', $post->discussion)) {
|
if ($post->user_id == $actor->id && (! $post->hidden_at || $post->hidden_user_id == $actor->id) && $actor->can('reply', $post->discussion)) {
|
||||||
$allowEditing = $this->settings->get('allow_post_editing');
|
$allowEditing = $this->settings->get('allow_post_editing');
|
||||||
|
|
||||||
@@ -128,4 +128,14 @@ class PostPolicy extends AbstractPolicy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $actor
|
||||||
|
* @param Post $post
|
||||||
|
* @return bool|null
|
||||||
|
*/
|
||||||
|
public function hide(User $actor, Post $post)
|
||||||
|
{
|
||||||
|
return $this->edit($actor, $post);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ namespace Flarum\Settings;
|
|||||||
* Within Flarum, this can be used to test out new setting values in a system
|
* Within Flarum, this can be used to test out new setting values in a system
|
||||||
* before they are committed to the database.
|
* before they are committed to the database.
|
||||||
*
|
*
|
||||||
* @see \Flarum\Forum\RecompileFrontendAssets For an example usage.
|
* @see \Flarum\Forum\ValidateCustomLess For an example usage.
|
||||||
*/
|
*/
|
||||||
class OverrideSettingsRepository implements SettingsRepositoryInterface
|
class OverrideSettingsRepository implements SettingsRepositoryInterface
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user