mirror of
https://github.com/flarum/framework.git
synced 2025-02-21 14:24:57 +08:00
Frontend refactor (#1471)
Refactor Frontend + Asset code - Use Laravel's Filesystem component for asset IO, meaning theoretically assets should be storable on S3 etc. - More reliable checking for asset recompilation when debug mode is on, so you don't have to constantly delete the compiled assets to force a recompile. Should also fix issues with locale JS files being recompiled with the same name and cached. - Remove JavaScript minification, because it will be done by Webpack (exception is for the TextFormatter JS). - Add support for JS sourcemaps. - Separate frontend view and assets completely. This is an important distinction because frontend assets are compiled independent of a request, whereas putting together a view depends on a request. - Bind frontend view/asset factory instances to the container (in service providers) rather than subclassing. Asset and content populators can be added to these factories – these are simply objects that populate the asset compilers or the view with information. - Add RouteHandlerFactory functions that make it easy to hook up a frontend controller with a frontend instance ± some content. - Remove the need for "nojs" - Fix cache:clear command - Recompile assets when settings/enabled extensions change
This commit is contained in:
parent
92d5f8f1b3
commit
651a6bf4ea
@ -45,7 +45,7 @@
|
||||
"matthiasmullie/minify": "^1.3",
|
||||
"monolog/monolog": "^1.16.0",
|
||||
"nikic/fast-route": "^0.6",
|
||||
"oyejorge/less.php": "~1.5",
|
||||
"oyejorge/less.php": "^1.7",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/http-server-handler": "^1.0",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
@ -57,7 +57,8 @@
|
||||
"symfony/yaml": "^3.3",
|
||||
"tobscure/json-api": "^0.3.0",
|
||||
"zendframework/zend-diactoros": "^1.7",
|
||||
"zendframework/zend-stratigility": "^3.0"
|
||||
"zendframework/zend-stratigility": "^3.0",
|
||||
"axy/sourcemap": "^0.1.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^0.9.4",
|
||||
|
@ -1,6 +1,5 @@
|
||||
import User from './models/User';
|
||||
import username from './helpers/username';
|
||||
import extractText from './utils/extractText';
|
||||
import extract from './utils/extract';
|
||||
|
||||
/**
|
||||
@ -23,6 +22,10 @@ export default class Translator {
|
||||
this.locale = null;
|
||||
}
|
||||
|
||||
addTranslations(translations) {
|
||||
Object.assign(this.translations, translations);
|
||||
}
|
||||
|
||||
trans(id, parameters) {
|
||||
const translation = this.translations[id];
|
||||
|
||||
|
@ -13,9 +13,8 @@ namespace Flarum\Admin;
|
||||
|
||||
use Flarum\Admin\Middleware\RequireAdministrateAbility;
|
||||
use Flarum\Event\ConfigureMiddleware;
|
||||
use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Frontend\RecompileFrontendAssets;
|
||||
use Flarum\Http\Middleware\AuthenticateWithSession;
|
||||
use Flarum\Http\Middleware\DispatchRoute;
|
||||
use Flarum\Http\Middleware\HandleErrors;
|
||||
@ -26,7 +25,6 @@ use Flarum\Http\Middleware\StartSession;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
|
||||
class AdminServiceProvider extends AbstractServiceProvider
|
||||
@ -64,6 +62,19 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
|
||||
return $pipe;
|
||||
});
|
||||
|
||||
$this->app->bind('flarum.admin.assets', function () {
|
||||
return $this->app->make('flarum.frontend.assets.defaults')('admin');
|
||||
});
|
||||
|
||||
$this->app->bind('flarum.admin.frontend', function () {
|
||||
$view = $this->app->make('flarum.frontend.view.defaults')('admin');
|
||||
|
||||
$view->setAssets($this->app->make('flarum.admin.assets'));
|
||||
$view->add($this->app->make(Content\AdminPayload::class));
|
||||
|
||||
return $view;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,12 +86,15 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
|
||||
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum.admin');
|
||||
|
||||
$this->registerListeners();
|
||||
$this->app->make('events')->subscribe(
|
||||
new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.admin.assets'),
|
||||
$this->app->make('flarum.locales')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the forum client routes.
|
||||
*
|
||||
* @param RouteCollection $routes
|
||||
*/
|
||||
protected function populateRoutes(RouteCollection $routes)
|
||||
@ -90,36 +104,4 @@ class AdminServiceProvider extends AbstractServiceProvider
|
||||
$callback = include __DIR__.'/routes.php';
|
||||
$callback($routes, $factory);
|
||||
}
|
||||
|
||||
protected function registerListeners()
|
||||
{
|
||||
$dispatcher = $this->app->make('events');
|
||||
|
||||
// Flush web app assets when the theme is changed
|
||||
$dispatcher->listen(Saved::class, function (Saved $event) {
|
||||
if (preg_match('/^theme_|^custom_less$/i', $event->key)) {
|
||||
$this->getWebAppAssets()->flushCss();
|
||||
}
|
||||
});
|
||||
|
||||
// Flush web app assets when extensions are changed
|
||||
$dispatcher->listen(Enabled::class, [$this, 'flushWebAppAssets']);
|
||||
$dispatcher->listen(Disabled::class, [$this, 'flushWebAppAssets']);
|
||||
|
||||
// Check the format of custom LESS code
|
||||
$dispatcher->subscribe(CheckCustomLessFormat::class);
|
||||
}
|
||||
|
||||
public function flushWebAppAssets()
|
||||
{
|
||||
$this->getWebAppAssets()->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Flarum\Frontend\FrontendAssets
|
||||
*/
|
||||
protected function getWebAppAssets()
|
||||
{
|
||||
return $this->app->make(Frontend::class)->getAssets();
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +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\Admin;
|
||||
|
||||
use Flarum\Foundation\ValidationException;
|
||||
use Flarum\Settings\Event\Serializing;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Less_Exception_Parser;
|
||||
use Less_Parser;
|
||||
|
||||
class CheckCustomLessFormat
|
||||
{
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Serializing::class, [$this, 'check']);
|
||||
}
|
||||
|
||||
public function check(Serializing $event)
|
||||
{
|
||||
if ($event->key === 'custom_less') {
|
||||
$parser = new Less_Parser();
|
||||
|
||||
try {
|
||||
// Check the custom less format before saving
|
||||
// Variables names are not checked, we would have to set them and call getCss() to check them
|
||||
$parser->parse($event->value);
|
||||
} catch (Less_Exception_Parser $e) {
|
||||
throw new ValidationException([
|
||||
'custom_less' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
framework/core/src/Admin/Content/AdminPayload.php
Normal file
60
framework/core/src/Admin/Content/AdminPayload.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Admin\Content;
|
||||
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Group\Permission;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class AdminPayload implements ContentInterface
|
||||
{
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var ExtensionManager
|
||||
*/
|
||||
protected $extensions;
|
||||
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
* @param ExtensionManager $extensions
|
||||
* @param ConnectionInterface $db
|
||||
*/
|
||||
public function __construct(SettingsRepositoryInterface $settings, ExtensionManager $extensions, ConnectionInterface $db)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
$this->extensions = $extensions;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function populate(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$document->payload['settings'] = $this->settings->all();
|
||||
$document->payload['permissions'] = Permission::map();
|
||||
$document->payload['extensions'] = $this->extensions->getExtensions()->toArray();
|
||||
|
||||
$document->payload['phpVersion'] = PHP_VERSION;
|
||||
$document->payload['mysqlVersion'] = $this->db->selectOne('select version() as version')->version;
|
||||
}
|
||||
}
|
@ -1,79 +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\Admin\Controller;
|
||||
|
||||
use Flarum\Admin\Frontend;
|
||||
use Flarum\Extension\ExtensionManager;
|
||||
use Flarum\Frontend\AbstractFrontendController;
|
||||
use Flarum\Group\Permission;
|
||||
use Flarum\Settings\Event\Deserializing;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class FrontendController extends AbstractFrontendController
|
||||
{
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var ExtensionManager
|
||||
*/
|
||||
protected $extensions;
|
||||
|
||||
/**
|
||||
* @var ConnectionInterface
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @param Frontend $webApp
|
||||
* @param Dispatcher $events
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
* @param ExtensionManager $extensions
|
||||
* @param ConnectionInterface $db
|
||||
*/
|
||||
public function __construct(Frontend $webApp, Dispatcher $events, SettingsRepositoryInterface $settings, ExtensionManager $extensions, ConnectionInterface $db)
|
||||
{
|
||||
$this->webApp = $webApp;
|
||||
$this->events = $events;
|
||||
$this->settings = $settings;
|
||||
$this->extensions = $extensions;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getView(ServerRequestInterface $request)
|
||||
{
|
||||
$view = parent::getView($request);
|
||||
|
||||
$settings = $this->settings->all();
|
||||
|
||||
$this->events->dispatch(
|
||||
new Deserializing($settings)
|
||||
);
|
||||
|
||||
$view->setVariable('settings', $settings);
|
||||
$view->setVariable('permissions', Permission::map());
|
||||
$view->setVariable('extensions', $this->extensions->getExtensions()->toArray());
|
||||
|
||||
$view->setVariable('phpVersion', PHP_VERSION);
|
||||
$view->setVariable('mysqlVersion', $this->db->selectOne('select version() as version')->version);
|
||||
|
||||
return $view;
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Admin\Controller;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
|
||||
@ -17,6 +16,6 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
||||
$map->get(
|
||||
'/',
|
||||
'index',
|
||||
$route->toController(Controller\FrontendController::class)
|
||||
$route->toAdmin()
|
||||
);
|
||||
};
|
||||
|
@ -11,8 +11,7 @@
|
||||
|
||||
namespace Flarum\Api\Controller;
|
||||
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Flarum\Settings\Event\Serializing;
|
||||
use Flarum\Settings\Event;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\AssertPermissionTrait;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
@ -53,14 +52,16 @@ class SetSettingsController implements RequestHandlerInterface
|
||||
|
||||
$settings = $request->getParsedBody();
|
||||
|
||||
$this->dispatcher->dispatch(new Event\Saving($settings));
|
||||
|
||||
foreach ($settings as $k => $v) {
|
||||
$this->dispatcher->dispatch(new Serializing($k, $v));
|
||||
$this->dispatcher->dispatch(new Event\Serializing($k, $v));
|
||||
|
||||
$this->settings->set($k, $v);
|
||||
|
||||
$this->dispatcher->dispatch(new Saved($k, $v));
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(new Event\Saved($settings));
|
||||
|
||||
return new EmptyResponse(204);
|
||||
}
|
||||
}
|
||||
|
@ -12,29 +12,37 @@
|
||||
namespace Flarum\Extend;
|
||||
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Frontend\Event\Rendering;
|
||||
use Flarum\Frontend\Asset\ExtensionAssets;
|
||||
use Flarum\Frontend\CompilerFactory;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Events\Dispatcher;
|
||||
|
||||
class Assets implements ExtenderInterface
|
||||
{
|
||||
protected $appName;
|
||||
protected $frontend;
|
||||
|
||||
protected $assets = [];
|
||||
protected $css = [];
|
||||
protected $js;
|
||||
|
||||
public function __construct($appName)
|
||||
public function __construct($frontend)
|
||||
{
|
||||
$this->appName = $appName;
|
||||
$this->frontend = $frontend;
|
||||
}
|
||||
|
||||
public function asset($path)
|
||||
public function css($path)
|
||||
{
|
||||
$this->assets[] = $path;
|
||||
$this->css[] = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function asset($path)
|
||||
{
|
||||
return $this->css($path);
|
||||
}
|
||||
|
||||
public function js($path)
|
||||
{
|
||||
$this->js = $path;
|
||||
@ -44,35 +52,13 @@ class Assets implements ExtenderInterface
|
||||
|
||||
public function __invoke(Container $container, Extension $extension = null)
|
||||
{
|
||||
$container->make(Dispatcher::class)->listen(
|
||||
Rendering::class,
|
||||
function (Rendering $event) use ($extension) {
|
||||
if (! $this->matches($event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event->addAssets($this->assets);
|
||||
|
||||
if ($this->js) {
|
||||
$event->view->getJs()->addString(function () use ($extension) {
|
||||
$name = $extension->getId();
|
||||
|
||||
return 'var module={};'.file_get_contents($this->js).";\nflarum.extensions['$name']=module.exports";
|
||||
});
|
||||
}
|
||||
$container->resolving(
|
||||
"flarum.$this->frontend.assets",
|
||||
function (CompilerFactory $assets) use ($extension) {
|
||||
$assets->add(function () use ($extension) {
|
||||
return new ExtensionAssets($extension, $this->css, $this->js);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function matches(Rendering $event)
|
||||
{
|
||||
switch ($this->appName) {
|
||||
case 'admin':
|
||||
return $event->isAdmin();
|
||||
case 'forum':
|
||||
return $event->isForum();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,6 +196,7 @@ class Formatter
|
||||
$configurator = $this->getConfigurator();
|
||||
$configurator->enableJavaScript();
|
||||
$configurator->javascript->exportMethods = ['preview'];
|
||||
$configurator->javascript->setMinifier('MatthiasMullieMinify');
|
||||
|
||||
return $configurator->finalize([
|
||||
'returnParser' => false,
|
||||
|
51
framework/core/src/Forum/Asset/CustomCss.php
Normal file
51
framework/core/src/Forum/Asset/CustomCss.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?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\Forum\Asset;
|
||||
|
||||
use Flarum\Frontend\Asset\AssetInterface;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
|
||||
class CustomCss implements AssetInterface
|
||||
{
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
*/
|
||||
public function __construct(SettingsRepositoryInterface $settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function css(SourceCollector $sources)
|
||||
{
|
||||
$sources->addString(function () {
|
||||
return $this->settings->get('custom_less');
|
||||
});
|
||||
}
|
||||
|
||||
public function js(SourceCollector $sources)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeJs(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeCss(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
}
|
51
framework/core/src/Forum/Asset/FormatterJs.php
Normal file
51
framework/core/src/Forum/Asset/FormatterJs.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?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\Forum\Asset;
|
||||
|
||||
use Flarum\Formatter\Formatter;
|
||||
use Flarum\Frontend\Asset\AssetInterface;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
|
||||
class FormatterJs implements AssetInterface
|
||||
{
|
||||
/**
|
||||
* @var Formatter
|
||||
*/
|
||||
protected $formatter;
|
||||
|
||||
/**
|
||||
* @param Formatter $formatter
|
||||
*/
|
||||
public function __construct(Formatter $formatter)
|
||||
{
|
||||
$this->formatter = $formatter;
|
||||
}
|
||||
|
||||
public function js(SourceCollector $sources)
|
||||
{
|
||||
$sources->addString(function () {
|
||||
return $this->formatter->getJs();
|
||||
});
|
||||
}
|
||||
|
||||
public function css(SourceCollector $sources)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeJs(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeCss(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
}
|
27
framework/core/src/Forum/Content/AssertRegistered.php
Normal file
27
framework/core/src/Forum/Content/AssertRegistered.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?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\Forum\Content;
|
||||
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\User\AssertPermissionTrait;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class AssertRegistered implements ContentInterface
|
||||
{
|
||||
use AssertPermissionTrait;
|
||||
|
||||
public function populate(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$this->assertRegistered($request->getAttribute('actor'));
|
||||
}
|
||||
}
|
@ -9,17 +9,18 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Forum\Controller;
|
||||
namespace Flarum\Forum\Content;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Forum\Frontend;
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Http\Exception\RouteNotFoundException;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class DiscussionController extends FrontendController
|
||||
class Discussion implements ContentInterface
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
@ -32,23 +33,24 @@ class DiscussionController extends FrontendController
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @var Factory
|
||||
*/
|
||||
public function __construct(Frontend $webApp, Dispatcher $events, Client $api, UrlGenerator $url)
|
||||
{
|
||||
parent::__construct($webApp, $events);
|
||||
|
||||
$this->api = $api;
|
||||
$this->url = $url;
|
||||
}
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param Client $api
|
||||
* @param UrlGenerator $url
|
||||
* @param Factory $view
|
||||
*/
|
||||
protected function getView(Request $request)
|
||||
public function __construct(Client $api, UrlGenerator $url, Factory $view)
|
||||
{
|
||||
$view = parent::getView($request);
|
||||
$this->api = $api;
|
||||
$this->url = $url;
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function populate(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$queryParams = $request->getQueryParams();
|
||||
$page = max(1, array_get($queryParams, 'page'));
|
||||
|
||||
@ -61,35 +63,35 @@ class DiscussionController extends FrontendController
|
||||
]
|
||||
];
|
||||
|
||||
$document = $this->getDocument($request->getAttribute('actor'), $params);
|
||||
$apiDocument = $this->getApiDocument($request->getAttribute('actor'), $params);
|
||||
|
||||
$getResource = function ($link) use ($document) {
|
||||
return array_first($document->included, function ($value, $key) use ($link) {
|
||||
$getResource = function ($link) use ($apiDocument) {
|
||||
return array_first($apiDocument->included, function ($value) use ($link) {
|
||||
return $value->type === $link->type && $value->id === $link->id;
|
||||
});
|
||||
};
|
||||
|
||||
$url = function ($newQueryParams) use ($queryParams, $document) {
|
||||
$url = function ($newQueryParams) use ($queryParams, $apiDocument) {
|
||||
$newQueryParams = array_merge($queryParams, $newQueryParams);
|
||||
$queryString = http_build_query($newQueryParams);
|
||||
|
||||
return $this->url->to('forum')->route('discussion', ['id' => $document->data->id]).
|
||||
return $this->url->to('forum')->route('discussion', ['id' => $apiDocument->data->id]).
|
||||
($queryString ? '?'.$queryString : '');
|
||||
};
|
||||
|
||||
$posts = [];
|
||||
|
||||
foreach ($document->included as $resource) {
|
||||
foreach ($apiDocument->included as $resource) {
|
||||
if ($resource->type === 'posts' && isset($resource->relationships->discussion) && isset($resource->attributes->contentHtml)) {
|
||||
$posts[] = $resource;
|
||||
}
|
||||
}
|
||||
|
||||
$view->title = $document->data->attributes->title;
|
||||
$view->document = $document;
|
||||
$view->content = app('view')->make('flarum.forum::frontend.content.discussion', compact('document', 'page', 'getResource', 'posts', 'url'));
|
||||
$document->title = $apiDocument->data->attributes->title;
|
||||
$document->content = $this->view->make('flarum.forum::frontend.content.discussion', compact('apiDocument', 'page', 'getResource', 'posts', 'url'));
|
||||
$document->payload['apiDocument'] = $apiDocument;
|
||||
|
||||
return $view;
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,7 +102,7 @@ class DiscussionController extends FrontendController
|
||||
* @return object
|
||||
* @throws RouteNotFoundException
|
||||
*/
|
||||
protected function getDocument(User $actor, array $params)
|
||||
protected function getApiDocument(User $actor, array $params)
|
||||
{
|
||||
$response = $this->api->send('Flarum\Api\Controller\ShowDiscussionController', $actor, $params);
|
||||
$statusCode = $response->getStatusCode();
|
97
framework/core/src/Forum/Content/Index.php
Normal file
97
framework/core/src/Forum/Content/Index.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?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\Forum\Content;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Controller\ListDiscussionsController;
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class Index implements ContentInterface
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @param Client $api
|
||||
* @param Factory $view
|
||||
*/
|
||||
public function __construct(Client $api, Factory $view)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function populate(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
$sort = array_pull($queryParams, 'sort');
|
||||
$q = array_pull($queryParams, 'q');
|
||||
$page = array_pull($queryParams, 'page', 1);
|
||||
|
||||
$sortMap = $this->getSortMap();
|
||||
|
||||
$params = [
|
||||
'sort' => $sort && isset($sortMap[$sort]) ? $sortMap[$sort] : '',
|
||||
'filter' => compact('q'),
|
||||
'page' => ['offset' => ($page - 1) * 20, 'limit' => 20]
|
||||
];
|
||||
|
||||
$apiDocument = $this->getApiDocument($request->getAttribute('actor'), $params);
|
||||
|
||||
$document->content = $this->view->make('flarum.forum::frontend.content.index', compact('apiDocument', 'page', 'forum'));
|
||||
$document->payload['apiDocument'] = $apiDocument;
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of sort query param values and their API sort params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getSortMap()
|
||||
{
|
||||
return [
|
||||
'latest' => '-lastTime',
|
||||
'top' => '-commentsCount',
|
||||
'newest' => '-startTime',
|
||||
'oldest' => 'startTime'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of an API request to list discussions.
|
||||
*
|
||||
* @param User $actor
|
||||
* @param array $params
|
||||
* @return object
|
||||
*/
|
||||
private function getApiDocument(User $actor, array $params)
|
||||
{
|
||||
return json_decode($this->api->send(ListDiscussionsController::class, $actor, $params)->getBody());
|
||||
}
|
||||
}
|
@ -1,30 +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\Forum\Controller;
|
||||
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class AuthorizedWebAppController extends FrontendController
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(Request $request)
|
||||
{
|
||||
if (! $request->getAttribute('session')->get('user_id')) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
|
||||
return parent::render($request);
|
||||
}
|
||||
}
|
@ -1,28 +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\Forum\Controller;
|
||||
|
||||
use Flarum\Forum\Frontend;
|
||||
use Flarum\Frontend\AbstractFrontendController;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class FrontendController extends AbstractFrontendController
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(Frontend $webApp, Dispatcher $events)
|
||||
{
|
||||
$this->webApp = $webApp;
|
||||
$this->events = $events;
|
||||
}
|
||||
}
|
@ -1,87 +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\Forum\Controller;
|
||||
|
||||
use Flarum\Api\Client as ApiClient;
|
||||
use Flarum\Forum\Frontend;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class IndexController extends FrontendController
|
||||
{
|
||||
/**
|
||||
* @var ApiClient
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* A map of sort query param values to their API sort param.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $sortMap = [
|
||||
'latest' => '-lastTime',
|
||||
'top' => '-commentsCount',
|
||||
'newest' => '-startTime',
|
||||
'oldest' => 'startTime'
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(Frontend $webApp, Dispatcher $events, ApiClient $api)
|
||||
{
|
||||
parent::__construct($webApp, $events);
|
||||
|
||||
$this->api = $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getView(Request $request)
|
||||
{
|
||||
$view = parent::getView($request);
|
||||
|
||||
$queryParams = $request->getQueryParams();
|
||||
|
||||
$sort = array_pull($queryParams, 'sort');
|
||||
$q = array_pull($queryParams, 'q');
|
||||
$page = array_pull($queryParams, 'page', 1);
|
||||
|
||||
$params = [
|
||||
'sort' => $sort && isset($this->sortMap[$sort]) ? $this->sortMap[$sort] : '',
|
||||
'filter' => compact('q'),
|
||||
'page' => ['offset' => ($page - 1) * 20, 'limit' => 20]
|
||||
];
|
||||
|
||||
$document = $this->getDocument($request->getAttribute('actor'), $params);
|
||||
|
||||
$view->document = $document;
|
||||
$view->content = app('view')->make('flarum.forum::frontend.content.index', compact('document', 'page', 'forum'));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of an API request to list discussions.
|
||||
*
|
||||
* @param User $actor
|
||||
* @param array $params
|
||||
* @return object
|
||||
*/
|
||||
private function getDocument(User $actor, array $params)
|
||||
{
|
||||
return json_decode($this->api->send('Flarum\Api\Controller\ListDiscussionsController', $actor, $params)->getBody());
|
||||
}
|
||||
}
|
@ -13,8 +13,6 @@ namespace Flarum\Forum;
|
||||
|
||||
use Flarum\Event\ConfigureForumRoutes;
|
||||
use Flarum\Event\ConfigureMiddleware;
|
||||
use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Http\Middleware\AuthenticateWithSession;
|
||||
use Flarum\Http\Middleware\CollectGarbage;
|
||||
@ -28,7 +26,6 @@ use Flarum\Http\Middleware\StartSession;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Zend\Stratigility\MiddlewarePipe;
|
||||
@ -69,6 +66,27 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
|
||||
return $pipe;
|
||||
});
|
||||
|
||||
$this->app->bind('flarum.forum.assets', function () {
|
||||
$assets = $this->app->make('flarum.frontend.assets.defaults')('forum');
|
||||
|
||||
$assets->add(function () {
|
||||
return [
|
||||
$this->app->make(Asset\FormatterJs::class),
|
||||
$this->app->make(Asset\CustomCss::class)
|
||||
];
|
||||
});
|
||||
|
||||
return $assets;
|
||||
});
|
||||
|
||||
$this->app->bind('flarum.forum.frontend', function () {
|
||||
$view = $this->app->make('flarum.frontend.view.defaults')('forum');
|
||||
|
||||
$view->setAssets($this->app->make('flarum.forum.assets'));
|
||||
|
||||
return $view;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,9 +103,13 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
'settings' => $this->app->make(SettingsRepositoryInterface::class)
|
||||
]);
|
||||
|
||||
$this->flushWebAppAssetsWhenThemeChanged();
|
||||
|
||||
$this->flushWebAppAssetsWhenExtensionsChanged();
|
||||
$this->app->make('events')->subscribe(
|
||||
new RecompileFrontendAssets(
|
||||
$this->app->make('flarum.forum.assets'),
|
||||
$this->app->make('flarum.locales'),
|
||||
$this->app
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,7 +133,7 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
if (isset($routes->getRouteData()[0]['GET'][$defaultRoute])) {
|
||||
$toDefaultController = $routes->getRouteData()[0]['GET'][$defaultRoute];
|
||||
} else {
|
||||
$toDefaultController = $factory->toController(Controller\IndexController::class);
|
||||
$toDefaultController = $factory->toForum(Content\Index::class);
|
||||
}
|
||||
|
||||
$routes->get(
|
||||
@ -120,34 +142,4 @@ class ForumServiceProvider extends AbstractServiceProvider
|
||||
$toDefaultController
|
||||
);
|
||||
}
|
||||
|
||||
protected function flushWebAppAssetsWhenThemeChanged()
|
||||
{
|
||||
$this->app->make('events')->listen(Saved::class, function (Saved $event) {
|
||||
if (preg_match('/^theme_|^custom_less$/i', $event->key)) {
|
||||
$this->getWebAppAssets()->flushCss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function flushWebAppAssetsWhenExtensionsChanged()
|
||||
{
|
||||
$events = $this->app->make('events');
|
||||
|
||||
$events->listen(Enabled::class, [$this, 'flushWebAppAssets']);
|
||||
$events->listen(Disabled::class, [$this, 'flushWebAppAssets']);
|
||||
}
|
||||
|
||||
public function flushWebAppAssets()
|
||||
{
|
||||
$this->getWebAppAssets()->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Flarum\Frontend\FrontendAssets
|
||||
*/
|
||||
protected function getWebAppAssets()
|
||||
{
|
||||
return $this->app->make(Frontend::class)->getAssets();
|
||||
}
|
||||
}
|
||||
|
@ -1,68 +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\Forum;
|
||||
|
||||
use Flarum\Formatter\Formatter;
|
||||
use Flarum\Frontend\AbstractFrontend;
|
||||
use Flarum\Frontend\FrontendAssetsFactory;
|
||||
use Flarum\Frontend\FrontendViewFactory;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
|
||||
class Frontend extends AbstractFrontend
|
||||
{
|
||||
/**
|
||||
* @var Formatter
|
||||
*/
|
||||
protected $formatter;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(
|
||||
FrontendAssetsFactory $assets,
|
||||
FrontendViewFactory $view,
|
||||
SettingsRepositoryInterface $settings,
|
||||
LocaleManager $locales,
|
||||
Formatter $formatter
|
||||
) {
|
||||
parent::__construct($assets, $view, $settings, $locales);
|
||||
|
||||
$this->formatter = $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getView()
|
||||
{
|
||||
$view = parent::getView();
|
||||
|
||||
$view->getJs()->addString(function () {
|
||||
return $this->formatter->getJs();
|
||||
});
|
||||
|
||||
$view->getCss()->addString(function () {
|
||||
return $this->settings->get('custom_less');
|
||||
});
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getName()
|
||||
{
|
||||
return 'forum';
|
||||
}
|
||||
}
|
110
framework/core/src/Forum/RecompileFrontendAssets.php
Normal file
110
framework/core/src/Forum/RecompileFrontendAssets.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?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\Forum;
|
||||
|
||||
use Flarum\Foundation\ValidationException;
|
||||
use Flarum\Frontend\CompilerFactory;
|
||||
use Flarum\Frontend\RecompileFrontendAssets as BaseListener;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Flarum\Settings\Event\Saving;
|
||||
use Flarum\Settings\OverrideSettingsRepository;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use League\Flysystem\Adapter\NullAdapter;
|
||||
use League\Flysystem\Filesystem;
|
||||
use Less_Exception_Parser;
|
||||
|
||||
class RecompileFrontendAssets extends BaseListener
|
||||
{
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @param CompilerFactory $assets
|
||||
* @param LocaleManager $locales
|
||||
* @param Container $container
|
||||
*/
|
||||
public function __construct(CompilerFactory $assets, LocaleManager $locales, Container $container)
|
||||
{
|
||||
parent::__construct($assets, $locales);
|
||||
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
parent::subscribe($events);
|
||||
|
||||
$events->listen(Saving::class, [$this, 'whenSettingsSaving']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Saving $event
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function whenSettingsSaving(Saving $event)
|
||||
{
|
||||
if (isset($event->settings['custom_less'])) {
|
||||
// We haven't saved the settings yet, but we want to trial a full
|
||||
// recompile of the CSS to see if this custom LESS will break
|
||||
// anything. In order to do that, we will temporarily override the
|
||||
// settings repository with the new settings so that the recompile
|
||||
// is effective. We will also use a dummy filesystem so that nothing
|
||||
// is actually written yet.
|
||||
|
||||
$settings = $this->container->make(SettingsRepositoryInterface::class);
|
||||
|
||||
$this->container->extend(
|
||||
SettingsRepositoryInterface::class,
|
||||
function ($settings) use ($event) {
|
||||
return new OverrideSettingsRepository($settings, $event->settings);
|
||||
}
|
||||
);
|
||||
|
||||
$assetsDir = $this->assets->getAssetsDir();
|
||||
$this->assets->setAssetsDir(new FilesystemAdapter(new Filesystem(new NullAdapter)));
|
||||
|
||||
try {
|
||||
$this->assets->makeCss()->commit();
|
||||
|
||||
foreach ($this->locales->getLocales() as $locale => $name) {
|
||||
$this->assets->makeLocaleCss($locale)->commit();
|
||||
}
|
||||
} catch (Less_Exception_Parser $e) {
|
||||
throw new ValidationException(['custom_less' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$this->assets->setAssetsDir($assetsDir);
|
||||
$this->container->instance(SettingsRepositoryInterface::class, $settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Saved $event
|
||||
*/
|
||||
public function whenSettingsSaved(Saved $event)
|
||||
{
|
||||
parent::whenSettingsSaved($event);
|
||||
|
||||
if (isset($event->settings['custom_less'])) {
|
||||
$this->flushCss();
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Flarum\Forum\Content;
|
||||
use Flarum\Forum\Controller;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\RouteHandlerFactory;
|
||||
@ -17,31 +18,31 @@ return function (RouteCollection $map, RouteHandlerFactory $route) {
|
||||
$map->get(
|
||||
'/all',
|
||||
'index',
|
||||
$route->toController(Controller\IndexController::class)
|
||||
$route->toForum(Content\Index::class)
|
||||
);
|
||||
|
||||
$map->get(
|
||||
'/d/{id:\d+(?:-[^/]*)?}[/{near:[^/]*}]',
|
||||
'discussion',
|
||||
$route->toController(Controller\DiscussionController::class)
|
||||
$route->toForum(Content\Discussion::class)
|
||||
);
|
||||
|
||||
$map->get(
|
||||
'/u/{username}[/{filter:[^/]*}]',
|
||||
'user',
|
||||
$route->toController(Controller\FrontendController::class)
|
||||
$route->toForum()
|
||||
);
|
||||
|
||||
$map->get(
|
||||
'/settings',
|
||||
'settings',
|
||||
$route->toController(Controller\AuthorizedWebAppController::class)
|
||||
$route->toForum(Content\AssertRegistered::class)
|
||||
);
|
||||
|
||||
$map->get(
|
||||
'/notifications',
|
||||
'notifications',
|
||||
$route->toController(Controller\AuthorizedWebAppController::class)
|
||||
$route->toForum(Content\AssertRegistered::class)
|
||||
);
|
||||
|
||||
$map->get(
|
||||
|
@ -11,10 +11,9 @@
|
||||
|
||||
namespace Flarum\Foundation\Console;
|
||||
|
||||
use Flarum\Admin\Frontend as AdminWebApp;
|
||||
use Flarum\Console\AbstractCommand;
|
||||
use Flarum\Forum\Frontend as ForumWebApp;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Illuminate\Contracts\Cache\Store;
|
||||
|
||||
class CacheClearCommand extends AbstractCommand
|
||||
@ -24,16 +23,6 @@ class CacheClearCommand extends AbstractCommand
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var ForumWebApp
|
||||
*/
|
||||
protected $forum;
|
||||
|
||||
/**
|
||||
* @var AdminWebApp
|
||||
*/
|
||||
protected $admin;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
@ -41,15 +30,11 @@ class CacheClearCommand extends AbstractCommand
|
||||
|
||||
/**
|
||||
* @param Store $cache
|
||||
* @param ForumWebApp $forum
|
||||
* @param AdminWebApp $admin
|
||||
* @param Application $app
|
||||
*/
|
||||
public function __construct(Store $cache, ForumWebApp $forum, AdminWebApp $admin, Application $app)
|
||||
public function __construct(Store $cache, Application $app)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->forum = $forum;
|
||||
$this->admin = $admin;
|
||||
$this->app = $app;
|
||||
|
||||
parent::__construct();
|
||||
@ -72,13 +57,12 @@ class CacheClearCommand extends AbstractCommand
|
||||
{
|
||||
$this->info('Clearing the cache...');
|
||||
|
||||
$this->forum->getAssets()->flush();
|
||||
$this->admin->getAssets()->flush();
|
||||
|
||||
$this->cache->flush();
|
||||
|
||||
$storagePath = $this->app->storagePath();
|
||||
array_map('unlink', glob($storagePath.'/formatter/*'));
|
||||
array_map('unlink', glob($storagePath.'/locale/*'));
|
||||
|
||||
event(new ClearingCache);
|
||||
}
|
||||
}
|
||||
|
@ -9,13 +9,8 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Event;
|
||||
namespace Flarum\Foundation\Event;
|
||||
|
||||
use Flarum\Frontend\Event\Rendering;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class ConfigureClientView extends Rendering
|
||||
class ClearingCache
|
||||
{
|
||||
}
|
@ -20,6 +20,7 @@ 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;
|
||||
@ -188,6 +189,7 @@ class Site
|
||||
|
||||
$app->register(DiscussionServiceProvider::class);
|
||||
$app->register(FormatterServiceProvider::class);
|
||||
$app->register(FrontendServiceProvider::class);
|
||||
$app->register(GroupServiceProvider::class);
|
||||
$app->register(NotificationServiceProvider::class);
|
||||
$app->register(PostServiceProvider::class);
|
||||
@ -231,6 +233,11 @@ class Site
|
||||
'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'
|
||||
|
@ -1,184 +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\Frontend;
|
||||
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
|
||||
abstract class AbstractFrontend
|
||||
{
|
||||
/**
|
||||
* @var FrontendAssetsFactory
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var FrontendViewFactory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @param FrontendAssetsFactory $assets
|
||||
* @param FrontendViewFactory $view
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct(FrontendAssetsFactory $assets, FrontendViewFactory $view, SettingsRepositoryInterface $settings, LocaleManager $locales)
|
||||
{
|
||||
$this->assets = $assets;
|
||||
$this->view = $view;
|
||||
$this->settings = $settings;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FrontendView
|
||||
*/
|
||||
public function getView()
|
||||
{
|
||||
$view = $this->view->make($this->getLayout(), $this->getAssets());
|
||||
|
||||
$this->addDefaultAssets($view);
|
||||
$this->addCustomLess($view);
|
||||
$this->addTranslations($view);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FrontendAssets
|
||||
*/
|
||||
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 'flarum.forum::frontend.'.$this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a regular expression to match against translation keys.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTranslationFilter()
|
||||
{
|
||||
return '/^.+(?:\.|::)(?:'.$this->getName().'|lib)\./';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FrontendView $view
|
||||
*/
|
||||
private function addDefaultAssets(FrontendView $view)
|
||||
{
|
||||
$root = __DIR__.'/../..';
|
||||
$name = $this->getName();
|
||||
|
||||
$view->getJs()->addFile("$root/js/dist/$name.js");
|
||||
$view->getCss()->addFile("$root/less/$name.less");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FrontendView $view
|
||||
*/
|
||||
private function addCustomLess(FrontendView $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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 FrontendView $view
|
||||
*/
|
||||
private function addTranslations(FrontendView $view)
|
||||
{
|
||||
$translations = array_get($this->locales->getTranslator()->getCatalogue()->all(), '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);
|
||||
}
|
||||
}
|
@ -1,57 +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\Frontend;
|
||||
|
||||
use Flarum\Event\ConfigureClientView;
|
||||
use Flarum\Frontend\Event\Rendering;
|
||||
use Flarum\Http\Controller\AbstractHtmlController;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
abstract class AbstractFrontendController extends AbstractHtmlController
|
||||
{
|
||||
/**
|
||||
* @var AbstractFrontend
|
||||
*/
|
||||
protected $webApp;
|
||||
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(Request $request)
|
||||
{
|
||||
$view = $this->getView($request);
|
||||
|
||||
$this->events->dispatch(
|
||||
new ConfigureClientView($this, $view, $request)
|
||||
);
|
||||
$this->events->dispatch(
|
||||
new Rendering($this, $view, $request)
|
||||
);
|
||||
|
||||
return $view->render($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return \Flarum\Frontend\FrontendView
|
||||
*/
|
||||
protected function getView(Request $request)
|
||||
{
|
||||
return $this->webApp->getView();
|
||||
}
|
||||
}
|
39
framework/core/src/Frontend/Asset/AssetInterface.php
Normal file
39
framework/core/src/Frontend/Asset/AssetInterface.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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\Frontend\Asset;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
|
||||
interface AssetInterface
|
||||
{
|
||||
/**
|
||||
* @param SourceCollector $sources
|
||||
*/
|
||||
public function js(SourceCollector $sources);
|
||||
|
||||
/**
|
||||
* @param SourceCollector $sources
|
||||
*/
|
||||
public function css(SourceCollector $sources);
|
||||
|
||||
/**
|
||||
* @param SourceCollector $sources
|
||||
* @param string $locale
|
||||
*/
|
||||
public function localeJs(SourceCollector $sources, string $locale);
|
||||
|
||||
/**
|
||||
* @param SourceCollector $sources
|
||||
* @param string $locale
|
||||
*/
|
||||
public function localeCss(SourceCollector $sources, string $locale);
|
||||
}
|
48
framework/core/src/Frontend/Asset/CoreAssets.php
Normal file
48
framework/core/src/Frontend/Asset/CoreAssets.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Frontend\Asset;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
|
||||
class CoreAssets implements AssetInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function js(SourceCollector $sources)
|
||||
{
|
||||
$sources->addFile(__DIR__."/../../../js/dist/$this->name.js");
|
||||
}
|
||||
|
||||
public function css(SourceCollector $sources)
|
||||
{
|
||||
$sources->addFile(__DIR__."/../../../less/$this->name.less");
|
||||
}
|
||||
|
||||
public function localeJs(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeCss(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
}
|
80
framework/core/src/Frontend/Asset/ExtensionAssets.php
Normal file
80
framework/core/src/Frontend/Asset/ExtensionAssets.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Frontend\Asset;
|
||||
|
||||
use Flarum\Extension\Extension;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
|
||||
class ExtensionAssets implements AssetInterface
|
||||
{
|
||||
/**
|
||||
* @var Extension
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $css;
|
||||
|
||||
/**
|
||||
* @var string|callable|null
|
||||
*/
|
||||
protected $js;
|
||||
|
||||
/**
|
||||
* @param Extension $extension
|
||||
* @param array $css
|
||||
* @param string|callable|null $js
|
||||
*/
|
||||
public function __construct(Extension $extension, array $css, $js = null)
|
||||
{
|
||||
$this->extension = $extension;
|
||||
$this->css = $css;
|
||||
$this->js = $js;
|
||||
}
|
||||
|
||||
public function js(SourceCollector $sources)
|
||||
{
|
||||
if ($this->js) {
|
||||
$sources->addString(function () {
|
||||
$name = $this->extension->getId();
|
||||
|
||||
return 'var module={};'.$this->getContent($this->js).";flarum.extensions['$name']=module.exports";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function css(SourceCollector $sources)
|
||||
{
|
||||
foreach ($this->css as $asset) {
|
||||
if (is_callable($asset)) {
|
||||
$sources->addString($asset);
|
||||
} else {
|
||||
$sources->addFile($asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getContent($asset)
|
||||
{
|
||||
return is_callable($asset) ? $asset() : file_get_contents($asset);
|
||||
}
|
||||
|
||||
public function localeJs(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeCss(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
}
|
@ -1,51 +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\Frontend\Asset;
|
||||
|
||||
use Illuminate\Cache\Repository;
|
||||
|
||||
class JsCompiler extends RevisionCompiler
|
||||
{
|
||||
/**
|
||||
* @var Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $filename
|
||||
* @param bool $watch
|
||||
* @param Repository $cache
|
||||
*/
|
||||
public function __construct($path, $filename, $watch = false, Repository $cache = null)
|
||||
{
|
||||
parent::__construct($path, $filename, $watch);
|
||||
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function format($string)
|
||||
{
|
||||
return $string.";\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCacheDifferentiator()
|
||||
{
|
||||
return $this->watch;
|
||||
}
|
||||
}
|
@ -1,70 +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\Frontend\Asset;
|
||||
|
||||
use Less_Exception_Parser;
|
||||
use Less_Parser;
|
||||
|
||||
class LessCompiler extends RevisionCompiler
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cachePath;
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $filename
|
||||
* @param bool $watch
|
||||
* @param string $cachePath
|
||||
*/
|
||||
public function __construct($path, $filename, $watch, $cachePath)
|
||||
{
|
||||
parent::__construct($path, $filename, $watch);
|
||||
|
||||
$this->cachePath = $cachePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile()
|
||||
{
|
||||
if (! count($this->files) || ! count($this->strings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ini_set('xdebug.max_nesting_level', 200);
|
||||
|
||||
$parser = new Less_Parser([
|
||||
'compress' => true,
|
||||
'cache_dir' => $this->cachePath,
|
||||
'import_dirs' => [
|
||||
base_path('vendor/components/font-awesome/less') => '',
|
||||
],
|
||||
]);
|
||||
|
||||
try {
|
||||
foreach ($this->files as $file) {
|
||||
$parser->parseFile($file);
|
||||
}
|
||||
|
||||
foreach ($this->strings as $callback) {
|
||||
$parser->parse($callback());
|
||||
}
|
||||
|
||||
return $parser->getCss();
|
||||
} catch (Less_Exception_Parser $e) {
|
||||
// TODO: log an error somewhere?
|
||||
}
|
||||
}
|
||||
}
|
65
framework/core/src/Frontend/Asset/LessVariables.php
Normal file
65
framework/core/src/Frontend/Asset/LessVariables.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?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\Frontend\Asset;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
|
||||
class LessVariables implements AssetInterface
|
||||
{
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
*/
|
||||
public function __construct(SettingsRepositoryInterface $settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function css(SourceCollector $sources)
|
||||
{
|
||||
$this->addLessVariables($sources);
|
||||
}
|
||||
|
||||
public function localeCss(SourceCollector $sources, string $locale)
|
||||
{
|
||||
$this->addLessVariables($sources);
|
||||
}
|
||||
|
||||
private function addLessVariables(SourceCollector $compiler)
|
||||
{
|
||||
$vars = [
|
||||
'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'
|
||||
];
|
||||
|
||||
$compiler->addString(function () use ($vars) {
|
||||
return array_reduce(array_keys($vars), function ($string, $name) use ($vars) {
|
||||
return $string."@$name: {$vars[$name]};";
|
||||
}, '');
|
||||
});
|
||||
}
|
||||
|
||||
public function js(SourceCollector $sources)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeJs(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
}
|
53
framework/core/src/Frontend/Asset/LocaleAssets.php
Normal file
53
framework/core/src/Frontend/Asset/LocaleAssets.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Frontend\Asset;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
|
||||
class LocaleAssets implements AssetInterface
|
||||
{
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct(LocaleManager $locales)
|
||||
{
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
public function localeJs(SourceCollector $sources, string $locale)
|
||||
{
|
||||
foreach ($this->locales->getJsFiles($locale) as $file) {
|
||||
$sources->addFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
public function localeCss(SourceCollector $sources, string $locale)
|
||||
{
|
||||
foreach ($this->locales->getCssFiles($locale) as $file) {
|
||||
$sources->addFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
public function js(SourceCollector $sources)
|
||||
{
|
||||
}
|
||||
|
||||
public function css(SourceCollector $sources)
|
||||
{
|
||||
}
|
||||
}
|
@ -1,33 +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\Frontend\Asset;
|
||||
|
||||
class LocaleJsCompiler extends JsCompiler
|
||||
{
|
||||
protected $translations = [];
|
||||
|
||||
public function setTranslations(array $translations)
|
||||
{
|
||||
$this->translations = $translations;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
$output = 'flarum.core.app.translator.translations='.json_encode($this->translations).";\n";
|
||||
|
||||
foreach ($this->files as $filename) {
|
||||
$output .= file_get_contents($filename);
|
||||
}
|
||||
|
||||
return $this->format($output);
|
||||
}
|
||||
}
|
@ -1,207 +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\Frontend\Asset;
|
||||
|
||||
class RevisionCompiler implements CompilerInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $files = [];
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $strings = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $watch;
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $filename
|
||||
* @param bool $watch
|
||||
*/
|
||||
public function __construct($path, $filename, $watch = false)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->filename = $filename;
|
||||
$this->watch = $watch;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFilename($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addFile($file)
|
||||
{
|
||||
$this->files[] = $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addString(callable $callback)
|
||||
{
|
||||
$this->strings[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFile()
|
||||
{
|
||||
$old = $current = $this->getRevision();
|
||||
|
||||
$ext = pathinfo($this->filename, PATHINFO_EXTENSION);
|
||||
$file = $this->path.'/'.substr_replace($this->filename, '-'.$old, -strlen($ext) - 1, 0);
|
||||
|
||||
if ($this->watch || ! $old) {
|
||||
$cacheDifferentiator = [$this->getCacheDifferentiator()];
|
||||
|
||||
foreach ($this->files as $source) {
|
||||
$cacheDifferentiator[] = [$source, filemtime($source)];
|
||||
}
|
||||
|
||||
foreach ($this->strings as $callback) {
|
||||
$cacheDifferentiator[] = $callback();
|
||||
}
|
||||
|
||||
$current = hash('crc32b', serialize($cacheDifferentiator));
|
||||
}
|
||||
|
||||
$exists = file_exists($file);
|
||||
|
||||
if (! $exists || $old !== $current) {
|
||||
if ($exists) {
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
$file = $this->path.'/'.substr_replace($this->filename, '-'.$current, -strlen($ext) - 1, 0);
|
||||
|
||||
if ($content = $this->compile()) {
|
||||
$this->putRevision($current);
|
||||
|
||||
file_put_contents($file, $content);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getCacheDifferentiator()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
protected function format($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile()
|
||||
{
|
||||
$output = '';
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
$output .= $this->formatFile($file);
|
||||
}
|
||||
|
||||
foreach ($this->strings as $callback) {
|
||||
$output .= $this->format($callback());
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
protected function formatFile($file)
|
||||
{
|
||||
return $this->format(file_get_contents($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getRevisionFile()
|
||||
{
|
||||
return $this->path.'/rev-manifest.json';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getRevision()
|
||||
{
|
||||
if (file_exists($file = $this->getRevisionFile())) {
|
||||
$manifest = json_decode(file_get_contents($file), true);
|
||||
|
||||
return array_get($manifest, $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $revision
|
||||
* @return int
|
||||
*/
|
||||
protected function putRevision($revision)
|
||||
{
|
||||
if (file_exists($file = $this->getRevisionFile())) {
|
||||
$manifest = json_decode(file_get_contents($file), true);
|
||||
} else {
|
||||
$manifest = [];
|
||||
}
|
||||
|
||||
$manifest[$this->filename] = $revision;
|
||||
|
||||
return file_put_contents($this->getRevisionFile(), json_encode($manifest));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$revision = $this->getRevision();
|
||||
|
||||
$ext = pathinfo($this->filename, PATHINFO_EXTENSION);
|
||||
|
||||
$file = $this->path.'/'.substr_replace($this->filename, '-'.$revision, -strlen($ext) - 1, 0);
|
||||
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
87
framework/core/src/Frontend/Asset/Translations.php
Normal file
87
framework/core/src/Frontend/Asset/Translations.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?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\Frontend\Asset;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
|
||||
class Translations implements AssetInterface
|
||||
{
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
/**
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct(LocaleManager $locales)
|
||||
{
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->filter = function () {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
public function localeJs(SourceCollector $sources, string $locale)
|
||||
{
|
||||
$sources->addString(function () use ($locale) {
|
||||
$translations = $this->getTranslations($locale);
|
||||
|
||||
return 'flarum.core.app.translator.addTranslations('.json_encode($translations).')';
|
||||
});
|
||||
}
|
||||
|
||||
private function getTranslations(string $locale)
|
||||
{
|
||||
$translations = $this->locales->getTranslator()->getCatalogue($locale)->all('messages');
|
||||
|
||||
return array_only(
|
||||
$translations,
|
||||
array_filter(array_keys($translations), $this->filter)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function getFilter(): callable
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $filter
|
||||
*/
|
||||
public function setFilter(callable $filter)
|
||||
{
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
public function js(SourceCollector $sources)
|
||||
{
|
||||
}
|
||||
|
||||
public function css(SourceCollector $sources)
|
||||
{
|
||||
}
|
||||
|
||||
public function localeCss(SourceCollector $sources, string $locale)
|
||||
{
|
||||
}
|
||||
}
|
@ -9,34 +9,31 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Frontend\Asset;
|
||||
namespace Flarum\Frontend\Compiler;
|
||||
|
||||
interface CompilerInterface
|
||||
{
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public function setFilename($filename);
|
||||
public function getFilename(): string;
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string $filename
|
||||
*/
|
||||
public function addFile($file);
|
||||
public function setFilename(string $filename);
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function addString(callable $callback);
|
||||
public function addSources(callable $callback);
|
||||
|
||||
public function commit();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFile();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function compile();
|
||||
public function getUrl(): ?string;
|
||||
|
||||
public function flush();
|
||||
}
|
84
framework/core/src/Frontend/Compiler/JsCompiler.php
Normal file
84
framework/core/src/Frontend/Compiler/JsCompiler.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?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\Frontend\Compiler;
|
||||
|
||||
use axy\sourcemap\SourceMap;
|
||||
use Flarum\Frontend\Compiler\Source\FileSource;
|
||||
|
||||
class JsCompiler extends RevisionCompiler
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function save(string $file, array $sources): bool
|
||||
{
|
||||
$mapFile = $file.'.map';
|
||||
|
||||
$map = new SourceMap();
|
||||
$map->file = $mapFile;
|
||||
$output = [];
|
||||
$line = 0;
|
||||
|
||||
// For each of the sources, get their content and add it to the
|
||||
// output. For file sources, if a sourcemap is present, add it to
|
||||
// the output sourcemap.
|
||||
foreach ($sources as $source) {
|
||||
$content = $source->getContent();
|
||||
|
||||
if ($source instanceof FileSource) {
|
||||
$sourceMap = $source->getPath().'.map';
|
||||
|
||||
if (file_exists($sourceMap)) {
|
||||
$map->concat($sourceMap, $line);
|
||||
}
|
||||
}
|
||||
|
||||
$content = $this->format($content);
|
||||
$output[] = $content;
|
||||
$line += substr_count($content, "\n") + 1;
|
||||
}
|
||||
|
||||
// Add a comment to the end of our file to point to the sourcemap
|
||||
// we just constructed. We will then write the JS file, save the
|
||||
// map to a temporary location, and then move it to the asset dir.
|
||||
$output[] = '//# sourceMappingURL='.$this->assetsDir->url($mapFile);
|
||||
|
||||
$this->assetsDir->put($file, implode("\n", $output));
|
||||
|
||||
$mapTemp = tempnam(sys_get_temp_dir(), $mapFile);
|
||||
$map->save($mapTemp);
|
||||
$this->assetsDir->put($mapFile, file_get_contents($mapTemp));
|
||||
@unlink($mapTemp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function format(string $string): string
|
||||
{
|
||||
return preg_replace('~//# sourceMappingURL.*$~s', '', $string).";\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function delete(string $file)
|
||||
{
|
||||
parent::delete($file);
|
||||
|
||||
if ($this->assetsDir->has($mapFile = $file.'.map')) {
|
||||
$this->assetsDir->delete($mapFile);
|
||||
}
|
||||
}
|
||||
}
|
96
framework/core/src/Frontend/Compiler/LessCompiler.php
Normal file
96
framework/core/src/Frontend/Compiler/LessCompiler.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?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\Frontend\Compiler;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\FileSource;
|
||||
use Less_Parser;
|
||||
|
||||
class LessCompiler extends RevisionCompiler
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheDir;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $importDirs = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCacheDir(): string
|
||||
{
|
||||
return $this->cacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cacheDir
|
||||
*/
|
||||
public function setCacheDir(string $cacheDir)
|
||||
{
|
||||
$this->cacheDir = $cacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getImportDirs(): array
|
||||
{
|
||||
return $this->importDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $importDirs
|
||||
*/
|
||||
public function setImportDirs(array $importDirs)
|
||||
{
|
||||
$this->importDirs = $importDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function compile(array $sources): string
|
||||
{
|
||||
if (! count($sources)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
ini_set('xdebug.max_nesting_level', 200);
|
||||
|
||||
$parser = new Less_Parser([
|
||||
'compress' => true,
|
||||
'cache_dir' => $this->cacheDir,
|
||||
'import_dirs' => $this->importDirs
|
||||
]);
|
||||
|
||||
foreach ($sources as $source) {
|
||||
if ($source instanceof FileSource) {
|
||||
$parser->parseFile($source->getPath());
|
||||
} else {
|
||||
$parser->parse($source->getContent());
|
||||
}
|
||||
}
|
||||
|
||||
return $parser->getCss();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getCacheDifferentiator()
|
||||
{
|
||||
return time();
|
||||
}
|
||||
}
|
276
framework/core/src/Frontend/Compiler/RevisionCompiler.php
Normal file
276
framework/core/src/Frontend/Compiler/RevisionCompiler.php
Normal file
@ -0,0 +1,276 @@
|
||||
<?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\Frontend\Compiler;
|
||||
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Flarum\Frontend\Compiler\Source\SourceInterface;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
|
||||
class RevisionCompiler implements CompilerInterface
|
||||
{
|
||||
const REV_MANIFEST = 'rev-manifest.json';
|
||||
|
||||
const EMPTY_REVISION = 'empty';
|
||||
|
||||
/**
|
||||
* @var FilesystemAdapter
|
||||
*/
|
||||
protected $assetsDir;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $sourcesCallbacks = [];
|
||||
|
||||
/**
|
||||
* @param FilesystemAdapter $assetsDir
|
||||
* @param string $filename
|
||||
*/
|
||||
public function __construct(FilesystemAdapter $assetsDir, string $filename)
|
||||
{
|
||||
$this->assetsDir = $assetsDir;
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilename(): string
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFilename(string $filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$sources = $this->getSources();
|
||||
|
||||
$oldRevision = $this->getRevision();
|
||||
|
||||
$newRevision = $this->calculateRevision($sources);
|
||||
|
||||
$oldFile = $oldRevision ? $this->getFilenameForRevision($oldRevision) : null;
|
||||
|
||||
if ($oldRevision !== $newRevision || ($oldFile && ! $this->assetsDir->has($oldFile))) {
|
||||
$newFile = $this->getFilenameForRevision($newRevision);
|
||||
|
||||
if (! $this->save($newFile, $sources)) {
|
||||
// If no file was written (because the sources were empty), we
|
||||
// will set the revision to a special value so that we can tell
|
||||
// that this file does not have a URL.
|
||||
$newRevision = static::EMPTY_REVISION;
|
||||
}
|
||||
|
||||
$this->putRevision($newRevision);
|
||||
|
||||
if ($oldFile) {
|
||||
$this->delete($oldFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSources(callable $callback)
|
||||
{
|
||||
$this->sourcesCallbacks[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SourceInterface[]
|
||||
*/
|
||||
protected function getSources()
|
||||
{
|
||||
$sources = new SourceCollector;
|
||||
|
||||
foreach ($this->sourcesCallbacks as $callback) {
|
||||
$callback($sources);
|
||||
}
|
||||
|
||||
return $sources->getSources();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
$revision = $this->getRevision();
|
||||
|
||||
if (! $revision) {
|
||||
$this->commit();
|
||||
|
||||
$revision = $this->getRevision();
|
||||
|
||||
if (! $revision) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($revision === static::EMPTY_REVISION) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$file = $this->getFilenameForRevision($revision);
|
||||
|
||||
return $this->assetsDir->url($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param SourceInterface[] $sources
|
||||
* @return bool true if the file was written, false if there was nothing to write
|
||||
*/
|
||||
protected function save(string $file, array $sources): bool
|
||||
{
|
||||
if ($content = $this->compile($sources)) {
|
||||
$this->assetsDir->put($file, $content);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SourceInterface[] $sources
|
||||
* @return string
|
||||
*/
|
||||
protected function compile(array $sources): string
|
||||
{
|
||||
$output = '';
|
||||
|
||||
foreach ($sources as $source) {
|
||||
$output .= $this->format($source->getContent());
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
protected function format(string $string): string
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename for the given revision.
|
||||
*
|
||||
* @param string $revision
|
||||
* @return string
|
||||
*/
|
||||
protected function getFilenameForRevision(string $revision): string
|
||||
{
|
||||
$ext = pathinfo($this->filename, PATHINFO_EXTENSION);
|
||||
|
||||
return substr_replace($this->filename, '-'.$revision, -strlen($ext) - 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getRevision(): ?string
|
||||
{
|
||||
if ($this->assetsDir->has(static::REV_MANIFEST)) {
|
||||
$manifest = json_decode($this->assetsDir->read(static::REV_MANIFEST), true);
|
||||
|
||||
return array_get($manifest, $this->filename);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $revision
|
||||
*/
|
||||
protected function putRevision(?string $revision)
|
||||
{
|
||||
if ($this->assetsDir->has(static::REV_MANIFEST)) {
|
||||
$manifest = json_decode($this->assetsDir->read(static::REV_MANIFEST), true);
|
||||
} else {
|
||||
$manifest = [];
|
||||
}
|
||||
|
||||
if ($revision) {
|
||||
$manifest[$this->filename] = $revision;
|
||||
} else {
|
||||
unset($manifest[$this->filename]);
|
||||
}
|
||||
|
||||
$this->assetsDir->put(static::REV_MANIFEST, json_encode($manifest));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SourceInterface[] $sources
|
||||
* @return string
|
||||
*/
|
||||
protected function calculateRevision(array $sources): string
|
||||
{
|
||||
$cacheDifferentiator = [$this->getCacheDifferentiator()];
|
||||
|
||||
foreach ($sources as $source) {
|
||||
$cacheDifferentiator[] = $source->getCacheDifferentiator();
|
||||
}
|
||||
|
||||
return hash('crc32b', serialize($cacheDifferentiator));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getCacheDifferentiator()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
if ($revision = $this->getRevision()) {
|
||||
$file = $this->getFilenameForRevision($revision);
|
||||
|
||||
$this->delete($file);
|
||||
|
||||
$this->putRevision(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*/
|
||||
protected function delete(string $file)
|
||||
{
|
||||
if ($this->assetsDir->has($file)) {
|
||||
$this->assetsDir->delete($file);
|
||||
}
|
||||
}
|
||||
}
|
52
framework/core/src/Frontend/Compiler/Source/FileSource.php
Normal file
52
framework/core/src/Frontend/Compiler/Source/FileSource.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\Frontend\Compiler\Source;
|
||||
|
||||
class FileSource implements SourceInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct(string $path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(): string
|
||||
{
|
||||
return file_get_contents($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheDifferentiator()
|
||||
{
|
||||
return [$this->path, filemtime($this->path)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?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\Frontend\Compiler\Source;
|
||||
|
||||
class SourceCollector
|
||||
{
|
||||
/**
|
||||
* @var SourceInterface[]
|
||||
*/
|
||||
protected $sources = [];
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @return $this
|
||||
*/
|
||||
public function addFile(string $file)
|
||||
{
|
||||
$this->sources[] = new FileSource($file);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function addString(callable $callback)
|
||||
{
|
||||
$this->sources[] = new StringSource($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SourceInterface[]
|
||||
*/
|
||||
public function getSources()
|
||||
{
|
||||
return $this->sources;
|
||||
}
|
||||
}
|
@ -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\Frontend\Compiler\Source;
|
||||
|
||||
interface SourceInterface
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(): string;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheDifferentiator();
|
||||
}
|
50
framework/core/src/Frontend/Compiler/Source/StringSource.php
Normal file
50
framework/core/src/Frontend/Compiler/Source/StringSource.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?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\Frontend\Compiler\Source;
|
||||
|
||||
class StringSource implements SourceInterface
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
private $content;
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function __construct(callable $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(): string
|
||||
{
|
||||
if (is_null($this->content)) {
|
||||
$this->content = call_user_func($this->callback);
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCacheDifferentiator()
|
||||
{
|
||||
return $this->getContent();
|
||||
}
|
||||
}
|
239
framework/core/src/Frontend/CompilerFactory.php
Normal file
239
framework/core/src/Frontend/CompilerFactory.php
Normal file
@ -0,0 +1,239 @@
|
||||
<?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\Frontend;
|
||||
|
||||
use Flarum\Frontend\Asset\AssetInterface;
|
||||
use Flarum\Frontend\Compiler\CompilerInterface;
|
||||
use Flarum\Frontend\Compiler\JsCompiler;
|
||||
use Flarum\Frontend\Compiler\LessCompiler;
|
||||
use Flarum\Frontend\Compiler\Source\SourceCollector;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
|
||||
/**
|
||||
* A factory class for creating frontend asset compilers.
|
||||
*/
|
||||
class CompilerFactory
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var FilesystemAdapter
|
||||
*/
|
||||
protected $assetsDir;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheDir;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $lessImportDirs;
|
||||
|
||||
/**
|
||||
* @var AssetInterface[]
|
||||
*/
|
||||
protected $assets = [];
|
||||
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
protected $addCallbacks = [];
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param FilesystemAdapter $assetsDir
|
||||
* @param string $cacheDir
|
||||
* @param array|null $lessImportDirs
|
||||
*/
|
||||
public function __construct(string $name, FilesystemAdapter $assetsDir, string $cacheDir = null, array $lessImportDirs = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->assetsDir = $assetsDir;
|
||||
$this->cacheDir = $cacheDir;
|
||||
$this->lessImportDirs = $lessImportDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function add(callable $callback)
|
||||
{
|
||||
$this->addCallbacks[] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JsCompiler
|
||||
*/
|
||||
public function makeJs(): JsCompiler
|
||||
{
|
||||
$compiler = new JsCompiler($this->assetsDir, $this->name.'.js');
|
||||
|
||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) {
|
||||
$asset->js($sources);
|
||||
});
|
||||
|
||||
return $compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LessCompiler
|
||||
*/
|
||||
public function makeCss(): LessCompiler
|
||||
{
|
||||
$compiler = $this->makeLessCompiler($this->name.'.css');
|
||||
|
||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) {
|
||||
$asset->css($sources);
|
||||
});
|
||||
|
||||
return $compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $locale
|
||||
* @return JsCompiler
|
||||
*/
|
||||
public function makeLocaleJs(string $locale): JsCompiler
|
||||
{
|
||||
$compiler = new JsCompiler($this->assetsDir, $this->name.'-'.$locale.'.js');
|
||||
|
||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) use ($locale) {
|
||||
$asset->localeJs($sources, $locale);
|
||||
});
|
||||
|
||||
return $compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $locale
|
||||
* @return LessCompiler
|
||||
*/
|
||||
public function makeLocaleCss(string $locale): LessCompiler
|
||||
{
|
||||
$compiler = $this->makeLessCompiler($this->name.'-'.$locale.'.css');
|
||||
|
||||
$this->addSources($compiler, function (AssetInterface $asset, SourceCollector $sources) use ($locale) {
|
||||
$asset->localeCss($sources, $locale);
|
||||
});
|
||||
|
||||
return $compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return LessCompiler
|
||||
*/
|
||||
protected function makeLessCompiler(string $filename): LessCompiler
|
||||
{
|
||||
$compiler = new LessCompiler($this->assetsDir, $filename);
|
||||
|
||||
if ($this->cacheDir) {
|
||||
$compiler->setCacheDir($this->cacheDir.'/less');
|
||||
}
|
||||
|
||||
if ($this->lessImportDirs) {
|
||||
$compiler->setImportDirs($this->lessImportDirs);
|
||||
}
|
||||
|
||||
return $compiler;
|
||||
}
|
||||
|
||||
protected function fireAddCallbacks()
|
||||
{
|
||||
foreach ($this->addCallbacks as $callback) {
|
||||
$assets = $callback($this);
|
||||
$this->assets = array_merge($this->assets, is_array($assets) ? $assets : [$assets]);
|
||||
}
|
||||
|
||||
$this->addCallbacks = [];
|
||||
}
|
||||
|
||||
private function addSources(CompilerInterface $compiler, callable $callback)
|
||||
{
|
||||
$compiler->addSources(function ($sources) use ($callback) {
|
||||
$this->fireAddCallbacks();
|
||||
|
||||
foreach ($this->assets as $asset) {
|
||||
$callback($asset, $sources);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FilesystemAdapter
|
||||
*/
|
||||
public function getAssetsDir(): FilesystemAdapter
|
||||
{
|
||||
return $this->assetsDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilesystemAdapter $assetsDir
|
||||
*/
|
||||
public function setAssetsDir(FilesystemAdapter $assetsDir)
|
||||
{
|
||||
$this->assetsDir = $assetsDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCacheDir(): ?string
|
||||
{
|
||||
return $this->cacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cacheDir
|
||||
*/
|
||||
public function setCacheDir(?string $cacheDir)
|
||||
{
|
||||
$this->cacheDir = $cacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLessImportDirs(): array
|
||||
{
|
||||
return $this->lessImportDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $lessImportDirs
|
||||
*/
|
||||
public function setLessImportDirs(array $lessImportDirs)
|
||||
{
|
||||
$this->lessImportDirs = $lessImportDirs;
|
||||
}
|
||||
}
|
24
framework/core/src/Frontend/Content/ContentInterface.php
Normal file
24
framework/core/src/Frontend/Content/ContentInterface.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?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\Frontend\Content;
|
||||
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
interface ContentInterface
|
||||
{
|
||||
/**
|
||||
* @param HtmlDocument $document
|
||||
* @param Request $request
|
||||
*/
|
||||
public function populate(HtmlDocument $document, Request $request);
|
||||
}
|
99
framework/core/src/Frontend/Content/CorePayload.php
Normal file
99
framework/core/src/Frontend/Content/CorePayload.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?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\Frontend\Content;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Controller\ShowUserController;
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\User\User;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class CorePayload implements ContentInterface
|
||||
{
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
private $locales;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $api;
|
||||
|
||||
/**
|
||||
* @param LocaleManager $locales
|
||||
* @param Client $api
|
||||
*/
|
||||
public function __construct(LocaleManager $locales, Client $api)
|
||||
{
|
||||
$this->locales = $locales;
|
||||
$this->api = $api;
|
||||
}
|
||||
|
||||
public function populate(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$document->payload = array_merge(
|
||||
$document->payload,
|
||||
$this->buildPayload($document, $request)
|
||||
);
|
||||
}
|
||||
|
||||
private function buildPayload(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$data = $this->getDataFromApiDocument($document->getForumApiDocument());
|
||||
|
||||
$actor = $request->getAttribute('actor');
|
||||
|
||||
if ($actor->exists) {
|
||||
$user = $this->getUserApiDocument($actor);
|
||||
$data = array_merge($data, $this->getDataFromApiDocument($user));
|
||||
}
|
||||
|
||||
return [
|
||||
'resources' => $data,
|
||||
'session' => [
|
||||
'userId' => $actor->id,
|
||||
'csrfToken' => $request->getAttribute('session')->token()
|
||||
],
|
||||
'locales' => $this->locales->getLocales(),
|
||||
'locale' => $request->getAttribute('locale')
|
||||
];
|
||||
}
|
||||
|
||||
private function getDataFromApiDocument(array $apiDocument): array
|
||||
{
|
||||
$data[] = $apiDocument['data'];
|
||||
|
||||
if (isset($apiDocument['included'])) {
|
||||
$data = array_merge($data, $apiDocument['included']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getUserApiDocument(User $user): array
|
||||
{
|
||||
// TODO: to avoid an extra query, something like
|
||||
// $controller = new ShowUserController(new PreloadedUserRepository($user));
|
||||
|
||||
return $this->getResponseBody(
|
||||
$this->api->send(ShowUserController::class, $user, ['id' => $user->id])
|
||||
);
|
||||
}
|
||||
|
||||
private function getResponseBody(ResponseInterface $response)
|
||||
{
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
}
|
36
framework/core/src/Frontend/Content/Layout.php
Normal file
36
framework/core/src/Frontend/Content/Layout.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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\Frontend\Content;
|
||||
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class Layout implements ContentInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $layoutView;
|
||||
|
||||
/**
|
||||
* @param string $layoutView
|
||||
*/
|
||||
public function __construct(string $layoutView)
|
||||
{
|
||||
$this->layoutView = $layoutView;
|
||||
}
|
||||
|
||||
public function populate(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$document->layoutView = $this->layoutView;
|
||||
}
|
||||
}
|
50
framework/core/src/Frontend/Content/Meta.php
Normal file
50
framework/core/src/Frontend/Content/Meta.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?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\Frontend\Content;
|
||||
|
||||
use Flarum\Frontend\HtmlDocument;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class Meta implements ContentInterface
|
||||
{
|
||||
public function populate(HtmlDocument $document, Request $request)
|
||||
{
|
||||
$document->meta = array_merge($document->meta, $this->buildMeta($document));
|
||||
$document->head = array_merge($document->head, $this->buildHead($document));
|
||||
}
|
||||
|
||||
private function buildMeta(HtmlDocument $document)
|
||||
{
|
||||
$forumApiDocument = $document->getForumApiDocument();
|
||||
|
||||
$meta = [
|
||||
'viewport' => 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1',
|
||||
'description' => array_get($forumApiDocument, 'data.attributes.forumDescription'),
|
||||
'theme-color' => array_get($forumApiDocument, 'data.attributes.themePrimaryColor')
|
||||
];
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
private function buildHead(HtmlDocument $document)
|
||||
{
|
||||
$head = [
|
||||
'font' => '<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700,600">'
|
||||
];
|
||||
|
||||
if ($faviconUrl = array_get($document->getForumApiDocument(), 'data.attributes.faviconUrl')) {
|
||||
$head['favicon'] = '<link rel="shortcut icon" href="'.e($faviconUrl).'">';
|
||||
}
|
||||
|
||||
return $head;
|
||||
}
|
||||
}
|
43
framework/core/src/Frontend/Controller.php
Normal file
43
framework/core/src/Frontend/Controller.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?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\Frontend;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
|
||||
class Controller implements RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var HtmlDocumentFactory
|
||||
*/
|
||||
protected $document;
|
||||
|
||||
/**
|
||||
* @param HtmlDocumentFactory $document
|
||||
*/
|
||||
public function __construct(HtmlDocumentFactory $document)
|
||||
{
|
||||
$this->document = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
return new HtmlResponse(
|
||||
$this->document->make($request)->render()
|
||||
);
|
||||
}
|
||||
}
|
@ -1,81 +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\Frontend\Event;
|
||||
|
||||
use Flarum\Admin\Controller\FrontendController as AdminFrontendController;
|
||||
use Flarum\Forum\Controller\FrontendController as ForumFrontendController;
|
||||
use Flarum\Frontend\AbstractFrontendController;
|
||||
use Flarum\Frontend\FrontendView;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class Rendering
|
||||
{
|
||||
/**
|
||||
* @var AbstractFrontendController
|
||||
*/
|
||||
public $controller;
|
||||
|
||||
/**
|
||||
* @var FrontendView
|
||||
*/
|
||||
public $view;
|
||||
|
||||
/**
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @param AbstractFrontendController $controller
|
||||
* @param FrontendView $view
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
public function __construct(AbstractFrontendController $controller, FrontendView $view, ServerRequestInterface $request)
|
||||
{
|
||||
$this->controller = $controller;
|
||||
$this->view = $view;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function isForum()
|
||||
{
|
||||
return $this->controller instanceof ForumFrontendController;
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
{
|
||||
return $this->controller instanceof AdminFrontendController;
|
||||
}
|
||||
|
||||
public function addAssets($files)
|
||||
{
|
||||
foreach ((array) $files as $file) {
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
|
||||
switch ($ext) {
|
||||
case 'js':
|
||||
$this->view->getJs()->addFile($file);
|
||||
break;
|
||||
|
||||
case 'css':
|
||||
case 'less':
|
||||
$this->view->getCss()->addFile($file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addBootstrapper($bootstrapper)
|
||||
{
|
||||
$this->view->loadModule($bootstrapper);
|
||||
}
|
||||
}
|
@ -1,165 +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\Frontend;
|
||||
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Frontend\Asset\JsCompiler;
|
||||
use Flarum\Frontend\Asset\LessCompiler;
|
||||
use Flarum\Frontend\Asset\LocaleJsCompiler as LocaleJsCompiler;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
|
||||
class FrontendAssets
|
||||
{
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
@ -1,55 +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\Frontend;
|
||||
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\Cache\Repository;
|
||||
|
||||
class FrontendAssetsFactory
|
||||
{
|
||||
/**
|
||||
* @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 FrontendAssets
|
||||
*/
|
||||
public function make($name)
|
||||
{
|
||||
return new FrontendAssets($name, $this->app, $this->cache, $this->locales);
|
||||
}
|
||||
}
|
@ -12,14 +12,73 @@
|
||||
namespace Flarum\Frontend;
|
||||
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||
|
||||
class FrontendServiceProvider extends AbstractServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
// Yo dawg, I heard you like factories, so I made you a factory to
|
||||
// create your factory. We expose a couple of factory functions that
|
||||
// will create frontend factories and configure them with some default
|
||||
// settings common to both the forum and admin frontends.
|
||||
|
||||
$this->app->singleton('flarum.frontend.assets.defaults', function () {
|
||||
return function (string $name) {
|
||||
$assets = new CompilerFactory(
|
||||
$name,
|
||||
$this->app->make('filesystem')->disk('flarum-assets'),
|
||||
$this->app->storagePath()
|
||||
);
|
||||
|
||||
$assets->setLessImportDirs([
|
||||
$this->app->basePath().'/vendor/components/font-awesome/less' => ''
|
||||
]);
|
||||
|
||||
$assets->add(function () use ($name) {
|
||||
$translations = $this->app->make(Asset\Translations::class);
|
||||
$translations->setFilter(function (string $id) use ($name) {
|
||||
return preg_match('/^.+(?:\.|::)(?:'.$name.'|lib)\./', $id);
|
||||
});
|
||||
|
||||
return [
|
||||
new Asset\CoreAssets($name),
|
||||
$this->app->make(Asset\LessVariables::class),
|
||||
$translations,
|
||||
$this->app->make(Asset\LocaleAssets::class)
|
||||
];
|
||||
});
|
||||
|
||||
return $assets;
|
||||
};
|
||||
});
|
||||
|
||||
$this->app->singleton('flarum.frontend.view.defaults', function () {
|
||||
return function (string $name) {
|
||||
$view = $this->app->make(HtmlDocumentFactory::class);
|
||||
|
||||
$view->setCommitAssets($this->app->inDebugMode());
|
||||
|
||||
$view->add(new Content\Layout('flarum::frontend.'.$name));
|
||||
$view->add($this->app->make(Content\CorePayload::class));
|
||||
$view->add($this->app->make(Content\Meta::class));
|
||||
|
||||
return $view;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum');
|
||||
|
||||
$this->app->make(ViewFactory::class)->share([
|
||||
'translator' => $this->app->make('translator'),
|
||||
'url' => $this->app->make(UrlGenerator::class)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -1,488 +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\Frontend;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Serializer\AbstractSerializer;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Frontend\Asset\CompilerInterface;
|
||||
use Flarum\Frontend\Asset\LocaleJsCompiler;
|
||||
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 FrontendView
|
||||
{
|
||||
/**
|
||||
* The title of the document, displayed in the <title> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $title;
|
||||
|
||||
/**
|
||||
* The description of the document, displayed in a <meta> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* The language of the document, displayed as the value of the attribute `dir` in the <html> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $language;
|
||||
|
||||
/**
|
||||
* The text direction of the document, displayed as the value of the attribute `dir` in the <html> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $direction;
|
||||
|
||||
/**
|
||||
* The path to the client layout view to display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $layout;
|
||||
|
||||
/**
|
||||
* The SEO content of the page, displayed within the layout in <noscript>
|
||||
* tags.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $content;
|
||||
|
||||
/**
|
||||
* An API response to be preloaded into the page.
|
||||
*
|
||||
* This should be a JSON-API document.
|
||||
*
|
||||
* @var null|array|object
|
||||
*/
|
||||
public $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 = [];
|
||||
|
||||
/**
|
||||
* A map of <link> tags to be generated.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $links = [];
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $js;
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $css;
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $localeJs;
|
||||
|
||||
/**
|
||||
* @var CompilerInterface
|
||||
*/
|
||||
protected $localeCss;
|
||||
|
||||
/**
|
||||
* @var FrontendAssets
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @var AbstractSerializer
|
||||
*/
|
||||
protected $userSerializer;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @param string $layout
|
||||
* @param FrontendAssets $assets
|
||||
* @param Client $api
|
||||
* @param Factory $view
|
||||
* @param LocaleManager $locales
|
||||
* @param AbstractSerializer $userSerializer
|
||||
* @param Application $app
|
||||
*/
|
||||
public function __construct($layout, FrontendAssets $assets, Client $api, Factory $view, LocaleManager $locales, AbstractSerializer $userSerializer, Application $app)
|
||||
{
|
||||
$this->layout = $layout;
|
||||
$this->api = $api;
|
||||
$this->assets = $assets;
|
||||
$this->view = $view;
|
||||
$this->locales = $locales;
|
||||
$this->userSerializer = $userSerializer;
|
||||
$this->app = $app;
|
||||
|
||||
$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);
|
||||
|
||||
foreach ($this->locales->getJsFiles($locale) as $file) {
|
||||
$this->localeJs->addFile($file);
|
||||
}
|
||||
|
||||
foreach ($this->locales->getCssFiles($locale) as $file) {
|
||||
$this->localeCss->addFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a <link> tag.
|
||||
*
|
||||
* @param string $relation
|
||||
* @param string $target
|
||||
*/
|
||||
public function link($relation, $target)
|
||||
{
|
||||
$this->links[$relation] = $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the canonical URL for this page.
|
||||
*
|
||||
* This will signal to search engines what URL should be used for this
|
||||
* content, if it can be found under multiple addresses. This is an
|
||||
* important tool to tackle duplicate content.
|
||||
*
|
||||
* @param string $url
|
||||
*/
|
||||
public function setCanonicalUrl($url)
|
||||
{
|
||||
$this->link('canonical', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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', $this->app->inDebugMode());
|
||||
|
||||
$view = $this->view->make('flarum.forum::frontend.app');
|
||||
|
||||
$view->title = $this->buildTitle(array_get($forum, 'data.attributes.title'));
|
||||
$view->description = $this->description ?: array_get($forum, 'data.attributes.description');
|
||||
$view->language = $this->language ?: $this->locales->getLocale();
|
||||
$view->direction = $this->direction ?: 'ltr';
|
||||
|
||||
$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 = $this->buildHeadContent();
|
||||
$view->foot = $this->buildFootContent(array_get($forum, 'data.attributes.footerHtml'));
|
||||
|
||||
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->make($this->layout);
|
||||
|
||||
$view->content = $this->buildContent();
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function buildContent()
|
||||
{
|
||||
$view = $this->view->make('flarum.forum::frontend.content');
|
||||
|
||||
$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);
|
||||
}, array_filter($files));
|
||||
}
|
||||
|
||||
protected function buildHeadContent()
|
||||
{
|
||||
$html = implode("\n", $this->head);
|
||||
|
||||
foreach ($this->links as $rel => $href) {
|
||||
$html .= "\n<link rel=\"$rel\" href=\"$href\" />";
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
protected function buildFootContent($customFooterHtml)
|
||||
{
|
||||
return implode("\n", $this->foot)."\n".$customFooterHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompilerInterface
|
||||
*/
|
||||
public function getJs()
|
||||
{
|
||||
return $this->js;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompilerInterface
|
||||
*/
|
||||
public function getCss()
|
||||
{
|
||||
return $this->css;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LocaleJsCompiler
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param Request $request
|
||||
* @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.
|
||||
*
|
||||
* @param Request $request
|
||||
* @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.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
protected function buildSession(Request $request)
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
$session = $request->getAttribute('session');
|
||||
|
||||
return [
|
||||
'userId' => $actor->id,
|
||||
'csrfToken' => $session->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;
|
||||
}
|
||||
}
|
@ -1,72 +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\Frontend;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Serializer\CurrentUserSerializer;
|
||||
use Flarum\Foundation\Application;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
|
||||
class FrontendViewFactory
|
||||
{
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @var CurrentUserSerializer
|
||||
*/
|
||||
protected $userSerializer;
|
||||
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @param Client $api
|
||||
* @param Factory $view
|
||||
* @param LocaleManager $locales
|
||||
* @param CurrentUserSerializer $userSerializer
|
||||
* @param Application $app
|
||||
*/
|
||||
public function __construct(Client $api, Factory $view, LocaleManager $locales, CurrentUserSerializer $userSerializer, Application $app)
|
||||
{
|
||||
$this->api = $api;
|
||||
$this->view = $view;
|
||||
$this->locales = $locales;
|
||||
$this->userSerializer = $userSerializer;
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $layout
|
||||
* @param FrontendAssets $assets
|
||||
* @return FrontendView
|
||||
*/
|
||||
public function make($layout, FrontendAssets $assets)
|
||||
{
|
||||
return new FrontendView($layout, $assets, $this->api, $this->view, $this->locales, $this->userSerializer, $this->app);
|
||||
}
|
||||
}
|
251
framework/core/src/Frontend/HtmlDocument.php
Normal file
251
framework/core/src/Frontend/HtmlDocument.php
Normal file
@ -0,0 +1,251 @@
|
||||
<?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\Frontend;
|
||||
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
/**
|
||||
* A view which renders a HTML skeleton for Flarum's frontend app.
|
||||
*/
|
||||
class HtmlDocument implements Renderable
|
||||
{
|
||||
/**
|
||||
* The title of the document, displayed in the <title> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $title;
|
||||
|
||||
/**
|
||||
* The language of the document, displayed as the value of the attribute `lang` in the <html> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $language;
|
||||
|
||||
/**
|
||||
* The text direction of the document, displayed as the value of the attribute `dir` in the <html> tag.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $direction;
|
||||
|
||||
/**
|
||||
* The name of the frontend app view to display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $appView = 'flarum::frontend.app';
|
||||
|
||||
/**
|
||||
* The name of the frontend layout view to display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $layoutView;
|
||||
|
||||
/**
|
||||
* The name of the frontend content view to display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $contentView = 'flarum::frontend.content';
|
||||
|
||||
/**
|
||||
* The SEO content of the page, displayed within the layout in <noscript> tags.
|
||||
*
|
||||
* @var string|Renderable
|
||||
*/
|
||||
public $content;
|
||||
|
||||
/**
|
||||
* Other variables to preload into the Flarum JS.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $payload = [];
|
||||
|
||||
/**
|
||||
* An array of meta tags to append to the page's <head>.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $meta = [];
|
||||
|
||||
/**
|
||||
* The canonical URL for this page.
|
||||
*
|
||||
* This will signal to search engines what URL should be used for this
|
||||
* content, if it can be found under multiple addresses. This is an
|
||||
* important tool to tackle duplicate content.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
public $canonicalUrl;
|
||||
|
||||
/**
|
||||
* An array of strings to append to the page's <head>.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $head = [];
|
||||
|
||||
/**
|
||||
* An array of strings to prepend before the page's </body>.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $foot = [];
|
||||
|
||||
/**
|
||||
* An array of JavaScript URLs to load.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $js = [];
|
||||
|
||||
/**
|
||||
* An array of CSS URLs to load.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $css = [];
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $forumApiDocument;
|
||||
|
||||
/**
|
||||
* @param Factory $view
|
||||
* @param array $forumApiDocument
|
||||
*/
|
||||
public function __construct(Factory $view, array $forumApiDocument)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->forumApiDocument = $forumApiDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
$this->view->share('forum', array_get($this->forumApiDocument, 'data.attributes'));
|
||||
|
||||
return $this->makeView()->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return View
|
||||
*/
|
||||
protected function makeView(): View
|
||||
{
|
||||
return $this->view->make($this->appView)->with([
|
||||
'title' => $this->makeTitle(),
|
||||
'payload' => $this->payload,
|
||||
'layout' => $this->makeLayout(),
|
||||
'language' => $this->language,
|
||||
'direction' => $this->direction,
|
||||
'js' => $this->makeJs(),
|
||||
'head' => $this->makeHead(),
|
||||
'foot' => $this->makeFoot(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function makeTitle(): string
|
||||
{
|
||||
return ($this->title ? $this->title.' - ' : '').array_get($this->forumApiDocument, 'data.attributes.title');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return View
|
||||
*/
|
||||
protected function makeLayout(): View
|
||||
{
|
||||
if ($this->layoutView) {
|
||||
return $this->view->make($this->layoutView)->with('content', $this->makeContent());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return View
|
||||
*/
|
||||
protected function makeContent(): View
|
||||
{
|
||||
return $this->view->make($this->contentView)->with('content', $this->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function makeHead(): string
|
||||
{
|
||||
$head = array_map(function ($url) {
|
||||
return '<link rel="stylesheet" href="'.e($url).'">';
|
||||
}, $this->css);
|
||||
|
||||
if ($this->canonicalUrl) {
|
||||
$head[] = '<link rel="canonical" href="'.e($this->canonicalUrl).'">';
|
||||
}
|
||||
|
||||
$head = array_merge($head, array_map(function ($content, $name) {
|
||||
return '<meta name="'.e($name).'" content="'.e($content).'">';
|
||||
}, $this->meta, array_keys($this->meta)));
|
||||
|
||||
return implode("\n", array_merge($head, $this->head));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function makeJs(): string
|
||||
{
|
||||
return implode("\n", array_map(function ($url) {
|
||||
return '<script src="'.e($url).'"></script>';
|
||||
}, $this->js));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function makeFoot(): string
|
||||
{
|
||||
return implode("\n", $this->foot);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getForumApiDocument(): array
|
||||
{
|
||||
return $this->forumApiDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $forumApiDocument
|
||||
*/
|
||||
public function setForumApiDocument(array $forumApiDocument)
|
||||
{
|
||||
$this->forumApiDocument = $forumApiDocument;
|
||||
}
|
||||
}
|
180
framework/core/src/Frontend/HtmlDocumentFactory.php
Normal file
180
framework/core/src/Frontend/HtmlDocumentFactory.php
Normal file
@ -0,0 +1,180 @@
|
||||
<?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\Frontend;
|
||||
|
||||
use Flarum\Api\Client;
|
||||
use Flarum\Api\Controller\ShowForumController;
|
||||
use Flarum\Frontend\Compiler\CompilerInterface;
|
||||
use Flarum\Frontend\Content\ContentInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
|
||||
class HtmlDocumentFactory
|
||||
{
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $api;
|
||||
|
||||
/**
|
||||
* @var CompilerFactory
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $commitAssets;
|
||||
|
||||
/**
|
||||
* @var ContentInterface[]
|
||||
*/
|
||||
protected $content = [];
|
||||
|
||||
/**
|
||||
* @param Factory $view
|
||||
* @param Client $api
|
||||
* @param CompilerFactory|null $assets
|
||||
* @param bool $commitAssets
|
||||
*/
|
||||
public function __construct(Factory $view, Client $api, CompilerFactory $assets = null, bool $commitAssets = false)
|
||||
{
|
||||
$this->view = $view;
|
||||
$this->api = $api;
|
||||
$this->assets = $assets;
|
||||
$this->commitAssets = $commitAssets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContentInterface $content
|
||||
*/
|
||||
public function add($content)
|
||||
{
|
||||
$this->content[] = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return HtmlDocument
|
||||
*/
|
||||
public function make(Request $request): HtmlDocument
|
||||
{
|
||||
$forumDocument = $this->getForumDocument($request);
|
||||
|
||||
$view = new HtmlDocument($this->view, $forumDocument);
|
||||
|
||||
$locale = $request->getAttribute('locale');
|
||||
|
||||
$js = [$this->assets->makeJs(), $this->assets->makeLocaleJs($locale)];
|
||||
$css = [$this->assets->makeCss(), $this->assets->makeLocaleCss($locale)];
|
||||
|
||||
$this->maybeCommitAssets(array_merge($js, $css));
|
||||
|
||||
$view->js = array_merge($view->js, $this->getUrls($js));
|
||||
$view->css = array_merge($view->css, $this->getUrls($css));
|
||||
|
||||
$this->populate($view, $request);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompilerFactory
|
||||
*/
|
||||
public function getAssets(): CompilerFactory
|
||||
{
|
||||
return $this->assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CompilerFactory $assets
|
||||
*/
|
||||
public function setAssets(CompilerFactory $assets)
|
||||
{
|
||||
$this->assets = $assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HtmlDocument $view
|
||||
* @param Request $request
|
||||
*/
|
||||
protected function populate(HtmlDocument $view, Request $request)
|
||||
{
|
||||
foreach ($this->content as $content) {
|
||||
$content->populate($view, $request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
private function getForumDocument(Request $request): array
|
||||
{
|
||||
$actor = $request->getAttribute('actor');
|
||||
|
||||
return $this->getResponseBody(
|
||||
$this->api->send(ShowForumController::class, $actor)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
* @return array
|
||||
*/
|
||||
private function getResponseBody(Response $response)
|
||||
{
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
private function maybeCommitAssets(array $compilers)
|
||||
{
|
||||
if ($this->commitAssets) {
|
||||
foreach ($compilers as $compiler) {
|
||||
$compiler->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CompilerInterface[] $compilers
|
||||
* @return string[]
|
||||
*/
|
||||
private function getUrls(array $compilers)
|
||||
{
|
||||
return array_filter(array_map(function (CompilerInterface $compiler) {
|
||||
return $compiler->getUrl();
|
||||
}, $compilers));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getCommitAssets(): bool
|
||||
{
|
||||
return $this->commitAssets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $commitAssets
|
||||
*/
|
||||
public function setCommitAssets(bool $commitAssets)
|
||||
{
|
||||
$this->commitAssets = $commitAssets;
|
||||
}
|
||||
}
|
84
framework/core/src/Frontend/RecompileFrontendAssets.php
Normal file
84
framework/core/src/Frontend/RecompileFrontendAssets.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?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\Frontend;
|
||||
|
||||
use Flarum\Extension\Event\Disabled;
|
||||
use Flarum\Extension\Event\Enabled;
|
||||
use Flarum\Foundation\Event\ClearingCache;
|
||||
use Flarum\Locale\LocaleManager;
|
||||
use Flarum\Settings\Event\Saved;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class RecompileFrontendAssets
|
||||
{
|
||||
/**
|
||||
* @var CompilerFactory
|
||||
*/
|
||||
protected $assets;
|
||||
|
||||
/**
|
||||
* @var LocaleManager
|
||||
*/
|
||||
protected $locales;
|
||||
|
||||
/**
|
||||
* @param CompilerFactory $assets
|
||||
* @param LocaleManager $locales
|
||||
*/
|
||||
public function __construct(CompilerFactory $assets, LocaleManager $locales)
|
||||
{
|
||||
$this->assets = $assets;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Saved::class, [$this, 'whenSettingsSaved']);
|
||||
$events->listen(Enabled::class, [$this, 'flush']);
|
||||
$events->listen(Disabled::class, [$this, 'flush']);
|
||||
$events->listen(ClearingCache::class, [$this, 'flush']);
|
||||
}
|
||||
|
||||
public function whenSettingsSaved(Saved $event)
|
||||
{
|
||||
if (preg_grep('/^theme_/i', array_keys($event->settings))) {
|
||||
$this->flushCss();
|
||||
}
|
||||
}
|
||||
|
||||
public function flush()
|
||||
{
|
||||
$this->flushCss();
|
||||
$this->flushJs();
|
||||
}
|
||||
|
||||
protected function flushCss()
|
||||
{
|
||||
$this->assets->makeCss()->flush();
|
||||
|
||||
foreach ($this->locales->getLocales() as $locale => $name) {
|
||||
$this->assets->makeLocaleCss($locale)->flush();
|
||||
}
|
||||
}
|
||||
|
||||
protected function flushJs()
|
||||
{
|
||||
$this->assets->makeJs()->flush();
|
||||
|
||||
foreach ($this->locales->getLocales() as $locale => $name) {
|
||||
$this->assets->makeLocaleJs($locale)->flush();
|
||||
}
|
||||
}
|
||||
}
|
@ -25,13 +25,13 @@ class ControllerRouteHandler
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var string|callable
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* @param Container $container
|
||||
* @param string $controller
|
||||
* @param string|callable $controller
|
||||
*/
|
||||
public function __construct(Container $container, $controller)
|
||||
{
|
||||
@ -54,12 +54,16 @@ class ControllerRouteHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string|callable $class
|
||||
* @return RequestHandlerInterface
|
||||
*/
|
||||
protected function resolveController($class)
|
||||
{
|
||||
$controller = $this->container->make($class);
|
||||
if (is_callable($class)) {
|
||||
$controller = $this->container->call($class);
|
||||
} else {
|
||||
$controller = $this->container->make($class);
|
||||
}
|
||||
|
||||
if (! ($controller instanceof RequestHandlerInterface)) {
|
||||
throw new InvalidArgumentException(
|
||||
|
@ -46,6 +46,8 @@ class SetLocale implements Middleware
|
||||
$this->locales->setLocale($locale);
|
||||
}
|
||||
|
||||
$request = $request->withAttribute('locale', $this->locales->getLocale());
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Flarum\Http;
|
||||
|
||||
use Flarum\Frontend\Controller as FrontendController;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class RouteHandlerFactory
|
||||
@ -29,11 +30,47 @@ class RouteHandlerFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @param string|callable $controller
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toController($controller)
|
||||
{
|
||||
return new ControllerRouteHandler($this->container, $controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $frontend
|
||||
* @param string|null $content
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toFrontend(string $frontend, string $content = null)
|
||||
{
|
||||
return $this->toController(function (Container $container) use ($frontend, $content) {
|
||||
$frontend = $container->make($frontend);
|
||||
|
||||
if ($content) {
|
||||
$frontend->add($container->make($content));
|
||||
}
|
||||
|
||||
return new FrontendController($frontend);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $content
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toForum(string $content = null)
|
||||
{
|
||||
return $this->toFrontend('flarum.forum.frontend', $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $content
|
||||
* @return ControllerRouteHandler
|
||||
*/
|
||||
public function toAdmin(string $content = null)
|
||||
{
|
||||
return $this->toFrontend('flarum.admin.frontend', $content);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class LocaleServiceProvider extends AbstractServiceProvider
|
||||
*/
|
||||
public function boot(Dispatcher $events)
|
||||
{
|
||||
$locales = $this->app->make('flarum.localeManager');
|
||||
$locales = $this->app->make('flarum.locales');
|
||||
|
||||
$locales->addLocale($this->getDefaultLocale(), 'Default');
|
||||
|
||||
@ -38,7 +38,7 @@ class LocaleServiceProvider extends AbstractServiceProvider
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton(LocaleManager::class);
|
||||
$this->app->alias(LocaleManager::class, 'flarum.localeManager');
|
||||
$this->app->alias(LocaleManager::class, 'flarum.locales');
|
||||
|
||||
$this->app->singleton('translator', function () {
|
||||
$translator = new Translator($this->getDefaultLocale(), new MessageSelector());
|
||||
|
@ -14,26 +14,15 @@ namespace Flarum\Settings\Event;
|
||||
class Saved
|
||||
{
|
||||
/**
|
||||
* The setting key that was set.
|
||||
*
|
||||
* @var string
|
||||
* @var array
|
||||
*/
|
||||
public $key;
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* The setting value that was set.
|
||||
*
|
||||
* @var string
|
||||
* @param array $settings
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param string $key The setting key that was set.
|
||||
* @param string $value The setting value that was set.
|
||||
*/
|
||||
public function __construct($key, $value)
|
||||
public function __construct(array $settings)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
}
|
||||
|
@ -9,17 +9,20 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Admin;
|
||||
namespace Flarum\Settings\Event;
|
||||
|
||||
use Flarum\Frontend\AbstractFrontend;
|
||||
|
||||
class Frontend extends AbstractFrontend
|
||||
class Saving
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @var array
|
||||
*/
|
||||
protected function getName()
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* @param array $settings
|
||||
*/
|
||||
public function __construct(array &$settings)
|
||||
{
|
||||
return 'admin';
|
||||
$this->settings = &$settings;
|
||||
}
|
||||
}
|
61
framework/core/src/Settings/OverrideSettingsRepository.php
Normal file
61
framework/core/src/Settings/OverrideSettingsRepository.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* A settings repository decorator that allows overriding certain values.
|
||||
*
|
||||
* The `OverrideSettingsRepository` class decorates another
|
||||
* `SettingsRepositoryInterface` instance but allows certain settings to be
|
||||
* overridden with predefined values. It does not affect writing methods.
|
||||
*
|
||||
* Within Flarum, this can be used to test out new setting values in a system
|
||||
* before they are committed to the database.
|
||||
*
|
||||
* @see \Flarum\Forum\RecompileFrontendAssets For an example usage.
|
||||
*/
|
||||
class OverrideSettingsRepository implements SettingsRepositoryInterface
|
||||
{
|
||||
protected $inner;
|
||||
|
||||
protected $overrides = [];
|
||||
|
||||
public function __construct(SettingsRepositoryInterface $inner, array $overrides)
|
||||
{
|
||||
$this->inner = $inner;
|
||||
$this->overrides = $overrides;
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
return array_merge($this->inner->all(), $this->overrides);
|
||||
}
|
||||
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (array_key_exists($key, $this->overrides)) {
|
||||
return $this->overrides[$key];
|
||||
}
|
||||
|
||||
return array_get($this->all(), $key, $default);
|
||||
}
|
||||
|
||||
public function set($key, $value)
|
||||
{
|
||||
$this->inner->set($key, $value);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
$this->inner->delete($key);
|
||||
}
|
||||
}
|
@ -1,37 +1,37 @@
|
||||
<div id="app" class="App">
|
||||
|
||||
<div id="app-navigation" class="App-navigation"></div>
|
||||
<div id="app-navigation" class="App-navigation"></div>
|
||||
|
||||
<div id="drawer" class="App-drawer">
|
||||
<div id="drawer" class="App-drawer">
|
||||
|
||||
<header id="header" class="App-header">
|
||||
<div id="header-navigation" class="Header-navigation"></div>
|
||||
<div class="container">
|
||||
<h1 class="Header-title">
|
||||
<a href="{{ array_get($forum, 'attributes.baseUrl') }}">
|
||||
<?php $title = array_get($forum, 'attributes.title'); ?>
|
||||
@if ($logo = array_get($forum, 'attributes.logoUrl'))
|
||||
<img src="{{ $logo }}" alt="{{ $title }}" class="Header-logo">
|
||||
@else
|
||||
{{ $title }}
|
||||
@endif
|
||||
</a>
|
||||
</h1>
|
||||
<div id="header-primary" class="Header-primary"></div>
|
||||
<div id="header-secondary" class="Header-secondary"></div>
|
||||
</div>
|
||||
</header>
|
||||
<header id="header" class="App-header">
|
||||
<div id="header-navigation" class="Header-navigation"></div>
|
||||
<div class="container">
|
||||
<h1 class="Header-title">
|
||||
<a href="{{ array_get($forum, 'baseUrl') }}">
|
||||
<?php $title = array_get($forum, 'title'); ?>
|
||||
@if ($logo = array_get($forum, 'logoUrl'))
|
||||
<img src="{{ $logo }}" alt="{{ $title }}" class="Header-logo">
|
||||
@else
|
||||
{{ $title }}
|
||||
@endif
|
||||
</a>
|
||||
</h1>
|
||||
<div id="header-primary" class="Header-primary"></div>
|
||||
<div id="header-secondary" class="Header-secondary"></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
<main class="App-content">
|
||||
<div class="container">
|
||||
<div id="admin-navigation" class="App-nav sideNav"></div>
|
||||
</div>
|
||||
|
||||
<div id="content" class="sideNavOffset"></div>
|
||||
<main class="App-content">
|
||||
<div class="container">
|
||||
<div id="admin-navigation" class="App-nav sideNav"></div>
|
||||
</div>
|
||||
|
||||
{!! $content !!}
|
||||
</main>
|
||||
<div id="content" class="sideNavOffset"></div>
|
||||
|
||||
{!! $content !!}
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
@ -1,63 +1,40 @@
|
||||
<!doctype html>
|
||||
<html dir="{{ $direction }}" lang="{{ $language }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ $title }}</title>
|
||||
<meta name="description" content="{{ $description }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
|
||||
<meta name="theme-color" content="{{ array_get($forum, 'attributes.themePrimaryColor') }}">
|
||||
@if (! $allowJs)
|
||||
<meta name="robots" content="noindex" />
|
||||
@endif
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ $title }}</title>
|
||||
|
||||
@foreach ($cssUrls as $url)
|
||||
<link rel="stylesheet" href="{{ $url }}">
|
||||
@endforeach
|
||||
{!! $head !!}
|
||||
</head>
|
||||
|
||||
@if ($faviconUrl = array_get($forum, 'attributes.faviconUrl'))
|
||||
<link href="{{ $faviconUrl }}" rel="shortcut icon">
|
||||
@endif
|
||||
<body>
|
||||
{!! $layout !!}
|
||||
|
||||
{!! $head !!}
|
||||
</head>
|
||||
<div id="modal"></div>
|
||||
<div id="alerts"></div>
|
||||
|
||||
<body>
|
||||
{!! $layout !!}
|
||||
<script>
|
||||
document.getElementById('flarum-loading').style.display = 'block';
|
||||
var flarum = {extensions: {}};
|
||||
</script>
|
||||
|
||||
<div id="modal"></div>
|
||||
<div id="alerts"></div>
|
||||
{!! $js !!}
|
||||
|
||||
@if ($allowJs)
|
||||
<script>
|
||||
document.getElementById('flarum-loading').style.display = 'block';
|
||||
var flarum = {extensions: {}};
|
||||
</script>
|
||||
<script>
|
||||
document.getElementById('flarum-loading').style.display = 'none';
|
||||
|
||||
@foreach ($jsUrls as $url)
|
||||
<script src="{{ $url }}"></script>
|
||||
@endforeach
|
||||
try {
|
||||
flarum.core.app.load(@json($payload));
|
||||
flarum.core.app.bootExtensions(flarum.extensions);
|
||||
flarum.core.app.boot();
|
||||
} catch (e) {
|
||||
var error = document.getElementById('flarum-loading-error');
|
||||
error.innerHTML += document.getElementById('flarum-content').textContent;
|
||||
error.style.display = 'block';
|
||||
throw e;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.getElementById('flarum-loading').style.display = 'none';
|
||||
@if (! $debug)
|
||||
try {
|
||||
@endif
|
||||
flarum.core.app.load(@json($payload));
|
||||
flarum.core.app.bootExtensions(flarum.extensions);
|
||||
flarum.core.app.boot();
|
||||
@if (! $debug)
|
||||
} catch (e) {
|
||||
window.location += (window.location.search ? '&' : '?') + 'nojs=1';
|
||||
throw e;
|
||||
}
|
||||
@endif
|
||||
</script>
|
||||
@else
|
||||
<script>
|
||||
window.history.replaceState(null, null, window.location.toString().replace(/([&?]nojs=1$|nojs=1&)/, ''));
|
||||
</script>
|
||||
@endif
|
||||
|
||||
{!! $foot !!}
|
||||
</body>
|
||||
{!! $foot !!}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,23 +1,23 @@
|
||||
<div id="flarum-loading" style="display: none">
|
||||
{{ $translator->trans('core.views.content.loading_text') }}
|
||||
{{ $translator->trans('core.views.content.loading_text') }}
|
||||
</div>
|
||||
|
||||
@if ($allowJs)
|
||||
<noscript>
|
||||
<noscript>
|
||||
<div class="Alert">
|
||||
<div class="container">
|
||||
{{ $translator->trans('core.views.content.javascript_disabled_message') }}
|
||||
</div>
|
||||
<div class="container">
|
||||
{{ $translator->trans('core.views.content.javascript_disabled_message') }}
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<div id="flarum-loading-error" style="display: none">
|
||||
<div class="Alert">
|
||||
<div class="container">
|
||||
{{ $translator->trans('core.views.content.load_error_message') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<noscript id="flarum-content">
|
||||
{!! $content !!}
|
||||
</noscript>
|
||||
@else
|
||||
<div class="Alert Alert--error">
|
||||
<div class="container">
|
||||
{{ $translator->trans('core.views.content.load_error_message') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!! $content !!}
|
||||
@endif
|
||||
</noscript>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="container">
|
||||
<h2>{{ $document->data->attributes->title }}</h2>
|
||||
<h2>{{ $apiDocument->data->attributes->title }}</h2>
|
||||
|
||||
<div>
|
||||
@foreach ($posts as $post)
|
||||
@ -15,11 +15,11 @@
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if (isset($document->links->prev))
|
||||
@if (isset($apiDocument->links->prev))
|
||||
<a href="{{ $url(['page' => $page - 1]) }}">« {{ $translator->trans('core.views.discussion.previous_page_button') }}</a>
|
||||
@endif
|
||||
|
||||
@if (isset($document->links->next))
|
||||
@if (isset($apiDocument->links->next))
|
||||
<a href="{{ $url(['page' => $page + 1]) }}">{{ $translator->trans('core.views.discussion.next_page_button') }} »</a>
|
||||
@endif
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<h2>{{ $translator->trans('core.views.index.all_discussions_heading') }}</h2>
|
||||
|
||||
<ul>
|
||||
@foreach ($document->data as $discussion)
|
||||
@foreach ($apiDocument->data as $discussion)
|
||||
<li>
|
||||
<a href="{{ $url->to('forum')->route('discussion', [
|
||||
'id' => $discussion->id . (trim($discussion->attributes->slug) ? '-' . $discussion->attributes->slug : '')
|
||||
@ -15,11 +15,11 @@
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
@if (isset($document->links->prev))
|
||||
@if (isset($apiDocument->links->prev))
|
||||
<a href="{{ $url->to('forum')->route('index') }}?page={{ $page - 1 }}">« {{ $translator->trans('core.views.index.previous_page_button') }}</a>
|
||||
@endif
|
||||
|
||||
@if (isset($document->links->next))
|
||||
@if (isset($apiDocument->links->next))
|
||||
<a href="{{ $url->to('forum')->route('index') }}?page={{ $page + 1 }}">{{ $translator->trans('core.views.index.next_page_button') }} »</a>
|
||||
@endif
|
||||
</div>
|
||||
|
@ -1,64 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Forum Web App Template
|
||||
*
|
||||
* NOTE: You shouldn't edit this file directly. Your changes will be overwritten
|
||||
* when you update Flarum. See flarum.org/docs/templates to learn how to
|
||||
* customize your forum's layout.
|
||||
*
|
||||
* Flarum's JavaScript app mounts various components into key elements in
|
||||
* this template. They are distinguished by their ID attributes:
|
||||
*
|
||||
* - #app
|
||||
* - #app-navigation
|
||||
* - #drawer
|
||||
* - #header
|
||||
* - #header-navigation
|
||||
* - #home-link
|
||||
* - #header-primary
|
||||
* - #header-secondary
|
||||
* - #content
|
||||
* - #composer
|
||||
*/
|
||||
?>
|
||||
{!! array_get($forum, 'attributes.headerHtml') !!}
|
||||
{!! array_get($forum, 'headerHtml') !!}
|
||||
|
||||
<div id="app" class="App">
|
||||
|
||||
<div id="app-navigation" class="App-navigation"></div>
|
||||
<div id="app-navigation" class="App-navigation"></div>
|
||||
|
||||
<div id="drawer" class="App-drawer">
|
||||
<div id="drawer" class="App-drawer">
|
||||
|
||||
<header id="header" class="App-header">
|
||||
<div id="header-navigation" class="Header-navigation"></div>
|
||||
<div class="container">
|
||||
<h1 class="Header-title">
|
||||
<a href="{{ array_get($forum, 'attributes.baseUrl') }}" id="home-link">
|
||||
<?php $title = array_get($forum, 'attributes.title'); ?>
|
||||
@if ($logo = array_get($forum, 'attributes.logoUrl'))
|
||||
<img src="{{ $logo }}" alt="{{ $title }}" class="Header-logo">
|
||||
@else
|
||||
{{ $title }}
|
||||
@endif
|
||||
</a>
|
||||
</h1>
|
||||
<div id="header-primary" class="Header-primary"></div>
|
||||
<div id="header-secondary" class="Header-secondary"></div>
|
||||
</div>
|
||||
</header>
|
||||
<header id="header" class="App-header">
|
||||
<div id="header-navigation" class="Header-navigation"></div>
|
||||
<div class="container">
|
||||
<h1 class="Header-title">
|
||||
<a href="{{ array_get($forum, 'baseUrl') }}" id="home-link">
|
||||
@if ($logo = array_get($forum, 'logoUrl'))
|
||||
<img src="{{ $logo }}" alt="{{ array_get($forum, 'title') }}" class="Header-logo">
|
||||
@else
|
||||
{{ array_get($forum, 'title') }}
|
||||
@endif
|
||||
</a>
|
||||
</h1>
|
||||
<div id="header-primary" class="Header-primary"></div>
|
||||
<div id="header-secondary" class="Header-secondary"></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</div>
|
||||
|
||||
<main class="App-content">
|
||||
<div id="content"></div>
|
||||
|
||||
{!! $content !!}
|
||||
|
||||
<div class="App-composer">
|
||||
<div class="container">
|
||||
<div id="composer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<main class="App-content">
|
||||
<div id="content"></div>
|
||||
|
||||
{!! $content !!}
|
||||
|
||||
<div class="App-composer">
|
||||
<div class="container">
|
||||
<div id="composer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
|
||||
{!! array_get($forum, 'footerHtml') !!}
|
||||
|
Loading…
x
Reference in New Issue
Block a user