mirror of
https://github.com/flarum/core.git
synced 2025-08-08 09:26:34 +02:00
Refactor the web app bootstrapping code
- All custom JS variables are now preloaded into the `app.data` object, rather than directly on the `app` object. This means that admin settings are available in `app.data.settings` rather than `app.settings`, etc. - Cleaner route handler generation - Renamed ConfigureClientView to ConfigureWebApp, though the former still exists and is deprecated - Partial fix for #881 (strips ?nojs=1 from URL if possible, so that refreshing will attempt to load JS version again)
This commit is contained in:
@@ -1,316 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Http\Controller;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Asset\AssetManager;
|
||||
use Flarum\Asset\JsCompiler;
|
||||
use Flarum\Asset\LessCompiler;
|
||||
use Flarum\Event\ConfigureClientView;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Locale\JsCompiler as LocaleJsCompiler;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
/**
|
||||
* This action sets up a ClientView, and preloads it with the assets necessary
|
||||
* to boot a Flarum client.
|
||||
*
|
||||
* Subclasses should set a $clientName, $layout, and $translationKeys. The
|
||||
* client name will be used to locate the client assets (or alternatively,
|
||||
* subclasses can overwrite the addAssets method), and set up asset compilers
|
||||
* which write to the assets directory. Configured LESS customizations will be
|
||||
* appended.
|
||||
*
|
||||
* A locale compiler is set up for the actor's locale, including the
|
||||
* translations specified in $translationKeys. Additionally, an event is fired
|
||||
* before the ClientView is returned, giving extensions an opportunity to add
|
||||
* assets, translations, or alter the view.
|
||||
*/
|
||||
abstract class AbstractClientController extends AbstractHtmlController
|
||||
{
|
||||
/**
|
||||
* The name of the client. This is used to locate assets within the js/
|
||||
* and less/ directories. It is also used as the filename of the compiled
|
||||
* asset files.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $clientName;
|
||||
|
||||
/**
|
||||
* The name of the view to include as the page layout.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $layout;
|
||||
|
||||
/**
|
||||
* A regex matching the keys of the translations that should be included in
|
||||
* the compiled locale file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $translations;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Foundation\Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Settings\SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* @var Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @param \Flarum\Foundation\Application $app
|
||||
* @param Client $api
|
||||
* @param LocaleManager $locales
|
||||
* @param \Flarum\Settings\SettingsRepositoryInterface $settings
|
||||
* @param Dispatcher $events
|
||||
* @param Repository $cache
|
||||
*/
|
||||
public function __construct(
|
||||
Application $app,
|
||||
Client $api,
|
||||
LocaleManager $locales,
|
||||
SettingsRepositoryInterface $settings,
|
||||
Dispatcher $events,
|
||||
Repository $cache
|
||||
) {
|
||||
$this->app = $app;
|
||||
$this->api = $api;
|
||||
$this->locales = $locales;
|
||||
$this->settings = $settings;
|
||||
$this->events = $events;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return ClientView
|
||||
*/
|
||||
public function render(Request $request)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
$assets = $this->getAssets();
|
||||
$locale = $this->locales->getLocale();
|
||||
$localeCompiler = $locale ? $this->getLocaleCompiler($locale) : null;
|
||||
|
||||
$view = new ClientView(
|
||||
$this->api,
|
||||
$request,
|
||||
$actor,
|
||||
$assets,
|
||||
$this->layout,
|
||||
$localeCompiler
|
||||
);
|
||||
|
||||
$view->setVariable('locales', $this->locales->getLocales());
|
||||
$view->setVariable('locale', $locale);
|
||||
|
||||
$this->events->fire(
|
||||
new ConfigureClientView($this, $view)
|
||||
);
|
||||
|
||||
if ($localeCompiler) {
|
||||
$translations = array_get($this->locales->getTranslator()->getMessages(), 'messages', []);
|
||||
|
||||
$translations = $this->filterTranslations($translations);
|
||||
|
||||
$localeCompiler->setTranslations($translations);
|
||||
}
|
||||
|
||||
app('view')->share('translator', $this->locales->getTranslator());
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the client's assets so that they will be regenerated from scratch
|
||||
* on the next render.
|
||||
*/
|
||||
public function flushAssets()
|
||||
{
|
||||
$this->flushCss();
|
||||
$this->flushJs();
|
||||
}
|
||||
|
||||
public function flushCss()
|
||||
{
|
||||
$this->getAssets()->flushCss();
|
||||
}
|
||||
|
||||
public function flushJs()
|
||||
{
|
||||
$this->getAssets()->flushJs();
|
||||
|
||||
$this->flushLocales();
|
||||
}
|
||||
|
||||
public function flushLocales()
|
||||
{
|
||||
$locales = array_keys($this->locales->getLocales());
|
||||
|
||||
foreach ($locales as $locale) {
|
||||
$this->getLocaleCompiler($locale)->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the asset manager, preloaded with a JavaScript compiler and a LESS
|
||||
* compiler. Automatically add the files necessary to boot a Flarum client,
|
||||
* as well as any configured LESS customizations.
|
||||
*
|
||||
* @return AssetManager
|
||||
*/
|
||||
protected function getAssets()
|
||||
{
|
||||
$public = $this->getAssetDirectory();
|
||||
$watch = $this->app->config('debug');
|
||||
|
||||
$assets = new AssetManager(
|
||||
new JsCompiler($public, "$this->clientName.js", $watch, $this->cache),
|
||||
new LessCompiler($public, "$this->clientName.css", $watch, $this->app->storagePath().'/less')
|
||||
);
|
||||
|
||||
$this->addAssets($assets);
|
||||
$this->addCustomizations($assets);
|
||||
|
||||
return $assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the assets necessary to boot a Flarum client, found within the
|
||||
* directory specified by the $clientName property.
|
||||
*
|
||||
* @param AssetManager $assets
|
||||
*/
|
||||
protected function addAssets(AssetManager $assets)
|
||||
{
|
||||
$root = __DIR__.'/../../..';
|
||||
|
||||
$assets->addFile("$root/js/$this->clientName/dist/app.js");
|
||||
$assets->addFile("$root/less/$this->clientName/app.less");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any configured JS/LESS customizations to the asset manager.
|
||||
*
|
||||
* @param AssetManager $assets
|
||||
*/
|
||||
protected function addCustomizations(AssetManager $assets)
|
||||
{
|
||||
$assets->addLess(function () {
|
||||
$less = '';
|
||||
|
||||
foreach ($this->getLessVariables() as $name => $value) {
|
||||
$less .= "@$name: $value;";
|
||||
}
|
||||
|
||||
$less .= $this->settings->get('custom_less');
|
||||
|
||||
return $less;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the values of any LESS variables to compile into the CSS, based on
|
||||
* the forum's configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getLessVariables()
|
||||
{
|
||||
return [
|
||||
'config-primary-color' => $this->settings->get('theme_primary_color') ?: '#000',
|
||||
'config-secondary-color' => $this->settings->get('theme_secondary_color') ?: '#000',
|
||||
'config-dark-mode' => $this->settings->get('theme_dark_mode') ? 'true' : 'false',
|
||||
'config-colored-header' => $this->settings->get('theme_colored_header') ? 'true' : 'false'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the locale compiler for the given locale.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return LocaleJsCompiler
|
||||
*/
|
||||
protected function getLocaleCompiler($locale)
|
||||
{
|
||||
$compiler = new LocaleJsCompiler(
|
||||
$this->getAssetDirectory(),
|
||||
"$this->clientName-$locale.js",
|
||||
$this->app->config('debug'),
|
||||
$this->cache
|
||||
);
|
||||
|
||||
foreach ($this->locales->getJsFiles($locale) as $file) {
|
||||
$compiler->addFile($file);
|
||||
}
|
||||
|
||||
return $compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the directory where assets should be written.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAssetDirectory()
|
||||
{
|
||||
return public_path().'/assets';
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a selection of keys from a collection of translations.
|
||||
*
|
||||
* @param array $translations
|
||||
* @return array
|
||||
*/
|
||||
protected function filterTranslations(array $translations)
|
||||
{
|
||||
if (! $this->translations) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$filtered = array_filter(array_keys($translations), function ($id) {
|
||||
return preg_match($this->translations, $id);
|
||||
});
|
||||
|
||||
return array_only($translations, $filtered);
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ abstract class AbstractHtmlController implements ControllerInterface
|
||||
$view = $this->render($request);
|
||||
|
||||
$response = new Response;
|
||||
$response->getBody()->write($view->render());
|
||||
$response->getBody()->write($view);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
52
src/Http/Controller/AbstractWebAppController.php
Normal file
52
src/Http/Controller/AbstractWebAppController.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Http\Controller;
|
||||
|
||||
use Flarum\Event\ConfigureWebApp;
|
||||
use Flarum\Http\WebApp\AbstractWebApp;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
abstract class AbstractWebAppController extends AbstractHtmlController
|
||||
{
|
||||
/**
|
||||
* @var AbstractWebApp
|
||||
*/
|
||||
protected $webApp;
|
||||
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(Request $request)
|
||||
{
|
||||
$view = $this->getView($request);
|
||||
|
||||
$this->events->fire(
|
||||
new ConfigureWebApp($this, $view, $request)
|
||||
);
|
||||
|
||||
return $view->render($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return \Flarum\Http\WebApp\WebAppView
|
||||
*/
|
||||
protected function getView(Request $request)
|
||||
{
|
||||
return $this->webApp->getView();
|
||||
}
|
||||
}
|
@@ -1,371 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Http\Controller;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Asset\AssetManager;
|
||||
use Flarum\Core\User;
|
||||
use Flarum\Locale\JsCompiler;
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
/**
|
||||
* This class represents a view which boots up Flarum's client.
|
||||
*/
|
||||
class ClientView implements Renderable
|
||||
{
|
||||
/**
|
||||
* The user who is using the client.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
protected $actor;
|
||||
|
||||
/**
|
||||
* The title of the document, displayed in the <title> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* The SEO content of the page, displayed in <noscript> tags.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* The path to the client layout view to display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $layout;
|
||||
|
||||
/**
|
||||
* An API response that should be preloaded into the page.
|
||||
*
|
||||
* @var null|array|object
|
||||
*/
|
||||
protected $document;
|
||||
|
||||
/**
|
||||
* Other variables to preload into the page.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $variables = [];
|
||||
|
||||
/**
|
||||
* An array of JS modules to import before booting the app.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bootstrappers = ['locale'];
|
||||
|
||||
/**
|
||||
* An array of strings to append to the page's <head>.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headStrings = [];
|
||||
|
||||
/**
|
||||
* An array of strings to prepend before the page's </body>.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $footStrings = [];
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var AssetManager
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var JsCompiler
|
||||
*/
|
||||
protected $localeJs;
|
||||
|
||||
/**
|
||||
* @param Client $api
|
||||
* @param Request $request
|
||||
* @param User $actor
|
||||
* @param AssetManager $assets
|
||||
* @param string $layout
|
||||
* @param JsCompiler $localeJs
|
||||
*/
|
||||
public function __construct(
|
||||
Client $api,
|
||||
Request $request,
|
||||
User $actor,
|
||||
AssetManager $assets,
|
||||
$layout,
|
||||
JsCompiler $localeJs = null
|
||||
) {
|
||||
$this->api = $api;
|
||||
$this->request = $request;
|
||||
$this->actor = $actor;
|
||||
$this->assets = $assets;
|
||||
$this->layout = $layout;
|
||||
$this->localeJs = $localeJs;
|
||||
|
||||
$this->addHeadString('<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700,600">', 'font');
|
||||
}
|
||||
|
||||
/**
|
||||
* The title of the document, to be displayed in the <title> tag.
|
||||
*
|
||||
* @param null|string $title
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SEO content of the page, to be displayed in <noscript> tags.
|
||||
*
|
||||
* @param null|string $content
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the client layout view to display.
|
||||
*
|
||||
* @param string $layout
|
||||
*/
|
||||
public function setLayout($layout)
|
||||
{
|
||||
$this->layout = $layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string to be appended to the page's <head>.
|
||||
*
|
||||
* @param string $string
|
||||
*/
|
||||
public function addHeadString($string, $name = null)
|
||||
{
|
||||
if ($name) {
|
||||
$this->headStrings[$name] = $string;
|
||||
} else {
|
||||
$this->headStrings[] = $string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string to be prepended before the page's </body>.
|
||||
*
|
||||
* @param string $string
|
||||
*/
|
||||
public function addFootString($string)
|
||||
{
|
||||
$this->footStrings[] = $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an API response to be preloaded into the page. This should be a
|
||||
* JSON-API document.
|
||||
*
|
||||
* @param null|array|object $document
|
||||
*/
|
||||
public function setDocument($document)
|
||||
{
|
||||
$this->document = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a variable to be preloaded into the app.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setVariable($name, $value)
|
||||
{
|
||||
$this->variables[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JavaScript module to be imported before the app is booted.
|
||||
*
|
||||
* @param string $string
|
||||
*/
|
||||
public function addBootstrapper($string)
|
||||
{
|
||||
$this->bootstrappers[] = $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view's asset manager.
|
||||
*
|
||||
* @return AssetManager
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string contents of the view.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$view = app('view')->file(__DIR__.'/../../../views/app.blade.php');
|
||||
|
||||
$forum = $this->getForumDocument();
|
||||
$data = $this->getDataFromDocument($forum);
|
||||
|
||||
if ($this->actor->exists) {
|
||||
$user = $this->getUserDocument();
|
||||
$data = array_merge($data, $this->getDataFromDocument($user));
|
||||
}
|
||||
|
||||
$view->app = [
|
||||
'preload' => [
|
||||
'data' => $data,
|
||||
'session' => $this->getSession(),
|
||||
'document' => $this->document
|
||||
]
|
||||
] + $this->variables;
|
||||
$view->bootstrappers = $this->bootstrappers;
|
||||
|
||||
$noJs = array_get($this->request->getQueryParams(), 'nojs');
|
||||
|
||||
$view->title = ($this->title ? $this->title.' - ' : '').$forum->data->attributes->title;
|
||||
$view->forum = $forum->data;
|
||||
$view->layout = app('view')->file($this->layout, [
|
||||
'forum' => $forum->data,
|
||||
'content' => app('view')->file(__DIR__.'/../../../views/content.blade.php', [
|
||||
'content' => $this->content,
|
||||
'noJs' => $noJs,
|
||||
'forum' => $forum->data
|
||||
])
|
||||
]);
|
||||
$view->noJs = $noJs;
|
||||
|
||||
$view->styles = [$this->assets->getCssFile()];
|
||||
$view->scripts = [$this->assets->getJsFile()];
|
||||
|
||||
if ($this->localeJs) {
|
||||
$view->scripts[] = $this->localeJs->getFile();
|
||||
}
|
||||
|
||||
$view->head = implode("\n", $this->headStrings);
|
||||
$view->foot = implode("\n", $this->footStrings);
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string contents of the view.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of an API request to show the forum.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function getForumDocument()
|
||||
{
|
||||
return json_decode($this->api->send('Flarum\Api\Controller\ShowForumController', $this->actor)->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of an API request to show the current user.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function getUserDocument()
|
||||
{
|
||||
// TODO: calling on the API here results in an extra query to get
|
||||
// the user + their groups, when we already have this information on
|
||||
// $this->actor. Can we simply run the CurrentUserSerializer
|
||||
// manually? Or can we somehow inject this data into the ShowDiscussionController?
|
||||
$document = json_decode($this->api->send(
|
||||
'Flarum\Api\Controller\ShowUserController',
|
||||
$this->actor,
|
||||
['id' => $this->actor->id]
|
||||
)->getBody());
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of data by merging the 'data' and 'included' keys of a
|
||||
* JSON-API document.
|
||||
*
|
||||
* @param object $document
|
||||
* @return array
|
||||
*/
|
||||
protected function getDataFromDocument($document)
|
||||
{
|
||||
$data[] = $document->data;
|
||||
|
||||
if (isset($document->included)) {
|
||||
$data = array_merge($data, $document->included);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the current session.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getSession()
|
||||
{
|
||||
$session = $this->request->getAttribute('session');
|
||||
|
||||
return [
|
||||
'userId' => $this->actor->id,
|
||||
'csrfToken' => $session->get('csrf_token')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
public function getActor()
|
||||
{
|
||||
return $this->actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Http;
|
||||
|
||||
use Flarum\Http\Controller\ControllerInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
trait GenerateRouteHandlerTrait
|
||||
{
|
||||
/**
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function getHandlerGenerator(Container $container)
|
||||
{
|
||||
return function ($class) use ($container) {
|
||||
return function (ServerRequestInterface $request, $routeParams) use ($class, $container) {
|
||||
$controller = $container->make($class);
|
||||
|
||||
if (! ($controller instanceof ControllerInterface)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Route handler must be an instance of '.ControllerInterface::class
|
||||
);
|
||||
}
|
||||
|
||||
$request = $request->withQueryParams(array_merge($request->getQueryParams(), $routeParams));
|
||||
|
||||
return $controller->handle($request);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
71
src/Http/Handler/ControllerRouteHandler.php
Normal file
71
src/Http/Handler/ControllerRouteHandler.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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\Handler;
|
||||
|
||||
use Flarum\Http\Controller\ControllerInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class ControllerRouteHandler
|
||||
{
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @param string $controller
|
||||
*/
|
||||
public function __construct(Container $container, $controller)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param array $routeParams
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function __invoke(ServerRequestInterface $request, array $routeParams)
|
||||
{
|
||||
$controller = $this->resolveController($this->controller);
|
||||
|
||||
$request = $request->withQueryParams(array_merge($request->getQueryParams(), $routeParams));
|
||||
|
||||
return $controller->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @return ControllerInterface
|
||||
*/
|
||||
protected function resolveController($class)
|
||||
{
|
||||
$controller = $this->container->make($class);
|
||||
|
||||
if (! ($controller instanceof ControllerInterface)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Controller must be an instance of '.ControllerInterface::class
|
||||
);
|
||||
}
|
||||
|
||||
return $controller;
|
||||
}
|
||||
}
|
38
src/Http/Handler/RouteHandlerFactory.php
Normal file
38
src/Http/Handler/RouteHandlerFactory.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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\Handler;
|
||||
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class RouteHandlerFactory
|
||||
{
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toController($controller)
|
||||
{
|
||||
return new ControllerRouteHandler($this->container, $controller);
|
||||
}
|
||||
}
|
188
src/Http/WebApp/AbstractWebApp.php
Normal file
188
src/Http/WebApp/AbstractWebApp.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?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\WebApp;
|
||||
|
||||
use Flarum\Http\Controller;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
|
||||
abstract class AbstractWebApp
|
||||
{
|
||||
/**
|
||||
* @var WebAppAssetsFactory
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var WebAppViewFactory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @param WebAppAssetsFactory $assets
|
||||
* @param WebAppViewFactory $view
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct(WebAppAssetsFactory $assets, WebAppViewFactory $view, SettingsRepositoryInterface $settings, LocaleManager $locales)
|
||||
{
|
||||
$this->assets = $assets;
|
||||
$this->view = $view;
|
||||
$this->settings = $settings;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WebAppView
|
||||
*/
|
||||
public function getView()
|
||||
{
|
||||
$view = $this->view->make($this->getLayout(), $this->getAssets());
|
||||
|
||||
$this->addDefaultAssets($view);
|
||||
$this->addCustomLess($view);
|
||||
$this->addTranslations($view);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WebAppAssets
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return $this->assets->make($this->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the client.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getName();
|
||||
|
||||
/**
|
||||
* Get the path to the client layout view.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getLayout()
|
||||
{
|
||||
return __DIR__.'/../../../views/'.$this->getName().'.blade.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a regular expression to match against translation keys.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTranslationFilter()
|
||||
{
|
||||
return '/^[^\.]+\.(?:'.$this->getName().'|lib)\./';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebAppView $view
|
||||
*/
|
||||
private function addDefaultAssets(WebAppView $view)
|
||||
{
|
||||
$root = __DIR__.'/../../..';
|
||||
$name = $this->getName();
|
||||
|
||||
$view->getJs()->addFile("$root/js/$name/dist/app.js");
|
||||
$view->getCss()->addFile("$root/less/$name/app.less");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebAppView $view
|
||||
*/
|
||||
private function addCustomLess(WebAppView $view)
|
||||
{
|
||||
$css = $view->getCss();
|
||||
$localeCss = $view->getLocaleCss();
|
||||
|
||||
$lessVariables = function () {
|
||||
$less = '';
|
||||
|
||||
foreach ($this->getLessVariables() as $name => $value) {
|
||||
$less .= "@$name: $value;";
|
||||
}
|
||||
|
||||
return $less;
|
||||
};
|
||||
|
||||
$css->addString($lessVariables);
|
||||
$localeCss->addString($lessVariables);
|
||||
|
||||
$css->addString(function () {
|
||||
return $this->settings->get('custom_less');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the values of any LESS variables to compile into the CSS, based on
|
||||
* the forum's configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getLessVariables()
|
||||
{
|
||||
return [
|
||||
'config-primary-color' => $this->settings->get('theme_primary_color') ?: '#000',
|
||||
'config-secondary-color' => $this->settings->get('theme_secondary_color') ?: '#000',
|
||||
'config-dark-mode' => $this->settings->get('theme_dark_mode') ? 'true' : 'false',
|
||||
'config-colored-header' => $this->settings->get('theme_colored_header') ? 'true' : 'false'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebAppView $view
|
||||
*/
|
||||
private function addTranslations(WebAppView $view)
|
||||
{
|
||||
$translations = array_get($this->locales->getTranslator()->getMessages(), 'messages', []);
|
||||
|
||||
$translations = $this->filterTranslations($translations);
|
||||
|
||||
$view->getLocaleJs()->setTranslations($translations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a selection of keys from a collection of translations.
|
||||
*
|
||||
* @param array $translations
|
||||
* @return array
|
||||
*/
|
||||
private function filterTranslations(array $translations)
|
||||
{
|
||||
$filter = $this->getTranslationFilter();
|
||||
|
||||
if (! $filter) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$filtered = array_filter(array_keys($translations), function ($id) use ($filter) {
|
||||
return preg_match($filter, $id);
|
||||
});
|
||||
|
||||
return array_only($translations, $filtered);
|
||||
}
|
||||
}
|
164
src/Http/WebApp/WebAppAssets.php
Normal file
164
src/Http/WebApp/WebAppAssets.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\Http\WebApp;
|
||||
|
||||
use Flarum\Asset\JsCompiler;
|
||||
use Flarum\Asset\LessCompiler;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Locale\JsCompiler as LocaleJsCompiler;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
|
||||
class WebAppAssets
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @var Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param Application $app
|
||||
* @param Repository $cache
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct($name, Application $app, Repository $cache, LocaleManager $locales)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->app = $app;
|
||||
$this->cache = $cache;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
public function flush()
|
||||
{
|
||||
$this->flushJs();
|
||||
$this->flushCss();
|
||||
}
|
||||
|
||||
public function flushJs()
|
||||
{
|
||||
$this->getJs()->flush();
|
||||
$this->flushLocaleJs();
|
||||
}
|
||||
|
||||
public function flushLocaleJs()
|
||||
{
|
||||
foreach ($this->locales->getLocales() as $locale => $info) {
|
||||
$this->getLocaleJs($locale)->flush();
|
||||
}
|
||||
}
|
||||
|
||||
public function flushCss()
|
||||
{
|
||||
$this->getCss()->flush();
|
||||
$this->flushLocaleCss();
|
||||
}
|
||||
|
||||
public function flushLocaleCss()
|
||||
{
|
||||
foreach ($this->locales->getLocales() as $locale => $info) {
|
||||
$this->getLocaleCss($locale)->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JsCompiler
|
||||
*/
|
||||
public function getJs()
|
||||
{
|
||||
return new JsCompiler(
|
||||
$this->getDestination(),
|
||||
"$this->name.js",
|
||||
$this->shouldWatch(),
|
||||
$this->cache
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LessCompiler
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
return new LessCompiler(
|
||||
$this->getDestination(),
|
||||
"$this->name.css",
|
||||
$this->shouldWatch(),
|
||||
$this->getLessStorage()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $locale
|
||||
* @return LocaleJsCompiler
|
||||
*/
|
||||
public function getLocaleJs($locale)
|
||||
{
|
||||
return new LocaleJsCompiler(
|
||||
$this->getDestination(),
|
||||
"$this->name-$locale.js",
|
||||
$this->shouldWatch(),
|
||||
$this->cache
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $locale
|
||||
* @return LessCompiler
|
||||
*/
|
||||
public function getLocaleCss($locale)
|
||||
{
|
||||
return new LessCompiler(
|
||||
$this->getDestination(),
|
||||
"$this->name-$locale.css",
|
||||
$this->shouldWatch(),
|
||||
$this->getLessStorage()
|
||||
);
|
||||
}
|
||||
|
||||
protected function getDestination()
|
||||
{
|
||||
return $this->app->publicPath().'/assets';
|
||||
}
|
||||
|
||||
protected function shouldWatch()
|
||||
{
|
||||
return $this->app->config('debug');
|
||||
}
|
||||
|
||||
protected function getLessStorage()
|
||||
{
|
||||
return $this->app->storagePath().'/less';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
54
src/Http/WebApp/WebAppAssetsFactory.php
Normal file
54
src/Http/WebApp/WebAppAssetsFactory.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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\WebApp;
|
||||
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
|
||||
class WebAppAssetsFactory
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @var Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @param Application $app
|
||||
* @param Repository $cache
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct(Application $app, Repository $cache, LocaleManager $locales)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->cache = $cache;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return WebAppAssets
|
||||
*/
|
||||
public function make($name)
|
||||
{
|
||||
return new WebAppAssets($name, $this->app, $this->cache, $this->locales);
|
||||
}
|
||||
}
|
446
src/Http/WebApp/WebAppView.php
Normal file
446
src/Http/WebApp/WebAppView.php
Normal file
@@ -0,0 +1,446 @@
|
||||
<?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\WebApp;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Serializer\AbstractSerializer;
|
||||
use Flarum\Api\Serializer\CurrentUserSerializer;
|
||||
use Flarum\Asset\CompilerInterface;
|
||||
use Flarum\Core\Exception\ValidationException;
|
||||
use Flarum\Core\User;
|
||||
use Flarum\Locale\JsCompiler;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\View\Factory;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Tobscure\JsonApi\Document;
|
||||
use Tobscure\JsonApi\Resource;
|
||||
|
||||
/**
|
||||
* This class represents a view which boots up Flarum's client.
|
||||
*/
|
||||
class WebAppView
|
||||
{
|
||||
/**
|
||||
* The title of the document, displayed in the <title> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* The description of the document, displayed in a <meta> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* The path to the client layout view to display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $layout;
|
||||
|
||||
/**
|
||||
* The SEO content of the page, displayed within the layout in <noscript>
|
||||
* tags.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* An API response to be preloaded into the page.
|
||||
*
|
||||
* @var null|array|object
|
||||
*/
|
||||
protected $document;
|
||||
|
||||
/**
|
||||
* Other variables to preload into the page.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $variables = [];
|
||||
|
||||
/**
|
||||
* An array of JS modules to load before booting the app.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modules = ['locale'];
|
||||
|
||||
/**
|
||||
* An array of strings to append to the page's <head>.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $head = [];
|
||||
|
||||
/**
|
||||
* An array of strings to prepend before the page's </body>.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $foot = [];
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $js;
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $css;
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $localeJs;
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $localeCss;
|
||||
|
||||
/**
|
||||
* @var ClientAssets
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @var AbstractSerializer
|
||||
*/
|
||||
protected $userSerializer;
|
||||
|
||||
/**
|
||||
* @param string $layout
|
||||
* @param ClientAssets $assets
|
||||
* @param Client $api
|
||||
* @param Factory $view
|
||||
* @param LocaleManager $locales
|
||||
* @param AbstractSerializer $userSerializer
|
||||
*/
|
||||
public function __construct($layout, WebAppAssets $assets, Client $api, Factory $view, LocaleManager $locales, AbstractSerializer $userSerializer)
|
||||
{
|
||||
$this->layout = $layout;
|
||||
$this->api = $api;
|
||||
$this->assets = $assets;
|
||||
$this->view = $view;
|
||||
$this->locales = $locales;
|
||||
$this->userSerializer = $userSerializer;
|
||||
|
||||
$this->addHeadString('<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700,600">', 'font');
|
||||
|
||||
$this->js = $this->assets->getJs();
|
||||
$this->css = $this->assets->getCss();
|
||||
|
||||
$locale = $this->locales->getLocale();
|
||||
$this->localeJs = $this->assets->getLocaleJs($locale);
|
||||
$this->localeCss = $this->assets->getLocaleCss($locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* The title of the document, to be displayed in the <title> tag.
|
||||
*
|
||||
* @param null|string $title
|
||||
*/
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SEO content of the page, to be displayed in <noscript> tags.
|
||||
*
|
||||
* @param null|string $content
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the client layout view to display.
|
||||
*
|
||||
* @param string $layout
|
||||
*/
|
||||
public function setLayout($layout)
|
||||
{
|
||||
$this->layout = $layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string to be appended to the page's <head>.
|
||||
*
|
||||
* @param string $string
|
||||
* @param null|string $name
|
||||
*/
|
||||
public function addHeadString($string, $name = null)
|
||||
{
|
||||
if ($name) {
|
||||
$this->head[$name] = $string;
|
||||
} else {
|
||||
$this->head[] = $string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a string to be prepended before the page's </body>.
|
||||
*
|
||||
* @param string $string
|
||||
*/
|
||||
public function addFootString($string)
|
||||
{
|
||||
$this->foot[] = $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an API response to be preloaded into the page. This should be a
|
||||
* JSON-API document.
|
||||
*
|
||||
* @param null|array|object $document
|
||||
*/
|
||||
public function setDocument($document)
|
||||
{
|
||||
$this->document = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a variable to be preloaded into the app.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setVariable($name, $value)
|
||||
{
|
||||
$this->variables[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JavaScript module to be imported before the app is booted.
|
||||
*
|
||||
* @param string $module
|
||||
*/
|
||||
public function loadModule($module)
|
||||
{
|
||||
$this->modules[] = $module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string contents of the view.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string
|
||||
*/
|
||||
public function render(Request $request)
|
||||
{
|
||||
$forum = $this->getForumDocument($request);
|
||||
|
||||
$this->view->share('translator', $this->locales->getTranslator());
|
||||
$this->view->share('allowJs', ! array_get($request->getQueryParams(), 'nojs'));
|
||||
$this->view->share('forum', array_get($forum, 'data'));
|
||||
$this->view->share('debug', array_get($forum, 'data.attributes.debug'));
|
||||
|
||||
$view = $this->view->file(__DIR__.'/../../../views/app.blade.php');
|
||||
|
||||
$view->title = $this->buildTitle(array_get($forum, 'data.attributes.title'));
|
||||
$view->description = $this->description;
|
||||
|
||||
$view->modules = $this->modules;
|
||||
$view->payload = $this->buildPayload($request, $forum);
|
||||
|
||||
$view->layout = $this->buildLayout();
|
||||
|
||||
$baseUrl = array_get($forum, 'data.attributes.baseUrl');
|
||||
$view->cssUrls = $this->buildCssUrls($baseUrl);
|
||||
$view->jsUrls = $this->buildJsUrls($baseUrl);
|
||||
|
||||
$view->head = implode("\n", $this->head);
|
||||
$view->foot = implode("\n", $this->foot);
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
protected function buildTitle($forumTitle)
|
||||
{
|
||||
return ($this->title ? $this->title.' - ' : '').$forumTitle;
|
||||
}
|
||||
|
||||
protected function buildPayload(Request $request, $forum)
|
||||
{
|
||||
$data = $this->getDataFromDocument($forum);
|
||||
|
||||
if ($request->getAttribute('actor')->exists) {
|
||||
$user = $this->getUserDocument($request);
|
||||
$data = array_merge($data, $this->getDataFromDocument($user));
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'resources' => $data,
|
||||
'session' => $this->buildSession($request),
|
||||
'document' => $this->document,
|
||||
'locales' => $this->locales->getLocales(),
|
||||
'locale' => $this->locales->getLocale()
|
||||
];
|
||||
|
||||
return array_merge($payload, $this->variables);
|
||||
}
|
||||
|
||||
protected function buildLayout()
|
||||
{
|
||||
$view = $this->view->file($this->layout);
|
||||
|
||||
$view->content = $this->buildContent();
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function buildContent()
|
||||
{
|
||||
$view = $this->view->file(__DIR__.'/../../../views/content.blade.php');
|
||||
|
||||
$view->content = $this->content;
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function buildCssUrls($baseUrl)
|
||||
{
|
||||
return $this->buildAssetUrls($baseUrl, [$this->css->getFile(), $this->localeCss->getFile()]);
|
||||
}
|
||||
|
||||
protected function buildJsUrls($baseUrl)
|
||||
{
|
||||
return $this->buildAssetUrls($baseUrl, [$this->js->getFile(), $this->localeJs->getFile()]);
|
||||
}
|
||||
|
||||
protected function buildAssetUrls($baseUrl, $files)
|
||||
{
|
||||
return array_map(function ($file) use ($baseUrl) {
|
||||
return $baseUrl.str_replace(public_path(), '', $file);
|
||||
}, $files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompilerInterface
|
||||
*/
|
||||
public function getJs()
|
||||
{
|
||||
return $this->js;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompilerInterface
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
return $this->css;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JsCompiler
|
||||
*/
|
||||
public function getLocaleJs()
|
||||
{
|
||||
return $this->localeJs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompilerInterface
|
||||
*/
|
||||
public function getLocaleCss()
|
||||
{
|
||||
return $this->localeCss;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of an API request to show the forum.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getForumDocument(Request $request)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
|
||||
$response = $this->api->send('Flarum\Api\Controller\ShowForumController', $actor);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of an API request to show the current user.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUserDocument(Request $request)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
|
||||
$this->userSerializer->setActor($actor);
|
||||
|
||||
$resource = new Resource($actor, $this->userSerializer);
|
||||
|
||||
$document = new Document($resource->with('groups'));
|
||||
|
||||
return $document->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the current session.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function buildSession(Request $request)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
$session = $request->getAttribute('session');
|
||||
|
||||
return [
|
||||
'userId' => $actor->id,
|
||||
'csrfToken' => $session->get('csrf_token')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of data by merging the 'data' and 'included' keys of a
|
||||
* JSON-API document.
|
||||
*
|
||||
* @param array $document
|
||||
* @return array
|
||||
*/
|
||||
private function getDataFromDocument(array $document)
|
||||
{
|
||||
$data[] = $document['data'];
|
||||
|
||||
if (isset($document['included'])) {
|
||||
$data = array_merge($data, $document['included']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
63
src/Http/WebApp/WebAppViewFactory.php
Normal file
63
src/Http/WebApp/WebAppViewFactory.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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\WebApp;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Serializer\CurrentUserSerializer;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
|
||||
class WebAppViewFactory
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @var CurrentUserSerializer
|
||||
*/
|
||||
protected $userSerializer;
|
||||
|
||||
/**
|
||||
* @param Client $api
|
||||
* @param Factory $view
|
||||
* @param LocaleManager $locales
|
||||
* @param CurrentUserSerializer $userSerializer
|
||||
*/
|
||||
public function __construct(Client $api, Factory $view, LocaleManager $locales, CurrentUserSerializer $userSerializer)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->view = $view;
|
||||
$this->locales = $locales;
|
||||
$this->userSerializer = $userSerializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $layout
|
||||
* @param WebAppAssets $assets
|
||||
* @return WebAppView
|
||||
*/
|
||||
public function make($layout, WebAppAssets $assets)
|
||||
{
|
||||
return new WebAppView($layout, $assets, $this->api, $this->view, $this->locales, $this->userSerializer);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user