Merge branch 'master' into 1236-database-changes

This commit is contained in:
Toby Zerner 2018-08-24 21:07:00 +09:30
commit f2f9c3c21c
28 changed files with 1002 additions and 653 deletions

View File

@ -44,6 +44,9 @@
"league/flysystem": "^1.0.11",
"league/oauth2-client": "~1.0",
"matthiasmullie/minify": "^1.3",
"middlewares/base-path": "^1.1",
"middlewares/base-path-router": "^0.2.1",
"middlewares/request-handler": "^1.2",
"monolog/monolog": "^1.16.0",
"nikic/fast-route": "^0.6",
"oyejorge/less.php": "^1.7",
@ -58,6 +61,7 @@
"symfony/yaml": "^3.3",
"tobscure/json-api": "^0.3.0",
"zendframework/zend-diactoros": "^1.8.4",
"zendframework/zend-httphandlerrunner": "^1.0",
"zendframework/zend-stratigility": "^3.0"
},
"require-dev": {

View File

@ -17,7 +17,8 @@ use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Frontend\RecompileFrontendAssets;
use Flarum\Http\Middleware\AuthenticateWithSession;
use Flarum\Http\Middleware\DispatchRoute;
use Flarum\Http\Middleware\HandleErrors;
use Flarum\Http\Middleware\HandleErrorsWithView;
use Flarum\Http\Middleware\HandleErrorsWithWhoops;
use Flarum\Http\Middleware\ParseJsonBody;
use Flarum\Http\Middleware\RememberFromCookie;
use Flarum\Http\Middleware\SetLocale;
@ -46,8 +47,11 @@ class AdminServiceProvider extends AbstractServiceProvider
$pipe = new MiddlewarePipe;
// All requests should first be piped through our global error handler
$debugMode = ! $app->isUpToDate() || $app->inDebugMode();
$pipe->pipe($app->make(HandleErrors::class, ['debug' => $debugMode]));
if ($app->inDebugMode()) {
$pipe->pipe($app->make(HandleErrorsWithWhoops::class));
} else {
$pipe->pipe($app->make(HandleErrorsWithView::class));
}
$pipe->pipe($app->make(ParseJsonBody::class));
$pipe->pipe($app->make(StartSession::class));
@ -58,7 +62,7 @@ class AdminServiceProvider extends AbstractServiceProvider
event(new ConfigureMiddleware($pipe, 'admin'));
$pipe->pipe($app->make(DispatchRoute::class, ['routes' => $app->make('flarum.admin.routes')]));
$pipe->pipe(new DispatchRoute($app->make('flarum.admin.routes')));
return $pipe;
});

View File

@ -66,7 +66,7 @@ class ApiServiceProvider extends AbstractServiceProvider
event(new ConfigureMiddleware($pipe, 'api'));
$pipe->pipe($app->make(DispatchRoute::class, ['routes' => $app->make('flarum.api.routes')]));
$pipe->pipe(new DispatchRoute($app->make('flarum.api.routes')));
return $pipe;
});

View File

@ -12,17 +12,27 @@
namespace Flarum\Bus;
use Flarum\Foundation\AbstractServiceProvider;
use Illuminate\Contracts\Bus\Dispatcher as BusContract;
use Illuminate\Bus\Dispatcher as BaseDispatcher;
use Illuminate\Contracts\Bus\Dispatcher as DispatcherContract;
use Illuminate\Contracts\Bus\QueueingDispatcher as QueueingDispatcherContract;
use Illuminate\Contracts\Queue\Factory as QueueFactoryContract;
class BusServiceProvider extends AbstractServiceProvider
{
public function register()
{
$this->app->bind(BusContract::class, function ($app) {
$this->app->bind(BaseDispatcher::class, function ($app) {
return new Dispatcher($app, function ($connection = null) use ($app) {
return $app[QueueFactoryContract::class]->connection($connection);
});
});
$this->app->alias(
BaseDispatcher::class, DispatcherContract::class
);
$this->app->alias(
BaseDispatcher::class, QueueingDispatcherContract::class
);
}
}

View File

@ -12,74 +12,37 @@
namespace Flarum\Console;
use Flarum\Console\Event\Configuring;
use Flarum\Database\Console\GenerateMigrationCommand;
use Flarum\Database\Console\MigrateCommand;
use Flarum\Database\Console\ResetCommand;
use Flarum\Foundation\Application;
use Flarum\Foundation\Console\CacheClearCommand;
use Flarum\Foundation\Console\InfoCommand;
use Flarum\Foundation\Site;
use Flarum\Install\Console\InstallCommand;
use Flarum\Install\InstallServiceProvider;
use Illuminate\Contracts\Events\Dispatcher;
use Symfony\Component\Console\Application as ConsoleApplication;
class Server
{
/**
* @param Site $site
* @return Server
*/
public static function fromSite(Site $site)
{
return new static($site->boot());
}
protected $commands;
public function __construct(Application $app)
public function __construct(array $commands)
{
$this->app = $app;
$this->commands = $commands;
}
public function listen()
{
$console = $this->getConsoleApplication();
$console = new ConsoleApplication('Flarum', Application::VERSION);
foreach ($this->commands as $command) {
$console->add($command);
}
$this->extend($console);
exit($console->run());
}
/**
* @return ConsoleApplication
*/
protected function getConsoleApplication()
private function extend(ConsoleApplication $console)
{
$console = new ConsoleApplication('Flarum', $this->app->version());
$app = Application::getInstance();
$this->app->register(InstallServiceProvider::class);
$commands = [
InstallCommand::class,
MigrateCommand::class,
ResetCommand::class,
GenerateMigrationCommand::class,
];
if ($this->app->isInstalled()) {
$commands = array_merge($commands, [
InfoCommand::class,
CacheClearCommand::class
]);
}
foreach ($commands as $command) {
$console->add($this->app->make(
$command,
['config' => $this->app->isInstalled() ? $this->app->make('flarum.config') : []]
));
}
$events = $this->app->make(Dispatcher::class);
$events->fire(new Configuring($this->app, $console));
return $console;
$events = $app->make(Dispatcher::class);
$events->fire(new Configuring($app, $console));
}
}

View File

@ -50,9 +50,7 @@ class DatabaseServiceProvider extends AbstractServiceProvider
*/
public function boot()
{
if ($this->app->isInstalled()) {
AbstractModel::setConnectionResolver($this->app->make('Illuminate\Database\ConnectionResolverInterface'));
AbstractModel::setEventDispatcher($this->app->make('events'));
}
AbstractModel::setConnectionResolver($this->app->make('Illuminate\Database\ConnectionResolverInterface'));
AbstractModel::setEventDispatcher($this->app->make('events'));
}
}

View File

@ -17,7 +17,8 @@ use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Http\Middleware\AuthenticateWithSession;
use Flarum\Http\Middleware\CollectGarbage;
use Flarum\Http\Middleware\DispatchRoute;
use Flarum\Http\Middleware\HandleErrors;
use Flarum\Http\Middleware\HandleErrorsWithView;
use Flarum\Http\Middleware\HandleErrorsWithWhoops;
use Flarum\Http\Middleware\ParseJsonBody;
use Flarum\Http\Middleware\RememberFromCookie;
use Flarum\Http\Middleware\SetLocale;
@ -49,8 +50,11 @@ class ForumServiceProvider extends AbstractServiceProvider
$pipe = new MiddlewarePipe;
// All requests should first be piped through our global error handler
$debugMode = ! $app->isUpToDate() || $app->inDebugMode();
$pipe->pipe($app->make(HandleErrors::class, ['debug' => $debugMode]));
if ($app->inDebugMode()) {
$pipe->pipe($app->make(HandleErrorsWithWhoops::class));
} else {
$pipe->pipe($app->make(HandleErrorsWithView::class));
}
$pipe->pipe($app->make(ParseJsonBody::class));
$pipe->pipe($app->make(CollectGarbage::class));
@ -62,7 +66,7 @@ class ForumServiceProvider extends AbstractServiceProvider
event(new ConfigureMiddleware($pipe, 'forum'));
$pipe->pipe($app->make(DispatchRoute::class, ['routes' => $app->make('flarum.forum.routes')]));
$pipe->pipe(new DispatchRoute($app->make('flarum.forum.routes')));
return $pipe;
});

View File

@ -0,0 +1,25 @@
<?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;
interface AppInterface
{
/**
* @return \Psr\Http\Server\RequestHandlerInterface
*/
public function getRequestHandler();
/**
* @return \Symfony\Component\Console\Command\Command[]
*/
public function getConsoleCommands();
}

View File

@ -11,7 +11,6 @@
namespace Flarum\Foundation;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
use Illuminate\Events\EventServiceProvider;
@ -114,27 +113,6 @@ class Application extends Container implements ApplicationContract
}
}
/**
* Determine if Flarum has been installed.
*
* @return bool
*/
public function isInstalled(): bool
{
return $this->bound('flarum.config');
}
public function isUpToDate(): bool
{
$settings = $this->make(SettingsRepositoryInterface::class);
try {
$version = $settings->get('version');
} finally {
return isset($version) && $version === $this->version();
}
}
/**
* @param string $key
* @param mixed $default
@ -142,7 +120,7 @@ class Application extends Container implements ApplicationContract
*/
public function config($key, $default = null)
{
return $this->isInstalled() ? array_get($this->make('flarum.config'), $key, $default) : $default;
return array_get($this->make('flarum.config'), $key, $default);
}
/**
@ -152,7 +130,7 @@ class Application extends Container implements ApplicationContract
*/
public function inDebugMode()
{
return ! $this->isInstalled() || $this->config('debug');
return $this->config('debug', true);
}
/**
@ -163,7 +141,7 @@ class Application extends Container implements ApplicationContract
*/
public function url($path = null)
{
$config = $this->isInstalled() ? $this->make('flarum.config') : [];
$config = $this->make('flarum.config');
$url = array_get($config, 'url', array_get($_SERVER, 'REQUEST_URI'));
if (is_array($url)) {

View File

@ -0,0 +1,120 @@
<?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\Database\Console\GenerateMigrationCommand;
use Flarum\Database\Console\MigrateCommand;
use Flarum\Database\Console\ResetCommand;
use Flarum\Foundation\Console\CacheClearCommand;
use Flarum\Foundation\Console\InfoCommand;
use Flarum\Http\Middleware\DispatchRoute;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Container\Container;
use Middlewares\BasePath;
use Middlewares\BasePathRouter;
use Middlewares\RequestHandler;
use Zend\Stratigility\MiddlewarePipe;
class InstalledApp implements AppInterface
{
/**
* @var Container
*/
protected $container;
/**
* @var array
*/
protected $config;
public function __construct(Container $container, array $config)
{
$this->container = $container;
$this->config = $config;
}
/**
* @return \Psr\Http\Server\RequestHandlerInterface
*/
public function getRequestHandler()
{
if ($this->inMaintenanceMode()) {
return new MaintenanceModeHandler();
} elseif ($this->needsUpdate()) {
return $this->getUpdaterHandler();
}
$pipe = new MiddlewarePipe;
$pipe->pipe(new BasePath($this->basePath()));
$pipe->pipe(
new BasePathRouter([
$this->subPath('api') => 'flarum.api.middleware',
$this->subPath('admin') => 'flarum.admin.middleware',
'/' => 'flarum.forum.middleware',
])
);
$pipe->pipe(new RequestHandler($this->container));
return $pipe;
}
private function inMaintenanceMode(): bool
{
return $this->config['offline'] ?? false;
}
private function needsUpdate(): bool
{
$settings = $this->container->make(SettingsRepositoryInterface::class);
$version = $settings->get('version');
return $version !== Application::VERSION;
}
/**
* @return \Psr\Http\Server\RequestHandlerInterface
*/
public function getUpdaterHandler()
{
$pipe = new MiddlewarePipe;
$pipe->pipe(
new DispatchRoute($this->container->make('flarum.update.routes'))
);
return $pipe;
}
private function basePath(): string
{
return parse_url($this->config['url'], PHP_URL_PATH) ?: '/';
}
private function subPath($pathName): string
{
return '/'.$this->config['paths'][$pathName];
}
/**
* @return \Symfony\Component\Console\Command\Command[]
*/
public function getConsoleCommands()
{
return [
$this->container->make(GenerateMigrationCommand::class),
$this->container->make(InfoCommand::class, ['config' => $this->config]),
$this->container->make(MigrateCommand::class),
$this->container->make(ResetCommand::class),
$this->container->make(CacheClearCommand::class),
];
}
}

View File

@ -0,0 +1,221 @@
<?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\Admin\AdminServiceProvider;
use Flarum\Api\ApiServiceProvider;
use Flarum\Bus\BusServiceProvider;
use Flarum\Database\DatabaseServiceProvider;
use Flarum\Database\MigrationServiceProvider;
use Flarum\Discussion\DiscussionServiceProvider;
use Flarum\Extension\ExtensionServiceProvider;
use Flarum\Formatter\FormatterServiceProvider;
use Flarum\Forum\ForumServiceProvider;
use Flarum\Frontend\FrontendServiceProvider;
use Flarum\Group\GroupServiceProvider;
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;
use Flarum\User\UserServiceProvider;
use Illuminate\Cache\FileStore;
use Illuminate\Cache\Repository as CacheRepository;
use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Contracts\Cache\Store;
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;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
class InstalledSite implements SiteInterface
{
/**
* @var string
*/
protected $basePath;
/**
* @var string
*/
protected $publicPath;
/**
* @var string
*/
protected $storagePath;
/**
* @var array
*/
protected $config;
public function __construct($basePath, $publicPath, array $config)
{
$this->basePath = $basePath;
$this->publicPath = $publicPath;
$this->config = $config;
}
/**
* Create and boot a Flarum application instance.
*
* @return AppInterface
*/
public function bootApp(): AppInterface
{
return new InstalledApp(
$this->bootLaravel(),
$this->config
);
}
/**
* @param $storagePath
* @return static
*/
public function setStoragePath($storagePath)
{
$this->storagePath = $storagePath;
return $this;
}
protected function bootLaravel(): Application
{
$laravel = new Application($this->basePath, $this->publicPath);
if ($this->storagePath) {
$laravel->useStoragePath($this->storagePath);
}
$laravel->instance('env', 'production');
$laravel->instance('flarum.config', $this->config);
$laravel->instance('config', $config = $this->getIlluminateConfig($laravel));
$this->registerLogger($laravel);
$this->registerCache($laravel);
$laravel->register(DatabaseServiceProvider::class);
$laravel->register(MigrationServiceProvider::class);
$laravel->register(SettingsServiceProvider::class);
$laravel->register(LocaleServiceProvider::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(DiscussionServiceProvider::class);
$laravel->register(FormatterServiceProvider::class);
$laravel->register(FrontendServiceProvider::class);
$laravel->register(GroupServiceProvider::class);
$laravel->register(NotificationServiceProvider::class);
$laravel->register(PostServiceProvider::class);
$laravel->register(SearchServiceProvider::class);
$laravel->register(SessionServiceProvider::class);
$laravel->register(UserServiceProvider::class);
$laravel->register(UpdateServiceProvider::class);
$laravel->register(ApiServiceProvider::class);
$laravel->register(ForumServiceProvider::class);
$laravel->register(AdminServiceProvider::class);
$laravel->register(ExtensionServiceProvider::class);
$laravel->boot();
return $laravel;
}
/**
* @param Application $app
* @return ConfigRepository
*/
protected function getIlluminateConfig(Application $app)
{
return new ConfigRepository([
'view' => [
'paths' => [],
'compiled' => $app->storagePath().'/views',
],
'mail' => [
'driver' => 'mail',
],
'filesystems' => [
'default' => 'local',
'cloud' => 's3',
'disks' => [
'flarum-assets' => [
'driver' => 'local',
'root' => $app->publicPath().'/assets',
'url' => $app->url('assets')
],
'flarum-avatars' => [
'driver' => 'local',
'root' => $app->publicPath().'/assets/avatars'
]
]
],
'session' => [
'lifetime' => 120,
'files' => $app->storagePath().'/sessions',
'cookie' => 'session'
]
]);
}
protected function registerLogger(Application $app)
{
$logPath = $app->storagePath().'/logs/flarum.log';
$handler = new StreamHandler($logPath, Logger::INFO);
$handler->setFormatter(new LineFormatter(null, null, true, true));
$app->instance('log', new Logger($app->environment(), [$handler]));
$app->alias('log', LoggerInterface::class);
}
protected function registerCache(Application $app)
{
$app->singleton('cache.store', function ($app) {
return new CacheRepository($app->make('cache.filestore'));
});
$app->alias('cache.store', Repository::class);
$app->singleton('cache.filestore', function ($app) {
return new FileStore(new Filesystem, $app->storagePath().'/cache');
});
$app->alias('cache.filestore', Store::class);
}
}

View 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\Foundation;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Tobscure\JsonApi\Document;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Response\JsonResponse;
class MaintenanceModeHandler implements RequestHandlerInterface
{
const MESSAGE = 'Currently down for maintenance. Please come back later.';
/**
* Handle the request and return a response.
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
// Special handling for API requests: they get a proper API response
if ($this->isApiRequest($request)) {
return $this->apiResponse();
}
// By default, return a simple text message.
return new HtmlResponse(self::MESSAGE, 503);
}
private function isApiRequest(ServerRequestInterface $request): bool
{
return str_contains(
$request->getHeaderLine('Accept'),
'application/vnd.api+json'
);
}
private function apiResponse(): ResponseInterface
{
return new JsonResponse(
(new Document)->setErrors([
'status' => '503',
'title' => self::MESSAGE
]),
503,
['Content-Type' => 'application/vnd.api+json']
);
}
}

View File

@ -11,281 +11,53 @@
namespace Flarum\Foundation;
use Flarum\Admin\AdminServiceProvider;
use Flarum\Api\ApiServiceProvider;
use Flarum\Bus\BusServiceProvider as BusProvider;
use Flarum\Database\DatabaseServiceProvider;
use Flarum\Database\MigrationServiceProvider;
use Flarum\Discussion\DiscussionServiceProvider;
use Flarum\Extension\ExtensionServiceProvider;
use Flarum\Formatter\FormatterServiceProvider;
use Flarum\Forum\ForumServiceProvider;
use Flarum\Frontend\FrontendServiceProvider;
use Flarum\Group\GroupServiceProvider;
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\User\UserServiceProvider;
use Illuminate\Bus\BusServiceProvider;
use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Filesystem\FilesystemServiceProvider;
use Illuminate\Hashing\HashServiceProvider;
use Illuminate\Mail\MailServiceProvider;
use Illuminate\Validation\ValidationServiceProvider;
use Illuminate\View\ViewServiceProvider;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use InvalidArgumentException;
use RuntimeException;
// TODO: This should be an interface maybe?
class Site
{
/**
* @var Application
* @param array $paths
* @return SiteInterface
*/
protected $app;
/**
* @var string
*/
protected $basePath;
/**
* @var string
*/
protected $publicPath;
/**
* @var string
*/
protected $storagePath;
/**
* @var array
*/
protected $config;
protected $extenders = [];
public function __construct()
public static function fromPaths(array $paths)
{
$this->basePath = getcwd();
$this->publicPath = $this->basePath;
}
/**
* @return Application
*/
public function boot()
{
return $this->getApp();
}
/**
* @param $basePath
* @return static
*/
public function setBasePath($basePath)
{
$this->basePath = $basePath;
return $this;
}
/**
* @param $publicPath
* @return static
*/
public function setPublicPath($publicPath)
{
$this->publicPath = $publicPath;
return $this;
}
/**
* @param $storagePath
* @return static
*/
public function setStoragePath($storagePath)
{
$this->storagePath = $storagePath;
return $this;
}
/**
* @param array $config
* @return static
*/
public function setConfig(array $config)
{
$this->config = $config;
return $this;
}
protected function getConfig()
{
if (empty($this->config) && file_exists($file = $this->basePath.'/config.php')) {
$this->config = include $file;
if (! isset($paths['base'])) {
throw new InvalidArgumentException(
'No base path given'
);
}
return $this->config;
}
/**
* @return Application
*/
protected function getApp()
{
if ($this->app !== null) {
return $this->app;
if (! isset($paths['public'])) {
$paths['public'] = $paths['base'];
}
date_default_timezone_set('UTC');
$app = new Application($this->basePath, $this->publicPath);
if ($this->storagePath) {
$app->useStoragePath($this->storagePath);
}
$app->instance('env', 'production');
$app->instance('flarum.config', $this->getConfig());
$app->instance('config', $config = $this->getIlluminateConfig($app));
$this->registerLogger($app);
$this->registerCache($app);
$app->register(DatabaseServiceProvider::class);
$app->register(MigrationServiceProvider::class);
$app->register(SettingsServiceProvider::class);
$app->register(LocaleServiceProvider::class);
$app->register(BusServiceProvider::class);
$app->register(FilesystemServiceProvider::class);
$app->register(HashServiceProvider::class);
$app->register(MailServiceProvider::class);
$app->register(ViewServiceProvider::class);
$app->register(ValidationServiceProvider::class);
$app->register(BusProvider::class);
if ($app->isInstalled() && $app->isUpToDate()) {
$settings = $app->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'));
$app->register(DiscussionServiceProvider::class);
$app->register(FormatterServiceProvider::class);
$app->register(FrontendServiceProvider::class);
$app->register(GroupServiceProvider::class);
$app->register(NotificationServiceProvider::class);
$app->register(PostServiceProvider::class);
$app->register(SearchServiceProvider::class);
$app->register(UserServiceProvider::class);
$app->register(ApiServiceProvider::class);
$app->register(ForumServiceProvider::class);
$app->register(AdminServiceProvider::class);
foreach ($this->extenders as $extender) {
// TODO: Create extenders architecture
// $extender->apply($app);
}
$app->register(ExtensionServiceProvider::class);
}
$app->boot();
$this->app = $app;
return $app;
}
/**
* @param Application $app
* @return ConfigRepository
*/
protected function getIlluminateConfig(Application $app)
{
return new ConfigRepository([
'view' => [
'paths' => [],
'compiled' => $app->storagePath().'/views',
],
'mail' => [
'driver' => 'mail',
],
'filesystems' => [
'default' => 'local',
'cloud' => 's3',
'disks' => [
'flarum-assets' => [
'driver' => 'local',
'root' => $app->publicPath().'/assets',
'url' => $app->url('assets')
],
'flarum-avatars' => [
'driver' => 'local',
'root' => $app->publicPath().'/assets/avatars'
]
]
],
'session' => [
'lifetime' => 120,
'files' => $app->storagePath().'/sessions',
'cookie' => 'session'
]
]);
}
/**
* @param Application $app
*/
protected function registerLogger(Application $app)
{
$logger = new Logger($app->environment());
$logPath = $app->storagePath().'/logs/flarum.log';
$handler = new StreamHandler($logPath, Logger::DEBUG);
$handler->setFormatter(new LineFormatter(null, null, true, true));
$logger->pushHandler($handler);
$app->instance('log', $logger);
$app->alias('log', 'Psr\Log\LoggerInterface');
}
/**
* @param Application $app
*/
protected function registerCache(Application $app)
{
$app->singleton('cache.store', function ($app) {
return new \Illuminate\Cache\Repository($app->make('cache.filestore'));
});
$app->singleton('cache.filestore', function ($app) {
return new \Illuminate\Cache\FileStore(
new \Illuminate\Filesystem\Filesystem(),
$app->storagePath().'/cache'
if (static::hasConfigFile($paths['base'])) {
return new InstalledSite(
$paths['base'],
$paths['public'],
static::loadConfig($paths['base'])
);
});
} else {
return new UninstalledSite($paths['base'], $paths['public']);
}
}
$app->alias('cache.filestore', 'Illuminate\Contracts\Cache\Store');
$app->alias('cache.store', 'Illuminate\Contracts\Cache\Repository');
private static function hasConfigFile($basePath)
{
return file_exists("$basePath/config.php");
}
private static function loadConfig($basePath): array
{
$config = include "$basePath/config.php";
if (! is_array($config)) {
throw new RuntimeException('config.php should return an array');
}
return $config;
}
}

View File

@ -0,0 +1,22 @@
<?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;
interface SiteInterface
{
/**
* Create and boot a Flarum application instance.
*
* @return AppInterface
*/
public function bootApp(): AppInterface;
}

View File

@ -0,0 +1,135 @@
<?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\Install\Installer;
use Flarum\Install\InstallServiceProvider;
use Flarum\Locale\LocaleServiceProvider;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\Settings\UninstalledSettingsRepository;
use Flarum\User\SessionServiceProvider;
use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Filesystem\FilesystemServiceProvider;
use Illuminate\Validation\ValidationServiceProvider;
use Illuminate\View\Engines\EngineResolver;
use Illuminate\View\Engines\PhpEngine;
use Illuminate\View\FileViewFinder;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
class UninstalledSite implements SiteInterface
{
/**
* @var string
*/
protected $basePath;
/**
* @var string
*/
protected $publicPath;
/**
* @var string
*/
protected $storagePath;
public function __construct($basePath, $publicPath)
{
$this->basePath = $basePath;
$this->publicPath = $publicPath;
}
/**
* Create and boot a Flarum application instance.
*
* @return AppInterface
*/
public function bootApp(): AppInterface
{
return new Installer(
$this->bootLaravel()
);
}
private function bootLaravel(): Application
{
$laravel = new Application($this->basePath, $this->publicPath);
if ($this->storagePath) {
$laravel->useStoragePath($this->storagePath);
}
$laravel->instance('env', 'production');
$laravel->instance('flarum.config', []);
$laravel->instance('config', $config = $this->getIlluminateConfig($laravel));
$this->registerLogger($laravel);
$laravel->register(LocaleServiceProvider::class);
$laravel->register(FilesystemServiceProvider::class);
$laravel->register(SessionServiceProvider::class);
$laravel->register(ValidationServiceProvider::class);
$laravel->register(InstallServiceProvider::class);
$laravel->singleton(
SettingsRepositoryInterface::class,
UninstalledSettingsRepository::class
);
$laravel->singleton('view', function ($app) {
$engines = new EngineResolver();
$engines->register('php', function () {
return new PhpEngine();
});
$finder = new FileViewFinder($app->make('files'), []);
$dispatcher = $app->make(Dispatcher::class);
return new \Illuminate\View\Factory(
$engines, $finder, $dispatcher
);
});
$laravel->boot();
return $laravel;
}
/**
* @param Application $app
* @return ConfigRepository
*/
protected function getIlluminateConfig(Application $app)
{
return new ConfigRepository([
'session' => [
'lifetime' => 120,
'files' => $app->storagePath().'/sessions',
'cookie' => 'session'
]
]);
}
protected function registerLogger(Application $app)
{
$logPath = $app->storagePath().'/logs/flarum-installer.log';
$handler = new StreamHandler($logPath, Logger::DEBUG);
$handler->setFormatter(new LineFormatter(null, null, true, true));
$app->instance('log', new Logger('Flarum Installer', [$handler]));
$app->alias('log', LoggerInterface::class);
}
}

View File

@ -13,7 +13,6 @@ namespace Flarum\Http\Middleware;
use Exception;
use Flarum\Settings\SettingsRepositoryInterface;
use Franzl\Middleware\Whoops\WhoopsRunner;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
@ -23,7 +22,7 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\Translation\TranslatorInterface;
use Zend\Diactoros\Response\HtmlResponse;
class HandleErrors implements Middleware
class HandleErrorsWithView implements Middleware
{
/**
* @var ViewFactory
@ -45,25 +44,18 @@ class HandleErrors implements Middleware
*/
protected $settings;
/**
* @var bool
*/
protected $debug;
/**
* @param ViewFactory $view
* @param LoggerInterface $logger
* @param TranslatorInterface $translator
* @param SettingsRepositoryInterface $settings
* @param bool $debug
*/
public function __construct(ViewFactory $view, LoggerInterface $logger, TranslatorInterface $translator, SettingsRepositoryInterface $settings, $debug = false)
public function __construct(ViewFactory $view, LoggerInterface $logger, TranslatorInterface $translator, SettingsRepositoryInterface $settings)
{
$this->view = $view;
$this->logger = $logger;
$this->translator = $translator;
$this->settings = $settings;
$this->debug = $debug;
}
/**
@ -74,11 +66,7 @@ class HandleErrors implements Middleware
try {
return $handler->handle($request);
} catch (Exception $e) {
if ($this->debug) {
return WhoopsRunner::handle($e, $request);
} else {
return $this->formatException($e);
}
return $this->formatException($e);
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Http\Middleware;
use Exception;
use Franzl\Middleware\Whoops\WhoopsRunner;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface as Middleware;
use Psr\Http\Server\RequestHandlerInterface as Handler;
class HandleErrorsWithWhoops implements Middleware
{
/**
* Catch all errors that happen during further middleware execution.
*/
public function process(Request $request, Handler $handler): Response
{
try {
return $handler->handle($request);
} catch (Exception $e) {
return WhoopsRunner::handle($e, $request);
}
}
}

View File

@ -11,149 +11,36 @@
namespace Flarum\Http;
use Flarum\Foundation\Application;
use Flarum\Foundation\Site;
use Flarum\Http\Middleware\DispatchRoute;
use Flarum\Http\Middleware\HandleErrors;
use Flarum\Http\Middleware\StartSession;
use Flarum\Install\InstallServiceProvider;
use Flarum\Update\UpdateServiceProvider;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface as Middleware;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Zend\Diactoros\Response\HtmlResponse;
use Zend\Diactoros\Server as DiactorosServer;
use Zend\Stratigility\MiddlewarePipe;
use function Zend\Stratigility\middleware;
use function Zend\Stratigility\path;
use Psr\Http\Server\RequestHandlerInterface;
use Throwable;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\ServerRequestFactory;
use Zend\HttpHandlerRunner\Emitter\SapiEmitter;
use Zend\HttpHandlerRunner\RequestHandlerRunner;
use Zend\Stratigility\Middleware\ErrorResponseGenerator;
class Server implements Middleware, Handler
class Server
{
/**
* @param Site $site
* @return Server
*/
public static function fromSite(Site $site)
{
return new static($site->boot());
}
protected $requestHandler;
public function __construct(Application $app)
public function __construct(RequestHandlerInterface $requestHandler)
{
$this->app = $app;
$this->requestHandler = $requestHandler;
}
public function listen()
{
DiactorosServer::createServer(
[$this, 'handle'],
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
)->listen();
}
$runner = new RequestHandlerRunner(
$this->requestHandler,
new SapiEmitter,
[ServerRequestFactory::class, 'fromGlobals'],
function (Throwable $e) {
$generator = new ErrorResponseGenerator;
/**
* Use as PSR-15 middleware.
*/
public function process(Request $request, Handler $handler): Response
{
$middleware = $this->getMiddleware($request->getUri()->getPath());
return $middleware->process($request, $handler);
}
/**
* Use as PSR-15 request handler.
*/
public function handle(Request $request): Response
{
$middleware = $this->getMiddleware($request->getUri()->getPath());
return $middleware->handle($request);
}
/**
* @param string $requestPath
* @return MiddlewarePipe
*/
protected function getMiddleware($requestPath)
{
$pipe = new MiddlewarePipe;
if (! $this->app->isInstalled()) {
return $this->getInstallerMiddleware($pipe);
}
if ($this->app->isDownForMaintenance()) {
return $this->getMaintenanceMiddleware($pipe);
}
if (! $this->app->isUpToDate()) {
return $this->getUpdaterMiddleware($pipe);
}
$api = parse_url($this->app->url('api'), PHP_URL_PATH);
$admin = parse_url($this->app->url('admin'), PHP_URL_PATH);
$forum = parse_url($this->app->url(''), PHP_URL_PATH) ?: '/';
if ($this->pathStartsWith($requestPath, $api)) {
$pipe->pipe(path($api, $this->app->make('flarum.api.middleware')));
} elseif ($this->pathStartsWith($requestPath, $admin)) {
$pipe->pipe(path($admin, $this->app->make('flarum.admin.middleware')));
} else {
$pipe->pipe(path($forum, $this->app->make('flarum.forum.middleware')));
}
return $pipe;
}
private function pathStartsWith($path, $prefix)
{
return $path === $prefix || starts_with($path, "$prefix/");
}
protected function getInstallerMiddleware(MiddlewarePipe $pipe)
{
$this->app->register(InstallServiceProvider::class);
// FIXME: Re-enable HandleErrors middleware, if possible
// (Right now it tries to resolve a database connection because of the injected settings repo instance)
// We could register a different settings repo when Flarum is not installed
//$pipe->pipe($this->app->make(HandleErrors::class, ['debug' => true]));
//$pipe->pipe($this->app->make(StartSession::class));
$pipe->pipe($this->app->make(DispatchRoute::class, ['routes' => $this->app->make('flarum.install.routes')]));
return $pipe;
}
protected function getMaintenanceMiddleware(MiddlewarePipe $pipe)
{
$pipe->pipe(middleware(function () {
return new HtmlResponse(file_get_contents($this->getErrorDir().'/503.html', 503));
}));
// TODO: FOR API render JSON-API error document for HTTP 503
return $pipe;
}
protected function getUpdaterMiddleware(MiddlewarePipe $pipe)
{
$this->app->register(UpdateServiceProvider::class);
$pipe->pipe($this->app->make(DispatchRoute::class, ['routes' => $this->app->make('flarum.update.routes')]));
// TODO: FOR API render JSON-API error document for HTTP 503
return $pipe;
}
private function getErrorDir()
{
return __DIR__.'/../../error';
return $generator($e, new ServerRequest, new Response);
}
);
$runner->run();
}
}

View File

@ -13,26 +13,19 @@ namespace Flarum\Install\Console;
use Exception;
use Flarum\Console\AbstractCommand;
use Flarum\Database\AbstractModel;
use Flarum\Database\DatabaseMigrationRepository;
use Flarum\Database\Migrator;
use Flarum\Discussion\DiscussionServiceProvider;
use Flarum\Extension\ExtensionManager;
use Flarum\Formatter\FormatterServiceProvider;
use Flarum\Group\Group;
use Flarum\Group\GroupServiceProvider;
use Flarum\Install\Prerequisite\PrerequisiteInterface;
use Flarum\Notification\NotificationServiceProvider;
use Flarum\Post\PostServiceProvider;
use Flarum\Search\SearchServiceProvider;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\User;
use Flarum\User\UserServiceProvider;
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\ConnectionResolverInterface;
use Illuminate\Database\Schema\Builder;
use Illuminate\Database\Connectors\ConnectionFactory;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Hashing\BcryptHasher;
use Illuminate\Validation\Factory;
use PDO;
use Symfony\Component\Console\Input\InputOption;
@ -54,6 +47,16 @@ class InstallCommand extends AbstractCommand
*/
protected $filesystem;
/**
* @var ConnectionInterface
*/
protected $db;
/**
* @var Migrator
*/
protected $migrator;
/**
* @param Application $application
* @param Filesystem $filesystem
@ -177,22 +180,10 @@ class InstallCommand extends AbstractCommand
$this->storeConfiguration($this->dataSource->isDebugMode());
$resolver = $this->application->make(ConnectionResolverInterface::class);
AbstractModel::setConnectionResolver($resolver);
AbstractModel::setEventDispatcher($this->application->make('events'));
$this->runMigrations();
$this->writeSettings();
$this->application->register(UserServiceProvider::class);
$this->application->register(FormatterServiceProvider::class);
$this->application->register(DiscussionServiceProvider::class);
$this->application->register(GroupServiceProvider::class);
$this->application->register(NotificationServiceProvider::class);
$this->application->register(SearchServiceProvider::class);
$this->application->register(PostServiceProvider::class);
$this->createAdminUser();
$this->enableBundledExtensions();
@ -211,7 +202,7 @@ class InstallCommand extends AbstractCommand
$config = [
'debug' => $debugMode,
'database' => [
'database' => $laravelDbConfig = [
'driver' => $dbConfig['driver'],
'host' => $dbConfig['host'],
'database' => $dbConfig['database'],
@ -232,15 +223,22 @@ class InstallCommand extends AbstractCommand
$this->info('Testing config');
$this->application->instance('flarum.config', $config);
/* @var $db \Illuminate\Database\ConnectionInterface */
$db = $this->application->make('flarum.db');
$version = $db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
$factory = new ConnectionFactory($this->application);
$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(
@ -251,23 +249,17 @@ class InstallCommand extends AbstractCommand
protected function runMigrations()
{
$this->application->bind(Builder::class, function ($container) {
return $container->make(ConnectionInterface::class)->getSchemaBuilder();
});
$this->migrator->getRepository()->createRepository();
$this->migrator->run(__DIR__.'/../../../migrations');
$migrator = $this->application->make(Migrator::class);
$migrator->getRepository()->createRepository();
$migrator->run(__DIR__.'/../../../migrations');
foreach ($migrator->getNotes() as $note) {
foreach ($this->migrator->getNotes() as $note) {
$this->info($note);
}
}
protected function writeSettings()
{
$settings = $this->application->make(SettingsRepositoryInterface::class);
$settings = new DatabaseSettingsRepository($this->db);
$this->info('Writing default settings');
@ -288,21 +280,29 @@ class InstallCommand extends AbstractCommand
$this->info('Creating admin user '.$admin['username']);
$user = User::register(
$admin['username'],
$admin['email'],
$admin['password']
);
$uid = $this->db->table('users')->insertGetId([
'username' => $admin['username'],
'email' => $admin['email'],
'password' => (new BcryptHasher)->make($admin['password']),
'joined_at' => time(),
'is_email_confirmed' => 1,
]);
$user->is_email_confirmed = 1;
$user->save();
$user->groups()->sync([Group::ADMINISTRATOR_ID]);
$this->db->table('users_groups')->insert([
'user_id' => $uid,
'group_id' => Group::ADMINISTRATOR_ID,
]);
}
protected function enableBundledExtensions()
{
$extensions = $this->application->make(ExtensionManager::class);
$extensions = new ExtensionManager(
new DatabaseSettingsRepository($this->db),
$this->application,
$this->migrator,
$this->application->make(Dispatcher::class),
$this->application->make('files')
);
$migrator = $extensions->getMigrator();

View 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 Flarum\Foundation\AppInterface;
use Flarum\Http\Middleware\DispatchRoute;
use Flarum\Http\Middleware\HandleErrorsWithWhoops;
use Flarum\Http\Middleware\StartSession;
use Flarum\Install\Console\InstallCommand;
use Illuminate\Contracts\Container\Container;
use Zend\Stratigility\MiddlewarePipe;
class Installer implements AppInterface
{
/**
* @var Container
*/
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* @return \Psr\Http\Server\RequestHandlerInterface
*/
public function getRequestHandler()
{
$pipe = new MiddlewarePipe;
$pipe->pipe($this->container->make(HandleErrorsWithWhoops::class));
$pipe->pipe($this->container->make(StartSession::class));
$pipe->pipe(
new DispatchRoute($this->container->make('flarum.install.routes'))
);
return $pipe;
}
/**
* @return \Symfony\Component\Console\Command\Command[]
*/
public function getConsoleCommands()
{
return [
$this->container->make(InstallCommand::class),
];
}
}

View File

@ -13,6 +13,7 @@ namespace Flarum\Locale;
use Flarum\Event\ConfigureLocales;
use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Translation\Translator as TranslatorContract;
use Symfony\Component\Translation\MessageSelector;
@ -54,8 +55,8 @@ class LocaleServiceProvider extends AbstractServiceProvider
private function getDefaultLocale(): string
{
return $this->app->isInstalled() && $this->app->isUpToDate()
? $this->app->make('flarum.settings')->get('default_locale', 'en')
: 'en';
$repo = $this->app->make(SettingsRepositoryInterface::class);
return $repo->get('default_locale', 'en');
}
}

View File

@ -0,0 +1,35 @@
<?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\Settings;
class UninstalledSettingsRepository implements SettingsRepositoryInterface
{
public function all()
{
return [];
}
public function get($key, $default = null)
{
return $default;
}
public function set($key, $value)
{
// Do nothing
}
public function delete($keyLike)
{
// Do nothing
}
}

View File

@ -0,0 +1,35 @@
<?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\Foundation\AbstractServiceProvider;
use Illuminate\Session\FileSessionHandler;
use SessionHandlerInterface;
class SessionServiceProvider extends AbstractServiceProvider
{
/**
* {@inheritdoc}
*/
public function register()
{
$this->app->singleton('session.handler', function ($app) {
return new FileSessionHandler(
$app['files'],
$app['config']['session.files'],
$app['config']['session.lifetime']
);
});
$this->app->alias('session.handler', SessionHandlerInterface::class);
}
}

View File

@ -15,9 +15,7 @@ use Flarum\Event\ConfigureUserPreferences;
use Flarum\Event\GetPermission;
use Flarum\Foundation\AbstractServiceProvider;
use Illuminate\Contracts\Container\Container;
use Illuminate\Session\FileSessionHandler;
use RuntimeException;
use SessionHandlerInterface;
class UserServiceProvider extends AbstractServiceProvider
{
@ -26,24 +24,10 @@ class UserServiceProvider extends AbstractServiceProvider
*/
public function register()
{
$this->registerSession();
$this->registerGate();
$this->registerAvatarsFilesystem();
}
protected function registerSession()
{
$this->app->singleton('session.handler', function ($app) {
return new FileSessionHandler(
$app['files'],
$app['config']['session.files'],
$app['config']['session.lifetime']
);
});
$this->app->alias('session.handler', SessionHandlerInterface::class);
}
protected function registerGate()
{
$this->app->singleton('flarum.gate', function ($app) {

View File

@ -84,7 +84,7 @@ class CreateUserControllerTest extends ApiControllerTestCase
public function disabling_sign_up_prevents_user_creation()
{
/** @var SettingsRepositoryInterface $settings */
$settings = $this->app->make(SettingsRepositoryInterface::class);
$settings = app(SettingsRepositoryInterface::class);
$settings->set('allow_sign_up', false);
try {

View File

@ -12,9 +12,8 @@
namespace Flarum\Tests\Install;
use Flarum\Install\Console\InstallCommand;
use Flarum\Install\InstallServiceProvider;
use Flarum\Tests\Test\TestCase;
use Flarum\User\User;
use Illuminate\Database\Connectors\ConnectionFactory;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\StreamOutput;
@ -27,13 +26,12 @@ class DefaultInstallationCommandTest extends TestCase
*/
public function allows_forum_installation()
{
if (file_exists($this->app->basePath().DIRECTORY_SEPARATOR.'config.php')) {
unlink($this->app->basePath().DIRECTORY_SEPARATOR.'config.php');
if (file_exists(base_path('config.php'))) {
unlink(base_path('config.php'));
}
$this->app->register(InstallServiceProvider::class);
/** @var InstallCommand $command */
$command = $this->app->make(InstallCommand::class);
$command = app(InstallCommand::class);
$command->setDataSource($this->configuration);
$body = fopen('php://temp', 'wb+');
@ -42,10 +40,20 @@ class DefaultInstallationCommandTest extends TestCase
$command->run($input, $output);
$this->assertFileExists($this->app->basePath().DIRECTORY_SEPARATOR.'config.php');
$this->assertFileExists(base_path('config.php'));
$admin = $this->configuration->getAdminUser();
$this->assertEquals(User::find(1)->username, $admin['username']);
$this->assertEquals(
$this->getDatabase()->table('users')->find(1)->username,
$admin['username']
);
}
private function getDatabase()
{
$factory = new ConnectionFactory(app());
return $factory->make($this->configuration->getDatabaseConfiguration());
}
}

View File

@ -11,14 +11,17 @@
namespace Flarum\Tests\Test\Concerns;
use Flarum\Database\DatabaseMigrationRepository;
use Flarum\Database\Migrator;
use Flarum\Foundation\Application;
use Flarum\Foundation\Site;
use Flarum\Foundation\InstalledSite;
use Flarum\Foundation\SiteInterface;
use Flarum\Foundation\UninstalledSite;
use Flarum\Http\Server;
use Flarum\Install\Console\DataProviderInterface;
use Flarum\Install\Console\DefaultsDataProvider;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Schema\Builder;
use Illuminate\Database\Connectors\ConnectionFactory;
trait CreatesForum
{
@ -28,7 +31,7 @@ trait CreatesForum
protected $http;
/**
* @var Site
* @var SiteInterface
*/
protected $site;
@ -51,41 +54,51 @@ trait CreatesForum
protected function createsSite()
{
$this->site = (new Site)
->setBasePath(__DIR__.'/../../tmp')
->setPublicPath(__DIR__.'/../../tmp/public');
if ($this->isInstalled) {
$this->site = new InstalledSite(
__DIR__.'/../../tmp',
__DIR__.'/../../tmp/public',
$this->getFlarumConfig()
);
} else {
$this->site = new UninstalledSite(
__DIR__.'/../../tmp',
__DIR__.'/../../tmp/public'
);
}
}
protected function createsHttpForum()
{
$this->http = Server::fromSite(
$this->site
);
$this->app = $this->site->bootApp();
$this->app = $this->http->app;
$this->http = new Server(
$this->app->getRequestHandler()
);
}
protected function refreshApplication()
protected function collectsConfiguration()
{
$this->createsSite();
$this->configuration = new DefaultsDataProvider();
$data = new DefaultsDataProvider();
$this->configuration->setDebugMode();
$this->configuration->setSetting('mail_driver', 'log');
$data->setDebugMode();
$data->setSetting('mail_driver', 'log');
$database = $data->getDatabaseConfiguration();
$database = $this->configuration->getDatabaseConfiguration();
$database['host'] = env('DB_HOST', $database['host']);
$database['database'] = env('DB_DATABASE', $database['database']);
$database['username'] = env('DB_USERNAME', $database['username']);
$database['password'] = env('DB_PASSWORD', $database['password']);
$data->setDatabaseConfiguration($database);
$this->configuration->setDatabaseConfiguration($database);
}
$this->configuration = $data;
protected function refreshApplication()
{
$this->collectsConfiguration();
$this->setsApplicationConfiguration($data);
$this->seedsDatabase();
$this->seedsApplication();
$this->createsSite();
$this->createsHttpForum();
}
@ -93,59 +106,56 @@ trait CreatesForum
protected function teardownApplication()
{
/** @var ConnectionInterface $connection */
$connection = $this->app->make(ConnectionInterface::class);
$connection = app(ConnectionInterface::class);
$connection->rollBack();
}
protected function setsApplicationConfiguration(DataProviderInterface $data)
protected function getFlarumConfig()
{
if ($this->isInstalled) {
$dbConfig = $data->getDatabaseConfiguration();
$this->site->setConfig(
$config = [
'debug' => $data->isDebugMode(),
'database' => [
'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' => $data->getBaseUrl(),
'paths' => [
'api' => 'api',
'admin' => 'admin',
],
]
);
}
$dbConfig = $this->configuration->getDatabaseConfiguration();
return [
'debug' => $this->configuration->isDebugMode(),
'database' => [
'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->configuration->getBaseUrl(),
'paths' => [
'api' => 'api',
'admin' => 'admin',
],
];
}
protected function seedsApplication()
protected function seedsDatabase()
{
if ($this->isInstalled) {
$app = app(\Illuminate\Contracts\Foundation\Application::class);
$app->bind(Builder::class, function ($container) {
return $container->make(ConnectionInterface::class)->getSchemaBuilder();
});
/** @var Migrator $migrator */
$migrator = $app->make(Migrator::class);
if (! $migrator->getRepository()->repositoryExists()) {
$migrator->getRepository()->createRepository();
}
$migrator->run(__DIR__.'/../../../migrations');
/** @var ConnectionInterface $connection */
$connection = $app->make(\Illuminate\Database\ConnectionInterface::class);
$connection->beginTransaction();
if (! $this->isInstalled) {
return;
}
$app = app(\Illuminate\Contracts\Foundation\Application::class);
$factory = new ConnectionFactory($app);
$db = $factory->make($this->configuration->getDatabaseConfiguration());
$repository = new DatabaseMigrationRepository($db, 'migrations');
$migrator = new Migrator($repository, $db, app('files'));
if (! $migrator->getRepository()->repositoryExists()) {
$migrator->getRepository()->createRepository();
}
$migrator->run(__DIR__.'/../../../migrations');
$db->beginTransaction();
}
}

View File

@ -11,22 +11,17 @@
namespace Flarum\Tests\Test\Concerns;
use Flarum\Api\ApiServiceProvider;
use Flarum\Api\Client;
use Flarum\User\Guest;
use Flarum\User\User;
use Flarum\User\UserServiceProvider;
use Psr\Http\Message\ResponseInterface;
trait MakesApiRequests
{
public function call(string $controller, User $actor = null, array $queryParams = [], array $body = []): ResponseInterface
{
$this->app->register(UserServiceProvider::class);
$this->app->register(ApiServiceProvider::class);
$this->app->make('flarum.api.middleware');
/** @var Client $api */
$api = $this->app->make(Client::class);
$api = app(Client::class);
return $api->send($controller, $actor ?? new Guest, $queryParams, $body);
}