mirror of
https://github.com/flarum/core.git
synced 2025-08-16 05:14:20 +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
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $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)
|
||||
|
||||
Copyright (c) 2014-2018 Toby Zerner
|
||||
Copyright (c) Toby Zerner
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
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
|
||||
|
||||
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": {
|
||||
"php": ">=7.1",
|
||||
"axy/sourcemap": "^0.1.4",
|
||||
"components/font-awesome": "^5.0.6",
|
||||
"components/font-awesome": "^5.4.2",
|
||||
"dflydev/fig-cookies": "^1.0.2",
|
||||
"doctrine/dbal": "^2.7",
|
||||
"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",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.3.7",
|
||||
"classnames": "^2.2.5",
|
||||
"color-thief-browser": "^2.0.2",
|
||||
"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.hotkeys": "^0.1.0",
|
||||
"lodash-es": "^4.17.10",
|
||||
"lodash-es": "^4.17.11",
|
||||
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
|
||||
"mithril": "^0.2.8",
|
||||
"moment": "^2.22.2",
|
||||
"punycode": "^2.1.1",
|
||||
"spin.js": "^3.1.0",
|
||||
"webpack": "^4.17.1",
|
||||
"webpack-cli": "^2.1.5",
|
||||
"webpack": "^4.26.0",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-merge": "^4.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --mode production",
|
||||
"watch": "webpack --mode development --watch"
|
||||
"dev": "webpack --mode development --watch",
|
||||
"build": "webpack --mode production"
|
||||
}
|
||||
}
|
||||
|
@@ -8,9 +8,11 @@
|
||||
*/
|
||||
|
||||
import DashboardWidget from './DashboardWidget';
|
||||
import icon from '../../common/helpers/icon';
|
||||
import listItems from '../../common/helpers/listItems';
|
||||
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 {
|
||||
className() {
|
||||
@@ -26,10 +28,16 @@ export default class StatusWidget extends DashboardWidget {
|
||||
items() {
|
||||
const items = new ItemList();
|
||||
|
||||
items.add('help', (
|
||||
<a href="http://flarum.org/docs/troubleshooting" target="_blank">
|
||||
{icon('fas fa-question-circle')} {app.translator.trans('core.admin.dashboard.help_link')}
|
||||
</a>
|
||||
items.add('tools', (
|
||||
<Dropdown
|
||||
label={app.translator.trans('core.admin.dashboard.tools_button')}
|
||||
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')]);
|
||||
@@ -38,4 +46,13 @@ export default class StatusWidget extends DashboardWidget {
|
||||
|
||||
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 ReplyPlaceholder from './components/ReplyPlaceholder';
|
||||
import TextEditor from './components/TextEditor';
|
||||
import TextEditorButton from './components/TextEditorButton';
|
||||
import AvatarEditor from './components/AvatarEditor';
|
||||
import Post from './components/Post';
|
||||
import SettingsPage from './components/SettingsPage';
|
||||
@@ -99,6 +100,7 @@ export default Object.assign(compat, {
|
||||
'components/DiscussionList': DiscussionList,
|
||||
'components/ReplyPlaceholder': ReplyPlaceholder,
|
||||
'components/TextEditor': TextEditor,
|
||||
'components/TextEditorButton': TextEditorButton,
|
||||
'components/AvatarEditor': AvatarEditor,
|
||||
'components/Post': Post,
|
||||
'components/SettingsPage': SettingsPage,
|
||||
|
@@ -3,6 +3,7 @@ import Button from '../../common/components/Button';
|
||||
import GroupBadge from '../../common/components/GroupBadge';
|
||||
import Group from '../../common/models/Group';
|
||||
import extractText from '../../common/utils/extractText';
|
||||
import ItemList from '../../common/utils/ItemList';
|
||||
|
||||
/**
|
||||
* The `EditUserModal` component displays a modal dialog with a login form.
|
||||
@@ -37,80 +38,91 @@ export default class EditUserModal extends Modal {
|
||||
return (
|
||||
<div className="Modal-body">
|
||||
<div className="Form">
|
||||
<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>
|
||||
|
||||
{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>
|
||||
{this.fields().toArray()}
|
||||
</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() {
|
||||
this.loading = true;
|
||||
const data = {
|
||||
|
@@ -102,7 +102,7 @@ export default class IndexPage extends Page {
|
||||
// previous hero. Maintain the same scroll position relative to the bottom
|
||||
// of the hero so that the sidebar doesn't jump around.
|
||||
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;
|
||||
|
||||
$('#app').css('min-height', $(window).height() + heroHeight);
|
||||
|
@@ -99,7 +99,7 @@ export default class PostStreamScrubber extends Component {
|
||||
<div className="Scrubber-bar"/>
|
||||
<div className="Scrubber-info">
|
||||
<strong>{viewing}</strong>
|
||||
<span class="Scrubber-description">{retain || this.description}</span>
|
||||
<span className="Scrubber-description">{retain || this.description}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Scrubber-after"/>
|
||||
@@ -132,7 +132,7 @@ export default class PostStreamScrubber extends Component {
|
||||
*/
|
||||
goToLast() {
|
||||
this.props.stream.goToLast();
|
||||
this.index = this.props.stream.count();
|
||||
this.index = this.count();
|
||||
this.renderScrollbar(true);
|
||||
}
|
||||
|
||||
@@ -190,7 +190,6 @@ export default class PostStreamScrubber extends Component {
|
||||
const marginTop = stream.getMarginTop();
|
||||
const viewportTop = scrollTop + marginTop;
|
||||
const viewportHeight = $(window).height() - marginTop;
|
||||
const viewportBottom = viewportTop + viewportHeight;
|
||||
|
||||
// Before looping through all of the posts, we reset the scrollbar
|
||||
// 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 $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.toggleClass('disabled', this.disabled());
|
||||
|
||||
|
@@ -160,7 +160,7 @@ export default class Search extends Component {
|
||||
search.loadingSources++;
|
||||
|
||||
source.search(query).then(() => {
|
||||
search.loadingSources--;
|
||||
search.loadingSources = Math.max(0, search.loadingSources - 1);
|
||||
m.redraw();
|
||||
});
|
||||
});
|
||||
@@ -189,6 +189,9 @@ export default class Search extends Component {
|
||||
* Navigate to the currently selected search result and close the list.
|
||||
*/
|
||||
selectResult() {
|
||||
clearTimeout(this.searchTimeout);
|
||||
this.loadingSources = 0;
|
||||
|
||||
if (this.value()) {
|
||||
m.route(this.getItem(this.index).find('a').attr('href'));
|
||||
} else {
|
||||
|
@@ -36,6 +36,9 @@ export default class TextEditor extends Component {
|
||||
|
||||
<ul className="TextEditor-controls Composer-footer">
|
||||
{listItems(this.controlItems().toArray())}
|
||||
<li className="TextEditor-toolbar">
|
||||
{this.toolbarItems().toArray()}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
@@ -70,7 +73,7 @@ export default class TextEditor extends Component {
|
||||
items.add('submit',
|
||||
Button.component({
|
||||
children: this.props.submitLabel,
|
||||
icon: 'fas fa-check',
|
||||
icon: 'fas fa-paper-plane',
|
||||
className: 'Button Button--primary',
|
||||
itemClassName: 'App-primaryControl',
|
||||
onclick: this.onsubmit.bind(this)
|
||||
@@ -80,10 +83,11 @@ export default class TextEditor extends Component {
|
||||
if (this.props.preview) {
|
||||
items.add('preview',
|
||||
Button.component({
|
||||
icon: 'fas fa-eye',
|
||||
icon: 'far fa-eye',
|
||||
className: 'Button Button--icon',
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an item list for the toolbar controls.
|
||||
*
|
||||
* @return {ItemList}
|
||||
*/
|
||||
toolbarItems() {
|
||||
return new ItemList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the text editor.
|
||||
*
|
||||
@@ -141,6 +154,8 @@ export default class TextEditor extends Component {
|
||||
const pos = index + insert.length;
|
||||
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;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
&.item-help {
|
||||
&.item-tools {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
@@ -56,6 +56,9 @@
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
> .Button--icon {
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
.App-primaryControl {
|
||||
width: auto;
|
||||
@@ -71,12 +74,15 @@
|
||||
}
|
||||
}
|
||||
.App-primaryControl, .App-backControl {
|
||||
margin: 0 !important;
|
||||
|
||||
> .Button {
|
||||
color: @header-control-color !important;
|
||||
|
||||
.Button-icon {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
.Button-label {
|
||||
display: none;
|
||||
|
@@ -51,7 +51,7 @@
|
||||
position: absolute;
|
||||
background: @primary-color;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
right: 3px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 7px;
|
||||
|
@@ -8,7 +8,6 @@
|
||||
@import "print";
|
||||
@import "scaffolding";
|
||||
@import "sideNav";
|
||||
@import "mixins";
|
||||
|
||||
@import "App";
|
||||
@import "Alert";
|
||||
@@ -27,5 +26,3 @@
|
||||
@import "Search";
|
||||
@import "Select";
|
||||
@import "Tooltip";
|
||||
|
||||
@import "variables";
|
||||
|
@@ -343,11 +343,19 @@
|
||||
}
|
||||
.TextEditor-controls {
|
||||
margin: 0;
|
||||
padding: 15px 0;
|
||||
padding: 10px 0;
|
||||
list-style-type: none;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
& li {
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.TextEditor-toolbar {
|
||||
.Button--icon {
|
||||
width: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,10 +365,6 @@
|
||||
padding: 10px 20px;
|
||||
border-top: 1px solid @control-bg;
|
||||
|
||||
li {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.fullScreen & {
|
||||
margin: 0;
|
||||
border-top: 0;
|
||||
|
@@ -147,26 +147,38 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
h1 {
|
||||
font-size: 160%;
|
||||
font-size: 225%;
|
||||
}
|
||||
h2 {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
font-size: 175%;
|
||||
}
|
||||
h3 {
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-size: 150%;
|
||||
}
|
||||
h4, h5, h6 {
|
||||
h4 {
|
||||
font-size: 125%;
|
||||
}
|
||||
h5, h6 {
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
}
|
||||
h6 {
|
||||
color: @muted-more-color;
|
||||
}
|
||||
img, iframe {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
.Post-body, .Post-preview {
|
||||
> *:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.Post--hidden {
|
||||
.Post-header, .Post-header a, .PostUser h3, .PostUser h3 a {
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -23,14 +24,18 @@ return [
|
||||
})
|
||||
->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');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,18 +9,21 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
|
||||
$table->dropPrimary(['id']);
|
||||
$table->renameColumn('id', '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->string('allowed_ips')->nullable();
|
||||
$table->string('scopes')->nullable();
|
||||
@@ -29,19 +32,25 @@ return [
|
||||
$table->dateTime('last_activity_at')->nullable();
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'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->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->renameColumn('key', 'id');
|
||||
$table->primary('id');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
@@ -33,21 +34,26 @@ return [
|
||||
'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('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('first_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) {
|
||||
$schema->table('discussions', function (Blueprint $table) {
|
||||
$table->dropForeign([
|
||||
'user_id', 'last_posted_user_id', 'hidden_user_id',
|
||||
'first_post_id', 'last_post_id'
|
||||
]);
|
||||
$schema->table('discussions', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['last_posted_user_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.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -26,15 +27,20 @@ return [
|
||||
})
|
||||
->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('discussion_id')->references('id')->on('discussions')->onDelete('cascade');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('discussion_user', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id', 'discussion_id']);
|
||||
$schema->table('discussion_user', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['discussion_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -23,14 +24,18 @@ return [
|
||||
})
|
||||
->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');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('email_tokens', function (Blueprint $table) {
|
||||
$schema->table('email_tokens', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -23,14 +24,18 @@ return [
|
||||
})
|
||||
->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');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('group_permission', function (Blueprint $table) {
|
||||
$schema->table('group_permission', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['group_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -26,15 +27,20 @@ return [
|
||||
})
|
||||
->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('group_id')->references('id')->on('groups')->onDelete('cascade');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('group_user', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id', 'group_id']);
|
||||
$schema->table('group_user', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['group_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -30,15 +31,20 @@ return [
|
||||
})
|
||||
->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('from_user_id')->references('id')->on('users')->onDelete('set null');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id', 'from_user_id']);
|
||||
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||
$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.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -23,14 +24,18 @@ return [
|
||||
})
|
||||
->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');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('password_tokens', function (Blueprint $table) {
|
||||
$schema->table('password_tokens', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
@@ -31,19 +32,23 @@ return [
|
||||
'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('edited_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) {
|
||||
$schema->table('posts', function (Blueprint $table) {
|
||||
$table->dropForeign([
|
||||
'user_id', 'discussion_id',
|
||||
'edited_user_id', 'hidden_user_id'
|
||||
]);
|
||||
$schema->table('posts', function (Blueprint $table) use ($schema) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['discussion_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.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('users', function (Blueprint $table) {
|
||||
$schema->table('users', function (Blueprint $table) use ($schema) {
|
||||
$table->index('joined_at');
|
||||
$table->index('last_seen_at');
|
||||
$table->index('discussion_count');
|
||||
$table->index('comment_count');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('users', function (Blueprint $table) {
|
||||
$schema->table('users', function (Blueprint $table) use ($schema) {
|
||||
$table->dropIndex(['joined_at']);
|
||||
$table->dropIndex(['last_seen_at']);
|
||||
$table->dropIndex(['discussion_count']);
|
||||
$table->dropIndex(['comment_count']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,12 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'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_user_id');
|
||||
$table->index('created_at');
|
||||
@@ -22,11 +23,13 @@ return [
|
||||
$table->index('comment_count');
|
||||
$table->index('participant_count');
|
||||
$table->index('hidden_at');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'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_user_id']);
|
||||
$table->dropIndex(['created_at']);
|
||||
@@ -34,6 +37,8 @@ return [
|
||||
$table->dropIndex(['comment_count']);
|
||||
$table->dropIndex(['participant_count']);
|
||||
$table->dropIndex(['hidden_at']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,19 +9,24 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||
$table->index('user_id');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||
$table->dropIndex(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,23 +9,28 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
return [
|
||||
'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', 'created_at']);
|
||||
$table->index(['user_id', 'created_at']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'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', '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\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Frontend\AddLocaleAssets;
|
||||
use Flarum\Frontend\AddTranslations;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\RecompileFrontendAssets;
|
||||
use Flarum\Http\Middleware as HttpMiddleware;
|
||||
use Flarum\Http\RouteCollection;
|
||||
@@ -62,17 +65,31 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.admin.routes')));
|
||||
});
|
||||
|
||||
$this->app->bind('flarum.admin.assets', function () {
|
||||
return $this->app->make('flarum.frontend.assets.defaults')('admin');
|
||||
$this->app->bind('flarum.assets.admin', function () {
|
||||
/** @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 () {
|
||||
$view = $this->app->make('flarum.frontend.view.defaults')('admin');
|
||||
$this->app->bind('flarum.frontend.admin', function () {
|
||||
/** @var \Flarum\Frontend\Frontend $frontend */
|
||||
$frontend = $this->app->make('flarum.frontend.factory')('admin');
|
||||
|
||||
$view->setAssets($this->app->make('flarum.admin.assets'));
|
||||
$view->add($this->app->make(Content\AdminPayload::class));
|
||||
$frontend->content($this->app->make(Content\AdminPayload::class));
|
||||
|
||||
return $view;
|
||||
return $frontend;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -87,7 +104,7 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
|
||||
$this->app->make('events')->subscribe(
|
||||
new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.admin.assets'),
|
||||
$this->app->make('flarum.assets.admin'),
|
||||
$this->app->make('flarum.locales')
|
||||
)
|
||||
);
|
||||
|
@@ -12,8 +12,7 @@
|
||||
namespace Flarum\Admin\Content;
|
||||
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Group\Permission;
|
||||
use Flarum\Settings\Event\Deserializing;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
@@ -21,7 +20,7 @@ use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class AdminPayload implements ContentInterface
|
||||
class AdminPayload
|
||||
{
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
@@ -52,7 +51,7 @@ class AdminPayload implements ContentInterface
|
||||
$this->events = $events;
|
||||
}
|
||||
|
||||
public function __invoke(HtmlDocument $document, Request $request)
|
||||
public function __invoke(Document $document, Request $request)
|
||||
{
|
||||
$settings = $this->settings->all();
|
||||
|
||||
|
@@ -25,7 +25,6 @@ use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Tobscure\JsonApi\ErrorHandler;
|
||||
use Tobscure\JsonApi\Exception\Handler\FallbackExceptionHandler;
|
||||
use Tobscure\JsonApi\Exception\Handler\InvalidParameterExceptionHandler;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
|
||||
@@ -80,7 +79,7 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
$handler->registerHandler(new ExceptionHandler\TokenMismatchExceptionHandler);
|
||||
$handler->registerHandler(new ExceptionHandler\ValidationExceptionHandler);
|
||||
$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;
|
||||
});
|
||||
|
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;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use Tobscure\JsonApi\Document;
|
||||
use Tobscure\JsonApi\ErrorHandler as JsonApiErrorHandler;
|
||||
|
||||
@@ -34,8 +35,12 @@ class ErrorHandler
|
||||
* @param Exception $e
|
||||
* @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);
|
||||
|
||||
$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;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Api\ErrorHandler;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
use Throwable;
|
||||
|
||||
class HandleErrors implements Middleware
|
||||
{
|
||||
@@ -40,7 +40,7 @@ class HandleErrors implements Middleware
|
||||
{
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
return $this->errorHandler->handle($e);
|
||||
}
|
||||
}
|
||||
|
@@ -301,4 +301,11 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
||||
'favicon.delete',
|
||||
$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);
|
||||
|
||||
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->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);
|
||||
|
||||
$notes = $this->manager->migrateDown($extension);
|
||||
|
||||
foreach ($notes as $note) {
|
||||
$this->info($note);
|
||||
}
|
||||
$this->manager->getMigrator()->setOutput($this->output);
|
||||
$this->manager->migrateDown($extension);
|
||||
|
||||
$this->info('DONE.');
|
||||
}
|
||||
|
@@ -25,7 +25,9 @@ class DatabaseServiceProvider extends AbstractServiceProvider
|
||||
$this->app->singleton('flarum.db', function () {
|
||||
$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'));
|
||||
|
||||
return $connection;
|
||||
|
@@ -29,7 +29,11 @@ abstract class Migration
|
||||
{
|
||||
return [
|
||||
'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) {
|
||||
$schema->drop($name);
|
||||
@@ -59,11 +63,13 @@ abstract class Migration
|
||||
{
|
||||
return [
|
||||
'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) {
|
||||
$type = array_shift($options);
|
||||
$table->addColumn($type, $columnName, $options);
|
||||
}
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
'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;
|
||||
|
||||
use Flarum\Discussion\Search\DiscussionSearch;
|
||||
use Flarum\Event\ScopeModelVisibility;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\Search\AbstractSearch;
|
||||
use Flarum\Search\GambitInterface;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
use LogicException;
|
||||
|
||||
class FulltextGambit implements GambitInterface
|
||||
@@ -36,26 +36,35 @@ class FulltextGambit implements GambitInterface
|
||||
$query = $search->getQuery();
|
||||
$grammar = $query->getGrammar();
|
||||
|
||||
$query
|
||||
->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])
|
||||
->leftJoin('posts', 'posts.discussion_id', '=', 'discussions.id')
|
||||
// Construct a subquery to fetch discussions which contain relevant
|
||||
// posts. Retrieve the collective relevance of each discussion's posts,
|
||||
// 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(function ($query) use ($search) {
|
||||
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]);
|
||||
})
|
||||
->whereRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (? IN BOOLEAN MODE)', [$bit])
|
||||
->groupBy('posts.discussion_id');
|
||||
|
||||
$search->setDefaultSort(function ($query) use ($bit) {
|
||||
$grammar = $query->getGrammar();
|
||||
// Join the subquery into the main search query and scope results to
|
||||
// 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('posts.content').') AGAINST (?) desc', [$bit]);
|
||||
$query->orderBy('posts_ft.score', 'desc');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -11,72 +11,49 @@
|
||||
|
||||
namespace Flarum\Extend;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Frontend\Asset\ExtensionAssets;
|
||||
use Flarum\Frontend\CompilerFactory;
|
||||
use Flarum\Frontend\HtmlDocumentFactory;
|
||||
use Flarum\Frontend\Assets;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\Frontend as ActualFrontend;
|
||||
use Flarum\Frontend\RecompileFrontendAssets;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class Frontend implements ExtenderInterface
|
||||
{
|
||||
protected $frontend;
|
||||
protected $inheritFrom;
|
||||
|
||||
protected $css = [];
|
||||
protected $js;
|
||||
protected $routes = [];
|
||||
protected $content = [];
|
||||
|
||||
public function __construct($frontend)
|
||||
public function __construct(string $frontend)
|
||||
{
|
||||
$this->frontend = $frontend;
|
||||
}
|
||||
|
||||
public function inherits($from)
|
||||
{
|
||||
$this->inheritFrom = $from;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function css($path)
|
||||
public function css(string $path)
|
||||
{
|
||||
$this->css[] = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function js($path)
|
||||
public function js(string $path)
|
||||
{
|
||||
$this->js = $path;
|
||||
|
||||
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');
|
||||
|
||||
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
|
||||
* @return $this
|
||||
@@ -90,56 +67,53 @@ class Frontend implements ExtenderInterface
|
||||
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
$this->registerFrontend($container);
|
||||
$this->registerAssets($container, $this->getModuleName($extension));
|
||||
$this->registerRoutes($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)
|
||||
{
|
||||
if (empty($this->css) && empty($this->js)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$container->resolving(
|
||||
"flarum.$this->frontend.assets",
|
||||
function (CompilerFactory $assets) use ($moduleName) {
|
||||
$assets->add(function () use ($moduleName) {
|
||||
return new ExtensionAssets(
|
||||
$moduleName, $this->css, $this->js
|
||||
);
|
||||
$abstract = 'flarum.assets.'.$this->frontend;
|
||||
|
||||
$container->resolving($abstract, function (Assets $assets) use ($moduleName) {
|
||||
if ($this->js) {
|
||||
$assets->js(function (SourceCollector $sources) use ($moduleName) {
|
||||
$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)
|
||||
@@ -166,14 +140,14 @@ class Frontend implements ExtenderInterface
|
||||
}
|
||||
|
||||
$container->resolving(
|
||||
"flarum.$this->frontend.frontend",
|
||||
function (HtmlDocumentFactory $view, Container $container) {
|
||||
"flarum.frontend.$this->frontend",
|
||||
function (ActualFrontend $frontend, Container $container) {
|
||||
foreach ($this->content as $content) {
|
||||
if (is_string($content)) {
|
||||
$content = $container->make($content);
|
||||
}
|
||||
|
||||
$view->add($content);
|
||||
$frontend->content($content);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@@ -223,22 +223,24 @@ class ExtensionManager
|
||||
*
|
||||
* @param Extension $extension
|
||||
* @param bool|true $up
|
||||
* @return array Notes from the migrator.
|
||||
* @return void
|
||||
*/
|
||||
public function migrate(Extension $extension, $up = true)
|
||||
{
|
||||
if ($extension->hasMigrations()) {
|
||||
$migrationDir = $extension->getPath().'/migrations';
|
||||
if (! $extension->hasMigrations()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
|
||||
return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
|
||||
});
|
||||
$migrationDir = $extension->getPath().'/migrations';
|
||||
|
||||
if ($up) {
|
||||
$this->migrator->run($migrationDir, $extension);
|
||||
} else {
|
||||
$this->migrator->reset($migrationDir, $extension);
|
||||
}
|
||||
$this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
|
||||
return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
|
||||
});
|
||||
|
||||
if ($up) {
|
||||
$this->migrator->run($migrationDir, $extension);
|
||||
} else {
|
||||
$this->migrator->reset($migrationDir, $extension);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,11 +272,16 @@ class ExtensionManager
|
||||
*/
|
||||
public function getEnabledExtensions()
|
||||
{
|
||||
$enabled = [];
|
||||
$extensions = $this->getExtensions();
|
||||
|
||||
return array_filter(array_map(function ($id) use ($extensions) {
|
||||
return $extensions[$id] ?? null;
|
||||
}, $this->getEnabled()));
|
||||
foreach ($this->getEnabled() as $id) {
|
||||
if (isset($extensions[$id])) {
|
||||
$enabled[$id] = $extensions[$id];
|
||||
}
|
||||
}
|
||||
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -122,10 +122,10 @@ class Formatter
|
||||
$configurator->Autolink;
|
||||
$configurator->tags->onDuplicate('replace');
|
||||
|
||||
$this->configureExternalLinks($configurator);
|
||||
|
||||
$this->events->dispatch(new Configuring($configurator));
|
||||
|
||||
$this->configureExternalLinks($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;
|
||||
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\User\AssertPermissionTrait;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class AssertRegistered implements ContentInterface
|
||||
class AssertRegistered
|
||||
{
|
||||
use AssertPermissionTrait;
|
||||
|
||||
public function __invoke(HtmlDocument $document, Request $request)
|
||||
public function __invoke(Document $document, Request $request)
|
||||
{
|
||||
$this->assertRegistered($request->getAttribute('actor'));
|
||||
}
|
||||
|
@@ -12,15 +12,14 @@
|
||||
namespace Flarum\Forum\Content;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Http\Exception\RouteNotFoundException;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class Discussion implements ContentInterface
|
||||
class Discussion
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
@@ -49,7 +48,7 @@ class Discussion implements ContentInterface
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function __invoke(HtmlDocument $document, Request $request)
|
||||
public function __invoke(Document $document, Request $request)
|
||||
{
|
||||
$queryParams = $request->getQueryParams();
|
||||
$page = max(1, array_get($queryParams, 'page'));
|
||||
|
@@ -13,13 +13,12 @@ namespace Flarum\Forum\Content;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Controller\ListDiscussionsController;
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class Index implements ContentInterface
|
||||
class Index
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
@@ -41,10 +40,7 @@ class Index implements ContentInterface
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __invoke(HtmlDocument $document, Request $request)
|
||||
public function __invoke(Document $document, Request $request)
|
||||
{
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
|
@@ -86,7 +86,7 @@ class SavePasswordController implements RequestHandlerInterface
|
||||
throw new ValidationException($validator);
|
||||
}
|
||||
} 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]));
|
||||
}
|
||||
|
@@ -13,8 +13,13 @@ namespace Flarum\Forum;
|
||||
|
||||
use Flarum\Event\ConfigureForumRoutes;
|
||||
use Flarum\Event\ConfigureMiddleware;
|
||||
use Flarum\Formatter\Formatter;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
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\Http\Middleware as HttpMiddleware;
|
||||
use Flarum\Http\RouteCollection;
|
||||
@@ -66,25 +71,32 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
$pipe->pipe(new HttpMiddleware\DispatchRoute($this->app->make('flarum.forum.routes')));
|
||||
});
|
||||
|
||||
$this->app->bind('flarum.forum.assets', function () {
|
||||
$assets = $this->app->make('flarum.frontend.assets.defaults')('forum');
|
||||
$this->app->bind('flarum.assets.forum', function () {
|
||||
/** @var Assets $assets */
|
||||
$assets = $this->app->make('flarum.assets.factory')('forum');
|
||||
|
||||
$assets->add(function () {
|
||||
return [
|
||||
$this->app->make(Asset\FormatterJs::class),
|
||||
$this->app->make(Asset\CustomCss::class)
|
||||
];
|
||||
$assets->js(function (SourceCollector $sources) {
|
||||
$sources->addFile(__DIR__.'/../../js/dist/forum.js');
|
||||
$sources->addString(function () {
|
||||
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;
|
||||
});
|
||||
|
||||
$this->app->bind('flarum.forum.frontend', function () {
|
||||
$view = $this->app->make('flarum.frontend.view.defaults')('forum');
|
||||
|
||||
$view->setAssets($this->app->make('flarum.forum.assets'));
|
||||
|
||||
return $view;
|
||||
$this->app->bind('flarum.frontend.forum', function () {
|
||||
return $this->app->make('flarum.frontend.factory')('forum');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,9 +114,18 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
'settings' => $this->app->make(SettingsRepositoryInterface::class)
|
||||
]);
|
||||
|
||||
$this->app->make('events')->subscribe(
|
||||
$events = $this->app->make('events');
|
||||
|
||||
$events->subscribe(
|
||||
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
|
||||
)
|
||||
|
@@ -12,8 +12,7 @@
|
||||
namespace Flarum\Forum;
|
||||
|
||||
use Flarum\Foundation\ValidationException;
|
||||
use Flarum\Frontend\CompilerFactory;
|
||||
use Flarum\Frontend\RecompileFrontendAssets as BaseListener;
|
||||
use Flarum\Frontend\Assets;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Flarum\Settings\Event\Saving;
|
||||
@@ -26,22 +25,32 @@ use League\Flysystem\Adapter\NullAdapter;
|
||||
use League\Flysystem\Filesystem;
|
||||
use Less_Exception_Parser;
|
||||
|
||||
class RecompileFrontendAssets extends BaseListener
|
||||
class ValidateCustomLess
|
||||
{
|
||||
/**
|
||||
* @var Assets
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @param CompilerFactory $assets
|
||||
* @param Assets $assets
|
||||
* @param LocaleManager $locales
|
||||
* @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;
|
||||
}
|
||||
|
||||
@@ -50,9 +59,8 @@ class RecompileFrontendAssets extends BaseListener
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
parent::subscribe($events);
|
||||
|
||||
$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)
|
||||
{
|
||||
parent::whenSettingsSaved($event);
|
||||
|
||||
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
|
||||
*/
|
||||
const VERSION = '0.1.0-beta.7';
|
||||
const VERSION = '0.1.0-beta.8.1';
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
if (empty($sources)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$mapFile = $file.'.map';
|
||||
|
||||
$map = new SourceMap();
|
||||
@@ -67,7 +71,7 @@ class JsCompiler extends RevisionCompiler
|
||||
*/
|
||||
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
|
||||
{
|
||||
|
@@ -13,7 +13,7 @@ namespace Flarum\Frontend\Compiler;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\Compiler\Source\SourceInterface;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
|
||||
class RevisionCompiler implements CompilerInterface
|
||||
{
|
||||
@@ -22,7 +22,7 @@ class RevisionCompiler implements CompilerInterface
|
||||
const EMPTY_REVISION = 'empty';
|
||||
|
||||
/**
|
||||
* @var FilesystemAdapter
|
||||
* @var Filesystem
|
||||
*/
|
||||
protected $assetsDir;
|
||||
|
||||
@@ -37,10 +37,10 @@ class RevisionCompiler implements CompilerInterface
|
||||
protected $sourcesCallbacks = [];
|
||||
|
||||
/**
|
||||
* @param FilesystemAdapter $assetsDir
|
||||
* @param Filesystem $assetsDir
|
||||
* @param string $filename
|
||||
*/
|
||||
public function __construct(FilesystemAdapter $assetsDir, string $filename)
|
||||
public function __construct(Filesystem $assetsDir, string $filename)
|
||||
{
|
||||
$this->assetsDir = $assetsDir;
|
||||
$this->filename = $filename;
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Flarum\Frontend\Compiler\Source;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class FileSource implements SourceInterface
|
||||
{
|
||||
/**
|
||||
@@ -23,6 +25,10 @@ class FileSource implements SourceInterface
|
||||
*/
|
||||
public function __construct(string $path)
|
||||
{
|
||||
if (! file_exists($path)) {
|
||||
throw new InvalidArgumentException("File not found at 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\Controller\ShowUserController;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Frontend\Document;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\User\User;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class CorePayload implements ContentInterface
|
||||
class CorePayload
|
||||
{
|
||||
/**
|
||||
* @var LocaleManager
|
||||
@@ -41,7 +41,7 @@ class CorePayload implements ContentInterface
|
||||
$this->api = $api;
|
||||
}
|
||||
|
||||
public function __invoke(HtmlDocument $document, Request $request)
|
||||
public function __invoke(Document $document, Request $request)
|
||||
{
|
||||
$document->payload = array_merge(
|
||||
$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());
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Frontend\Document;
|
||||
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->head = array_merge($document->head, $this->buildHead($document));
|
||||
}
|
||||
|
||||
private function buildMeta(HtmlDocument $document)
|
||||
private function buildMeta(Document $document)
|
||||
{
|
||||
$forumApiDocument = $document->getForumApiDocument();
|
||||
|
||||
$meta = [
|
||||
'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')
|
||||
];
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
private function buildHead(HtmlDocument $document)
|
||||
private function buildHead(Document $document)
|
||||
{
|
||||
$head = [];
|
||||
|
||||
|
@@ -19,25 +19,19 @@ use Zend\Diactoros\Response\HtmlResponse;
|
||||
class Controller implements RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var HtmlDocumentFactory
|
||||
* @var Frontend
|
||||
*/
|
||||
protected $document;
|
||||
protected $frontend;
|
||||
|
||||
/**
|
||||
* @param HtmlDocumentFactory $document
|
||||
*/
|
||||
public function __construct(HtmlDocumentFactory $document)
|
||||
public function __construct(Frontend $frontend)
|
||||
{
|
||||
$this->document = $document;
|
||||
$this->frontend = $frontend;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
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.
|
||||
*/
|
||||
class HtmlDocument implements Renderable
|
||||
class Document implements Renderable
|
||||
{
|
||||
/**
|
||||
* 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;
|
||||
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||
|
||||
class FrontendServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
// Yo dawg, I heard you like factories, so I made you a factory to
|
||||
// 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 () {
|
||||
$this->app->singleton('flarum.assets.factory', function () {
|
||||
return function (string $name) {
|
||||
$assets = new CompilerFactory(
|
||||
$assets = new Assets(
|
||||
$name,
|
||||
$this->app->make('filesystem')->disk('flarum-assets'),
|
||||
$this->app->storagePath()
|
||||
@@ -36,35 +33,26 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
||||
$this->app->basePath().'/vendor/components/font-awesome/less' => ''
|
||||
]);
|
||||
|
||||
$assets->add(function () use ($name) {
|
||||
$translations = $this->app->make(Asset\Translations::class);
|
||||
$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)
|
||||
];
|
||||
});
|
||||
$assets->css([$this, 'addBaseCss']);
|
||||
$assets->localeCss([$this, 'addBaseCss']);
|
||||
|
||||
return $assets;
|
||||
};
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.frontend.view.defaults', function () {
|
||||
$this->app->singleton('flarum.frontend.factory', function () {
|
||||
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));
|
||||
$view->add($this->app->make(Content\CorePayload::class));
|
||||
$view->add($this->app->make(Content\Meta::class));
|
||||
$frontend->content($this->app->make(Content\Assets::class)->forFrontend($name));
|
||||
$frontend->content($this->app->make(Content\CorePayload::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)
|
||||
]);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
/**
|
||||
* @var CompilerFactory
|
||||
* @var Assets
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
@@ -31,10 +31,10 @@ class RecompileFrontendAssets
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @param CompilerFactory $assets
|
||||
* @param Assets $assets
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct(CompilerFactory $assets, LocaleManager $locales)
|
||||
public function __construct(Assets $assets, LocaleManager $locales)
|
||||
{
|
||||
$this->assets = $assets;
|
||||
$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;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||
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\Log\LoggerInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Throwable;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
|
||||
class HandleErrorsWithView implements Middleware
|
||||
@@ -65,12 +65,12 @@ class HandleErrorsWithView implements Middleware
|
||||
{
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
return $this->formatException($e);
|
||||
}
|
||||
}
|
||||
|
||||
protected function formatException(Exception $error)
|
||||
protected function formatException(Throwable $error)
|
||||
{
|
||||
$status = 500;
|
||||
$errorCode = $error->getCode();
|
||||
@@ -81,11 +81,10 @@ class HandleErrorsWithView implements Middleware
|
||||
$status = $errorCode;
|
||||
}
|
||||
|
||||
// Log the exception (with trace)
|
||||
$this->logger->debug($error);
|
||||
|
||||
if (! $this->view->exists($name = "flarum.forum::error.$status")) {
|
||||
$name = 'flarum.forum::error.default';
|
||||
|
||||
$this->logger->error($error);
|
||||
}
|
||||
|
||||
$view = $this->view->make($name)
|
||||
|
@@ -11,15 +11,29 @@
|
||||
|
||||
namespace Flarum\Http\Middleware;
|
||||
|
||||
use Exception;
|
||||
use Franzl\Middleware\Whoops\WhoopsRunner;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\MiddlewareInterface as Middleware;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Throwable;
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -27,7 +41,11 @@ class HandleErrorsWithWhoops implements Middleware
|
||||
{
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Exception $e) {
|
||||
} catch (Throwable $e) {
|
||||
if ($e->getCode() !== 404) {
|
||||
$this->logger->error($e);
|
||||
}
|
||||
|
||||
return WhoopsRunner::handle($e, $request);
|
||||
}
|
||||
}
|
||||
|
@@ -11,8 +11,12 @@
|
||||
|
||||
namespace Flarum\Http;
|
||||
|
||||
use Closure;
|
||||
use Flarum\Frontend\Controller as FrontendController;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface as Handler;
|
||||
|
||||
class RouteHandlerFactory
|
||||
{
|
||||
@@ -21,56 +25,61 @@ class RouteHandlerFactory
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|callable $controller
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toController($controller)
|
||||
public function toController($controller): Closure
|
||||
{
|
||||
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|null $content
|
||||
* @return ControllerRouteHandler
|
||||
* @param string|callable|null $content
|
||||
*/
|
||||
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) {
|
||||
$frontend = $container->make("flarum.$frontend.frontend");
|
||||
$frontend = $container->make("flarum.frontend.$frontend");
|
||||
|
||||
if ($content) {
|
||||
$frontend->add($container->make($content));
|
||||
$frontend->content(is_callable($content) ? $content : $container->make($content));
|
||||
}
|
||||
|
||||
return new FrontendController($frontend);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $content
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toForum(string $content = null)
|
||||
public function toForum(string $content = null): Closure
|
||||
{
|
||||
return $this->toFrontend('forum', $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $content
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toAdmin(string $content = null)
|
||||
public function toAdmin(string $content = null): Closure
|
||||
{
|
||||
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;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Flarum\Console\AbstractCommand;
|
||||
use Flarum\Database\DatabaseMigrationRepository;
|
||||
use Flarum\Database\Migrator;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Foundation\Application as FlarumApplication;
|
||||
use Flarum\Foundation\Site;
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\Install\Prerequisite\PrerequisiteInterface;
|
||||
use Flarum\Settings\DatabaseSettingsRepository;
|
||||
@@ -186,9 +189,20 @@ class InstallCommand extends AbstractCommand
|
||||
|
||||
$this->createAdminUser();
|
||||
|
||||
$this->enableBundledExtensions();
|
||||
|
||||
$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) {
|
||||
@unlink($this->getConfigFile());
|
||||
|
||||
@@ -225,6 +239,8 @@ class InstallCommand extends AbstractCommand
|
||||
|
||||
$factory = new ConnectionFactory($this->application);
|
||||
|
||||
$laravelDbConfig['engine'] = 'InnoDB';
|
||||
|
||||
$this->db = $factory->make($laravelDbConfig);
|
||||
$version = $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
|
||||
@@ -281,7 +297,7 @@ class InstallCommand extends AbstractCommand
|
||||
'username' => $admin['username'],
|
||||
'email' => $admin['email'],
|
||||
'password' => (new BcryptHasher)->make($admin['password']),
|
||||
'joined_at' => time(),
|
||||
'joined_at' => Carbon::now(),
|
||||
'is_email_confirmed' => 1,
|
||||
]);
|
||||
|
||||
|
@@ -34,7 +34,6 @@ class InstallServiceProvider extends AbstractServiceProvider
|
||||
new PhpVersion('7.1.0'),
|
||||
new PhpExtensions([
|
||||
'dom',
|
||||
'fileinfo',
|
||||
'gd',
|
||||
'json',
|
||||
'mbstring',
|
||||
|
@@ -39,7 +39,7 @@ class ReadNotificationHandler
|
||||
])
|
||||
->update(['read_at' => Carbon::now()]);
|
||||
|
||||
$notification->is_read = true;
|
||||
$notification->read_at = Carbon::now();
|
||||
|
||||
return $notification;
|
||||
}
|
||||
|
@@ -115,9 +115,9 @@ class PostPolicy extends AbstractPolicy
|
||||
*/
|
||||
public function edit(User $actor, Post $post)
|
||||
{
|
||||
// A post is allowed to be edited if the user has permission to moderate
|
||||
// the discussion which it's in, or if they are the author and the post
|
||||
// hasn't been deleted by someone else.
|
||||
// A post is allowed to be edited if the user is the author, the post
|
||||
// hasn't been deleted by someone else, and the user is allowed to
|
||||
// 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)) {
|
||||
$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
|
||||
* 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
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user