1
0
mirror of https://github.com/flarum/core.git synced 2025-08-03 15:07:53 +02:00

Initial refactor of client actions, data preloading, SEO

An initial stab at flarum/core#126. Still WIP. Preliminary
implementation of flarum/core#128 and flarum/core#13.
This commit is contained in:
Toby Zerner
2015-07-07 15:29:21 +09:30
parent c7383601e2
commit 23eec806e6
27 changed files with 413 additions and 241 deletions

View File

@@ -33,6 +33,6 @@ class Client
$response = $action->handle(new Request($input, $actor));
return json_decode($response->getBody());
return new Response($response);
}
}

View File

@@ -0,0 +1,16 @@
<?php namespace Flarum\Api;
use Psr\Http\Message\ResponseInterface;
class Response
{
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
public function getBody()
{
return json_decode($this->response->getBody());
}
}

View File

@@ -1,5 +1,7 @@
<?php namespace Flarum\Api\Serializers;
use Flarum\Core;
class ForumSerializer extends Serializer
{
/**
@@ -21,7 +23,12 @@ class ForumSerializer extends Serializer
protected function getDefaultAttributes($forum)
{
return [
'title' => $forum->title
'title' => $forum->title,
'baseUrl' => Core::config('base_url'),
'apiUrl' => Core::config('api_url'),
'welcomeTitle' => Core::config('welcome_title'),
'welcomeMessage' => Core::config('welcome_message'),
'themePrimaryColor' => Core::config('theme_primary_color')
];
}
}

View File

@@ -0,0 +1,134 @@
<?php namespace Flarum\Forum\Actions;
use Flarum\Api\Client;
use Flarum\Assets\AssetManager;
use Flarum\Assets\JsCompiler;
use Flarum\Assets\LessCompiler;
use Flarum\Core;
use Flarum\Core\Users\User;
use Flarum\Forum\Events\RenderView;
use Flarum\Locale\JsCompiler as LocaleJsCompiler;
use Flarum\Locale\LocaleManager;
use Flarum\Support\ClientView;
use Flarum\Support\HtmlAction;
use Illuminate\Database\ConnectionInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
abstract class ClientAction extends HtmlAction
{
/**
* @var Client
*/
protected $apiClient;
protected $locales;
/**
* @param Client $apiClient
* @param LocaleManager $locales
*/
public function __construct(Client $apiClient, LocaleManager $locales)
{
$this->apiClient = $apiClient;
$this->locales = $locales;
}
/**
* @param Request $request
* @param array $routeParams
* @return \Flarum\Support\ClientView
*/
public function render(Request $request, array $routeParams = [])
{
$actor = app('flarum.actor');
$assets = $this->getAssets();
$locale = $this->getLocaleCompiler($actor);
$layout = 'flarum.forum::forum';
$view = new ClientView(
$request,
$actor,
$this->apiClient,
$layout,
$assets,
$locale
);
return $view;
}
protected function getAssets()
{
$public = $this->getAssetDirectory();
$assets = new AssetManager(
new JsCompiler($public, 'forum.js'),
new LessCompiler($public, 'forum.css')
);
$root = __DIR__.'/../../..';
$assets->addFile($root.'/js/forum/dist/app.js');
$assets->addFile($root.'/less/forum/app.less');
foreach ($this->getLessVariables() as $name => $value) {
$assets->addLess("@$name: $value;");
}
$assets->addLess(Core::config('custom_less'));
return $assets;
}
protected function getLessVariables()
{
return [
'fl-primary-color' => Core::config('theme_primary_color', '#000'),
'fl-secondary-color' => Core::config('theme_secondary_color', '#000'),
'fl-dark-mode' => Core::config('theme_dark_mode') ? 'true' : 'false',
'fl-colored-header' => Core::config('theme_colored_header') ? 'true' : 'false'
];
}
protected function getLocaleCompiler(User $actor)
{
$locale = $actor->locale ?: Core::config('locale', 'en');
// $translations = $this->locales->getTranslations($locale);
$jsFiles = $this->locales->getJsFiles($locale);
$compiler = new LocaleJsCompiler($this->getAssetDirectory(), 'locale-'.$locale.'.js');
// $compiler->setTranslations(static::filterTranslations($translations));
array_walk($jsFiles, [$compiler, 'addFile']);
return $compiler;
}
protected function getAssetDirectory()
{
return public_path().'/assets';
}
/**
* @param $translations
* @return array
*/
// protected static function filterTranslations($translations)
// {
// $filtered = [];
//
// foreach (static::$translations as $key) {
// $parts = explode('.', $key);
// $level = &$filtered;
//
// foreach ($parts as $part) {
// $level = &$level[$part];
// }
//
// $level = array_get($translations, $key);
// }
//
// return $filtered;
// }
}

View File

@@ -2,19 +2,25 @@
use Psr\Http\Message\ServerRequestInterface as Request;
class DiscussionAction extends IndexAction
class DiscussionAction extends ClientAction
{
protected function getDetails(Request $request, array $routeParams)
public function render(Request $request, array $routeParams = [])
{
$response = $this->apiClient->send(app('flarum.actor'), 'Flarum\Api\Actions\Discussions\ShowAction', [
$view = parent::render($request, $routeParams);
$actor = app('flarum.actor');
$action = 'Flarum\Api\Actions\Discussions\ShowAction';
$params = [
'id' => $routeParams['id'],
'page.near' => $routeParams['near']
]);
// TODO: return an object instead of an array?
return [
'title' => $response->data->attributes->title,
'response' => $response
];
$document = $this->apiClient->send($actor, $action, $params)->getBody();
$view->setTitle($document->data->attributes->title);
$view->setDocument($document);
$view->setContent(app('view')->make('flarum.forum::discussion', compact('document')));
return $view;
}
}

View File

@@ -11,33 +11,8 @@ use Flarum\Support\HtmlAction;
use Illuminate\Database\ConnectionInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
class IndexAction extends HtmlAction
class IndexAction extends ClientAction
{
/**
* @var Client
*/
protected $apiClient;
/**
* @var ConnectionInterface
*/
protected $database;
/**
* @var array
*/
public static $translations = [];
/**
* @param Client $apiClient
* @param ConnectionInterface $database
*/
public function __construct(Client $apiClient, ConnectionInterface $database)
{
$this->apiClient = $apiClient;
$this->database = $database;
}
/**
* @param Request $request
* @param array $routeParams
@@ -45,135 +20,22 @@ class IndexAction extends HtmlAction
*/
public function render(Request $request, array $routeParams = [])
{
$config = $this->database->table('config')
->whereIn('key', ['base_url', 'api_url', 'forum_title', 'welcome_title', 'welcome_message', 'theme_primary_color'])
->lists('value', 'key');
$session = [];
$view = parent::render($request, $routeParams);
$actor = app('flarum.actor');
$response = $this->apiClient->send($actor, 'Flarum\Api\Actions\Forum\ShowAction');
$data = [$response->data];
if (isset($response->included)) {
$data = array_merge($data, $response->included);
}
if ($actor->exists) {
$session = [
'userId' => $actor->id,
'token' => $request->getCookieParams()['flarum_remember'],
];
// 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?
$response = $this->apiClient->send($actor, 'Flarum\Api\Actions\Users\ShowAction', ['id' => $actor->id]);
$data[] = $response->data;
if (isset($response->included)) {
$data = array_merge($data, $response->included);
}
}
$details = $this->getDetails($request, $routeParams);
$data = array_merge($data, array_get($details, 'data', []));
$response = array_get($details, 'response');
$title = array_get($details, 'title');
$view = view('flarum.forum::index')
->with('title', ($title ? $title.' - ' : '').Core::config('forum_title'))
->with('config', $config)
->with('layout', 'flarum.forum::forum')
->with('data', $data)
->with('response', $response)
->with('session', $session);
$root = __DIR__.'/../../..';
$public = public_path().'/assets';
$assets = new AssetManager(
new JsCompiler($public, 'forum.js'),
new LessCompiler($public, 'forum.css')
);
$assets->addFile($root.'/js/forum/dist/app.js');
$assets->addFile($root.'/less/forum/app.less');
$variables = [
'fl-primary-color' => Core::config('theme_primary_color', '#000'),
'fl-secondary-color' => Core::config('theme_secondary_color', '#000'),
'fl-dark-mode' => Core::config('theme_dark_mode') ? 'true' : 'false',
'fl-colored-header' => Core::config('theme_colored_header') ? 'true' : 'false'
];
foreach ($variables as $name => $value) {
$assets->addLess("@$name: $value;");
}
$locale = $actor->locale ?: Core::config('locale', 'en');
$localeManager = app('flarum.localeManager');
$translations = $localeManager->getTranslations($locale);
$jsFiles = $localeManager->getJsFiles($locale);
$localeCompiler = new LocaleJsCompiler($public, 'locale-'.$locale.'.js');
$localeCompiler->setTranslations(static::filterTranslations($translations));
array_walk($jsFiles, [$localeCompiler, 'addFile']);
event(new RenderView($view, $assets, $this));
return $view
->with('styles', [$assets->getCssFile()])
->with('scripts', [$assets->getJsFile(), $localeCompiler->getFile()]);
}
/**
* @param Request $request
* @param array $routeParams
* @return array
*/
protected function getDetails(Request $request, array $routeParams)
{
$queryParams = $request->getQueryParams();
// Only preload data if we're viewing the default index with no filters,
// otherwise we have to do all kinds of crazy stuff
if (!count($queryParams) && $request->getUri()->getPath() === '/') {
$response = $this->apiClient->send(app('flarum.actor'), 'Flarum\Api\Actions\Discussions\IndexAction');
if (! count($queryParams) && $request->getUri()->getPath() === '/') {
$actor = app('flarum.actor');
$action = 'Flarum\Api\Actions\Discussions\IndexAction';
return [
'response' => $response
];
$document = $this->apiClient->send($actor, $action)->getBody();
$view->setDocument($document);
$view->setContent(app('view')->make('flarum.forum::index', compact('document')));
}
return [];
}
/**
* @param $translations
* @return array
*/
protected static function filterTranslations($translations)
{
$filtered = [];
foreach (static::$translations as $key) {
$parts = explode('.', $key);
$level = &$filtered;
foreach ($parts as $part) {
if (! isset($level[$part])) {
$level[$part] = [];
}
$level = &$level[$part];
}
$level = array_get($translations, $key);
}
return $filtered;
return $view;
}
}

View File

@@ -58,19 +58,13 @@ class ForumServiceProvider extends ServiceProvider
);
$routes->get(
'/d/{id:\d+}/{slug}',
'/d/{id:\d+}/{slug}[/{near}]',
'flarum.forum.discussion',
$this->action('Flarum\Forum\Actions\DiscussionAction')
);
$routes->get(
'/d/{id:\d+}/{slug}/{near}',
'flarum.forum.discussion.near',
$this->action('Flarum\Forum\Actions\DiscussionAction')
);
$routes->get(
'/u/{username}',
'/u/{username}[/{filter}]',
'flarum.forum.user',
$this->action('Flarum\Forum\Actions\IndexAction')
);

View File

@@ -0,0 +1,130 @@
<?php namespace Flarum\Support;
use Flarum\Api\Client;
use Flarum\Assets\AssetManager;
use Flarum\Core\Users\User;
use Psr\Http\Message\ServerRequestInterface as Request;
use Flarum\Locale\JsCompiler;
class ClientView
{
protected $actor;
protected $apiClient;
protected $title;
protected $document;
protected $content;
protected $request;
protected $layout;
public function __construct(
Request $request,
User $actor,
Client $apiClient,
$layout,
AssetManager $assets,
JsCompiler $locale
) {
$this->request = $request;
$this->actor = $actor;
$this->apiClient = $apiClient;
$this->layout = $layout;
$this->assets = $assets;
$this->locale = $locale;
}
public function setActor(User $actor)
{
$this->actor = $actor;
}
public function setTitle($title)
{
$this->title = $title;
}
public function setDocument($document)
{
$this->document = $document;
}
public function setContent($content)
{
$this->content = $content;
}
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->data = $data;
$view->session = $this->getSession();
$view->title = ($this->title ? $this->title . ' - ' : '') . $forum->data->attributes->title;
$view->document = $this->document;
$view->forum = $forum->data;
$view->layout = $this->layout;
$view->content = $this->content;
$view->styles = [$this->assets->getCssFile()];
$view->scripts = [$this->assets->getJsFile(), $this->locale->getFile()];
return $view->render();
}
public function __toString()
{
return $this->render();
}
protected function getForumDocument()
{
return $this->apiClient->send($this->actor, 'Flarum\Api\Actions\Forum\ShowAction')->getBody();
}
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?
$document = $this->apiClient->send(
$this->actor,
'Flarum\Api\Actions\Users\ShowAction',
['id' => $this->actor->id]
)->getBody();
return $document;
}
protected function getDataFromDocument($document)
{
$data[] = $document->data;
if (isset($document->included)) {
$data = array_merge($data, $document->included);
}
return $data;
}
protected function getSession()
{
return [
'userId' => $this->actor->id,
'token' => $this->request->getCookieParams()['flarum_remember'],
];
}
}