mirror of
https://github.com/flarum/core.git
synced 2025-08-23 08:33:45 +02:00
Compare commits
165 Commits
v0.1.0-bet
...
v0.1.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
a720f6f651 | ||
|
54d7c0d3b6 | ||
|
b5876d9f31 | ||
|
25ef4c10bd | ||
|
985b87da6c | ||
|
a6aa28566c | ||
|
e3340ba3e1 | ||
|
590b311570 | ||
|
935a968257 | ||
|
fe558eb0ba | ||
|
fda9cba4ce | ||
|
89f6cfd949 | ||
|
803582c437 | ||
|
8e86d38804 | ||
|
fd66722945 | ||
|
ce42b5e035 | ||
|
bfd3a667dd | ||
|
b669490d33 | ||
|
ba956f51ac | ||
|
c126b95451 | ||
|
7f7484e790 | ||
|
5d64056e89 | ||
|
e927254e99 | ||
|
8061bfd74a | ||
|
4c309d2ad7 | ||
|
54876cfbd6 | ||
|
9e2b796a7c | ||
|
7f5bd1e96b | ||
|
5e1680c458 | ||
|
6e26b988bd | ||
|
2e8d4e4b6b | ||
|
14bede2847 | ||
|
54660ebd63 | ||
|
1a62b7e07a | ||
|
4b04c0e0ce | ||
|
4d45ce389b | ||
|
d2674fb309 | ||
|
5eb69e1f59 | ||
|
f42142979d | ||
|
5f79d3b499 | ||
|
8e4d97260f | ||
|
ee3640e160 | ||
|
bd584802e5 | ||
|
f4dd045326 | ||
|
24522943f6 | ||
|
56fde28e43 | ||
|
1c1d661bdd | ||
|
d3be186fb6 | ||
|
8f8cc558be | ||
|
5ea9e1cf5e | ||
|
99a6066f96 | ||
|
8b7db726dc | ||
|
7a44086bf3 | ||
|
12fdfc9b54 | ||
|
ecc3b5e227 | ||
|
bf2c5a5564 | ||
|
d3a5c91845 | ||
|
e17bb0b433 | ||
|
c4ba41f850 | ||
|
0c4de6f163 | ||
|
cd313952c7 | ||
|
ef57b443c1 | ||
|
5154d7e5a6 | ||
|
2bd40b50c7 | ||
|
c50d58d0f4 | ||
|
8c65316961 | ||
|
0a818cfdf3 | ||
|
57204c6ed0 | ||
|
a21052c903 | ||
|
441ebacfd7 | ||
|
46acfb6c23 | ||
|
9910e884fc | ||
|
d292aaabf8 | ||
|
d822a6f84c | ||
|
26c3bcdb74 | ||
|
33deea4791 | ||
|
20227a2201 | ||
|
0493682dba | ||
|
49dda87e86 | ||
|
d959d08561 | ||
|
e8ab49abc1 | ||
|
296677b5fc | ||
|
f3931b537c | ||
|
d0ba4e5268 | ||
|
654ab4cc29 | ||
|
e0becd0c7b | ||
|
ed43ad9c3f | ||
|
4611abe5db | ||
|
df0bd52283 | ||
|
d387a9ff02 | ||
|
5556df54f9 | ||
|
cf746079ed | ||
|
4d10536d35 | ||
|
ba16ebe61f | ||
|
6484dc4982 | ||
|
1a9f1f7a3d | ||
|
4d1411e2a8 | ||
|
968152b740 | ||
|
af185fd3d1 | ||
|
ed9591c16f | ||
|
8ad326941f | ||
|
8e4f02d994 | ||
|
8ae85bc49f | ||
|
7ff9a90204 | ||
|
f4fb1ab272 | ||
|
484c6d2edb | ||
|
8b68ff6232 | ||
|
0a59b7164e | ||
|
0879829dc4 | ||
|
78ba3bd854 | ||
|
44c91099cd | ||
|
4585f03ee3 | ||
|
bc9e8f68f1 | ||
|
f5a21584c2 | ||
|
e0a508a765 | ||
|
89e018a4f0 | ||
|
de6001f4cf | ||
|
790d5beee5 | ||
|
abf224bb0a | ||
|
c7d2e165d7 | ||
|
0ab9facc4b | ||
|
9b68bbe44e | ||
|
862404f052 | ||
|
b9a93f3440 | ||
|
c67fb2d4b6 | ||
|
1b2d4f1e1d | ||
|
54fdc40d87 | ||
|
390148456c | ||
|
167059027e | ||
|
208bad393f | ||
|
8a93f8b6b6 | ||
|
9db04a4e19 | ||
|
ac5e26a254 | ||
|
9794a08f39 | ||
|
ababb8ebef | ||
|
cb3baf9955 | ||
|
dbe8cba14e | ||
|
9fe671c9bb | ||
|
0e5f334a0b | ||
|
e4514d8413 | ||
|
1080d25561 | ||
|
ba594de13a | ||
|
209d13affd | ||
|
671fdec8d0 | ||
|
9eca9192ca | ||
|
3468bdf511 | ||
|
54503d2c29 | ||
|
565131e2a7 | ||
|
f0da3cf304 | ||
|
6acc91577d | ||
|
3e0cd3a21f | ||
|
5c9fa4c62d | ||
|
4b00f7996b | ||
|
b58380e224 | ||
|
b0e996e7ff | ||
|
b41d9fb0e7 | ||
|
ed02eed88f | ||
|
c761802900 | ||
|
16eb1fa63b | ||
|
0ceb8d64df | ||
|
9712eccb03 | ||
|
9684fbc4da | ||
|
67f9375d47 | ||
|
0d16fac001 | ||
|
a8f5ca8d97 |
8
.github/ISSUE_TEMPLATE/security-vulnerability.md
vendored
Normal file
8
.github/ISSUE_TEMPLATE/security-vulnerability.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: "🔒 Security Vulnerability"
|
||||
about: "When you discover a security issue"
|
||||
---
|
||||
|
||||
If you discover a security vulnerability within Flarum, please send an email to [security@flarum.org](mailto:security@flarum.org) instead.
|
||||
**DO NOT open an issue on this repository.**
|
||||
We will address these with the utmost urgency and it will prevent vulnerabilities, which may be abused, from popping up on our issue tracker.
|
13
.github/SECURITY.md
vendored
Normal file
13
.github/SECURITY.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
During the beta phase, we will only patch security vulnerabilities in the latest beta release.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a security vulnerability within Flarum, please send an email to security@flarum.org so we can address it promptly.
|
||||
|
||||
We will get back to you as time allows.
|
||||
Discussions may commence internally, so you may not hear back immediately.
|
||||
When reporting a vulnerability, please provide your GitHub username (if available), so that we can invite you to collaborate on a [security advisory on GitHub](https://help.github.com/en/articles/about-maintainer-security-advisories).
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,6 +4,6 @@ composer.phar
|
||||
node_modules
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
/tests/tmp
|
||||
/tests/integration/tmp
|
||||
.vagrant
|
||||
.idea/*
|
||||
|
23
.travis.yml
23
.travis.yml
@@ -7,13 +7,14 @@ cache:
|
||||
|
||||
install:
|
||||
- composer install
|
||||
- mysql -e 'CREATE DATABASE flarum;'
|
||||
- mysql -e 'CREATE DATABASE flarum_test;'
|
||||
|
||||
before_script:
|
||||
- echo 'error_reporting = E_ALL' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover=coverage.xml
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- composer test:setup
|
||||
- composer test
|
||||
|
||||
jobs:
|
||||
include:
|
||||
@@ -23,8 +24,8 @@ jobs:
|
||||
- php: 7.2
|
||||
env: DB=mysql
|
||||
|
||||
- php: 7.2
|
||||
env: DB=mysql PREFIX=forum_
|
||||
- php: 7.3
|
||||
env: DB=mysql
|
||||
|
||||
- php: 7.1
|
||||
addons:
|
||||
@@ -36,6 +37,14 @@ jobs:
|
||||
mariadb: '10.2'
|
||||
env: DB=mariadb
|
||||
|
||||
- php: 7.3
|
||||
addons:
|
||||
mariadb: '10.2'
|
||||
env: DB=mariadb
|
||||
|
||||
- php: 7.2
|
||||
env: DB=mysql PREFIX=forum_
|
||||
|
||||
- stage: build
|
||||
language: generic
|
||||
if: branch = master AND type = push
|
||||
|
49
CHANGELOG.md
49
CHANGELOG.md
@@ -1,12 +1,59 @@
|
||||
# Changelog
|
||||
|
||||
## [0.1.0-beta.9](https://github.com/flarum/core/compare/v0.1.0-beta.8.2...v0.1.0-beta.9)
|
||||
|
||||
### Added
|
||||
- New `hasPermission()` helper method for `Group` objects ([9684fbc](https://github.com/flarum/core/commit/9684fbc4da07d32aa322d9228302a23418412cb9))
|
||||
- Expose supported mail drivers in IoC container ([208bad3](https://github.com/flarum/core/commit/208bad393f37bfdb76007afcddfa4b7451563e9d))
|
||||
- More test for some API endpoints ([1670590](https://github.com/flarum/core/commit/167059027e5a066d618599c90164ef1b5a509148))
|
||||
- The `Formatter\Rendering` event now receives the HTTP request instance as well ([0ab9fac](https://github.com/flarum/core/commit/0ab9facc4bd59a260575e6fc650793c663e5866a))
|
||||
- More and better validation in installer UIs
|
||||
- Check and enforce minimum MariaDB ([7ff9a90](https://github.com/flarum/core/commit/7ff9a90204923293adc520d3c02dc984845d4f9f))
|
||||
- Revert publication of assets when installation fails ([ed9591c](https://github.com/flarum/core/commit/ed9591c16fb2ea7a4be3387b805d855a53e0a7d5))
|
||||
- Benefit from Laravel's database reconnection logic in long-running tasks ([e0becd0](https://github.com/flarum/core/commit/e0becd0c7bda939048923c1f86648793feee78d5))
|
||||
- The "vendor path" (where Composer dependencies can be found) can now be configured ([5e1680c](https://github.com/flarum/core/commit/5e1680c458cd3ba274faeb92de3ac2053789131e))
|
||||
|
||||
### Changed
|
||||
- Performance: Actually cache translations on disk ([0d16fac](https://github.com/flarum/core/commit/0d16fac001bb735ee66e82871183516aeac269b7))
|
||||
- Allow per-site extenders to override extension extenders ([ba594de](https://github.com/flarum/core/commit/ba594de13a033480834d53d73f747b05fe9796f8))
|
||||
- Do not resolve objects from the IoC container (in service providers and extenders) until they are actually used
|
||||
- Replace event subscribers (that resolve objects from the IoC container) with listeners (that resolve lazily)
|
||||
- Use custom service provider for Mail component ([ac5e26a](https://github.com/flarum/core/commit/ac5e26a254d89e21bd4c115b6cbd40338e2e4b4b))
|
||||
- Update to Laravel 5.7, revert custom logic for building database index names
|
||||
- Refactored installer, extracted Installation class and pipeline for reuse in CLI and web installers ([790d5be](https://github.com/flarum/core/commit/790d5beee5e283178716bc8f9901c758d9e5b6a0))
|
||||
- Use whitelist for enabling pre-installed extensions during installation ([4585f03](https://github.com/flarum/core/commit/4585f03ee356c92942fbc2ae8c683c651b473954))
|
||||
- Update minimum MySQL version ([7ff9a90](https://github.com/flarum/core/commit/7ff9a90204923293adc520d3c02dc984845d4f9f))
|
||||
|
||||
### Fixed
|
||||
- Signing up via OAuth providers was broken ([67f9375](https://github.com/flarum/core/commit/67f9375d4745add194ae3249d526197c32fd5461))
|
||||
- Group badges were overlapping ([16eb1fa](https://github.com/flarum/core/commit/16eb1fa63b6d7b80ec30c24c0e406a2b7ab09934))
|
||||
- API: Endpoint for uninstalling extensions returned an error ([c761802](https://github.com/flarum/core/commit/c76180290056ddbab67baf5ede814fcedf1dcf14))
|
||||
- Documentation links in installer were outdated ([b58380e](https://github.com/flarum/core/commit/b58380e224ee54abdade3d0a4cc107ef5c91c9a9))
|
||||
- Event posts where counted when aggregating user posts ([671fdec](https://github.com/flarum/core/commit/671fdec8d0a092ccceb5d4d5f657d0f4287fc4c7))
|
||||
- Admins could not reset user passwords ([c67fb2d](https://github.com/flarum/core/commit/c67fb2d4b6a128c71d65dc6703310c0b62f91be2))
|
||||
- Several down migrations were invalid
|
||||
- Validation errors on reset password page resulted in HTTP 404 ([4611abe](https://github.com/flarum/core/commit/4611abe5db8b94ca3dc7bf9c447fca7c67358ee3))
|
||||
- `is:unread` gambit generated an invalid query ([e17bb0b](https://github.com/flarum/core/commit/e17bb0b4331f2c92459292195c6b7db8cde1f9f3))
|
||||
- Entire forum was breaking when the `custom_less` setting was missing from the database ([bf2c5a5](https://github.com/flarum/core/commit/bf2c5a5564dff3f5ef13efe7a8d69f2617570ce6))
|
||||
- Dropdown icon was not showing in user card when on user page ([12fdfc9](https://github.com/flarum/core/commit/12fdfc9b544a27f6fe59c82ad6bddd3420cc0181))
|
||||
- Requests were missing the `original*` attributes, which broke installations in subfolders ([56fde28](https://github.com/flarum/core/commit/56fde28e436f52fee0c03c538f0a6049bc584b53))
|
||||
- Special characters such as `%` and `_` could return incorrect results ([ee3640e](https://github.com/flarum/core/commit/ee3640e1605ff67fef4b3d5cd0596f14a6ae73c9))
|
||||
- FontAwesome component package changed paths in version 5.9.0 ([5eb69e1](https://github.com/flarum/core/commit/5eb69e1f59fa73fdfd5badbf41a05a6a040e7426))
|
||||
- Some server environments had problems accessing the system-wide tmp path for storing JS file maps ([54660eb](https://github.com/flarum/core/commit/54660ebd6311f9ea142f1b573263d0d907400786))
|
||||
- Content length of posts.content was not migrated to mediumText in 2017 ([590b311](https://github.com/flarum/core/commit/590b3115708bf94a9c7f169d98c6126380c7056e))
|
||||
- An error occurred when going to the previous route if there was no previous route found ([985b87da](https://github.com/flarum/core/commit/985b87da6c9942c568a1a192e2fdcfde72e030ee))
|
||||
|
||||
### Removed
|
||||
- `php flarum install --defaults` - this was meant to be used in our old development VM ([44c9109](https://github.com/flarum/core/commit/44c91099cd77138bb5fc29f14fb1e81a9781272d))
|
||||
- Obsolete `id` attributes in JSON-API responses ([ecc3b5e](https://github.com/flarum/core/commit/ecc3b5e2271f8d9b38d52cd54476d86995dbe32e) and [7a44086](https://github.com/flarum/core/commit/7a44086bf3a0e3ba907dceb13d07ac695eca05ea))
|
||||
|
||||
## [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))
|
||||
- Ensure InnoDB engine is used for all tables ([fb6b51b](https://github.com/flarum/core/commit/fb6b51b1cfef0af399607fe038603c8240800b2b), [6370f7e](https://github.com/flarum/core/commit/6370f7ecffa9ea7d5fb64d9551400edbc63318db))
|
||||
- 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))
|
||||
|
@@ -1,3 +0,0 @@
|
||||
# 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.
|
@@ -22,24 +22,24 @@
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"axy/sourcemap": "^0.1.4",
|
||||
"components/font-awesome": "^5.4.2",
|
||||
"components/font-awesome": "5.9.*",
|
||||
"dflydev/fig-cookies": "^1.0.2",
|
||||
"doctrine/dbal": "^2.7",
|
||||
"franzl/whoops-middleware": "^0.4.0",
|
||||
"illuminate/bus": "5.5.*",
|
||||
"illuminate/cache": "5.5.*",
|
||||
"illuminate/config": "5.5.*",
|
||||
"illuminate/container": "5.5.*",
|
||||
"illuminate/contracts": "5.5.*",
|
||||
"illuminate/database": "5.5.*",
|
||||
"illuminate/events": "5.5.*",
|
||||
"illuminate/filesystem": "5.5.*",
|
||||
"illuminate/hashing": "5.5.*",
|
||||
"illuminate/mail": "5.5.*",
|
||||
"illuminate/session": "5.5.*",
|
||||
"illuminate/support": "5.5.*",
|
||||
"illuminate/validation": "5.5.*",
|
||||
"illuminate/view": "5.5.*",
|
||||
"illuminate/bus": "5.7.*",
|
||||
"illuminate/cache": "5.7.*",
|
||||
"illuminate/config": "5.7.*",
|
||||
"illuminate/container": "5.7.*",
|
||||
"illuminate/contracts": "5.7.*",
|
||||
"illuminate/database": "5.7.*",
|
||||
"illuminate/events": "5.7.*",
|
||||
"illuminate/filesystem": "5.7.*",
|
||||
"illuminate/hashing": "5.7.*",
|
||||
"illuminate/mail": "5.7.*",
|
||||
"illuminate/session": "5.7.*",
|
||||
"illuminate/support": "5.7.*",
|
||||
"illuminate/validation": "5.7.*",
|
||||
"illuminate/view": "5.7.*",
|
||||
"intervention/image": "^2.3.0",
|
||||
"league/flysystem": "^1.0.11",
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
@@ -54,8 +54,7 @@
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"s9e/text-formatter": "^1.2.0",
|
||||
"symfony/config": "^3.3",
|
||||
"symfony/console": "^3.3",
|
||||
"symfony/http-foundation": "^3.3",
|
||||
"symfony/console": "^4.2",
|
||||
"symfony/translation": "^3.3",
|
||||
"symfony/yaml": "^3.3",
|
||||
"tobscure/json-api": "^0.3.0",
|
||||
@@ -87,5 +86,14 @@
|
||||
"branch-alias": {
|
||||
"dev-master": "0.1.x-dev"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"@test:unit",
|
||||
"@test:integration"
|
||||
],
|
||||
"test:unit": "phpunit -c tests/phpunit.unit.xml",
|
||||
"test:integration": "phpunit -c tests/phpunit.integration.xml",
|
||||
"test:setup": "@php tests/integration/setup.php"
|
||||
}
|
||||
}
|
||||
|
22
js/dist/admin.js
vendored
22
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
24
js/dist/forum.js
vendored
24
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
707
js/package-lock.json
generated
707
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,12 @@
|
||||
"private": true,
|
||||
"name": "@flarum/core",
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap": "^3.4.1",
|
||||
"classnames": "^2.2.5",
|
||||
"color-thief-browser": "^2.0.2",
|
||||
"expose-loader": "^0.7.5",
|
||||
"flarum-webpack-config": "0.1.0-beta.10",
|
||||
"jquery": "^3.3.1",
|
||||
"jquery": "^3.4.1",
|
||||
"jquery.hotkeys": "^0.1.0",
|
||||
"lodash-es": "^4.17.11",
|
||||
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
|
||||
|
@@ -84,17 +84,21 @@ export default class EditGroupModal extends Modal {
|
||||
return items;
|
||||
}
|
||||
|
||||
submitData() {
|
||||
return {
|
||||
nameSingular: this.nameSingular(),
|
||||
namePlural: this.namePlural(),
|
||||
color: this.color(),
|
||||
icon: this.icon()
|
||||
};
|
||||
}
|
||||
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.loading = true;
|
||||
|
||||
this.group.save({
|
||||
nameSingular: this.nameSingular(),
|
||||
namePlural: this.namePlural(),
|
||||
color: this.color(),
|
||||
icon: this.icon()
|
||||
}, {errorHandler: this.onerror.bind(this)})
|
||||
this.group.save(this.submitData(), {errorHandler: this.onerror.bind(this)})
|
||||
.then(this.hide.bind(this))
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
|
@@ -2,36 +2,55 @@ import Page from './Page';
|
||||
import FieldSet from '../../common/components/FieldSet';
|
||||
import Button from '../../common/components/Button';
|
||||
import Alert from '../../common/components/Alert';
|
||||
import Select from '../../common/components/Select';
|
||||
import LoadingIndicator from '../../common/components/LoadingIndicator';
|
||||
import saveSettings from '../utils/saveSettings';
|
||||
|
||||
export default class MailPage extends Page {
|
||||
init() {
|
||||
super.init();
|
||||
|
||||
this.loading = false;
|
||||
this.loading = true;
|
||||
this.saving = false;
|
||||
|
||||
this.fields = [
|
||||
'mail_driver',
|
||||
'mail_host',
|
||||
'mail_from',
|
||||
'mail_port',
|
||||
'mail_username',
|
||||
'mail_password',
|
||||
'mail_encryption'
|
||||
];
|
||||
this.driverFields = {};
|
||||
this.fields = ['mail_driver', 'mail_from'];
|
||||
this.values = {};
|
||||
|
||||
const settings = app.data.settings;
|
||||
this.fields.forEach(key => this.values[key] = m.prop(settings[key]));
|
||||
|
||||
this.localeOptions = {};
|
||||
const locales = app.locales;
|
||||
for (const i in locales) {
|
||||
this.localeOptions[i] = `${locales[i]} (${i})`;
|
||||
}
|
||||
app.request({
|
||||
method: 'GET',
|
||||
url: app.forum.attribute('apiUrl') + '/mail-drivers'
|
||||
}).then(response => {
|
||||
this.driverFields = response['data'].reduce(
|
||||
(hash, driver) => ({...hash, [driver['id']]: driver['attributes']['fields']}),
|
||||
{}
|
||||
);
|
||||
|
||||
Object.keys(this.driverFields).flatMap(key => this.driverFields[key]).forEach(
|
||||
key => {
|
||||
this.fields.push(key);
|
||||
this.values[key] = m.prop(settings[key]);
|
||||
}
|
||||
);
|
||||
this.loading = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
||||
view() {
|
||||
if (this.loading) {
|
||||
return (
|
||||
<div className="MailPage">
|
||||
<div className="container">
|
||||
<LoadingIndicator />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="MailPage">
|
||||
<div className="container">
|
||||
@@ -41,36 +60,6 @@ export default class MailPage extends Page {
|
||||
{app.translator.trans('core.admin.email.text')}
|
||||
</div>
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.email.server_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
children: [
|
||||
<div className="MailPage-MailSettings-input">
|
||||
<label>{app.translator.trans('core.admin.email.driver_label')}</label>
|
||||
<input className="FormControl" value={this.values.mail_driver() || ''} oninput={m.withAttr('value', this.values.mail_driver)} />
|
||||
<label>{app.translator.trans('core.admin.email.host_label')}</label>
|
||||
<input className="FormControl" value={this.values.mail_host() || ''} oninput={m.withAttr('value', this.values.mail_host)} />
|
||||
<label>{app.translator.trans('core.admin.email.port_label')}</label>
|
||||
<input className="FormControl" value={this.values.mail_port() || ''} oninput={m.withAttr('value', this.values.mail_port)} />
|
||||
<label>{app.translator.trans('core.admin.email.encryption_label')}</label>
|
||||
<input className="FormControl" value={this.values.mail_encryption() || ''} oninput={m.withAttr('value', this.values.mail_encryption)} />
|
||||
</div>
|
||||
]
|
||||
})}
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.email.account_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
children: [
|
||||
<div className="MailPage-MailSettings-input">
|
||||
<label>{app.translator.trans('core.admin.email.username_label')}</label>
|
||||
<input className="FormControl" value={this.values.mail_username() || ''} oninput={m.withAttr('value', this.values.mail_username)} />
|
||||
<label>{app.translator.trans('core.admin.email.password_label')}</label>
|
||||
<input className="FormControl" value={this.values.mail_password() || ''} oninput={m.withAttr('value', this.values.mail_password)} />
|
||||
</div>
|
||||
]
|
||||
})}
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.email.addresses_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
@@ -82,11 +71,35 @@ export default class MailPage extends Page {
|
||||
]
|
||||
})}
|
||||
|
||||
{FieldSet.component({
|
||||
label: app.translator.trans('core.admin.email.driver_heading'),
|
||||
className: 'MailPage-MailSettings',
|
||||
children: [
|
||||
<div className="MailPage-MailSettings-input">
|
||||
<label>{app.translator.trans('core.admin.email.driver_label')}</label>
|
||||
<Select value={this.values.mail_driver()} options={Object.keys(this.driverFields).reduce((memo, val) => ({...memo, [val]: val}), {})} onchange={this.values.mail_driver} />
|
||||
</div>
|
||||
]
|
||||
})}
|
||||
|
||||
{Object.keys(this.driverFields[this.values.mail_driver()]).length > 0 && FieldSet.component({
|
||||
label: app.translator.trans(`core.admin.email.${this.values.mail_driver()}_heading`),
|
||||
className: 'MailPage-MailSettings',
|
||||
children: [
|
||||
<div className="MailPage-MailSettings-input">
|
||||
{this.driverFields[this.values.mail_driver()].flatMap(field => [
|
||||
<label>{app.translator.trans(`core.admin.email.${field}_label`)}</label>,
|
||||
<input className="FormControl" value={this.values[field]() || ''} oninput={m.withAttr('value', this.values[field])} />
|
||||
])}
|
||||
</div>
|
||||
]
|
||||
})}
|
||||
|
||||
{Button.component({
|
||||
type: 'submit',
|
||||
className: 'Button Button--primary',
|
||||
children: app.translator.trans('core.admin.email.submit_button'),
|
||||
loading: this.loading,
|
||||
loading: this.saving,
|
||||
disabled: !this.changed()
|
||||
})}
|
||||
</form>
|
||||
@@ -102,9 +115,9 @@ export default class MailPage extends Page {
|
||||
onsubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.loading) return;
|
||||
if (this.saving) return;
|
||||
|
||||
this.loading = true;
|
||||
this.saving = true;
|
||||
app.alerts.dismiss(this.successAlert);
|
||||
|
||||
const settings = {};
|
||||
@@ -117,7 +130,7 @@ export default class MailPage extends Page {
|
||||
})
|
||||
.catch(() => {})
|
||||
.then(() => {
|
||||
this.loading = false;
|
||||
this.saving = false;
|
||||
m.redraw();
|
||||
});
|
||||
}
|
||||
|
@@ -77,7 +77,7 @@ export default class EditUserModal extends Modal {
|
||||
<label>{app.translator.trans('core.forum.edit_user.password_heading')}</label>
|
||||
<div>
|
||||
<label className="checkbox">
|
||||
<input type="checkbox" checked={this.setPassword()} onChange={e => {
|
||||
<input type="checkbox" onchange={e => {
|
||||
this.setPassword(e.target.checked);
|
||||
m.redraw(true);
|
||||
if (e.target.checked) this.$('[name=password]').select();
|
||||
|
@@ -86,6 +86,10 @@ export default class History {
|
||||
* @public
|
||||
*/
|
||||
back() {
|
||||
if (! this.canGoBack()) {
|
||||
return this.home();
|
||||
}
|
||||
|
||||
this.stack.pop();
|
||||
|
||||
m.route(this.getCurrent().url);
|
||||
|
@@ -29,6 +29,10 @@
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.Select {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
@import "fontawesome";
|
||||
@import "fa-brands";
|
||||
@import "fa-regular";
|
||||
@import "fa-solid";
|
||||
@import "brands";
|
||||
@import "regular";
|
||||
@import "solid";
|
||||
@fa-font-path: "./fonts";
|
||||
|
||||
@import "normalize";
|
||||
|
@@ -78,6 +78,7 @@
|
||||
|
||||
&, > li {
|
||||
display: inline-block;
|
||||
margin-right: 5px
|
||||
}
|
||||
}
|
||||
.UserCard-info {
|
||||
|
@@ -1,10 +1,5 @@
|
||||
.UserPage {
|
||||
.UserCard-controls {
|
||||
float: right;
|
||||
|
||||
.Dropdown-toggle .Button-icon {
|
||||
display: none;
|
||||
}
|
||||
.UserHero .Dropdown-toggle .Button-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -24,18 +23,14 @@ return [
|
||||
})
|
||||
->delete();
|
||||
|
||||
$schema->table('access_tokens', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('access_tokens', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,21 +9,18 @@
|
||||
* 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) use ($schema) {
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$table->dropPrimary(['id']);
|
||||
$table->renameColumn('id', 'key');
|
||||
$table->unique('key');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
|
||||
$schema->table('api_keys', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('allowed_ips')->nullable();
|
||||
$table->string('scopes')->nullable();
|
||||
@@ -32,25 +29,19 @@ 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) use ($schema) {
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('api_keys', function (Blueprint $table) {
|
||||
$table->dropUnique(['key']);
|
||||
$table->renameColumn('key', 'id');
|
||||
$table->primary('id');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* 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;
|
||||
@@ -34,26 +33,22 @@ return [
|
||||
'last_post_id' => $selectId('posts', 'last_post_id'),
|
||||
]);
|
||||
|
||||
$schema->table('discussions', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('discussions', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('discussions', function (Blueprint $table) {
|
||||
$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,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -27,20 +26,16 @@ return [
|
||||
})
|
||||
->delete();
|
||||
|
||||
$schema->table('discussion_user', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('discussion_user', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('discussion_user', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['discussion_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -24,18 +23,14 @@ return [
|
||||
})
|
||||
->delete();
|
||||
|
||||
$schema->table('email_tokens', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('email_tokens', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('email_tokens', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -24,18 +23,14 @@ return [
|
||||
})
|
||||
->delete();
|
||||
|
||||
$schema->table('group_permission', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('group_permission', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('group_permission', function (Blueprint $table) {
|
||||
$table->dropForeign(['group_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -27,20 +26,16 @@ return [
|
||||
})
|
||||
->delete();
|
||||
|
||||
$schema->table('group_user', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('group_user', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('group_user', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['group_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -31,20 +30,16 @@ return [
|
||||
})
|
||||
->update(['from_user_id' => null]);
|
||||
|
||||
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['from_user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Database\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
@@ -24,18 +23,14 @@ return [
|
||||
})
|
||||
->delete();
|
||||
|
||||
$schema->table('password_tokens', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('password_tokens', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('password_tokens', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,7 +9,6 @@
|
||||
* 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;
|
||||
@@ -32,23 +31,18 @@ return [
|
||||
'hidden_user_id' => $selectId('users', 'hidden_user_id'),
|
||||
]);
|
||||
|
||||
$schema->table('posts', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('posts', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('posts', function (Blueprint $table) {
|
||||
$table->dropForeign(['user_id']);
|
||||
$table->dropForeign(['discussion_id']);
|
||||
$table->dropForeign(['edited_user_id']);
|
||||
$table->dropForeign(['hidden_user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,30 +9,25 @@
|
||||
* 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) use ($schema) {
|
||||
$schema->table('users', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('users', function (Blueprint $table) {
|
||||
$table->dropIndex(['joined_at']);
|
||||
$table->dropIndex(['last_seen_at']);
|
||||
$table->dropIndex(['discussion_count']);
|
||||
$table->dropIndex(['comment_count']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,13 +9,12 @@
|
||||
* 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) use ($schema) {
|
||||
$schema->table('discussions', function (Blueprint $table) {
|
||||
$table->index('last_posted_at');
|
||||
$table->index('last_posted_user_id');
|
||||
$table->index('created_at');
|
||||
@@ -23,13 +22,11 @@ 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) use ($schema) {
|
||||
$schema->table('discussions', function (Blueprint $table) {
|
||||
$table->dropIndex(['last_posted_at']);
|
||||
$table->dropIndex(['last_posted_user_id']);
|
||||
$table->dropIndex(['created_at']);
|
||||
@@ -37,8 +34,6 @@ return [
|
||||
$table->dropIndex(['comment_count']);
|
||||
$table->dropIndex(['participant_count']);
|
||||
$table->dropIndex(['hidden_at']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,24 +9,19 @@
|
||||
* 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) use ($schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->index('user_id');
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) use ($schema) {
|
||||
$schema->table('notifications', function (Blueprint $table) {
|
||||
$table->dropIndex(['user_id']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -9,28 +9,23 @@
|
||||
* 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) use ($schema) {
|
||||
$schema->table('posts', function (Blueprint $table) {
|
||||
$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) use ($schema) {
|
||||
$schema->table('posts', function (Blueprint $table) {
|
||||
$table->dropIndex(['discussion_id', 'number']);
|
||||
$table->dropIndex(['discussion_id', 'created_at']);
|
||||
$table->dropIndex(['user_id', 'created_at']);
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
}
|
||||
];
|
||||
|
@@ -24,7 +24,7 @@ return [
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('auth_tokens', function (Blueprint $table) {
|
||||
$schema->table('registration_tokens', function (Blueprint $table) {
|
||||
$table->dropColumn('provider', 'identifier', 'user_attributes');
|
||||
|
||||
$table->string('payload', 150)->change();
|
||||
|
@@ -12,16 +12,17 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
|
||||
// https://github.com/doctrine/dbal/issues/2566#issuecomment-480217999
|
||||
return [
|
||||
'up' => function (Builder $schema) {
|
||||
$schema->table('posts', function (Blueprint $table) {
|
||||
$table->mediumText('content')->change();
|
||||
$table->mediumText('content')->comment(' ')->change();
|
||||
});
|
||||
},
|
||||
|
||||
'down' => function (Builder $schema) {
|
||||
$schema->table('posts', function (Blueprint $table) {
|
||||
$table->text('content')->change();
|
||||
$table->text('content')->comment('')->change();
|
||||
});
|
||||
}
|
||||
];
|
@@ -12,8 +12,11 @@
|
||||
namespace Flarum\Admin;
|
||||
|
||||
use Flarum\Event\ConfigureMiddleware;
|
||||
use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Frontend\AddLocaleAssets;
|
||||
use Flarum\Frontend\AddTranslations;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
@@ -22,6 +25,8 @@ use Flarum\Http\Middleware as HttpMiddleware;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
|
||||
class AdminServiceProvider extends AbstractServiceProvider
|
||||
@@ -36,7 +41,10 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.admin.routes', function () {
|
||||
return new RouteCollection;
|
||||
$routes = new RouteCollection;
|
||||
$this->populateRoutes($routes);
|
||||
|
||||
return $routes;
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.admin.middleware', function (Application $app) {
|
||||
@@ -53,6 +61,7 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
|
||||
$pipe->pipe($app->make(Middleware\RequireAdministrateAbility::class));
|
||||
|
||||
@@ -98,15 +107,30 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->populateRoutes($this->app->make('flarum.admin.routes'));
|
||||
|
||||
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum.admin');
|
||||
|
||||
$this->app->make('events')->subscribe(
|
||||
new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.assets.admin'),
|
||||
$this->app->make('flarum.locales')
|
||||
)
|
||||
$events = $this->app->make('events');
|
||||
|
||||
$events->listen(
|
||||
[Enabled::class, Disabled::class, ClearingCache::class],
|
||||
function () {
|
||||
$recompile = new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.assets.admin'),
|
||||
$this->app->make(LocaleManager::class)
|
||||
);
|
||||
$recompile->flush();
|
||||
}
|
||||
);
|
||||
|
||||
$events->listen(
|
||||
Saved::class,
|
||||
function (Saved $event) {
|
||||
$recompile = new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.assets.admin'),
|
||||
$this->app->make(LocaleManager::class)
|
||||
);
|
||||
$recompile->whenSettingsSaved($event);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -40,7 +40,10 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.api.routes', function () {
|
||||
return new RouteCollection;
|
||||
$routes = new RouteCollection;
|
||||
$this->populateRoutes($routes);
|
||||
|
||||
return $routes;
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.api.middleware', function (Application $app) {
|
||||
@@ -54,6 +57,7 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithHeader::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
|
||||
|
||||
event(new ConfigureMiddleware($pipe, 'api'));
|
||||
@@ -90,8 +94,6 @@ class ApiServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->populateRoutes($this->app->make('flarum.api.routes'));
|
||||
|
||||
$this->registerNotificationSerializers();
|
||||
|
||||
AbstractSerializeController::setContainer($this->app);
|
||||
|
@@ -102,7 +102,7 @@ abstract class AbstractSerializeController implements RequestHandlerInterface
|
||||
);
|
||||
|
||||
$serializer = static::$container->make($this->serializer);
|
||||
$serializer->setActor($request->getAttribute('actor'));
|
||||
$serializer->setRequest($request);
|
||||
|
||||
$element = $this->createElement($data, $serializer)
|
||||
->with($this->extractInclude($request))
|
||||
|
45
src/Api/Controller/ListMailDriversController.php
Normal file
45
src/Api/Controller/ListMailDriversController.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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\Api\Serializer\MailDriverSerializer;
|
||||
use Flarum\User\AssertPermissionTrait;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tobscure\JsonApi\Document;
|
||||
|
||||
class ListMailDriversController extends AbstractListController
|
||||
{
|
||||
use AssertPermissionTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $serializer = MailDriverSerializer::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function data(ServerRequestInterface $request, Document $document)
|
||||
{
|
||||
$this->assertAdmin($request->getAttribute('actor'));
|
||||
|
||||
$drivers = self::$container->make('mail.supported_drivers');
|
||||
array_walk($drivers, function (&$driver, $key) {
|
||||
$driver = [
|
||||
'id' => $key,
|
||||
'driver' => self::$container->make($driver),
|
||||
];
|
||||
});
|
||||
|
||||
return $drivers;
|
||||
}
|
||||
}
|
@@ -38,9 +38,10 @@ class UninstallExtensionController extends AbstractDeleteController
|
||||
|
||||
$name = array_get($request->getQueryParams(), 'name');
|
||||
|
||||
$extension = $this->extensions->getExtension($name);
|
||||
if ($this->extensions->getExtension($name) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->extensions->disable($extension);
|
||||
$this->extensions->uninstall($extension);
|
||||
$this->extensions->uninstall($name);
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Tobscure\JsonApi\AbstractSerializer as BaseAbstractSerializer;
|
||||
use Tobscure\JsonApi\Collection;
|
||||
use Tobscure\JsonApi\Relationship;
|
||||
@@ -28,6 +29,11 @@ use Tobscure\JsonApi\SerializerInterface;
|
||||
|
||||
abstract class AbstractSerializer extends BaseAbstractSerializer
|
||||
{
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
@@ -43,6 +49,23 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
||||
*/
|
||||
protected static $container;
|
||||
|
||||
/**
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*/
|
||||
public function setRequest(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->actor = $request->getAttribute('actor');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
@@ -51,14 +74,6 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
||||
return $this->actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $actor
|
||||
*/
|
||||
public function setActor(User $actor)
|
||||
{
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -231,7 +246,7 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
||||
{
|
||||
$serializer = static::$container->make($class);
|
||||
|
||||
$serializer->setActor($this->actor);
|
||||
$serializer->setRequest($this->request);
|
||||
|
||||
return $serializer;
|
||||
}
|
||||
|
@@ -37,14 +37,13 @@ class BasicPostSerializer extends AbstractSerializer
|
||||
}
|
||||
|
||||
$attributes = [
|
||||
'id' => (int) $post->id,
|
||||
'number' => (int) $post->number,
|
||||
'createdAt' => $this->formatDate($post->created_at),
|
||||
'contentType' => $post->type
|
||||
];
|
||||
|
||||
if ($post instanceof CommentPost) {
|
||||
$attributes['contentHtml'] = $post->content_html;
|
||||
$attributes['contentHtml'] = $post->formatContent($this->request);
|
||||
} else {
|
||||
$attributes['content'] = $post->content;
|
||||
}
|
||||
|
49
src/Api/Serializer/MailDriverSerializer.php
Normal file
49
src/Api/Serializer/MailDriverSerializer.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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\Serializer;
|
||||
|
||||
use Flarum\Mail\DriverInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class MailDriverSerializer extends AbstractSerializer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'mail-drivers';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Flarum\Mail\DriverInterface $driver
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function getDefaultAttributes($driver)
|
||||
{
|
||||
if (! ($driver['driver'] instanceof DriverInterface)) {
|
||||
throw new InvalidArgumentException(
|
||||
get_class($this).' can only serialize instances of '.DriverInterface::class
|
||||
);
|
||||
}
|
||||
|
||||
$driver = $driver['driver'];
|
||||
|
||||
return [
|
||||
'fields' => $driver->availableSettings(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getId($model)
|
||||
{
|
||||
return $model['id'];
|
||||
}
|
||||
}
|
@@ -44,7 +44,6 @@ class NotificationSerializer extends AbstractSerializer
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int) $notification->id,
|
||||
'contentType' => $notification->type,
|
||||
'content' => $notification->data,
|
||||
'createdAt' => $this->formatDate($notification->created_at),
|
||||
|
@@ -43,8 +43,6 @@ class PostSerializer extends BasicPostSerializer
|
||||
$canEdit = $gate->allows('edit', $post);
|
||||
|
||||
if ($post instanceof CommentPost) {
|
||||
$attributes['contentHtml'] = $post->content_html;
|
||||
|
||||
if ($canEdit) {
|
||||
$attributes['content'] = $post->content;
|
||||
}
|
||||
|
@@ -308,4 +308,11 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
||||
'cache.clear',
|
||||
$route->toController(Controller\ClearCacheController::class)
|
||||
);
|
||||
|
||||
// List available mail drivers and their configuration fields
|
||||
$map->get(
|
||||
'/mail-drivers',
|
||||
'mailDrivers.index',
|
||||
$route->toController(Controller\ListMailDriversController::class)
|
||||
);
|
||||
};
|
||||
|
@@ -55,13 +55,13 @@ abstract class AbstractModel extends Eloquent
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::saved(function (AbstractModel $model) {
|
||||
static::saved(function (self $model) {
|
||||
foreach ($model->releaseAfterSaveCallbacks() as $callback) {
|
||||
$callback($model);
|
||||
}
|
||||
});
|
||||
|
||||
static::deleted(function (AbstractModel $model) {
|
||||
static::deleted(function (self $model) {
|
||||
foreach ($model->releaseAfterDeleteCallbacks() as $callback) {
|
||||
$callback($model);
|
||||
}
|
||||
|
@@ -12,21 +12,21 @@
|
||||
namespace Flarum\Database\Console;
|
||||
|
||||
use Flarum\Console\AbstractCommand;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Flarum\Foundation\Application;
|
||||
|
||||
class MigrateCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* @var Container
|
||||
* @var Application
|
||||
*/
|
||||
protected $container;
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @param Application $application
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
public function __construct(Application $application)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->app = $application;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -55,16 +55,16 @@ class MigrateCommand extends AbstractCommand
|
||||
|
||||
public function upgrade()
|
||||
{
|
||||
$this->container->bind('Illuminate\Database\Schema\Builder', function ($container) {
|
||||
return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
|
||||
$this->app->bind('Illuminate\Database\Schema\Builder', function ($app) {
|
||||
return $app->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
|
||||
});
|
||||
|
||||
$migrator = $this->container->make('Flarum\Database\Migrator');
|
||||
$migrator = $this->app->make('Flarum\Database\Migrator');
|
||||
$migrator->setOutput($this->output);
|
||||
|
||||
$migrator->run(__DIR__.'/../../../migrations');
|
||||
|
||||
$extensions = $this->container->make('Flarum\Extension\ExtensionManager');
|
||||
$extensions = $this->app->make('Flarum\Extension\ExtensionManager');
|
||||
$extensions->getMigrator()->setOutput($this->output);
|
||||
|
||||
foreach ($extensions->getEnabledExtensions() as $name => $extension) {
|
||||
@@ -75,13 +75,13 @@ class MigrateCommand extends AbstractCommand
|
||||
}
|
||||
}
|
||||
|
||||
$this->container->make('Flarum\Settings\SettingsRepositoryInterface')->set('version', $this->container->version());
|
||||
$this->app->make('Flarum\Settings\SettingsRepositoryInterface')->set('version', $this->app->version());
|
||||
|
||||
$this->info('Publishing assets...');
|
||||
|
||||
$this->container->make('files')->copyDirectory(
|
||||
base_path().'/vendor/components/font-awesome/webfonts',
|
||||
public_path().'/assets/fonts'
|
||||
$this->app->make('files')->copyDirectory(
|
||||
$this->app->vendorPath().'/components/font-awesome/webfonts',
|
||||
$this->app->publicPath().'/assets/fonts'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -12,8 +12,9 @@
|
||||
namespace Flarum\Database;
|
||||
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Illuminate\Database\ConnectionResolver;
|
||||
use Illuminate\Database\Connectors\ConnectionFactory;
|
||||
use Illuminate\Database\Capsule\Manager;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Database\ConnectionResolverInterface;
|
||||
|
||||
class DatabaseServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
@@ -22,29 +23,39 @@ class DatabaseServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('flarum.db', function () {
|
||||
$factory = new ConnectionFactory($this->app);
|
||||
$this->app->singleton(Manager::class, function ($app) {
|
||||
$manager = new Manager($app);
|
||||
|
||||
$dbConfig = $this->app->config('database');
|
||||
$dbConfig['engine'] = 'InnoDB';
|
||||
$connection = $factory->make($dbConfig);
|
||||
$connection->setEventDispatcher($this->app->make('Illuminate\Contracts\Events\Dispatcher'));
|
||||
$config = $app->config('database');
|
||||
$config['engine'] = 'InnoDB';
|
||||
$config['prefix_indexes'] = true;
|
||||
|
||||
return $connection;
|
||||
$manager->addConnection($config, 'flarum');
|
||||
|
||||
return $manager;
|
||||
});
|
||||
|
||||
$this->app->alias('flarum.db', 'Illuminate\Database\ConnectionInterface');
|
||||
$this->app->singleton(ConnectionResolverInterface::class, function ($app) {
|
||||
$manager = $app->make(Manager::class);
|
||||
$manager->setAsGlobal();
|
||||
$manager->bootEloquent();
|
||||
|
||||
$this->app->singleton('Illuminate\Database\ConnectionResolverInterface', function () {
|
||||
$resolver = new ConnectionResolver([
|
||||
'flarum' => $this->app->make('flarum.db'),
|
||||
]);
|
||||
$resolver->setDefaultConnection('flarum');
|
||||
$dbManager = $manager->getDatabaseManager();
|
||||
$dbManager->setDefaultConnection('flarum');
|
||||
|
||||
return $resolver;
|
||||
return $dbManager;
|
||||
});
|
||||
|
||||
$this->app->alias('Illuminate\Database\ConnectionResolverInterface', 'db');
|
||||
$this->app->alias(ConnectionResolverInterface::class, 'db');
|
||||
|
||||
$this->app->singleton(ConnectionInterface::class, function ($app) {
|
||||
$resolver = $app->make(ConnectionResolverInterface::class);
|
||||
|
||||
return $resolver->connection();
|
||||
});
|
||||
|
||||
$this->app->alias(ConnectionInterface::class, 'db.connection');
|
||||
$this->app->alias(ConnectionInterface::class, 'flarum.db');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +63,7 @@ class DatabaseServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
AbstractModel::setConnectionResolver($this->app->make('Illuminate\Database\ConnectionResolverInterface'));
|
||||
AbstractModel::setConnectionResolver($this->app->make(ConnectionResolverInterface::class));
|
||||
AbstractModel::setEventDispatcher($this->app->make('events'));
|
||||
}
|
||||
}
|
||||
|
@@ -31,8 +31,6 @@ abstract class Migration
|
||||
'up' => function (Builder $schema) use ($name, $definition) {
|
||||
$schema->create($name, function (Blueprint $table) use ($schema, $definition) {
|
||||
$definition($table);
|
||||
|
||||
static::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
'down' => function (Builder $schema) use ($name) {
|
||||
@@ -68,8 +66,6 @@ abstract class Migration
|
||||
$type = array_shift($options);
|
||||
$table->addColumn($type, $columnName, $options);
|
||||
}
|
||||
|
||||
Migration::fixIndexNames($schema, $table);
|
||||
});
|
||||
},
|
||||
'down' => function (Builder $schema) use ($tableName, $columnDefinitions) {
|
||||
@@ -193,27 +189,4 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ namespace Flarum\Database;
|
||||
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Application;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
class MigrationServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
@@ -21,12 +22,12 @@ class MigrationServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('Flarum\Database\MigrationRepositoryInterface', function ($app) {
|
||||
$this->app->singleton(MigrationRepositoryInterface::class, function ($app) {
|
||||
return new DatabaseMigrationRepository($app['flarum.db'], 'migrations');
|
||||
});
|
||||
|
||||
$this->app->bind(MigrationCreator::class, function (Application $app) {
|
||||
return new MigrationCreator($app->make('Illuminate\Filesystem\Filesystem'), $app->basePath());
|
||||
return new MigrationCreator($app->make(Filesystem::class), $app->basePath());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -146,7 +146,9 @@ class Migrator
|
||||
*/
|
||||
public function reset($path, Extension $extension = null)
|
||||
{
|
||||
$migrations = array_reverse($this->repository->getRan($extension->getId()));
|
||||
$migrations = array_reverse($this->repository->getRan(
|
||||
$extension ? $extension->getId() : null
|
||||
));
|
||||
|
||||
$count = count($migrations);
|
||||
|
||||
|
@@ -98,7 +98,7 @@ class Discussion extends AbstractModel
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function (Discussion $discussion) {
|
||||
static::deleting(function (self $discussion) {
|
||||
Notification::whereSubjectModel(Post::class)
|
||||
->whereIn('subject_id', function ($query) use ($discussion) {
|
||||
$query->select('id')->from('posts')->where('discussion_id', $discussion->id);
|
||||
@@ -106,13 +106,13 @@ class Discussion extends AbstractModel
|
||||
->delete();
|
||||
});
|
||||
|
||||
static::deleted(function (Discussion $discussion) {
|
||||
static::deleted(function (self $discussion) {
|
||||
$discussion->raise(new Deleted($discussion));
|
||||
|
||||
Notification::whereSubject($discussion)->delete();
|
||||
});
|
||||
|
||||
static::saving(function (Discussion $discussion) {
|
||||
static::saving(function (self $discussion) {
|
||||
$event = new GetModelIsPrivate($discussion);
|
||||
|
||||
$discussion->is_private = static::$dispatcher->until($event) === true;
|
||||
|
@@ -20,9 +20,6 @@ use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class DiscussionMetadataUpdater
|
||||
{
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Posted::class, [$this, 'whenPostWasPosted']);
|
||||
@@ -31,9 +28,6 @@ class DiscussionMetadataUpdater
|
||||
$events->listen(Restored::class, [$this, 'whenPostWasRestored']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Posted $event
|
||||
*/
|
||||
public function whenPostWasPosted(Posted $event)
|
||||
{
|
||||
$discussion = $event->post->discussion;
|
||||
@@ -46,9 +40,6 @@ class DiscussionMetadataUpdater
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Post\Event\Deleted $event
|
||||
*/
|
||||
public function whenPostWasDeleted(Deleted $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
@@ -60,17 +51,11 @@ class DiscussionMetadataUpdater
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Post\Event\Hidden $event
|
||||
*/
|
||||
public function whenPostWasHidden(Hidden $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Restored $event
|
||||
*/
|
||||
public function whenPostWasRestored(Restored $event)
|
||||
{
|
||||
$discussion = $event->post->discussion;
|
||||
@@ -83,9 +68,6 @@ class DiscussionMetadataUpdater
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $post
|
||||
*/
|
||||
protected function removePost(Post $post)
|
||||
{
|
||||
$discussion = $post->discussion;
|
||||
|
@@ -15,7 +15,6 @@ use Flarum\Discussion\Event\Renamed;
|
||||
use Flarum\Notification\Blueprint\DiscussionRenamedBlueprint;
|
||||
use Flarum\Notification\NotificationSyncer;
|
||||
use Flarum\Post\DiscussionRenamedPost;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class DiscussionRenamedLogger
|
||||
{
|
||||
@@ -24,26 +23,12 @@ class DiscussionRenamedLogger
|
||||
*/
|
||||
protected $notifications;
|
||||
|
||||
/**
|
||||
* @param NotificationSyncer $notifications
|
||||
*/
|
||||
public function __construct(NotificationSyncer $notifications)
|
||||
{
|
||||
$this->notifications = $notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Renamed::class, [$this, 'whenDiscussionWasRenamed']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Discussion\Event\Renamed $event
|
||||
*/
|
||||
public function whenDiscussionWasRenamed(Renamed $event)
|
||||
public function handle(Renamed $event)
|
||||
{
|
||||
$post = DiscussionRenamedPost::reply(
|
||||
$event->discussion->id,
|
||||
|
@@ -49,8 +49,8 @@ class DiscussionRepository
|
||||
*/
|
||||
public function getReadIds(User $user)
|
||||
{
|
||||
return Discussion::leftJoin('discussions_users', 'discussions_users.discussion_id', '=', 'discussions.id')
|
||||
->where('user_id', $user->id)
|
||||
return Discussion::leftJoin('discussion_user', 'discussion_user.discussion_id', '=', 'discussions.id')
|
||||
->where('discussion_user.user_id', $user->id)
|
||||
->whereColumn('last_read_post_number', '>=', 'last_post_number')
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Flarum\Discussion;
|
||||
|
||||
use Flarum\Discussion\Event\Renamed;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
|
||||
class DiscussionServiceProvider extends AbstractServiceProvider
|
||||
@@ -24,6 +25,9 @@ class DiscussionServiceProvider extends AbstractServiceProvider
|
||||
|
||||
$events->subscribe(DiscussionMetadataUpdater::class);
|
||||
$events->subscribe(DiscussionPolicy::class);
|
||||
$events->subscribe(DiscussionRenamedLogger::class);
|
||||
|
||||
$events->listen(
|
||||
Renamed::class, DiscussionRenamedLogger::class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ class FulltextGambit implements GambitInterface
|
||||
// discussions that have a relevant title or that contain relevant posts.
|
||||
$query
|
||||
->addSelect('posts_ft.most_relevant_post_id')
|
||||
->join(
|
||||
->leftJoin(
|
||||
new Expression('('.$subquery->toSql().') '.$grammar->wrapTable('posts_ft')),
|
||||
'posts_ft.discussion_id', '=', 'discussions.id'
|
||||
)
|
||||
|
@@ -21,7 +21,7 @@ class Formatter implements ExtenderInterface, LifecycleInterface
|
||||
{
|
||||
protected $callback;
|
||||
|
||||
public function configure(callable $callback)
|
||||
public function configure($callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
|
||||
@@ -34,8 +34,14 @@ class Formatter implements ExtenderInterface, LifecycleInterface
|
||||
|
||||
$events->listen(
|
||||
Configuring::class,
|
||||
function (Configuring $event) {
|
||||
call_user_func($this->callback, $event->configurator);
|
||||
function (Configuring $event) use ($container) {
|
||||
if (is_string($this->callback)) {
|
||||
$callback = $container->make($this->callback);
|
||||
} else {
|
||||
$callback = $this->callback;
|
||||
}
|
||||
|
||||
$callback($event->configurator);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -11,12 +11,18 @@
|
||||
|
||||
namespace Flarum\Extend;
|
||||
|
||||
use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Frontend\Assets;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\Frontend as ActualFrontend;
|
||||
use Flarum\Frontend\RecompileFrontendAssets;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class Frontend implements ExtenderInterface
|
||||
@@ -107,11 +113,29 @@ class Frontend implements ExtenderInterface
|
||||
return $container->make('flarum.assets.factory')($this->frontend);
|
||||
});
|
||||
|
||||
$container->make('events')->subscribe(
|
||||
new RecompileFrontendAssets(
|
||||
$container->make($abstract),
|
||||
$container->make('flarum.locales')
|
||||
)
|
||||
/** @var \Illuminate\Contracts\Events\Dispatcher $events */
|
||||
$events = $container->make('events');
|
||||
|
||||
$events->listen(
|
||||
[Enabled::class, Disabled::class, ClearingCache::class],
|
||||
function () use ($container, $abstract) {
|
||||
$recompile = new RecompileFrontendAssets(
|
||||
$container->make($abstract),
|
||||
$container->make(LocaleManager::class)
|
||||
);
|
||||
$recompile->flush();
|
||||
}
|
||||
);
|
||||
|
||||
$events->listen(
|
||||
Saved::class,
|
||||
function (Saved $event) use ($container, $abstract) {
|
||||
$recompile = new RecompileFrontendAssets(
|
||||
$container->make($abstract),
|
||||
$container->make(LocaleManager::class)
|
||||
);
|
||||
$recompile->whenSettingsSaved($event);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -122,15 +146,20 @@ class Frontend implements ExtenderInterface
|
||||
return;
|
||||
}
|
||||
|
||||
$routes = $container->make("flarum.$this->frontend.routes");
|
||||
$factory = $container->make(RouteHandlerFactory::class);
|
||||
$container->resolving(
|
||||
"flarum.{$this->frontend}.routes",
|
||||
function (RouteCollection $collection, Container $container) {
|
||||
/** @var RouteHandlerFactory $factory */
|
||||
$factory = $container->make(RouteHandlerFactory::class);
|
||||
|
||||
foreach ($this->routes as $route) {
|
||||
$routes->get(
|
||||
$route['path'], $route['name'],
|
||||
$factory->toFrontend($this->frontend, $route['content'])
|
||||
);
|
||||
}
|
||||
foreach ($this->routes as $route) {
|
||||
$collection->get(
|
||||
$route['path'], $route['name'],
|
||||
$factory->toFrontend($this->frontend, $route['content'])
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function registerContent(Container $container)
|
||||
|
@@ -18,7 +18,7 @@ use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
class LanguagePack implements ExtenderInterface
|
||||
class LanguagePack implements ExtenderInterface, LifecycleInterface
|
||||
{
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
@@ -37,8 +37,16 @@ class LanguagePack implements ExtenderInterface
|
||||
);
|
||||
}
|
||||
|
||||
/** @var LocaleManager $locales */
|
||||
$locales = $container->make(LocaleManager::class);
|
||||
$container->resolving(
|
||||
LocaleManager::class,
|
||||
function (LocaleManager $locales) use ($extension, $locale, $title) {
|
||||
$this->registerLocale($locales, $extension, $locale, $title);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function registerLocale(LocaleManager $locales, Extension $extension, $locale, $title)
|
||||
{
|
||||
$locales->addLocale($locale, $title);
|
||||
|
||||
$directory = $extension->getPath().'/locale';
|
||||
@@ -63,4 +71,14 @@ class LanguagePack implements ExtenderInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onEnable(Container $container, Extension $extension)
|
||||
{
|
||||
$container->make('flarum.locales')->clearCache();
|
||||
}
|
||||
|
||||
public function onDisable(Container $container, Extension $extension)
|
||||
{
|
||||
$container->make('flarum.locales')->clearCache();
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ use Flarum\Extension\Extension;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class Locales implements ExtenderInterface
|
||||
class Locales implements ExtenderInterface, LifecycleInterface
|
||||
{
|
||||
protected $directory;
|
||||
|
||||
@@ -27,23 +27,35 @@ class Locales implements ExtenderInterface
|
||||
|
||||
public function extend(Container $container, Extension $extension = null)
|
||||
{
|
||||
/** @var LocaleManager $locales */
|
||||
$locales = $container->make(LocaleManager::class);
|
||||
$container->resolving(
|
||||
LocaleManager::class,
|
||||
function (LocaleManager $locales) {
|
||||
foreach (new DirectoryIterator($this->directory) as $file) {
|
||||
if (! $file->isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (new DirectoryIterator($this->directory) as $file) {
|
||||
if (! $file->isFile()) {
|
||||
continue;
|
||||
$extension = $file->getExtension();
|
||||
if (! in_array($extension, ['yml', 'yaml'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$locales->addTranslations(
|
||||
$file->getBasename(".$extension"),
|
||||
$file->getPathname()
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$extension = $file->getExtension();
|
||||
if (! in_array($extension, ['yml', 'yaml'])) {
|
||||
continue;
|
||||
}
|
||||
public function onEnable(Container $container, Extension $extension)
|
||||
{
|
||||
$container->make(LocaleManager::class)->clearCache();
|
||||
}
|
||||
|
||||
$locales->addTranslations(
|
||||
$file->getBasename(".$extension"),
|
||||
$file->getPathname()
|
||||
);
|
||||
}
|
||||
public function onDisable(Container $container, Extension $extension)
|
||||
{
|
||||
$container->make(LocaleManager::class)->clearCache();
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace Flarum\Extend;
|
||||
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
@@ -69,19 +70,21 @@ class Routes implements ExtenderInterface
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Flarum\Http\RouteCollection $collection */
|
||||
$collection = $container->make("flarum.{$this->appName}.routes");
|
||||
$container->resolving(
|
||||
"flarum.{$this->appName}.routes",
|
||||
function (RouteCollection $collection, Container $container) {
|
||||
/** @var RouteHandlerFactory $factory */
|
||||
$factory = $container->make(RouteHandlerFactory::class);
|
||||
|
||||
/** @var RouteHandlerFactory $factory */
|
||||
$factory = $container->make(RouteHandlerFactory::class);
|
||||
|
||||
foreach ($this->routes as $route) {
|
||||
$collection->addRoute(
|
||||
$route['method'],
|
||||
$route['path'],
|
||||
$route['name'],
|
||||
$factory->toController($route['handler'])
|
||||
);
|
||||
}
|
||||
foreach ($this->routes as $route) {
|
||||
$collection->addRoute(
|
||||
$route['method'],
|
||||
$route['path'],
|
||||
$route['name'],
|
||||
$factory->toController($route['handler'])
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,6 @@ namespace Flarum\Extension;
|
||||
use Flarum\Extension\Event\Disabling;
|
||||
use Flarum\Http\Exception\ForbiddenException;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class DefaultLanguagePackGuard
|
||||
{
|
||||
@@ -28,26 +27,17 @@ class DefaultLanguagePackGuard
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
public function handle(Disabling $event)
|
||||
{
|
||||
$events->listen(Disabling::class, [$this, 'whenExtensionWillBeDisabled']);
|
||||
}
|
||||
if (! in_array('flarum-locale', $event->extension->extra)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Disabling $event
|
||||
* @throws ForbiddenException
|
||||
*/
|
||||
public function whenExtensionWillBeDisabled(Disabling $event)
|
||||
{
|
||||
if (in_array('flarum-locale', $event->extension->extra)) {
|
||||
$defaultLocale = $this->settings->get('default_locale');
|
||||
$locale = array_get($event->extension->extra, 'flarum-locale.code');
|
||||
if ($locale === $defaultLocale) {
|
||||
throw new ForbiddenException('You cannot disable the default language pack!');
|
||||
}
|
||||
$defaultLocale = $this->settings->get('default_locale');
|
||||
$locale = array_get($event->extension->extra, 'flarum-locale.code');
|
||||
|
||||
if ($locale === $defaultLocale) {
|
||||
throw new ForbiddenException('You cannot disable the default language pack!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,12 +11,18 @@
|
||||
|
||||
namespace Flarum\Extension;
|
||||
|
||||
use Flarum\Database\Migrator;
|
||||
use Flarum\Extend\Compat;
|
||||
use Flarum\Extend\LifecycleInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Flysystem\Adapter\Local;
|
||||
use League\Flysystem\Filesystem;
|
||||
use League\Flysystem\FilesystemInterface;
|
||||
use League\Flysystem\MountManager;
|
||||
use League\Flysystem\Plugin\ListFiles;
|
||||
|
||||
/**
|
||||
* @property string $name
|
||||
@@ -307,6 +313,25 @@ class Extension implements Arrayable
|
||||
return realpath($this->path.'/assets/') !== false;
|
||||
}
|
||||
|
||||
public function copyAssetsTo(FilesystemInterface $target)
|
||||
{
|
||||
if (! $this->hasAssets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mount = new MountManager([
|
||||
'source' => $source = new Filesystem(new Local($this->getPath().'/assets')),
|
||||
'target' => $target,
|
||||
]);
|
||||
|
||||
$source->addPlugin(new ListFiles);
|
||||
$assetFiles = $source->listFiles('/', true);
|
||||
|
||||
foreach ($assetFiles as $file) {
|
||||
$mount->copy("source://$file[path]", "target://extensions/$this->id/$file[path]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the extension has migrations.
|
||||
*
|
||||
@@ -317,6 +342,19 @@ class Extension implements Arrayable
|
||||
return realpath($this->path.'/migrations/') !== false;
|
||||
}
|
||||
|
||||
public function migrate(Migrator $migrator, $direction = 'up')
|
||||
{
|
||||
if (! $this->hasMigrations()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($direction == 'up') {
|
||||
return $migrator->run($this->getPath().'/migrations', $this);
|
||||
} else {
|
||||
return $migrator->reset($this->getPath().'/migrations', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array result for the object.
|
||||
*
|
||||
|
@@ -67,11 +67,11 @@ class ExtensionManager
|
||||
*/
|
||||
public function getExtensions()
|
||||
{
|
||||
if (is_null($this->extensions) && $this->filesystem->exists($this->app->basePath().'/vendor/composer/installed.json')) {
|
||||
if (is_null($this->extensions) && $this->filesystem->exists($this->app->vendorPath().'/composer/installed.json')) {
|
||||
$extensions = new Collection();
|
||||
|
||||
// Load all packages installed by composer.
|
||||
$installed = json_decode($this->filesystem->get($this->app->basePath().'/vendor/composer/installed.json'), true);
|
||||
$installed = json_decode($this->filesystem->get($this->app->vendorPath().'/composer/installed.json'), true);
|
||||
|
||||
foreach ($installed as $package) {
|
||||
if (Arr::get($package, 'type') != 'flarum-extension' || empty(Arr::get($package, 'name'))) {
|
||||
@@ -222,26 +222,16 @@ class ExtensionManager
|
||||
* Runs the database migrations for the extension.
|
||||
*
|
||||
* @param Extension $extension
|
||||
* @param bool|true $up
|
||||
* @param string $direction
|
||||
* @return void
|
||||
*/
|
||||
public function migrate(Extension $extension, $up = true)
|
||||
public function migrate(Extension $extension, $direction = 'up')
|
||||
{
|
||||
if (! $extension->hasMigrations()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$migrationDir = $extension->getPath().'/migrations';
|
||||
|
||||
$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);
|
||||
}
|
||||
$extension->migrate($this->migrator, $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +242,7 @@ class ExtensionManager
|
||||
*/
|
||||
public function migrateDown(Extension $extension)
|
||||
{
|
||||
return $this->migrate($extension, false);
|
||||
return $this->migrate($extension, 'down');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,7 +293,7 @@ class ExtensionManager
|
||||
*/
|
||||
public function getEnabled()
|
||||
{
|
||||
return json_decode($this->config->get('extensions_enabled'), true);
|
||||
return json_decode($this->config->get('extensions_enabled'), true) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,6 +326,6 @@ class ExtensionManager
|
||||
*/
|
||||
protected function getExtensionsDir()
|
||||
{
|
||||
return $this->app->basePath().'/vendor';
|
||||
return $this->app->vendorPath();
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Flarum\Extension;
|
||||
|
||||
use Flarum\Extension\Event\Disabling;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
@@ -27,7 +28,7 @@ class ExtensionServiceProvider extends AbstractServiceProvider
|
||||
// Boot extensions when the app is booting. This must be done as a boot
|
||||
// listener on the app rather than in the service provider's boot method
|
||||
// below, so that extensions have a chance to register things on the
|
||||
// container before the core boot code runs.
|
||||
// container before the core boots up (and starts resolving services).
|
||||
$this->app->booting(function (Container $app) {
|
||||
$app->make('flarum.extensions')->extend($app);
|
||||
});
|
||||
@@ -38,8 +39,9 @@ class ExtensionServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$events = $this->app->make('events');
|
||||
|
||||
$events->subscribe(DefaultLanguagePackGuard::class);
|
||||
$this->app->make('events')->listen(
|
||||
Disabling::class,
|
||||
DefaultLanguagePackGuard::class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace Flarum\Formatter\Event;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use s9e\TextFormatter\Renderer;
|
||||
|
||||
class Rendering
|
||||
@@ -30,15 +31,22 @@ class Rendering
|
||||
*/
|
||||
public $xml;
|
||||
|
||||
/**
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @param Renderer $renderer
|
||||
* @param mixed $context
|
||||
* @param string $xml
|
||||
* @param ServerRequestInterface|null $request
|
||||
*/
|
||||
public function __construct(Renderer $renderer, $context, &$xml)
|
||||
public function __construct(Renderer $renderer, $context, &$xml, ServerRequestInterface $request = null)
|
||||
{
|
||||
$this->renderer = $renderer;
|
||||
$this->context = $context;
|
||||
$this->xml = &$xml;
|
||||
$this->request = $request;
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ use Flarum\Formatter\Event\Parsing;
|
||||
use Flarum\Formatter\Event\Rendering;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use s9e\TextFormatter\Configurator;
|
||||
use s9e\TextFormatter\Unparser;
|
||||
|
||||
@@ -69,13 +70,14 @@ class Formatter
|
||||
*
|
||||
* @param string $xml
|
||||
* @param mixed $context
|
||||
* @param ServerRequestInterface|null $request
|
||||
* @return string
|
||||
*/
|
||||
public function render($xml, $context = null)
|
||||
public function render($xml, $context = null, ServerRequestInterface $request = null)
|
||||
{
|
||||
$renderer = $this->getRenderer();
|
||||
|
||||
$this->events->dispatch(new Rendering($renderer, $context, $xml));
|
||||
$this->events->dispatch(new Rendering($renderer, $context, $xml, $request));
|
||||
|
||||
return $renderer->render($xml);
|
||||
}
|
||||
|
@@ -56,7 +56,7 @@ class ResponseFactory
|
||||
$provided,
|
||||
$registration->getSuggested(),
|
||||
[
|
||||
'token' => $token->id,
|
||||
'token' => $token->token,
|
||||
'provided' => array_keys($provided)
|
||||
]
|
||||
));
|
||||
|
@@ -58,7 +58,7 @@ class Index
|
||||
|
||||
$apiDocument = $this->getApiDocument($request->getAttribute('actor'), $params);
|
||||
|
||||
$document->content = $this->view->make('flarum.forum::frontend.content.index', compact('apiDocument', 'page', 'forum'));
|
||||
$document->content = $this->view->make('flarum.forum::frontend.content.index', compact('apiDocument', 'page'));
|
||||
$document->payload['apiDocument'] = $apiDocument;
|
||||
|
||||
return $document;
|
||||
|
@@ -18,6 +18,7 @@ use Flarum\User\PasswordToken;
|
||||
use Flarum\User\UserValidator;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
@@ -86,9 +87,9 @@ class SavePasswordController implements RequestHandlerInterface
|
||||
throw new ValidationException($validator);
|
||||
}
|
||||
} catch (ValidationException $e) {
|
||||
$request->getAttribute('session')->put('errors', $e->errors());
|
||||
$request->getAttribute('session')->put('errors', new MessageBag($e->errors()));
|
||||
|
||||
return new RedirectResponse($this->url->to('forum')->route('resetPassword', ['token' => $token->id]));
|
||||
return new RedirectResponse($this->url->to('forum')->route('resetPassword', ['token' => $token->token]));
|
||||
}
|
||||
|
||||
$token->user->changePassword($password);
|
||||
|
@@ -13,9 +13,12 @@ namespace Flarum\Forum;
|
||||
|
||||
use Flarum\Event\ConfigureForumRoutes;
|
||||
use Flarum\Event\ConfigureMiddleware;
|
||||
use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Formatter\Formatter;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Frontend\AddLocaleAssets;
|
||||
use Flarum\Frontend\AddTranslations;
|
||||
use Flarum\Frontend\Assets;
|
||||
@@ -25,6 +28,9 @@ use Flarum\Http\Middleware as HttpMiddleware;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Flarum\Settings\Event\Saving;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
@@ -41,7 +47,10 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.forum.routes', function () {
|
||||
return new RouteCollection;
|
||||
$routes = new RouteCollection;
|
||||
$this->populateRoutes($routes);
|
||||
|
||||
return $routes;
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.forum.middleware', function (Application $app) {
|
||||
@@ -59,6 +68,7 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
$pipe->pipe($app->make(HttpMiddleware\StartSession::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\RememberFromCookie::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\AuthenticateWithSession::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\CheckCsrfToken::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\SetLocale::class));
|
||||
$pipe->pipe($app->make(HttpMiddleware\ShareErrorsFromSession::class));
|
||||
|
||||
@@ -85,7 +95,7 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
$assets->css(function (SourceCollector $sources) {
|
||||
$sources->addFile(__DIR__.'/../../less/forum.less');
|
||||
$sources->addString(function () {
|
||||
return $this->app->make(SettingsRepositoryInterface::class)->get('custom_less');
|
||||
return $this->app->make(SettingsRepositoryInterface::class)->get('custom_less', '');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,8 +115,6 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->populateRoutes($this->app->make('flarum.forum.routes'));
|
||||
|
||||
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum.forum');
|
||||
|
||||
$this->app->make('view')->share([
|
||||
@@ -116,19 +124,45 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
|
||||
$events = $this->app->make('events');
|
||||
|
||||
$events->subscribe(
|
||||
new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.assets.forum'),
|
||||
$this->app->make('flarum.locales')
|
||||
)
|
||||
$events->listen(
|
||||
[Enabled::class, Disabled::class, ClearingCache::class],
|
||||
function () {
|
||||
$recompile = new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.assets.forum'),
|
||||
$this->app->make(LocaleManager::class)
|
||||
);
|
||||
$recompile->flush();
|
||||
}
|
||||
);
|
||||
|
||||
$events->subscribe(
|
||||
new ValidateCustomLess(
|
||||
$this->app->make('flarum.assets.forum'),
|
||||
$this->app->make('flarum.locales'),
|
||||
$this->app
|
||||
)
|
||||
$events->listen(
|
||||
Saved::class,
|
||||
function (Saved $event) {
|
||||
$recompile = new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.assets.forum'),
|
||||
$this->app->make(LocaleManager::class)
|
||||
);
|
||||
$recompile->whenSettingsSaved($event);
|
||||
|
||||
$validator = new ValidateCustomLess(
|
||||
$this->app->make('flarum.assets.forum'),
|
||||
$this->app->make('flarum.locales'),
|
||||
$this->app
|
||||
);
|
||||
$validator->whenSettingsSaved($event);
|
||||
}
|
||||
);
|
||||
|
||||
$events->listen(
|
||||
Saving::class,
|
||||
function (Saving $event) {
|
||||
$validator = new ValidateCustomLess(
|
||||
$this->app->make('flarum.assets.forum'),
|
||||
$this->app->make('flarum.locales'),
|
||||
$this->app
|
||||
);
|
||||
$validator->whenSettingsSaving($event);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,6 @@ use Flarum\Settings\Event\Saving;
|
||||
use Flarum\Settings\OverrideSettingsRepository;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use League\Flysystem\Adapter\NullAdapter;
|
||||
use League\Flysystem\Filesystem;
|
||||
@@ -54,67 +53,55 @@ class ValidateCustomLess
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Saving::class, [$this, 'whenSettingsSaving']);
|
||||
$events->listen(Saved::class, [$this, 'whenSettingsSaved']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Saving $event
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function whenSettingsSaving(Saving $event)
|
||||
{
|
||||
if (isset($event->settings['custom_less'])) {
|
||||
// We haven't saved the settings yet, but we want to trial a full
|
||||
// recompile of the CSS to see if this custom LESS will break
|
||||
// anything. In order to do that, we will temporarily override the
|
||||
// settings repository with the new settings so that the recompile
|
||||
// is effective. We will also use a dummy filesystem so that nothing
|
||||
// is actually written yet.
|
||||
|
||||
$settings = $this->container->make(SettingsRepositoryInterface::class);
|
||||
|
||||
$this->container->extend(
|
||||
SettingsRepositoryInterface::class,
|
||||
function ($settings) use ($event) {
|
||||
return new OverrideSettingsRepository($settings, $event->settings);
|
||||
}
|
||||
);
|
||||
|
||||
$assetsDir = $this->assets->getAssetsDir();
|
||||
$this->assets->setAssetsDir(new FilesystemAdapter(new Filesystem(new NullAdapter)));
|
||||
|
||||
try {
|
||||
$this->assets->makeCss()->commit();
|
||||
|
||||
foreach ($this->locales->getLocales() as $locale => $name) {
|
||||
$this->assets->makeLocaleCss($locale)->commit();
|
||||
}
|
||||
} catch (Less_Exception_Parser $e) {
|
||||
throw new ValidationException(['custom_less' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$this->assets->setAssetsDir($assetsDir);
|
||||
$this->container->instance(SettingsRepositoryInterface::class, $settings);
|
||||
if (! isset($event->settings['custom_less'])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Saved $event
|
||||
*/
|
||||
public function whenSettingsSaved(Saved $event)
|
||||
{
|
||||
if (isset($event->settings['custom_less'])) {
|
||||
$this->assets->makeCss()->flush();
|
||||
// We haven't saved the settings yet, but we want to trial a full
|
||||
// recompile of the CSS to see if this custom LESS will break
|
||||
// anything. In order to do that, we will temporarily override the
|
||||
// settings repository with the new settings so that the recompile
|
||||
// is effective. We will also use a dummy filesystem so that nothing
|
||||
// is actually written yet.
|
||||
|
||||
$settings = $this->container->make(SettingsRepositoryInterface::class);
|
||||
|
||||
$this->container->extend(
|
||||
SettingsRepositoryInterface::class,
|
||||
function ($settings) use ($event) {
|
||||
return new OverrideSettingsRepository($settings, $event->settings);
|
||||
}
|
||||
);
|
||||
|
||||
$assetsDir = $this->assets->getAssetsDir();
|
||||
$this->assets->setAssetsDir(new FilesystemAdapter(new Filesystem(new NullAdapter)));
|
||||
|
||||
try {
|
||||
$this->assets->makeCss()->commit();
|
||||
|
||||
foreach ($this->locales->getLocales() as $locale => $name) {
|
||||
$this->assets->makeLocaleCss($locale)->flush();
|
||||
$this->assets->makeLocaleCss($locale)->commit();
|
||||
}
|
||||
} catch (Less_Exception_Parser $e) {
|
||||
throw new ValidationException(['custom_less' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$this->assets->setAssetsDir($assetsDir);
|
||||
$this->container->instance(SettingsRepositoryInterface::class, $settings);
|
||||
}
|
||||
|
||||
public function whenSettingsSaved(Saved $event)
|
||||
{
|
||||
if (! isset($event->settings['custom_less'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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.8.1';
|
||||
const VERSION = '0.1.0-beta.9';
|
||||
|
||||
/**
|
||||
* The base path for the Flarum installation.
|
||||
@@ -41,6 +41,20 @@ class Application extends Container implements ApplicationContract
|
||||
*/
|
||||
protected $publicPath;
|
||||
|
||||
/**
|
||||
* The custom storage path defined by the developer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $storagePath;
|
||||
|
||||
/**
|
||||
* A custom vendor path to find dependencies in non-standard environments.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $vendorPath;
|
||||
|
||||
/**
|
||||
* Indicates if the application has "booted".
|
||||
*
|
||||
@@ -83,13 +97,6 @@ class Application extends Container implements ApplicationContract
|
||||
*/
|
||||
protected $deferredServices = [];
|
||||
|
||||
/**
|
||||
* The custom storage path defined by the developer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $storagePath;
|
||||
|
||||
/**
|
||||
* Create a new Flarum application instance.
|
||||
*
|
||||
@@ -226,7 +233,7 @@ class Application extends Container implements ApplicationContract
|
||||
*/
|
||||
protected function bindPathsInContainer()
|
||||
{
|
||||
foreach (['base', 'public', 'storage'] as $path) {
|
||||
foreach (['base', 'public', 'storage', 'vendor'] as $path) {
|
||||
$this->instance('path.'.$path, $this->{$path.'Path'}());
|
||||
}
|
||||
}
|
||||
@@ -261,6 +268,16 @@ class Application extends Container implements ApplicationContract
|
||||
return $this->storagePath ?: $this->basePath.DIRECTORY_SEPARATOR.'storage';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the vendor directory where dependencies are installed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function vendorPath()
|
||||
{
|
||||
return $this->vendorPath ?: $this->basePath.DIRECTORY_SEPARATOR.'vendor';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the storage directory.
|
||||
*
|
||||
@@ -276,6 +293,21 @@ class Application extends Container implements ApplicationContract
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the vendor directory.
|
||||
*
|
||||
* @param string $path
|
||||
* @return $this
|
||||
*/
|
||||
public function useVendorPath($path)
|
||||
{
|
||||
$this->vendorPath = $path;
|
||||
|
||||
$this->instance('path.vendor', $path);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or check the current application environment.
|
||||
*
|
||||
@@ -680,7 +712,7 @@ class Application extends Container implements ApplicationContract
|
||||
public function registerCoreContainerAliases()
|
||||
{
|
||||
$aliases = [
|
||||
'app' => [\Flarum\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
|
||||
'app' => [self::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
|
||||
'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class],
|
||||
'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
|
||||
'cache.store' => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class],
|
||||
|
@@ -22,6 +22,7 @@ use Illuminate\Contracts\Container\Container;
|
||||
use Middlewares\BasePath;
|
||||
use Middlewares\BasePathRouter;
|
||||
use Middlewares\RequestHandler;
|
||||
use Zend\Stratigility\Middleware\OriginalMessages;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
|
||||
class InstalledApp implements AppInterface
|
||||
@@ -42,6 +43,11 @@ class InstalledApp implements AppInterface
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Psr\Http\Server\RequestHandlerInterface
|
||||
*/
|
||||
@@ -56,6 +62,7 @@ class InstalledApp implements AppInterface
|
||||
$pipe = new MiddlewarePipe;
|
||||
|
||||
$pipe->pipe(new BasePath($this->basePath()));
|
||||
$pipe->pipe(new OriginalMessages);
|
||||
$pipe->pipe(
|
||||
new BasePathRouter([
|
||||
$this->subPath('api') => 'flarum.api.middleware',
|
||||
@@ -84,7 +91,7 @@ class InstalledApp implements AppInterface
|
||||
/**
|
||||
* @return \Psr\Http\Server\RequestHandlerInterface
|
||||
*/
|
||||
public function getUpdaterHandler()
|
||||
private function getUpdaterHandler()
|
||||
{
|
||||
$pipe = new MiddlewarePipe;
|
||||
$pipe->pipe(
|
||||
|
@@ -23,10 +23,10 @@ use Flarum\Forum\ForumServiceProvider;
|
||||
use Flarum\Frontend\FrontendServiceProvider;
|
||||
use Flarum\Group\GroupServiceProvider;
|
||||
use Flarum\Locale\LocaleServiceProvider;
|
||||
use Flarum\Mail\MailServiceProvider;
|
||||
use Flarum\Notification\NotificationServiceProvider;
|
||||
use Flarum\Post\PostServiceProvider;
|
||||
use Flarum\Search\SearchServiceProvider;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\Settings\SettingsServiceProvider;
|
||||
use Flarum\Update\UpdateServiceProvider;
|
||||
use Flarum\User\SessionServiceProvider;
|
||||
@@ -36,10 +36,10 @@ use Illuminate\Cache\Repository as CacheRepository;
|
||||
use Illuminate\Config\Repository as ConfigRepository;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
use Illuminate\Contracts\Cache\Store;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Filesystem\FilesystemServiceProvider;
|
||||
use Illuminate\Hashing\HashServiceProvider;
|
||||
use Illuminate\Mail\MailServiceProvider;
|
||||
use Illuminate\Validation\ValidationServiceProvider;
|
||||
use Illuminate\View\ViewServiceProvider;
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
@@ -73,7 +73,7 @@ class InstalledSite implements SiteInterface
|
||||
/**
|
||||
* Create and boot a Flarum application instance.
|
||||
*
|
||||
* @return AppInterface
|
||||
* @return InstalledApp
|
||||
*/
|
||||
public function bootApp(): AppInterface
|
||||
{
|
||||
@@ -100,6 +100,10 @@ class InstalledSite implements SiteInterface
|
||||
|
||||
$laravel->useStoragePath($this->paths['storage']);
|
||||
|
||||
if (isset($this->paths['vendor'])) {
|
||||
$laravel->useVendorPath($this->paths['vendor']);
|
||||
}
|
||||
|
||||
$laravel->instance('env', 'production');
|
||||
$laravel->instance('flarum.config', $this->config);
|
||||
$laravel->instance('config', $config = $this->getIlluminateConfig($laravel));
|
||||
@@ -107,51 +111,42 @@ class InstalledSite implements SiteInterface
|
||||
$this->registerLogger($laravel);
|
||||
$this->registerCache($laravel);
|
||||
|
||||
$laravel->register(DatabaseServiceProvider::class);
|
||||
$laravel->register(MigrationServiceProvider::class);
|
||||
$laravel->register(SettingsServiceProvider::class);
|
||||
$laravel->register(LocaleServiceProvider::class);
|
||||
$laravel->register(AdminServiceProvider::class);
|
||||
$laravel->register(ApiServiceProvider::class);
|
||||
$laravel->register(BusServiceProvider::class);
|
||||
$laravel->register(FilesystemServiceProvider::class);
|
||||
$laravel->register(HashServiceProvider::class);
|
||||
$laravel->register(MailServiceProvider::class);
|
||||
$laravel->register(ViewServiceProvider::class);
|
||||
$laravel->register(ValidationServiceProvider::class);
|
||||
|
||||
$settings = $laravel->make(SettingsRepositoryInterface::class);
|
||||
|
||||
$config->set('mail.driver', $settings->get('mail_driver'));
|
||||
$config->set('mail.host', $settings->get('mail_host'));
|
||||
$config->set('mail.port', $settings->get('mail_port'));
|
||||
$config->set('mail.from.address', $settings->get('mail_from'));
|
||||
$config->set('mail.from.name', $settings->get('forum_title'));
|
||||
$config->set('mail.encryption', $settings->get('mail_encryption'));
|
||||
$config->set('mail.username', $settings->get('mail_username'));
|
||||
$config->set('mail.password', $settings->get('mail_password'));
|
||||
|
||||
$laravel->register(DatabaseServiceProvider::class);
|
||||
$laravel->register(DiscussionServiceProvider::class);
|
||||
$laravel->register(ExtensionServiceProvider::class);
|
||||
$laravel->register(FilesystemServiceProvider::class);
|
||||
$laravel->register(FormatterServiceProvider::class);
|
||||
$laravel->register(ForumServiceProvider::class);
|
||||
$laravel->register(FrontendServiceProvider::class);
|
||||
$laravel->register(GroupServiceProvider::class);
|
||||
$laravel->register(HashServiceProvider::class);
|
||||
$laravel->register(LocaleServiceProvider::class);
|
||||
$laravel->register(MailServiceProvider::class);
|
||||
$laravel->register(MigrationServiceProvider::class);
|
||||
$laravel->register(NotificationServiceProvider::class);
|
||||
$laravel->register(PostServiceProvider::class);
|
||||
$laravel->register(SearchServiceProvider::class);
|
||||
$laravel->register(SessionServiceProvider::class);
|
||||
$laravel->register(UserServiceProvider::class);
|
||||
$laravel->register(SettingsServiceProvider::class);
|
||||
$laravel->register(UpdateServiceProvider::class);
|
||||
$laravel->register(UserServiceProvider::class);
|
||||
$laravel->register(ValidationServiceProvider::class);
|
||||
$laravel->register(ViewServiceProvider::class);
|
||||
|
||||
$laravel->register(ApiServiceProvider::class);
|
||||
$laravel->register(ForumServiceProvider::class);
|
||||
$laravel->register(AdminServiceProvider::class);
|
||||
|
||||
$laravel->register(ExtensionServiceProvider::class);
|
||||
$laravel->booting(function (Container $app) {
|
||||
// Run all local-site extenders before booting service providers
|
||||
// (but after those from "real" extensions, which have been set up
|
||||
// in a service provider above).
|
||||
foreach ($this->extenders as $extension) {
|
||||
$extension->extend($app);
|
||||
}
|
||||
});
|
||||
|
||||
$laravel->boot();
|
||||
|
||||
foreach ($this->extenders as $extension) {
|
||||
$extension->extend($laravel);
|
||||
}
|
||||
|
||||
return $laravel;
|
||||
}
|
||||
|
||||
|
@@ -59,6 +59,10 @@ class UninstalledSite implements SiteInterface
|
||||
|
||||
$laravel->useStoragePath($this->paths['storage']);
|
||||
|
||||
if (isset($this->paths['vendor'])) {
|
||||
$laravel->useVendorPath($this->paths['vendor']);
|
||||
}
|
||||
|
||||
$laravel->instance('env', 'production');
|
||||
$laravel->instance('flarum.config', []);
|
||||
$laravel->instance('config', $config = $this->getIlluminateConfig());
|
||||
|
@@ -58,7 +58,7 @@ class JsCompiler extends RevisionCompiler
|
||||
|
||||
$this->assetsDir->put($file, implode("\n", $output));
|
||||
|
||||
$mapTemp = tempnam(sys_get_temp_dir(), $mapFile);
|
||||
$mapTemp = @tempnam(storage_path('tmp'), $mapFile);
|
||||
$map->save($mapTemp);
|
||||
$this->assetsDir->put($mapFile, file_get_contents($mapTemp));
|
||||
@unlink($mapTemp);
|
||||
|
@@ -30,7 +30,7 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
||||
);
|
||||
|
||||
$assets->setLessImportDirs([
|
||||
$this->app->basePath().'/vendor/components/font-awesome/less' => ''
|
||||
$this->app->vendorPath().'/components/font-awesome/less' => ''
|
||||
]);
|
||||
|
||||
$assets->css([$this, 'addBaseCss']);
|
||||
@@ -72,8 +72,8 @@ class FrontendServiceProvider extends AbstractServiceProvider
|
||||
|
||||
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');
|
||||
$sources->addFile(__DIR__.'/../../less/common/variables.less');
|
||||
$sources->addFile(__DIR__.'/../../less/common/mixins.less');
|
||||
|
||||
$this->addLessVariables($sources);
|
||||
}
|
||||
|
@@ -11,12 +11,8 @@
|
||||
|
||||
namespace Flarum\Frontend;
|
||||
|
||||
use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class RecompileFrontendAssets
|
||||
{
|
||||
@@ -40,17 +36,6 @@ class RecompileFrontendAssets
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Saved::class, [$this, 'whenSettingsSaved']);
|
||||
$events->listen(Enabled::class, [$this, 'flush']);
|
||||
$events->listen(Disabled::class, [$this, 'flush']);
|
||||
$events->listen(ClearingCache::class, [$this, 'flush']);
|
||||
}
|
||||
|
||||
public function whenSettingsSaved(Saved $event)
|
||||
{
|
||||
if (preg_grep('/^theme_/i', array_keys($event->settings))) {
|
||||
|
@@ -62,7 +62,7 @@ class Group extends AbstractModel
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleted(function (Group $group) {
|
||||
static::deleted(function (self $group) {
|
||||
$group->raise(new Deleted($group));
|
||||
});
|
||||
}
|
||||
@@ -126,4 +126,19 @@ class Group extends AbstractModel
|
||||
{
|
||||
return $this->hasMany(Permission::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the group has a certain permission.
|
||||
*
|
||||
* @param string $permission
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPermission($permission)
|
||||
{
|
||||
if ($this->id == self::ADMINISTRATOR_ID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->permissions->contains('permission', $permission);
|
||||
}
|
||||
}
|
||||
|
@@ -15,4 +15,8 @@ use Exception;
|
||||
|
||||
class TokenMismatchException extends Exception
|
||||
{
|
||||
public function __construct($message = null, $code = 419, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
|
@@ -40,6 +40,7 @@ class AuthenticateWithHeader implements Middleware
|
||||
|
||||
$request = $request->withAttribute('apiKey', $key);
|
||||
$request = $request->withAttribute('bypassFloodgate', true);
|
||||
$request = $request->withAttribute('bypassCsrfToken', true);
|
||||
} elseif ($token = AccessToken::find($id)) {
|
||||
$token->touch();
|
||||
|
||||
|
48
src/Http/Middleware/CheckCsrfToken.php
Normal file
48
src/Http/Middleware/CheckCsrfToken.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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\Middleware;
|
||||
|
||||
use Flarum\Http\Exception\TokenMismatchException;
|
||||
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;
|
||||
|
||||
class CheckCsrfToken implements Middleware
|
||||
{
|
||||
public function process(Request $request, Handler $handler): Response
|
||||
{
|
||||
if (in_array($request->getMethod(), ['GET', 'HEAD', 'OPTIONS'])) {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
if ($request->getAttribute('bypassCsrfToken', false)) {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
if ($this->tokensMatch($request)) {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
throw new TokenMismatchException('CSRF token did not match');
|
||||
}
|
||||
|
||||
private function tokensMatch(Request $request): bool
|
||||
{
|
||||
$expected = (string) $request->getAttribute('session')->token();
|
||||
|
||||
$provided = $request->getParsedBody()['csrfToken'] ??
|
||||
$request->getHeaderLine('X-CSRF-Token');
|
||||
|
||||
return hash_equals($expected, $provided);
|
||||
}
|
||||
}
|
@@ -67,7 +67,7 @@ class StartSession implements Middleware
|
||||
return $this->withSessionCookie($response, $session);
|
||||
}
|
||||
|
||||
private function makeSession(Request $request)
|
||||
private function makeSession(Request $request): Store
|
||||
{
|
||||
return new Store(
|
||||
$this->config['cookie'],
|
||||
@@ -76,12 +76,12 @@ class StartSession implements Middleware
|
||||
);
|
||||
}
|
||||
|
||||
private function withCsrfTokenHeader(Response $response, Session $session)
|
||||
private function withCsrfTokenHeader(Response $response, Session $session): Response
|
||||
{
|
||||
return $response->withHeader('X-CSRF-Token', $session->token());
|
||||
}
|
||||
|
||||
private function withSessionCookie(Response $response, Session $session)
|
||||
private function withSessionCookie(Response $response, Session $session): Response
|
||||
{
|
||||
return FigResponseCookies::set(
|
||||
$response,
|
||||
@@ -89,7 +89,7 @@ class StartSession implements Middleware
|
||||
);
|
||||
}
|
||||
|
||||
private function getSessionLifetimeInSeconds()
|
||||
private function getSessionLifetimeInSeconds(): int
|
||||
{
|
||||
return $this->config['lifetime'] * 60;
|
||||
}
|
||||
|
58
src/Install/AdminUser.php
Normal file
58
src/Install/AdminUser.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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\Install;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Hashing\BcryptHasher;
|
||||
|
||||
class AdminUser
|
||||
{
|
||||
private $username;
|
||||
private $password;
|
||||
private $email;
|
||||
|
||||
public function __construct($username, $password, $email)
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->email = $email;
|
||||
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return [
|
||||
'username' => $this->username,
|
||||
'email' => $this->email,
|
||||
'password' => (new BcryptHasher)->make($this->password),
|
||||
'joined_at' => Carbon::now(),
|
||||
'is_email_confirmed' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
private function validate()
|
||||
{
|
||||
if (! filter_var($this->email, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new ValidationFailed('You must enter a valid email.');
|
||||
}
|
||||
|
||||
if (! $this->username || preg_match('/[^a-z0-9_-]/i', $this->username)) {
|
||||
throw new ValidationFailed('Username can only contain letters, numbers, underscores, and dashes.');
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,15 +11,9 @@
|
||||
|
||||
namespace Flarum\Install\Console;
|
||||
|
||||
use Flarum\Install\Installation;
|
||||
|
||||
interface DataProviderInterface
|
||||
{
|
||||
public function getDatabaseConfiguration();
|
||||
|
||||
public function getBaseUrl();
|
||||
|
||||
public function getAdminUser();
|
||||
|
||||
public function getSettings();
|
||||
|
||||
public function isDebugMode(): bool;
|
||||
public function configure(Installation $installation): Installation;
|
||||
}
|
||||
|
@@ -1,111 +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\Install\Console;
|
||||
|
||||
class DefaultsDataProvider implements DataProviderInterface
|
||||
{
|
||||
protected $databaseConfiguration = [
|
||||
'driver' => 'mysql',
|
||||
'host' => 'localhost',
|
||||
'database' => 'flarum',
|
||||
'username' => 'root',
|
||||
'password' => '',
|
||||
'prefix' => '',
|
||||
'port' => '3306',
|
||||
];
|
||||
|
||||
protected $debug = false;
|
||||
|
||||
protected $baseUrl = 'http://flarum.local';
|
||||
|
||||
protected $adminUser = [
|
||||
'username' => 'admin',
|
||||
'password' => 'password',
|
||||
'password_confirmation' => 'password',
|
||||
'email' => 'admin@example.com',
|
||||
];
|
||||
|
||||
protected $settings = [
|
||||
'allow_post_editing' => 'reply',
|
||||
'allow_renaming' => '10',
|
||||
'allow_sign_up' => '1',
|
||||
'custom_less' => '',
|
||||
'default_locale' => 'en',
|
||||
'default_route' => '/all',
|
||||
'extensions_enabled' => '[]',
|
||||
'forum_title' => 'Development Forum',
|
||||
'forum_description' => '',
|
||||
'mail_driver' => 'mail',
|
||||
'mail_from' => 'noreply@flarum.dev',
|
||||
'theme_colored_header' => '0',
|
||||
'theme_dark_mode' => '0',
|
||||
'theme_primary_color' => '#4D698E',
|
||||
'theme_secondary_color' => '#4D698E',
|
||||
'welcome_message' => 'This is beta software and you should not use it in production.',
|
||||
'welcome_title' => 'Welcome to Development Forum',
|
||||
];
|
||||
|
||||
public function getDatabaseConfiguration()
|
||||
{
|
||||
return $this->databaseConfiguration;
|
||||
}
|
||||
|
||||
public function setDatabaseConfiguration(array $databaseConfiguration)
|
||||
{
|
||||
$this->databaseConfiguration = $databaseConfiguration;
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->baseUrl;
|
||||
}
|
||||
|
||||
public function setBaseUrl($baseUrl)
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
}
|
||||
|
||||
public function getAdminUser()
|
||||
{
|
||||
return $this->adminUser;
|
||||
}
|
||||
|
||||
public function setAdminUser(array $adminUser)
|
||||
{
|
||||
$this->adminUser = $adminUser;
|
||||
}
|
||||
|
||||
public function getSettings()
|
||||
{
|
||||
return $this->settings;
|
||||
}
|
||||
|
||||
public function setSettings(array $settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function setSetting($key, $value)
|
||||
{
|
||||
$this->settings[$key] = $value;
|
||||
}
|
||||
|
||||
public function isDebugMode(): bool
|
||||
{
|
||||
return $this->debug;
|
||||
}
|
||||
|
||||
public function setDebugMode(bool $debug = true)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
}
|
||||
}
|
@@ -12,12 +12,14 @@
|
||||
namespace Flarum\Install\Console;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Install\AdminUser;
|
||||
use Flarum\Install\DatabaseConfig;
|
||||
use Flarum\Install\Installation;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class FileDataProvider implements DataProviderInterface
|
||||
{
|
||||
protected $default;
|
||||
protected $debug = false;
|
||||
protected $baseUrl = null;
|
||||
protected $databaseConfiguration = [];
|
||||
@@ -26,9 +28,6 @@ class FileDataProvider implements DataProviderInterface
|
||||
|
||||
public function __construct(InputInterface $input)
|
||||
{
|
||||
// Get default configuration
|
||||
$this->default = new DefaultsDataProvider();
|
||||
|
||||
// Get configuration file path
|
||||
$configurationFile = $input->getOption('file');
|
||||
|
||||
@@ -55,28 +54,35 @@ class FileDataProvider implements DataProviderInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function getDatabaseConfiguration()
|
||||
public function configure(Installation $installation): Installation
|
||||
{
|
||||
return $this->databaseConfiguration + $this->default->getDatabaseConfiguration();
|
||||
return $installation
|
||||
->debugMode($this->debug)
|
||||
->baseUrl($this->baseUrl ?? 'http://flarum.local')
|
||||
->databaseConfig($this->getDatabaseConfiguration())
|
||||
->adminUser($this->getAdminUser())
|
||||
->settings($this->settings);
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
private function getDatabaseConfiguration(): DatabaseConfig
|
||||
{
|
||||
return (! is_null($this->baseUrl)) ? $this->baseUrl : $this->default->getBaseUrl();
|
||||
return new DatabaseConfig(
|
||||
$this->databaseConfiguration['driver'] ?? 'mysql',
|
||||
$this->databaseConfiguration['host'] ?? 'localhost',
|
||||
$this->databaseConfiguration['port'] ?? 3306,
|
||||
$this->databaseConfiguration['database'] ?? 'flarum',
|
||||
$this->databaseConfiguration['username'] ?? 'root',
|
||||
$this->databaseConfiguration['password'] ?? '',
|
||||
$this->databaseConfiguration['prefix'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
public function getAdminUser()
|
||||
private function getAdminUser(): AdminUser
|
||||
{
|
||||
return $this->adminUser + $this->default->getAdminUser();
|
||||
}
|
||||
|
||||
public function getSettings()
|
||||
{
|
||||
return $this->settings + $this->default->getSettings();
|
||||
}
|
||||
|
||||
public function isDebugMode(): bool
|
||||
{
|
||||
return $this->debug;
|
||||
return new AdminUser(
|
||||
$this->adminUser['username'] ?? 'admin',
|
||||
$this->adminUser['password'] ?? 'password',
|
||||
$this->adminUser['email'] ?? 'admin@example.com'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -11,65 +11,32 @@
|
||||
|
||||
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;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Translation\Translator;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Database\Connectors\ConnectionFactory;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Hashing\BcryptHasher;
|
||||
use Illuminate\Validation\Factory;
|
||||
use PDO;
|
||||
use Flarum\Install\Installation;
|
||||
use Flarum\Install\Pipeline;
|
||||
use Flarum\Install\Step;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class InstallCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* @var Installation
|
||||
*/
|
||||
protected $installation;
|
||||
|
||||
/**
|
||||
* @var DataProviderInterface
|
||||
*/
|
||||
protected $dataSource;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
* @param Installation $installation
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
/**
|
||||
* @var Filesystem
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var Migrator
|
||||
*/
|
||||
protected $migrator;
|
||||
|
||||
/**
|
||||
* @param Application $application
|
||||
* @param Filesystem $filesystem
|
||||
*/
|
||||
public function __construct(Application $application, Filesystem $filesystem)
|
||||
public function __construct(Installation $installation)
|
||||
{
|
||||
$this->application = $application;
|
||||
$this->installation = $installation;
|
||||
|
||||
parent::__construct();
|
||||
$this->filesystem = $filesystem;
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
@@ -77,12 +44,6 @@ class InstallCommand extends AbstractCommand
|
||||
$this
|
||||
->setName('install')
|
||||
->setDescription("Run Flarum's installation migration and seeds")
|
||||
->addOption(
|
||||
'defaults',
|
||||
'd',
|
||||
InputOption::VALUE_NONE,
|
||||
'Create default settings and user'
|
||||
)
|
||||
->addOption(
|
||||
'file',
|
||||
'f',
|
||||
@@ -104,274 +65,64 @@ class InstallCommand extends AbstractCommand
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$prerequisites = $this->getPrerequisites();
|
||||
$prerequisites->check();
|
||||
$errors = $prerequisites->getErrors();
|
||||
$problems = $this->installation->prerequisites()->problems();
|
||||
|
||||
if (empty($errors)) {
|
||||
if ($problems->isEmpty()) {
|
||||
$this->info('Installing Flarum...');
|
||||
|
||||
$this->install();
|
||||
|
||||
$this->info('DONE.');
|
||||
} else {
|
||||
$this->output->writeln(
|
||||
'<error>Please fix the following errors before we can continue with the installation.</error>'
|
||||
);
|
||||
$this->showErrors($errors);
|
||||
$this->showProblems($problems);
|
||||
}
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
if ($this->dataSource === null) {
|
||||
if ($this->input->getOption('defaults')) {
|
||||
$this->dataSource = new DefaultsDataProvider();
|
||||
} elseif ($this->input->getOption('file')) {
|
||||
$this->dataSource = new FileDataProvider($this->input);
|
||||
} else {
|
||||
$this->dataSource = new UserDataProvider($this->input, $this->output, $this->getHelperSet()->get('question'));
|
||||
}
|
||||
if ($this->input->getOption('file')) {
|
||||
$this->dataSource = new FileDataProvider($this->input);
|
||||
} else {
|
||||
$this->dataSource = new UserDataProvider($this->input, $this->output, $this->getHelperSet()->get('question'));
|
||||
}
|
||||
}
|
||||
|
||||
public function setDataSource(DataProviderInterface $dataSource)
|
||||
{
|
||||
$this->dataSource = $dataSource;
|
||||
}
|
||||
|
||||
protected function install()
|
||||
{
|
||||
try {
|
||||
$this->dbConfig = $this->dataSource->getDatabaseConfiguration();
|
||||
$pipeline = $this->dataSource->configure(
|
||||
$this->installation->configPath($this->input->getOption('config'))
|
||||
)->build();
|
||||
|
||||
$validation = $this->getValidator()->make(
|
||||
$this->dbConfig,
|
||||
[
|
||||
'driver' => 'required|in:mysql',
|
||||
'host' => 'required',
|
||||
'database' => 'required|string',
|
||||
'username' => 'required|string',
|
||||
'prefix' => 'nullable|alpha_dash|max:10',
|
||||
'port' => 'nullable|integer|min:1|max:65535',
|
||||
]
|
||||
);
|
||||
|
||||
if ($validation->fails()) {
|
||||
throw new Exception(implode("\n", call_user_func_array('array_merge', $validation->getMessageBag()->toArray())));
|
||||
}
|
||||
|
||||
$this->baseUrl = $this->dataSource->getBaseUrl();
|
||||
$this->settings = $this->dataSource->getSettings();
|
||||
$this->adminUser = $admin = $this->dataSource->getAdminUser();
|
||||
|
||||
if (strlen($admin['password']) < 8) {
|
||||
throw new Exception('Password must be at least 8 characters.');
|
||||
}
|
||||
|
||||
if ($admin['password'] !== $admin['password_confirmation']) {
|
||||
throw new Exception('The password did not match its confirmation.');
|
||||
}
|
||||
|
||||
if (! filter_var($admin['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
throw new Exception('You must enter a valid email.');
|
||||
}
|
||||
|
||||
if (! $admin['username'] || preg_match('/[^a-z0-9_-]/i', $admin['username'])) {
|
||||
throw new Exception('Username can only contain letters, numbers, underscores, and dashes.');
|
||||
}
|
||||
|
||||
$this->storeConfiguration($this->dataSource->isDebugMode());
|
||||
|
||||
$this->runMigrations();
|
||||
|
||||
$this->writeSettings();
|
||||
|
||||
$this->createAdminUser();
|
||||
|
||||
$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());
|
||||
|
||||
throw $e;
|
||||
}
|
||||
$this->runPipeline($pipeline);
|
||||
}
|
||||
|
||||
protected function storeConfiguration(bool $debugMode)
|
||||
private function runPipeline(Pipeline $pipeline)
|
||||
{
|
||||
$dbConfig = $this->dbConfig;
|
||||
|
||||
$config = [
|
||||
'debug' => $debugMode,
|
||||
'database' => $laravelDbConfig = [
|
||||
'driver' => $dbConfig['driver'],
|
||||
'host' => $dbConfig['host'],
|
||||
'database' => $dbConfig['database'],
|
||||
'username' => $dbConfig['username'],
|
||||
'password' => $dbConfig['password'],
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => $dbConfig['prefix'],
|
||||
'port' => $dbConfig['port'],
|
||||
'strict' => false
|
||||
],
|
||||
'url' => $this->baseUrl,
|
||||
'paths' => [
|
||||
'api' => 'api',
|
||||
'admin' => 'admin',
|
||||
],
|
||||
];
|
||||
|
||||
$this->info('Testing config');
|
||||
|
||||
$factory = new ConnectionFactory($this->application);
|
||||
|
||||
$laravelDbConfig['engine'] = 'InnoDB';
|
||||
|
||||
$this->db = $factory->make($laravelDbConfig);
|
||||
$version = $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
|
||||
if (version_compare($version, '5.5.0', '<')) {
|
||||
throw new Exception('MySQL version too low. You need at least MySQL 5.5.');
|
||||
}
|
||||
|
||||
$repository = new DatabaseMigrationRepository(
|
||||
$this->db, 'migrations'
|
||||
);
|
||||
$files = $this->application->make('files');
|
||||
|
||||
$this->migrator = new Migrator($repository, $this->db, $files);
|
||||
|
||||
$this->info('Writing config');
|
||||
|
||||
file_put_contents(
|
||||
$this->getConfigFile(),
|
||||
'<?php return '.var_export($config, true).';'
|
||||
);
|
||||
$pipeline
|
||||
->on('start', function (Step $step) {
|
||||
$this->output->write($step->getMessage().'...');
|
||||
})->on('end', function () {
|
||||
$this->output->write("<info>done</info>\n");
|
||||
})->on('fail', function () {
|
||||
$this->output->write("<error>failed</error>\n");
|
||||
$this->output->writeln('Rolling back...');
|
||||
})->on('rollback', function (Step $step) {
|
||||
$this->output->writeln($step->getMessage().' (rollback)');
|
||||
})
|
||||
->run();
|
||||
}
|
||||
|
||||
protected function runMigrations()
|
||||
protected function showProblems($problems)
|
||||
{
|
||||
$this->migrator->setOutput($this->output);
|
||||
$this->migrator->getRepository()->createRepository();
|
||||
$this->migrator->run(__DIR__.'/../../../migrations');
|
||||
}
|
||||
|
||||
protected function writeSettings()
|
||||
{
|
||||
$settings = new DatabaseSettingsRepository($this->db);
|
||||
|
||||
$this->info('Writing default settings');
|
||||
|
||||
$settings->set('version', $this->application->version());
|
||||
|
||||
foreach ($this->settings as $k => $v) {
|
||||
$settings->set($k, $v);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createAdminUser()
|
||||
{
|
||||
$admin = $this->adminUser;
|
||||
|
||||
if ($admin['password'] !== $admin['password_confirmation']) {
|
||||
throw new Exception('The password did not match its confirmation.');
|
||||
}
|
||||
|
||||
$this->info('Creating admin user '.$admin['username']);
|
||||
|
||||
$uid = $this->db->table('users')->insertGetId([
|
||||
'username' => $admin['username'],
|
||||
'email' => $admin['email'],
|
||||
'password' => (new BcryptHasher)->make($admin['password']),
|
||||
'joined_at' => Carbon::now(),
|
||||
'is_email_confirmed' => 1,
|
||||
]);
|
||||
|
||||
$this->db->table('group_user')->insert([
|
||||
'user_id' => $uid,
|
||||
'group_id' => Group::ADMINISTRATOR_ID,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function enableBundledExtensions()
|
||||
{
|
||||
$extensions = new ExtensionManager(
|
||||
new DatabaseSettingsRepository($this->db),
|
||||
$this->application,
|
||||
$this->migrator,
|
||||
$this->application->make(Dispatcher::class),
|
||||
$this->application->make('files')
|
||||
$this->output->writeln(
|
||||
'<error>Please fix the following problems before we can continue with the installation.</error>'
|
||||
);
|
||||
|
||||
$disabled = [
|
||||
'flarum-akismet',
|
||||
'flarum-auth-facebook',
|
||||
'flarum-auth-github',
|
||||
'flarum-auth-twitter',
|
||||
'flarum-pusher',
|
||||
];
|
||||
foreach ($problems as $problem) {
|
||||
$this->info($problem['message']);
|
||||
|
||||
foreach ($extensions->getExtensions() as $name => $extension) {
|
||||
if (in_array($name, $disabled)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info('Enabling extension: '.$name);
|
||||
|
||||
$extensions->enable($name);
|
||||
}
|
||||
}
|
||||
|
||||
protected function publishAssets()
|
||||
{
|
||||
$this->filesystem->copyDirectory(
|
||||
$this->application->basePath().'/vendor/components/font-awesome/webfonts',
|
||||
$this->application->publicPath().'/assets/fonts'
|
||||
);
|
||||
}
|
||||
|
||||
protected function getConfigFile()
|
||||
{
|
||||
return $this->input->getOption('config') ?: base_path('config.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Flarum\Install\Prerequisite\PrerequisiteInterface
|
||||
*/
|
||||
protected function getPrerequisites()
|
||||
{
|
||||
return $this->application->make(PrerequisiteInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
protected function getValidator()
|
||||
{
|
||||
return new Factory($this->application->make(Translator::class));
|
||||
}
|
||||
|
||||
protected function showErrors($errors)
|
||||
{
|
||||
foreach ($errors as $error) {
|
||||
$this->info($error['message']);
|
||||
|
||||
if (isset($error['detail'])) {
|
||||
$this->output->writeln('<comment>'.$error['detail'].'</comment>');
|
||||
if (isset($problem['detail'])) {
|
||||
$this->output->writeln('<comment>'.$problem['detail'].'</comment>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace Flarum\Install\Console;
|
||||
|
||||
use Flarum\Install\AdminUser;
|
||||
use Flarum\Install\DatabaseConfig;
|
||||
use Flarum\Install\Installation;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
@@ -33,75 +36,91 @@ class UserDataProvider implements DataProviderInterface
|
||||
$this->questionHelper = $questionHelper;
|
||||
}
|
||||
|
||||
public function getDatabaseConfiguration()
|
||||
public function configure(Installation $installation): Installation
|
||||
{
|
||||
return $installation
|
||||
->debugMode(false)
|
||||
->baseUrl($this->getBaseUrl())
|
||||
->databaseConfig($this->getDatabaseConfiguration())
|
||||
->adminUser($this->getAdminUser())
|
||||
->settings($this->getSettings());
|
||||
}
|
||||
|
||||
private function getDatabaseConfiguration(): DatabaseConfig
|
||||
{
|
||||
$host = $this->ask('Database host:');
|
||||
$port = '3306';
|
||||
$port = 3306;
|
||||
|
||||
if (str_contains($host, ':')) {
|
||||
list($host, $port) = explode(':', $host, 2);
|
||||
}
|
||||
|
||||
return [
|
||||
'driver' => 'mysql',
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'database' => $this->ask('Database name:'),
|
||||
'username' => $this->ask('Database user:'),
|
||||
'password' => $this->secret('Database password:'),
|
||||
'prefix' => $this->ask('Prefix:'),
|
||||
];
|
||||
return new DatabaseConfig(
|
||||
'mysql',
|
||||
$host,
|
||||
intval($port),
|
||||
$this->ask('Database name:'),
|
||||
$this->ask('Database user:'),
|
||||
$this->secret('Database password:'),
|
||||
$this->ask('Prefix:')
|
||||
);
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
private function getBaseUrl()
|
||||
{
|
||||
return $this->baseUrl = rtrim($this->ask('Base URL:'), '/');
|
||||
}
|
||||
|
||||
public function getAdminUser()
|
||||
private function getAdminUser(): AdminUser
|
||||
{
|
||||
return [
|
||||
'username' => $this->ask('Admin username:'),
|
||||
'password' => $this->secret('Admin password:'),
|
||||
'password_confirmation' => $this->secret('Admin password (confirmation):'),
|
||||
'email' => $this->ask('Admin email address:'),
|
||||
];
|
||||
return new AdminUser(
|
||||
$this->ask('Admin username:'),
|
||||
$this->askForAdminPassword(),
|
||||
$this->ask('Admin email address:')
|
||||
);
|
||||
}
|
||||
|
||||
public function getSettings()
|
||||
private function askForAdminPassword()
|
||||
{
|
||||
while (true) {
|
||||
$password = $this->secret('Admin password:');
|
||||
|
||||
if (strlen($password) < 8) {
|
||||
$this->validationError('Password must be at least 8 characters.');
|
||||
continue;
|
||||
}
|
||||
|
||||
$confirmation = $this->secret('Admin password (confirmation):');
|
||||
|
||||
if ($password !== $confirmation) {
|
||||
$this->validationError('The password did not match its confirmation.');
|
||||
continue;
|
||||
}
|
||||
|
||||
return $password;
|
||||
}
|
||||
}
|
||||
|
||||
private function getSettings()
|
||||
{
|
||||
$title = $this->ask('Forum title:');
|
||||
$baseUrl = $this->baseUrl ?: 'http://localhost';
|
||||
|
||||
return [
|
||||
'allow_post_editing' => 'reply',
|
||||
'allow_renaming' => '10',
|
||||
'allow_sign_up' => '1',
|
||||
'custom_less' => '',
|
||||
'default_locale' => 'en',
|
||||
'default_route' => '/all',
|
||||
'extensions_enabled' => '[]',
|
||||
'forum_title' => $title,
|
||||
'forum_description' => '',
|
||||
'mail_driver' => 'mail',
|
||||
'mail_from' => 'noreply@'.preg_replace('/^www\./i', '', parse_url($baseUrl, PHP_URL_HOST)),
|
||||
'theme_colored_header' => '0',
|
||||
'theme_dark_mode' => '0',
|
||||
'theme_primary_color' => '#4D698E',
|
||||
'theme_secondary_color' => '#4D698E',
|
||||
'welcome_message' => 'This is beta software and you should not use it in production.',
|
||||
'welcome_title' => 'Welcome to '.$title,
|
||||
];
|
||||
}
|
||||
|
||||
protected function ask($question, $default = null)
|
||||
private function ask($question, $default = null)
|
||||
{
|
||||
$question = new Question("<question>$question</question> ", $default);
|
||||
|
||||
return $this->questionHelper->ask($this->input, $this->output, $question);
|
||||
}
|
||||
|
||||
protected function secret($question)
|
||||
private function secret($question)
|
||||
{
|
||||
$question = new Question("<question>$question</question> ");
|
||||
|
||||
@@ -110,8 +129,9 @@ class UserDataProvider implements DataProviderInterface
|
||||
return $this->questionHelper->ask($this->input, $this->output, $question);
|
||||
}
|
||||
|
||||
public function isDebugMode(): bool
|
||||
private function validationError($message)
|
||||
{
|
||||
return false;
|
||||
$this->output->writeln("<error>$message</error>");
|
||||
$this->output->writeln('Please try again.');
|
||||
}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace Flarum\Install\Controller;
|
||||
|
||||
use Flarum\Http\Controller\AbstractHtmlController;
|
||||
use Flarum\Install\Prerequisite\PrerequisiteInterface;
|
||||
use Flarum\Install\Installation;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
@@ -24,18 +24,18 @@ class IndexController extends AbstractHtmlController
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Install\Prerequisite\PrerequisiteInterface
|
||||
* @var Installation
|
||||
*/
|
||||
protected $prerequisite;
|
||||
protected $installation;
|
||||
|
||||
/**
|
||||
* @param Factory $view
|
||||
* @param PrerequisiteInterface $prerequisite
|
||||
* @param Installation $installation
|
||||
*/
|
||||
public function __construct(Factory $view, PrerequisiteInterface $prerequisite)
|
||||
public function __construct(Factory $view, Installation $installation)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->prerequisite = $prerequisite;
|
||||
$this->installation = $installation;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,13 +46,12 @@ class IndexController extends AbstractHtmlController
|
||||
{
|
||||
$view = $this->view->make('flarum.install::app')->with('title', 'Install Flarum');
|
||||
|
||||
$this->prerequisite->check();
|
||||
$errors = $this->prerequisite->getErrors();
|
||||
$problems = $this->installation->prerequisites()->problems();
|
||||
|
||||
if (count($errors)) {
|
||||
$view->with('content', $this->view->make('flarum.install::errors')->with('errors', $errors));
|
||||
} else {
|
||||
if ($problems->isEmpty()) {
|
||||
$view->with('content', $this->view->make('flarum.install::install'));
|
||||
} else {
|
||||
$view->with('content', $this->view->make('flarum.install::problems')->with('problems', $problems));
|
||||
}
|
||||
|
||||
return $view;
|
||||
|
@@ -11,21 +11,23 @@
|
||||
|
||||
namespace Flarum\Install\Controller;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Http\SessionAuthenticator;
|
||||
use Flarum\Install\Console\DefaultsDataProvider;
|
||||
use Flarum\Install\Console\InstallCommand;
|
||||
use Flarum\Install\AdminUser;
|
||||
use Flarum\Install\DatabaseConfig;
|
||||
use Flarum\Install\Installation;
|
||||
use Flarum\Install\StepFailed;
|
||||
use Flarum\Install\ValidationFailed;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Symfony\Component\Console\Input\StringInput;
|
||||
use Symfony\Component\Console\Output\StreamOutput;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
|
||||
class InstallController implements RequestHandlerInterface
|
||||
{
|
||||
protected $command;
|
||||
/**
|
||||
* @var Installation
|
||||
*/
|
||||
protected $installation;
|
||||
|
||||
/**
|
||||
* @var SessionAuthenticator
|
||||
@@ -34,12 +36,12 @@ class InstallController implements RequestHandlerInterface
|
||||
|
||||
/**
|
||||
* InstallController constructor.
|
||||
* @param InstallCommand $command
|
||||
* @param Installation $installation
|
||||
* @param SessionAuthenticator $authenticator
|
||||
*/
|
||||
public function __construct(InstallCommand $command, SessionAuthenticator $authenticator)
|
||||
public function __construct(Installation $installation, SessionAuthenticator $authenticator)
|
||||
{
|
||||
$this->command = $command;
|
||||
$this->installation = $installation;
|
||||
$this->authenticator = $authenticator;
|
||||
}
|
||||
|
||||
@@ -50,55 +52,78 @@ class InstallController implements RequestHandlerInterface
|
||||
public function handle(Request $request): ResponseInterface
|
||||
{
|
||||
$input = $request->getParsedBody();
|
||||
|
||||
$data = new DefaultsDataProvider;
|
||||
|
||||
$host = array_get($input, 'mysqlHost');
|
||||
$port = '3306';
|
||||
|
||||
if (str_contains($host, ':')) {
|
||||
list($host, $port) = explode(':', $host, 2);
|
||||
}
|
||||
|
||||
$data->setDatabaseConfiguration([
|
||||
'driver' => 'mysql',
|
||||
'host' => $host,
|
||||
'database' => array_get($input, 'mysqlDatabase'),
|
||||
'username' => array_get($input, 'mysqlUsername'),
|
||||
'password' => array_get($input, 'mysqlPassword'),
|
||||
'prefix' => array_get($input, 'tablePrefix'),
|
||||
'port' => $port,
|
||||
]);
|
||||
|
||||
$data->setAdminUser([
|
||||
'username' => array_get($input, 'adminUsername'),
|
||||
'password' => array_get($input, 'adminPassword'),
|
||||
'password_confirmation' => array_get($input, 'adminPasswordConfirmation'),
|
||||
'email' => array_get($input, 'adminEmail'),
|
||||
]);
|
||||
|
||||
$baseUrl = rtrim((string) $request->getUri(), '/');
|
||||
$data->setBaseUrl($baseUrl);
|
||||
|
||||
$data->setSetting('forum_title', array_get($input, 'forumTitle'));
|
||||
$data->setSetting('mail_from', 'noreply@'.preg_replace('/^www\./i', '', parse_url($baseUrl, PHP_URL_HOST)));
|
||||
$data->setSetting('welcome_title', 'Welcome to '.array_get($input, 'forumTitle'));
|
||||
|
||||
$body = fopen('php://temp', 'wb+');
|
||||
$input = new StringInput('');
|
||||
$output = new StreamOutput($body);
|
||||
|
||||
$this->command->setDataSource($data);
|
||||
|
||||
try {
|
||||
$this->command->run($input, $output);
|
||||
} catch (Exception $e) {
|
||||
return new HtmlResponse($e->getMessage(), 500);
|
||||
$pipeline = $this->installation
|
||||
->baseUrl($baseUrl)
|
||||
->databaseConfig($this->makeDatabaseConfig($input))
|
||||
->adminUser($this->makeAdminUser($input))
|
||||
->settings([
|
||||
'forum_title' => array_get($input, 'forumTitle'),
|
||||
'mail_from' => 'noreply@'.preg_replace('/^www\./i', '', parse_url($baseUrl, PHP_URL_HOST)),
|
||||
'welcome_title' => 'Welcome to '.array_get($input, 'forumTitle'),
|
||||
])
|
||||
->build();
|
||||
} catch (ValidationFailed $e) {
|
||||
return new Response\HtmlResponse($e->getMessage(), 500);
|
||||
}
|
||||
|
||||
try {
|
||||
$pipeline->run();
|
||||
} catch (StepFailed $e) {
|
||||
return new Response\HtmlResponse($e->getPrevious()->getMessage(), 500);
|
||||
}
|
||||
|
||||
$session = $request->getAttribute('session');
|
||||
$this->authenticator->logIn($session, 1);
|
||||
|
||||
return new Response($body);
|
||||
return new Response\EmptyResponse;
|
||||
}
|
||||
|
||||
private function makeDatabaseConfig(array $input): DatabaseConfig
|
||||
{
|
||||
$host = array_get($input, 'mysqlHost');
|
||||
$port = 3306;
|
||||
|
||||
if (str_contains($host, ':')) {
|
||||
list($host, $port) = explode(':', $host, 2);
|
||||
}
|
||||
|
||||
return new DatabaseConfig(
|
||||
'mysql',
|
||||
$host,
|
||||
intval($port),
|
||||
array_get($input, 'mysqlDatabase'),
|
||||
array_get($input, 'mysqlUsername'),
|
||||
array_get($input, 'mysqlPassword'),
|
||||
array_get($input, 'tablePrefix')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $input
|
||||
* @return AdminUser
|
||||
* @throws ValidationFailed
|
||||
*/
|
||||
private function makeAdminUser(array $input): AdminUser
|
||||
{
|
||||
return new AdminUser(
|
||||
array_get($input, 'adminUsername'),
|
||||
$this->getConfirmedAdminPassword($input),
|
||||
array_get($input, 'adminEmail')
|
||||
);
|
||||
}
|
||||
|
||||
private function getConfirmedAdminPassword(array $input): string
|
||||
{
|
||||
$password = array_get($input, 'adminPassword');
|
||||
$confirmation = array_get($input, 'adminPasswordConfirmation');
|
||||
|
||||
if ($password !== $confirmation) {
|
||||
throw new ValidationFailed('The admin password did not match its confirmation.');
|
||||
}
|
||||
|
||||
return $password;
|
||||
}
|
||||
}
|
||||
|
101
src/Install/DatabaseConfig.php
Normal file
101
src/Install/DatabaseConfig.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?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\Install;
|
||||
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
|
||||
class DatabaseConfig implements Arrayable
|
||||
{
|
||||
private $driver;
|
||||
private $host;
|
||||
private $port;
|
||||
private $database;
|
||||
private $username;
|
||||
private $password;
|
||||
private $prefix;
|
||||
|
||||
public function __construct($driver, $host, $port, $database, $username, $password, $prefix)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
$this->database = $database;
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->prefix = $prefix;
|
||||
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'driver' => $this->driver,
|
||||
'host' => $this->host,
|
||||
'port' => $this->port,
|
||||
'database' => $this->database,
|
||||
'username' => $this->username,
|
||||
'password' => $this->password,
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'prefix' => $this->prefix,
|
||||
'strict' => false,
|
||||
'engine' => 'InnoDB',
|
||||
'prefix_indexes' => true
|
||||
];
|
||||
}
|
||||
|
||||
private function validate()
|
||||
{
|
||||
if (empty($this->driver)) {
|
||||
throw new ValidationFailed('Please specify a database driver.');
|
||||
}
|
||||
|
||||
if ($this->driver !== 'mysql') {
|
||||
throw new ValidationFailed('Currently, only MySQL/MariaDB is supported.');
|
||||
}
|
||||
|
||||
if (empty($this->host)) {
|
||||
throw new ValidationFailed('Please specify the hostname of your database server.');
|
||||
}
|
||||
|
||||
if (! is_int($this->port) || $this->port < 1 || $this->port > 65535) {
|
||||
throw new ValidationFailed('Please provide a valid port number between 1 and 65535.');
|
||||
}
|
||||
|
||||
if (empty($this->database)) {
|
||||
throw new ValidationFailed('Please specify the database name.');
|
||||
}
|
||||
|
||||
if (! is_string($this->database)) {
|
||||
throw new ValidationFailed('The database name must be a non-empty string.');
|
||||
}
|
||||
|
||||
if (empty($this->username)) {
|
||||
throw new ValidationFailed('Please specify the username for accessing the database.');
|
||||
}
|
||||
|
||||
if (! is_string($this->database)) {
|
||||
throw new ValidationFailed('The username must be a non-empty string.');
|
||||
}
|
||||
|
||||
if (! empty($this->prefix)) {
|
||||
if (! preg_match('/^[\pL\pM\pN_-]+$/u', $this->prefix)) {
|
||||
throw new ValidationFailed('The prefix may only contain characters, dashes and underscores.');
|
||||
}
|
||||
|
||||
if (strlen($this->prefix) > 10) {
|
||||
throw new ValidationFailed('The prefix should be no longer than 10 characters.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,11 +14,6 @@ namespace Flarum\Install;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Install\Prerequisite\Composite;
|
||||
use Flarum\Install\Prerequisite\PhpExtensions;
|
||||
use Flarum\Install\Prerequisite\PhpVersion;
|
||||
use Flarum\Install\Prerequisite\PrerequisiteInterface;
|
||||
use Flarum\Install\Prerequisite\WritablePaths;
|
||||
|
||||
class InstallServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
@@ -27,32 +22,18 @@ class InstallServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind(
|
||||
PrerequisiteInterface::class,
|
||||
function () {
|
||||
return new Composite(
|
||||
new PhpVersion('7.1.0'),
|
||||
new PhpExtensions([
|
||||
'dom',
|
||||
'gd',
|
||||
'json',
|
||||
'mbstring',
|
||||
'openssl',
|
||||
'pdo_mysql',
|
||||
'tokenizer',
|
||||
]),
|
||||
new WritablePaths([
|
||||
base_path(),
|
||||
public_path('assets'),
|
||||
storage_path(),
|
||||
])
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->singleton('flarum.install.routes', function () {
|
||||
return new RouteCollection;
|
||||
});
|
||||
|
||||
$this->app->singleton(Installation::class, function () {
|
||||
return new Installation(
|
||||
$this->app->basePath(),
|
||||
$this->app->publicPath(),
|
||||
$this->app->storagePath(),
|
||||
$this->app->vendorPath()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
166
src/Install/Installation.php
Normal file
166
src/Install/Installation.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?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\Install;
|
||||
|
||||
class Installation
|
||||
{
|
||||
private $basePath;
|
||||
private $publicPath;
|
||||
private $storagePath;
|
||||
private $vendorPath;
|
||||
|
||||
private $configPath;
|
||||
private $debug = false;
|
||||
private $baseUrl;
|
||||
private $customSettings = [];
|
||||
|
||||
/** @var DatabaseConfig */
|
||||
private $dbConfig;
|
||||
|
||||
/** @var AdminUser */
|
||||
private $adminUser;
|
||||
|
||||
// A few instance variables to persist objects between steps.
|
||||
// Could also be local variables in build(), but this way
|
||||
// access in closures is easier. :)
|
||||
|
||||
/** @var \Illuminate\Database\ConnectionInterface */
|
||||
private $db;
|
||||
|
||||
public function __construct($basePath, $publicPath, $storagePath, $vendorPath)
|
||||
{
|
||||
$this->basePath = $basePath;
|
||||
$this->publicPath = $publicPath;
|
||||
$this->storagePath = $storagePath;
|
||||
$this->vendorPath = $vendorPath;
|
||||
}
|
||||
|
||||
public function configPath($path)
|
||||
{
|
||||
$this->configPath = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function debugMode($flag)
|
||||
{
|
||||
$this->debug = $flag;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function databaseConfig(DatabaseConfig $dbConfig)
|
||||
{
|
||||
$this->dbConfig = $dbConfig;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function baseUrl($baseUrl)
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function settings($settings)
|
||||
{
|
||||
$this->customSettings = $settings;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function adminUser(AdminUser $admin)
|
||||
{
|
||||
$this->adminUser = $admin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function prerequisites(): Prerequisite\PrerequisiteInterface
|
||||
{
|
||||
return new Prerequisite\Composite(
|
||||
new Prerequisite\PhpVersion('7.1.0'),
|
||||
new Prerequisite\PhpExtensions([
|
||||
'dom',
|
||||
'gd',
|
||||
'json',
|
||||
'mbstring',
|
||||
'openssl',
|
||||
'pdo_mysql',
|
||||
'tokenizer',
|
||||
]),
|
||||
new Prerequisite\WritablePaths([
|
||||
$this->basePath,
|
||||
$this->getAssetPath(),
|
||||
$this->storagePath,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
public function build(): Pipeline
|
||||
{
|
||||
$pipeline = new Pipeline;
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\ConnectToDatabase(
|
||||
$this->dbConfig,
|
||||
function ($connection) {
|
||||
$this->db = $connection;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\StoreConfig(
|
||||
$this->debug, $this->dbConfig, $this->baseUrl, $this->getConfigPath()
|
||||
);
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\RunMigrations($this->db, $this->getMigrationPath());
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\WriteSettings($this->db, $this->customSettings);
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\CreateAdminUser($this->db, $this->adminUser);
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\PublishAssets($this->vendorPath, $this->getAssetPath());
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\EnableBundledExtensions($this->db, $this->vendorPath, $this->getAssetPath());
|
||||
});
|
||||
|
||||
return $pipeline;
|
||||
}
|
||||
|
||||
private function getConfigPath()
|
||||
{
|
||||
return $this->basePath.'/'.($this->configPath ?? 'config.php');
|
||||
}
|
||||
|
||||
private function getAssetPath()
|
||||
{
|
||||
return "$this->publicPath/assets";
|
||||
}
|
||||
|
||||
private function getMigrationPath()
|
||||
{
|
||||
return __DIR__.'/../../migrations';
|
||||
}
|
||||
}
|
@@ -17,6 +17,8 @@ use Flarum\Http\Middleware\HandleErrorsWithWhoops;
|
||||
use Flarum\Http\Middleware\StartSession;
|
||||
use Flarum\Install\Console\InstallCommand;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Translation\Translator;
|
||||
use Illuminate\Validation\Factory;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
|
||||
class Installer implements AppInterface
|
||||
@@ -52,7 +54,10 @@ class Installer implements AppInterface
|
||||
public function getConsoleCommands()
|
||||
{
|
||||
return [
|
||||
$this->container->make(InstallCommand::class),
|
||||
new InstallCommand(
|
||||
$this->container->make(Installation::class),
|
||||
new Factory($this->container->make(Translator::class))
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user