mirror of
https://github.com/flarum/framework.git
synced 2025-03-15 00:05:12 +08:00
Merge branch 'master' into 1236-user-preferences
This commit is contained in:
commit
c19b2e99bd
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
|
||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -4,12 +4,37 @@
|
||||
|
||||
### 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))
|
||||
|
||||
### 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
|
||||
- Fix signing up via OAuth providers ([67f9375](https://github.com/flarum/core/commit/67f9375d4745add194ae3249d526197c32fd5461))
|
||||
- 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))
|
||||
|
||||
### Removed
|
||||
- `php flarum install --defaults` - this was meant to be used in our old development VM ([44c9109](https://github.com/flarum/core/commit/44c91099cd77138bb5fc29f14fb1e81a9781272d))
|
||||
|
||||
## [0.1.0-beta.8.1](https://github.com/flarum/core/compare/v0.1.0-beta.8...v0.1.0-beta.8.1)
|
||||
|
||||
|
@ -26,20 +26,20 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
4
js/dist/admin.js
vendored
4
js/dist/admin.js
vendored
File diff suppressed because one or more lines are too long
2
js/dist/admin.js.map
vendored
2
js/dist/admin.js.map
vendored
File diff suppressed because one or more lines are too long
8
js/dist/forum.js
vendored
8
js/dist/forum.js
vendored
File diff suppressed because one or more lines are too long
2
js/dist/forum.js.map
vendored
2
js/dist/forum.js.map
vendored
File diff suppressed because one or more lines are too long
701
js/package-lock.json
generated
701
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
"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",
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -41,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) {
|
||||
@ -103,8 +106,6 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->populateRoutes($this->app->make('flarum.admin.routes'));
|
||||
|
||||
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum.admin');
|
||||
|
||||
$events = $this->app->make('events');
|
||||
|
@ -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) {
|
||||
@ -90,8 +93,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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class BasicPostSerializer extends AbstractSerializer
|
||||
];
|
||||
|
||||
if ($post instanceof CommentPost) {
|
||||
$attributes['contentHtml'] = $post->content_html;
|
||||
$attributes['contentHtml'] = $post->formatContent($this->request);
|
||||
} else {
|
||||
$attributes['content'] = $post->content;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -30,6 +30,7 @@ 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;
|
||||
@ -46,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) {
|
||||
@ -110,8 +114,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([
|
||||
@ -140,15 +142,26 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
$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->subscribe(
|
||||
new ValidateCustomLess(
|
||||
$this->app->make('flarum.assets.forum'),
|
||||
$this->app->make('flarum.locales'),
|
||||
$this->app
|
||||
)
|
||||
$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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,11 @@ class InstalledApp implements AppInterface
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Psr\Http\Server\RequestHandlerInterface
|
||||
*/
|
||||
|
@ -26,7 +26,6 @@ use Flarum\Locale\LocaleServiceProvider;
|
||||
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 +35,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 +72,7 @@ class InstalledSite implements SiteInterface
|
||||
/**
|
||||
* Create and boot a Flarum application instance.
|
||||
*
|
||||
* @return AppInterface
|
||||
* @return InstalledApp
|
||||
*/
|
||||
public function bootApp(): AppInterface
|
||||
{
|
||||
@ -107,51 +106,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;
|
||||
}
|
||||
|
||||
|
83
src/Foundation/MailServiceProvider.php
Normal file
83
src/Foundation/MailServiceProvider.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?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\Foundation;
|
||||
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Mail\Mailer;
|
||||
use Illuminate\Mail\Transport\LogTransport;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Swift_Mailer;
|
||||
use Swift_SendmailTransport;
|
||||
use Swift_SmtpTransport;
|
||||
use Swift_Transport;
|
||||
|
||||
class MailServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('mail.supported_drivers', function () {
|
||||
return ['smtp', 'mail', 'log'];
|
||||
});
|
||||
|
||||
$this->app->singleton('swift.mailer', function ($app) {
|
||||
$settings = $app->make(SettingsRepositoryInterface::class);
|
||||
|
||||
return new Swift_Mailer(
|
||||
$this->buildTransport($settings)
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->singleton('mailer', function ($app) {
|
||||
$mailer = new Mailer(
|
||||
$app['view'], $app['swift.mailer'], $app['events']
|
||||
);
|
||||
|
||||
if ($app->bound('queue')) {
|
||||
$mailer->setQueue($app->make('queue'));
|
||||
}
|
||||
|
||||
$settings = $app->make(SettingsRepositoryInterface::class);
|
||||
$mailer->alwaysFrom($settings->get('mail_from'), $settings->get('forum_title'));
|
||||
|
||||
return $mailer;
|
||||
});
|
||||
}
|
||||
|
||||
private function buildTransport(SettingsRepositoryInterface $settings): Swift_Transport
|
||||
{
|
||||
switch ($settings->get('mail_driver')) {
|
||||
case 'smtp':
|
||||
return $this->buildSmtpTransport($settings);
|
||||
case 'mail':
|
||||
return new Swift_SendmailTransport;
|
||||
case 'log':
|
||||
return new LogTransport($this->app->make(LoggerInterface::class));
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid mail driver configuration');
|
||||
}
|
||||
}
|
||||
|
||||
private function buildSmtpTransport(SettingsRepositoryInterface $settings): Swift_Transport
|
||||
{
|
||||
$transport = new Swift_SmtpTransport(
|
||||
$settings->get('mail_host'),
|
||||
$settings->get('mail_port'),
|
||||
$settings->get('mail_encryption')
|
||||
);
|
||||
|
||||
$transport->setUsername($settings->get('mail_username'));
|
||||
$transport->setPassword($settings->get('mail_password'));
|
||||
|
||||
return $transport;
|
||||
}
|
||||
}
|
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,17 @@ 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()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
164
src/Install/Installation.php
Normal file
164
src/Install/Installation.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?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 $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)
|
||||
{
|
||||
$this->basePath = $basePath;
|
||||
$this->publicPath = $publicPath;
|
||||
$this->storagePath = $storagePath;
|
||||
}
|
||||
|
||||
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->basePath, $this->getAssetPath());
|
||||
});
|
||||
|
||||
$pipeline->pipe(function () {
|
||||
return new Steps\EnableBundledExtensions($this->db, $this->basePath, $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))
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
108
src/Install/Pipeline.php
Normal file
108
src/Install/Pipeline.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?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 Exception;
|
||||
use SplStack;
|
||||
|
||||
class Pipeline
|
||||
{
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
private $steps;
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
private $callbacks;
|
||||
|
||||
/**
|
||||
* @var SplStack
|
||||
*/
|
||||
private $successfulSteps;
|
||||
|
||||
public function __construct(array $steps = [])
|
||||
{
|
||||
$this->steps = $steps;
|
||||
}
|
||||
|
||||
public function pipe(callable $factory)
|
||||
{
|
||||
$this->steps[] = $factory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function on($event, callable $callback)
|
||||
{
|
||||
$this->callbacks[$event] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->successfulSteps = new SplStack;
|
||||
|
||||
try {
|
||||
foreach ($this->steps as $factory) {
|
||||
$this->runStep($factory);
|
||||
}
|
||||
} catch (StepFailed $failure) {
|
||||
$this->revertReversibleSteps();
|
||||
|
||||
throw $failure;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $factory
|
||||
* @throws StepFailed
|
||||
*/
|
||||
private function runStep(callable $factory)
|
||||
{
|
||||
/** @var Step $step */
|
||||
$step = $factory();
|
||||
|
||||
$this->fireCallbacks('start', $step);
|
||||
|
||||
try {
|
||||
$step->run();
|
||||
$this->successfulSteps->push($step);
|
||||
|
||||
$this->fireCallbacks('end', $step);
|
||||
} catch (Exception $e) {
|
||||
$this->fireCallbacks('fail', $step);
|
||||
|
||||
throw new StepFailed('Step failed', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function revertReversibleSteps()
|
||||
{
|
||||
foreach ($this->successfulSteps as $step) {
|
||||
if ($step instanceof ReversibleStep) {
|
||||
$this->fireCallbacks('rollback', $step);
|
||||
|
||||
$step->revert();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function fireCallbacks($event, Step $step)
|
||||
{
|
||||
if (isset($this->callbacks[$event])) {
|
||||
($this->callbacks[$event])($step);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Install\Prerequisite;
|
||||
|
||||
abstract class AbstractPrerequisite implements PrerequisiteInterface
|
||||
{
|
||||
protected $errors = [];
|
||||
|
||||
abstract public function check();
|
||||
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace Flarum\Install\Prerequisite;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class Composite implements PrerequisiteInterface
|
||||
{
|
||||
/**
|
||||
@ -25,21 +27,14 @@ class Composite implements PrerequisiteInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function check()
|
||||
public function problems(): Collection
|
||||
{
|
||||
return array_reduce(
|
||||
$this->prerequisites,
|
||||
function ($previous, PrerequisiteInterface $prerequisite) {
|
||||
return $prerequisite->check() && $previous;
|
||||
function (Collection $errors, PrerequisiteInterface $condition) {
|
||||
return $errors->concat($condition->problems());
|
||||
},
|
||||
true
|
||||
new Collection
|
||||
);
|
||||
}
|
||||
|
||||
public function getErrors()
|
||||
{
|
||||
return collect($this->prerequisites)->map(function (PrerequisiteInterface $prerequisite) {
|
||||
return $prerequisite->getErrors();
|
||||
})->reduce('array_merge', []);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
namespace Flarum\Install\Prerequisite;
|
||||
|
||||
class PhpExtensions extends AbstractPrerequisite
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class PhpExtensions implements PrerequisiteInterface
|
||||
{
|
||||
protected $extensions;
|
||||
|
||||
@ -20,14 +22,15 @@ class PhpExtensions extends AbstractPrerequisite
|
||||
$this->extensions = $extensions;
|
||||
}
|
||||
|
||||
public function check()
|
||||
public function problems(): Collection
|
||||
{
|
||||
foreach ($this->extensions as $extension) {
|
||||
if (! extension_loaded($extension)) {
|
||||
$this->errors[] = [
|
||||
return (new Collection($this->extensions))
|
||||
->reject(function ($extension) {
|
||||
return extension_loaded($extension);
|
||||
})->map(function ($extension) {
|
||||
return [
|
||||
'message' => "The PHP extension '$extension' is required.",
|
||||
];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
namespace Flarum\Install\Prerequisite;
|
||||
|
||||
class PhpVersion extends AbstractPrerequisite
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class PhpVersion implements PrerequisiteInterface
|
||||
{
|
||||
protected $minVersion;
|
||||
|
||||
@ -20,13 +22,17 @@ class PhpVersion extends AbstractPrerequisite
|
||||
$this->minVersion = $minVersion;
|
||||
}
|
||||
|
||||
public function check()
|
||||
public function problems(): Collection
|
||||
{
|
||||
$collection = new Collection;
|
||||
|
||||
if (version_compare(PHP_VERSION, $this->minVersion, '<')) {
|
||||
$this->errors[] = [
|
||||
$collection->push([
|
||||
'message' => "PHP $this->minVersion is required.",
|
||||
'detail' => 'You are running version '.PHP_VERSION.'. Talk to your hosting provider about upgrading to the latest PHP version.',
|
||||
];
|
||||
'detail' => 'You are running version '.PHP_VERSION.'. You might want to talk to your system administrator about upgrading to the latest PHP version.',
|
||||
]);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,18 @@
|
||||
|
||||
namespace Flarum\Install\Prerequisite;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface PrerequisiteInterface
|
||||
{
|
||||
public function check();
|
||||
|
||||
public function getErrors();
|
||||
/**
|
||||
* Verify that this prerequisite is fulfilled.
|
||||
*
|
||||
* If everything is okay, this method should return an empty Collection
|
||||
* instance. When problems are detected, it should return a Collection of
|
||||
* arrays, each having at least a "message" and optionally a "detail" key.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function problems(): Collection;
|
||||
}
|
||||
|
@ -11,7 +11,9 @@
|
||||
|
||||
namespace Flarum\Install\Prerequisite;
|
||||
|
||||
class WritablePaths extends AbstractPrerequisite
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class WritablePaths implements PrerequisiteInterface
|
||||
{
|
||||
protected $paths;
|
||||
|
||||
@ -20,21 +22,36 @@ class WritablePaths extends AbstractPrerequisite
|
||||
$this->paths = $paths;
|
||||
}
|
||||
|
||||
public function check()
|
||||
public function problems(): Collection
|
||||
{
|
||||
foreach ($this->paths as $path) {
|
||||
if (! file_exists($path)) {
|
||||
$this->errors[] = [
|
||||
return $this->getMissingPaths()
|
||||
->concat($this->getNonWritablePaths());
|
||||
}
|
||||
|
||||
private function getMissingPaths(): Collection
|
||||
{
|
||||
return (new Collection($this->paths))
|
||||
->reject(function ($path) {
|
||||
return file_exists($path);
|
||||
})->map(function ($path) {
|
||||
return [
|
||||
'message' => 'The '.$this->getAbsolutePath($path).' directory doesn\'t exist',
|
||||
'detail' => 'This directory is necessary for the installation. Please create the folder.',
|
||||
];
|
||||
} elseif (! is_writable($path)) {
|
||||
$this->errors[] = [
|
||||
});
|
||||
}
|
||||
|
||||
private function getNonWritablePaths(): Collection
|
||||
{
|
||||
return (new Collection($this->paths))
|
||||
->filter(function ($path) {
|
||||
return file_exists($path) && ! is_writable($path);
|
||||
})->map(function ($path) {
|
||||
return [
|
||||
'message' => 'The '.$this->getAbsolutePath($path).' directory is not writable.',
|
||||
'detail' => 'Please chmod this directory'.($path !== public_path() ? ' and its contents' : '').' to 0775.'
|
||||
];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function getAbsolutePath($path)
|
||||
|
17
src/Install/ReversibleStep.php
Normal file
17
src/Install/ReversibleStep.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?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;
|
||||
|
||||
interface ReversibleStep
|
||||
{
|
||||
public function revert();
|
||||
}
|
33
src/Install/Step.php
Normal file
33
src/Install/Step.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?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;
|
||||
|
||||
interface Step
|
||||
{
|
||||
/**
|
||||
* A one-line status message summarizing what's happening in this step.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMessage();
|
||||
|
||||
/**
|
||||
* Do the work that constitutes this step.
|
||||
*
|
||||
* This method should raise a `StepFailed` exception whenever something goes
|
||||
* wrong that should result in the entire installation being reverted.
|
||||
*
|
||||
* @return void
|
||||
* @throws StepFailed
|
||||
*/
|
||||
public function run();
|
||||
}
|
18
src/Install/StepFailed.php
Normal file
18
src/Install/StepFailed.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?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 Exception;
|
||||
|
||||
class StepFailed extends Exception
|
||||
{
|
||||
}
|
62
src/Install/Steps/ConnectToDatabase.php
Normal file
62
src/Install/Steps/ConnectToDatabase.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?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\Steps;
|
||||
|
||||
use Flarum\Install\DatabaseConfig;
|
||||
use Flarum\Install\Step;
|
||||
use Illuminate\Database\Connectors\MySqlConnector;
|
||||
use Illuminate\Database\MySqlConnection;
|
||||
use RangeException;
|
||||
|
||||
class ConnectToDatabase implements Step
|
||||
{
|
||||
private $dbConfig;
|
||||
private $store;
|
||||
|
||||
public function __construct(DatabaseConfig $dbConfig, callable $store)
|
||||
{
|
||||
$this->dbConfig = $dbConfig;
|
||||
$this->store = $store;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return 'Connecting to database';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$config = $this->dbConfig->toArray();
|
||||
$pdo = (new MySqlConnector)->connect($config);
|
||||
|
||||
$version = $pdo->query('SELECT VERSION()')->fetchColumn();
|
||||
|
||||
if (str_contains($version, 'MariaDB')) {
|
||||
if (version_compare($version, '10.0.5', '<')) {
|
||||
throw new RangeException('MariaDB version too low. You need at least MariaDB 10.0.5');
|
||||
}
|
||||
} else {
|
||||
if (version_compare($version, '5.6.0', '<')) {
|
||||
throw new RangeException('MySQL version too low. You need at least MySQL 5.6.');
|
||||
}
|
||||
}
|
||||
|
||||
($this->store)(
|
||||
new MySqlConnection(
|
||||
$pdo,
|
||||
$config['database'],
|
||||
$config['prefix'],
|
||||
$config
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
53
src/Install/Steps/CreateAdminUser.php
Normal file
53
src/Install/Steps/CreateAdminUser.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\Steps;
|
||||
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\Install\AdminUser;
|
||||
use Flarum\Install\Step;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
|
||||
class CreateAdminUser implements Step
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* @var AdminUser
|
||||
*/
|
||||
private $admin;
|
||||
|
||||
public function __construct(ConnectionInterface $database, AdminUser $admin)
|
||||
{
|
||||
$this->database = $database;
|
||||
$this->admin = $admin;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return 'Creating admin user '.$this->admin->getUsername();
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$uid = $this->database->table('users')->insertGetId(
|
||||
$this->admin->getAttributes()
|
||||
);
|
||||
|
||||
$this->database->table('group_user')->insert([
|
||||
'user_id' => $uid,
|
||||
'group_id' => Group::ADMINISTRATOR_ID,
|
||||
]);
|
||||
}
|
||||
}
|
122
src/Install/Steps/EnableBundledExtensions.php
Normal file
122
src/Install/Steps/EnableBundledExtensions.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?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\Steps;
|
||||
|
||||
use Flarum\Database\DatabaseMigrationRepository;
|
||||
use Flarum\Database\Migrator;
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Install\Step;
|
||||
use Flarum\Settings\DatabaseSettingsRepository;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Flysystem\Adapter\Local;
|
||||
use League\Flysystem\Filesystem;
|
||||
|
||||
class EnableBundledExtensions implements Step
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $basePath;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $assetPath;
|
||||
|
||||
public function __construct(ConnectionInterface $database, $basePath, $assetPath)
|
||||
{
|
||||
$this->database = $database;
|
||||
$this->basePath = $basePath;
|
||||
$this->assetPath = $assetPath;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return 'Enabling bundled extensions';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$extensions = $this->loadExtensions();
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
$extension->migrate($this->getMigrator());
|
||||
$extension->copyAssetsTo(
|
||||
new Filesystem(new Local($this->assetPath))
|
||||
);
|
||||
}
|
||||
|
||||
(new DatabaseSettingsRepository($this->database))->set(
|
||||
'extensions_enabled',
|
||||
$extensions->keys()->toJson()
|
||||
);
|
||||
}
|
||||
|
||||
const EXTENSION_WHITELIST = [
|
||||
'flarum-approval',
|
||||
'flarum-bbcode',
|
||||
'flarum-emoji',
|
||||
'flarum-lang-english',
|
||||
'flarum-flags',
|
||||
'flarum-likes',
|
||||
'flarum-lock',
|
||||
'flarum-markdown',
|
||||
'flarum-mentions',
|
||||
'flarum-statistics',
|
||||
'flarum-sticky',
|
||||
'flarum-subscriptions',
|
||||
'flarum-suspend',
|
||||
'flarum-tags',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
private function loadExtensions()
|
||||
{
|
||||
$json = file_get_contents("$this->basePath/vendor/composer/installed.json");
|
||||
|
||||
return (new Collection(json_decode($json, true)))
|
||||
->filter(function ($package) {
|
||||
return Arr::get($package, 'type') == 'flarum-extension';
|
||||
})->filter(function ($package) {
|
||||
return ! empty(Arr::get($package, 'name'));
|
||||
})->map(function ($package) {
|
||||
$extension = new Extension($this->basePath.'/vendor/'.Arr::get($package, 'name'), $package);
|
||||
$extension->setVersion(Arr::get($package, 'version'));
|
||||
|
||||
return $extension;
|
||||
})->filter(function (Extension $extension) {
|
||||
return in_array($extension->getId(), self::EXTENSION_WHITELIST);
|
||||
})->sortBy(function (Extension $extension) {
|
||||
return $extension->composerJsonAttribute('extra.flarum-extension.title');
|
||||
})->mapWithKeys(function (Extension $extension) {
|
||||
return [$extension->getId() => $extension];
|
||||
});
|
||||
}
|
||||
|
||||
private function getMigrator()
|
||||
{
|
||||
return $this->migrator = $this->migrator ?? new Migrator(
|
||||
new DatabaseMigrationRepository($this->database, 'migrations'),
|
||||
$this->database,
|
||||
new \Illuminate\Filesystem\Filesystem
|
||||
);
|
||||
}
|
||||
}
|
58
src/Install/Steps/PublishAssets.php
Normal file
58
src/Install/Steps/PublishAssets.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\Steps;
|
||||
|
||||
use Flarum\Install\ReversibleStep;
|
||||
use Flarum\Install\Step;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
class PublishAssets implements Step, ReversibleStep
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $basePath;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $assetPath;
|
||||
|
||||
public function __construct($basePath, $assetPath)
|
||||
{
|
||||
$this->basePath = $basePath;
|
||||
$this->assetPath = $assetPath;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return 'Publishing all assets';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
(new Filesystem)->copyDirectory(
|
||||
"$this->basePath/vendor/components/font-awesome/webfonts",
|
||||
$this->targetPath()
|
||||
);
|
||||
}
|
||||
|
||||
public function revert()
|
||||
{
|
||||
(new Filesystem)->deleteDirectory($this->targetPath());
|
||||
}
|
||||
|
||||
private function targetPath()
|
||||
{
|
||||
return "$this->assetPath/fonts";
|
||||
}
|
||||
}
|
60
src/Install/Steps/RunMigrations.php
Normal file
60
src/Install/Steps/RunMigrations.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?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\Steps;
|
||||
|
||||
use Flarum\Database\DatabaseMigrationRepository;
|
||||
use Flarum\Database\Migrator;
|
||||
use Flarum\Install\Step;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
class RunMigrations implements Step
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
public function __construct(ConnectionInterface $database, $path)
|
||||
{
|
||||
$this->database = $database;
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return 'Running migrations';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$migrator = $this->getMigrator();
|
||||
|
||||
$migrator->getRepository()->createRepository();
|
||||
$migrator->run($this->path);
|
||||
}
|
||||
|
||||
private function getMigrator()
|
||||
{
|
||||
$repository = new DatabaseMigrationRepository(
|
||||
$this->database, 'migrations'
|
||||
);
|
||||
$files = new Filesystem;
|
||||
|
||||
return new Migrator($repository, $this->database, $files);
|
||||
}
|
||||
}
|
72
src/Install/Steps/StoreConfig.php
Normal file
72
src/Install/Steps/StoreConfig.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?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\Steps;
|
||||
|
||||
use Flarum\Install\DatabaseConfig;
|
||||
use Flarum\Install\ReversibleStep;
|
||||
use Flarum\Install\Step;
|
||||
|
||||
class StoreConfig implements Step, ReversibleStep
|
||||
{
|
||||
private $debugMode;
|
||||
|
||||
private $dbConfig;
|
||||
|
||||
private $baseUrl;
|
||||
|
||||
private $configFile;
|
||||
|
||||
public function __construct($debugMode, DatabaseConfig $dbConfig, $baseUrl, $configFile)
|
||||
{
|
||||
$this->debugMode = $debugMode;
|
||||
$this->dbConfig = $dbConfig;
|
||||
$this->baseUrl = $baseUrl;
|
||||
|
||||
$this->configFile = $configFile;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return 'Writing config file';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
file_put_contents(
|
||||
$this->configFile,
|
||||
'<?php return '.var_export($this->buildConfig(), true).';'
|
||||
);
|
||||
}
|
||||
|
||||
public function revert()
|
||||
{
|
||||
@unlink($this->configFile);
|
||||
}
|
||||
|
||||
private function buildConfig()
|
||||
{
|
||||
return [
|
||||
'debug' => $this->debugMode,
|
||||
'database' => $this->dbConfig->toArray(),
|
||||
'url' => $this->baseUrl,
|
||||
'paths' => $this->getPathsConfig(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getPathsConfig()
|
||||
{
|
||||
return [
|
||||
'api' => 'api',
|
||||
'admin' => 'admin',
|
||||
];
|
||||
}
|
||||
}
|
80
src/Install/Steps/WriteSettings.php
Normal file
80
src/Install/Steps/WriteSettings.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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\Steps;
|
||||
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Install\Step;
|
||||
use Flarum\Settings\DatabaseSettingsRepository;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
|
||||
class WriteSettings implements Step
|
||||
{
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
private $database;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $custom;
|
||||
|
||||
public function __construct(ConnectionInterface $database, array $custom)
|
||||
{
|
||||
$this->database = $database;
|
||||
$this->custom = $custom;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return 'Writing default settings';
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$repo = new DatabaseSettingsRepository($this->database);
|
||||
|
||||
$repo->set('version', Application::VERSION);
|
||||
|
||||
foreach ($this->getSettings() as $key => $value) {
|
||||
$repo->set($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
private function getSettings()
|
||||
{
|
||||
return $this->custom + $this->getDefaults();
|
||||
}
|
||||
|
||||
private function getDefaults()
|
||||
{
|
||||
return [
|
||||
'allow_post_editing' => 'reply',
|
||||
'allow_renaming' => '10',
|
||||
'allow_sign_up' => '1',
|
||||
'custom_less' => '',
|
||||
'default_locale' => 'en',
|
||||
'default_route' => '/all',
|
||||
'extensions_enabled' => '[]',
|
||||
'forum_title' => 'A new Flarum forum',
|
||||
'forum_description' => '',
|
||||
'mail_driver' => 'mail',
|
||||
'mail_from' => 'noreply@localhost',
|
||||
'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 Flarum',
|
||||
];
|
||||
}
|
||||
}
|
18
src/Install/ValidationFailed.php
Normal file
18
src/Install/ValidationFailed.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?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 Exception;
|
||||
|
||||
class ValidationFailed extends Exception
|
||||
{
|
||||
}
|
@ -26,14 +26,8 @@ class LocaleServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot(Dispatcher $events)
|
||||
{
|
||||
$locales = $this->app->make('flarum.locales');
|
||||
|
||||
$locales->addLocale($this->getDefaultLocale(), 'Default');
|
||||
|
||||
$events->dispatch(new ConfigureLocales($locales));
|
||||
|
||||
$events->listen(ClearingCache::class, function () use ($locales) {
|
||||
$locales->clearCache();
|
||||
$events->listen(ClearingCache::class, function () {
|
||||
$this->app->make('flarum.locales')->clearCache();
|
||||
});
|
||||
}
|
||||
|
||||
@ -43,10 +37,16 @@ class LocaleServiceProvider extends AbstractServiceProvider
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton(LocaleManager::class, function () {
|
||||
return new LocaleManager(
|
||||
$locales = new LocaleManager(
|
||||
$this->app->make('translator'),
|
||||
$this->getCacheDir()
|
||||
);
|
||||
|
||||
$locales->addLocale($this->getDefaultLocale(), 'Default');
|
||||
|
||||
event(new ConfigureLocales($locales));
|
||||
|
||||
return $locales;
|
||||
});
|
||||
|
||||
$this->app->alias(LocaleManager::class, 'flarum.locales');
|
||||
|
@ -18,12 +18,12 @@ use Flarum\Post\Event\Posted;
|
||||
use Flarum\Post\Event\Restored;
|
||||
use Flarum\Post\Event\Revised;
|
||||
use Flarum\User\User;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* A standard comment in a discussion.
|
||||
*
|
||||
* @property string $parsed_content
|
||||
* @property string $content_html
|
||||
*/
|
||||
class CommentPost extends Post
|
||||
{
|
||||
@ -166,11 +166,12 @@ class CommentPost extends Post
|
||||
/**
|
||||
* Get the content rendered as HTML.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return string
|
||||
*/
|
||||
public function getContentHtmlAttribute()
|
||||
public function formatContent(ServerRequestInterface $request)
|
||||
{
|
||||
return static::$formatter->render($this->attributes['content'], $this);
|
||||
return static::$formatter->render($this->attributes['content'], $this, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,14 +26,14 @@ class PostServiceProvider extends AbstractServiceProvider
|
||||
$this->registerPostTypes();
|
||||
|
||||
$events = $this->app->make('events');
|
||||
$events->subscribe('Flarum\Post\PostPolicy');
|
||||
$events->subscribe(PostPolicy::class);
|
||||
}
|
||||
|
||||
public function registerPostTypes()
|
||||
{
|
||||
$models = [
|
||||
'Flarum\Post\CommentPost',
|
||||
'Flarum\Post\DiscussionRenamedPost'
|
||||
CommentPost::class,
|
||||
DiscussionRenamedPost::class
|
||||
];
|
||||
|
||||
$this->app->make('events')->fire(
|
||||
|
@ -11,9 +11,19 @@
|
||||
|
||||
namespace Flarum\Search;
|
||||
|
||||
use Flarum\Discussion\Search\DiscussionSearcher;
|
||||
use Flarum\Discussion\Search\Gambit\AuthorGambit;
|
||||
use Flarum\Discussion\Search\Gambit\CreatedGambit;
|
||||
use Flarum\Discussion\Search\Gambit\FulltextGambit as DiscussionFulltextGambit;
|
||||
use Flarum\Discussion\Search\Gambit\HiddenGambit;
|
||||
use Flarum\Discussion\Search\Gambit\UnreadGambit;
|
||||
use Flarum\Event\ConfigureDiscussionGambits;
|
||||
use Flarum\Event\ConfigureUserGambits;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\User\Search\Gambit\EmailGambit;
|
||||
use Flarum\User\Search\Gambit\FulltextGambit as UserFulltextGambit;
|
||||
use Flarum\User\Search\Gambit\GroupGambit;
|
||||
use Flarum\User\Search\UserSearcher;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class SearchServiceProvider extends AbstractServiceProvider
|
||||
@ -25,11 +35,6 @@ class SearchServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind(
|
||||
'Flarum\Discussion\Search\Fulltext\DriverInterface',
|
||||
'Flarum\Discussion\Search\Fulltext\MySqlFulltextDriver'
|
||||
);
|
||||
|
||||
$this->registerDiscussionGambits();
|
||||
|
||||
$this->registerUserGambits();
|
||||
@ -37,14 +42,14 @@ class SearchServiceProvider extends AbstractServiceProvider
|
||||
|
||||
public function registerUserGambits()
|
||||
{
|
||||
$this->app->when('Flarum\User\Search\UserSearcher')
|
||||
->needs('Flarum\Search\GambitManager')
|
||||
$this->app->when(UserSearcher::class)
|
||||
->needs(GambitManager::class)
|
||||
->give(function (Container $app) {
|
||||
$gambits = new GambitManager($app);
|
||||
|
||||
$gambits->setFulltextGambit('Flarum\User\Search\Gambit\FulltextGambit');
|
||||
$gambits->add('Flarum\User\Search\Gambit\EmailGambit');
|
||||
$gambits->add('Flarum\User\Search\Gambit\GroupGambit');
|
||||
$gambits->setFulltextGambit(UserFulltextGambit::class);
|
||||
$gambits->add(EmailGambit::class);
|
||||
$gambits->add(GroupGambit::class);
|
||||
|
||||
$app->make('events')->fire(
|
||||
new ConfigureUserGambits($gambits)
|
||||
@ -56,16 +61,16 @@ class SearchServiceProvider extends AbstractServiceProvider
|
||||
|
||||
public function registerDiscussionGambits()
|
||||
{
|
||||
$this->app->when('Flarum\Discussion\Search\DiscussionSearcher')
|
||||
->needs('Flarum\Search\GambitManager')
|
||||
$this->app->when(DiscussionSearcher::class)
|
||||
->needs(GambitManager::class)
|
||||
->give(function (Container $app) {
|
||||
$gambits = new GambitManager($app);
|
||||
|
||||
$gambits->setFulltextGambit('Flarum\Discussion\Search\Gambit\FulltextGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\AuthorGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\CreatedGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\HiddenGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\UnreadGambit');
|
||||
$gambits->setFulltextGambit(DiscussionFulltextGambit::class);
|
||||
$gambits->add(AuthorGambit::class);
|
||||
$gambits->add(CreatedGambit::class);
|
||||
$gambits->add(HiddenGambit::class);
|
||||
$gambits->add(UnreadGambit::class);
|
||||
|
||||
$app->make('events')->fire(
|
||||
new ConfigureDiscussionGambits($gambits)
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Flarum\Settings;
|
||||
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
|
||||
class SettingsServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
@ -23,7 +24,7 @@ class SettingsServiceProvider extends AbstractServiceProvider
|
||||
$this->app->singleton(SettingsRepositoryInterface::class, function () {
|
||||
return new MemoryCacheSettingsRepository(
|
||||
new DatabaseSettingsRepository(
|
||||
$this->app->make('Illuminate\Database\ConnectionInterface')
|
||||
$this->app->make(ConnectionInterface::class)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
@ -23,10 +23,11 @@ class UpdateServiceProvider extends AbstractServiceProvider
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('flarum.update.routes', function () {
|
||||
return new RouteCollection;
|
||||
});
|
||||
$routes = new RouteCollection;
|
||||
$this->populateRoutes($routes);
|
||||
|
||||
$this->loadViewsFrom(__DIR__.'/../../views/install', 'flarum.update');
|
||||
return $routes;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,7 +35,7 @@ class UpdateServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->populateRoutes($this->app->make('flarum.update.routes'));
|
||||
$this->loadViewsFrom(__DIR__.'/../../views/install', 'flarum.update');
|
||||
}
|
||||
|
||||
/**
|
||||
|
105
src/User/AccountActivationMailer.php
Normal file
105
src/User/AccountActivationMailer.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?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\User;
|
||||
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\Event\Registered;
|
||||
use Illuminate\Contracts\Mail\Mailer;
|
||||
use Illuminate\Contracts\Translation\Translator;
|
||||
use Illuminate\Mail\Message;
|
||||
|
||||
class AccountActivationMailer
|
||||
{
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var Mailer
|
||||
*/
|
||||
protected $mailer;
|
||||
|
||||
/**
|
||||
* @var UrlGenerator
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @var Translator
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @param \Flarum\Settings\SettingsRepositoryInterface $settings
|
||||
* @param Mailer $mailer
|
||||
* @param UrlGenerator $url
|
||||
* @param Translator $translator
|
||||
*/
|
||||
public function __construct(SettingsRepositoryInterface $settings, Mailer $mailer, UrlGenerator $url, Translator $translator)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
$this->mailer = $mailer;
|
||||
$this->url = $url;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function handle(Registered $event)
|
||||
{
|
||||
$user = $event->user;
|
||||
|
||||
if ($user->is_email_confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->getEmailData($user, $user->email);
|
||||
|
||||
$body = $this->translator->trans('core.email.activate_account.body', $data);
|
||||
|
||||
$this->mailer->raw($body, function (Message $message) use ($user, $data) {
|
||||
$message->to($user->email);
|
||||
$message->subject('['.$data['{forum}'].'] '.$this->translator->trans('core.email.activate_account.subject'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param string $email
|
||||
* @return EmailToken
|
||||
*/
|
||||
protected function generateToken(User $user, $email)
|
||||
{
|
||||
$token = EmailToken::generate($email, $user->id);
|
||||
$token->save();
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data that should be made available to email templates.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $email
|
||||
* @return array
|
||||
*/
|
||||
protected function getEmailData(User $user, $email)
|
||||
{
|
||||
$token = $this->generateToken($user, $email);
|
||||
|
||||
return [
|
||||
'{username}' => $user->display_name,
|
||||
'{url}' => $this->url->to('forum')->route('confirmEmail', ['token' => $token->token]),
|
||||
'{forum}' => $this->settings->get('forum_title')
|
||||
];
|
||||
}
|
||||
}
|
@ -14,8 +14,6 @@ namespace Flarum\User;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\Event\EmailChangeRequested;
|
||||
use Flarum\User\Event\Registered;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Mail\Mailer;
|
||||
use Illuminate\Contracts\Translation\Translator;
|
||||
use Illuminate\Mail\Message;
|
||||
@ -42,12 +40,6 @@ class EmailConfirmationMailer
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @param \Flarum\Settings\SettingsRepositoryInterface $settings
|
||||
* @param Mailer $mailer
|
||||
* @param UrlGenerator $url
|
||||
* @param Translator $translator
|
||||
*/
|
||||
public function __construct(SettingsRepositoryInterface $settings, Mailer $mailer, UrlGenerator $url, Translator $translator)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
@ -56,40 +48,7 @@ class EmailConfirmationMailer
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Registered::class, [$this, 'whenUserWasRegistered']);
|
||||
$events->listen(EmailChangeRequested::class, [$this, 'whenUserEmailChangeWasRequested']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\User\Event\Registered $event
|
||||
*/
|
||||
public function whenUserWasRegistered(Registered $event)
|
||||
{
|
||||
$user = $event->user;
|
||||
|
||||
if ($user->is_email_confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->getEmailData($user, $user->email);
|
||||
|
||||
$body = $this->translator->trans('core.email.activate_account.body', $data);
|
||||
|
||||
$this->mailer->raw($body, function (Message $message) use ($user, $data) {
|
||||
$message->to($user->email);
|
||||
$message->subject('['.$data['{forum}'].'] '.$this->translator->trans('core.email.activate_account.subject'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\User\Event\EmailChangeRequested $event
|
||||
*/
|
||||
public function whenUserEmailChangeWasRequested(EmailChangeRequested $event)
|
||||
public function handle(EmailChangeRequested $event)
|
||||
{
|
||||
$email = $event->email;
|
||||
$data = $this->getEmailData($event->user, $email);
|
||||
|
@ -400,4 +400,16 @@ class Gate implements GateContract
|
||||
{
|
||||
// TODO: Implement abilities() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw result from the authorization callback.
|
||||
*
|
||||
* @param string $ability
|
||||
* @param array|mixed $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function raw($ability, $arguments = [])
|
||||
{
|
||||
// TODO: Implement raw() method.
|
||||
}
|
||||
}
|
||||
|
@ -14,24 +14,15 @@ namespace Flarum\User;
|
||||
use Flarum\Group\Group;
|
||||
use Flarum\User\Event\Saving;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class SelfDemotionGuard
|
||||
{
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Saving::class, [$this, 'whenUserWillBeSaved']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent an admin from removing their admin permission via the API.
|
||||
* @param Saving $event
|
||||
* @throws PermissionDeniedException
|
||||
*/
|
||||
public function whenUserWillBeSaved(Saving $event)
|
||||
public function handle(Saving $event)
|
||||
{
|
||||
// Non-admin users pose no problem
|
||||
if (! $event->actor->isAdmin()) {
|
||||
|
@ -753,7 +753,7 @@ class User extends AbstractModel
|
||||
*/
|
||||
public function refreshCommentCount()
|
||||
{
|
||||
$this->comment_count = $this->posts()->count();
|
||||
$this->comment_count = $this->posts()->where('type', 'comment')->count();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -14,7 +14,13 @@ namespace Flarum\User;
|
||||
use Flarum\Event\ConfigureUserPreferences;
|
||||
use Flarum\Event\GetPermission;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\User\Event\EmailChangeRequested;
|
||||
use Flarum\User\Event\Registered;
|
||||
use Flarum\User\Event\Saving;
|
||||
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Filesystem\Factory;
|
||||
use League\Flysystem\FilesystemInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class UserServiceProvider extends AbstractServiceProvider
|
||||
@ -36,18 +42,18 @@ class UserServiceProvider extends AbstractServiceProvider
|
||||
});
|
||||
});
|
||||
|
||||
$this->app->alias('flarum.gate', 'Illuminate\Contracts\Auth\Access\Gate');
|
||||
$this->app->alias('flarum.gate', GateContract::class);
|
||||
$this->app->alias('flarum.gate', Gate::class);
|
||||
}
|
||||
|
||||
protected function registerAvatarsFilesystem()
|
||||
{
|
||||
$avatarsFilesystem = function (Container $app) {
|
||||
return $app->make('Illuminate\Contracts\Filesystem\Factory')->disk('flarum-avatars')->getDriver();
|
||||
return $app->make(Factory::class)->disk('flarum-avatars')->getDriver();
|
||||
};
|
||||
|
||||
$this->app->when(AvatarUploader::class)
|
||||
->needs('League\Flysystem\FilesystemInterface')
|
||||
->needs(FilesystemInterface::class)
|
||||
->give($avatarsFilesystem);
|
||||
}
|
||||
|
||||
@ -83,8 +89,10 @@ class UserServiceProvider extends AbstractServiceProvider
|
||||
|
||||
$events = $this->app->make('events');
|
||||
|
||||
$events->subscribe(SelfDemotionGuard::class);
|
||||
$events->subscribe(EmailConfirmationMailer::class);
|
||||
$events->listen(Saving::class, SelfDemotionGuard::class);
|
||||
$events->listen(Registered::class, AccountActivationMailer::class);
|
||||
$events->listen(EmailChangeRequested::class, EmailConfirmationMailer::class);
|
||||
|
||||
$events->subscribe(UserMetadataUpdater::class);
|
||||
$events->subscribe(UserPolicy::class);
|
||||
|
||||
|
@ -1,40 +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\Tests\Api\Controller;
|
||||
|
||||
use Flarum\Api\Controller\DeleteDiscussionController;
|
||||
use Flarum\Discussion\Discussion;
|
||||
|
||||
class DeleteDiscussionControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
protected $controller = DeleteDiscussionController::class;
|
||||
protected $discussion;
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->discussion = Discussion::start(__CLASS__, $this->getNormalUser());
|
||||
|
||||
$this->discussion->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function admin_can_delete()
|
||||
{
|
||||
$this->actor = $this->getAdminUser();
|
||||
|
||||
$response = $this->callWith([], ['id' => $this->discussion->id]);
|
||||
|
||||
$this->assertEquals(204, $response->getStatusCode());
|
||||
}
|
||||
}
|
@ -1,50 +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\Tests\Api\Controller;
|
||||
|
||||
use Flarum\Api\Controller\ListDiscussionsController;
|
||||
use Flarum\Discussion\Discussion;
|
||||
|
||||
class ListDiscussionControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
protected $controller = ListDiscussionsController::class;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function shows_index_for_guest()
|
||||
{
|
||||
$response = $this->callWith();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$data = json_decode($response->getBody()->getContents(), true);
|
||||
|
||||
$this->assertEquals(Discussion::count(), count($data['data']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function can_search_for_author()
|
||||
{
|
||||
$user = $this->getNormalUser();
|
||||
|
||||
$response = $this->callWith([], [
|
||||
'filter' => [
|
||||
'q' => 'author:'.$user->username.' foo'
|
||||
],
|
||||
'include' => 'mostRelevantPost'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
}
|
@ -1,86 +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\Tests\Api\Controller;
|
||||
|
||||
use Flarum\Api\Controller\ShowDiscussionController;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Tests\Test\Concerns\ManagesContent;
|
||||
|
||||
class ShowDiscussionControllerTest extends ApiControllerTestCase
|
||||
{
|
||||
use ManagesContent;
|
||||
|
||||
protected $controller = ShowDiscussionController::class;
|
||||
|
||||
/**
|
||||
* @var Discussion
|
||||
*/
|
||||
protected $discussion;
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->discussion = Discussion::start(__CLASS__, $this->getNormalUser());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function author_can_see_discussion()
|
||||
{
|
||||
$this->discussion->save();
|
||||
|
||||
$this->actor = $this->getNormalUser();
|
||||
|
||||
$response = $this->callWith([], ['id' => $this->discussion->id]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function guest_cannot_see_empty_discussion()
|
||||
{
|
||||
$this->discussion->save();
|
||||
|
||||
$response = $this->callWith([], ['id' => $this->discussion->id]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function guest_can_see_discussion()
|
||||
{
|
||||
$this->discussion->save();
|
||||
|
||||
$this->addPostByNormalUser();
|
||||
|
||||
$response = $this->callWith([], ['id' => $this->discussion->id]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function guests_cannot_see_private_discussion()
|
||||
{
|
||||
$this->discussion->is_private = true;
|
||||
$this->discussion->save();
|
||||
|
||||
$this->callWith([], ['id' => $this->discussion->id]);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user