Add the ability to override backend assets and layouts using a Skin

This commit is contained in:
Sam Georges 2014-09-20 11:25:57 +10:00
parent 6cc36d39f2
commit 65573da89b
6 changed files with 173 additions and 71 deletions

View File

@ -2,7 +2,6 @@
use URL;
use Config;
use Session;
use Request;
use October\Rain\Router\Helper as RouterHelper;
@ -29,9 +28,8 @@ class BackendHelper
*/
public function skinAsset($path = null)
{
$path = RouterHelper::normalizeUrl($path);
$skinPath = Skin::getActive()->skinPath;
return URL::asset($skinPath . $path);
$skinPath = Skin::getActive()->getPath($path, true);
return URL::asset($skinPath);
}
/**

View File

@ -127,7 +127,7 @@ class Controller extends Extendable
* Define layout and view paths
*/
$this->layout = 'default';
$this->layoutPath = ['modules/backend/layouts'];
$this->layoutPath = Skin::getActive()->getLayoutPaths();
// Option A: (@todo Determine which is faster by benchmark)
// $relativePath = strtolower(str_replace('\\', '/', get_called_class()));

View File

@ -1,6 +1,9 @@
<?php namespace Backend\Classes;
use Str;
use File;
use Config;
use October\Rain\Router\Helper as RouterHelper;
/**
* Skin Base class
@ -16,8 +19,29 @@ abstract class Skin
*/
abstract public function skinDetails();
/**
* @var string The absolute path to this skin.
*/
public $skinPath;
/**
* @var string The public path to this skin.
*/
public $publicSkinPath;
/**
* @var string The default skin path, usually the root level of modules/backend.
*/
public $defaultSkinPath;
/**
* @var string The default public skin path.
*/
public $defaultPublicSkinPath;
/**
* @var Self Cache of the active skin.
*/
private static $skinCache;
/**
@ -25,7 +49,54 @@ abstract class Skin
*/
public function __construct()
{
$this->skinPath = str_replace('\\', '/', get_called_class());
$this->defaultSkinPath = PATH_BASE . '/modules/backend';
/*
* Guess the skin path
*/
$class = get_called_class();
$classFolder = strtolower(Str::getRealClass($class));
$classFile = realpath(dirname(File::fromClass($class)));
$this->skinPath = $classFile
? $classFile . '/' . $classFolder
: $this->defaultSkinPath;
$this->publicSkinPath = File::localToPublic($this->skinPath);
$this->defaultPublicSkinPath = File::localToPublic($this->defaultSkinPath);
traceLog($this->skinPath);
}
/**
* Looks up a path to a skin-based file, if it doesn't exist, the default path is used.
* @param string $path
* @param boolean $isPublic
* @return string
*/
public function getPath($path = null, $isPublic = false)
{
$path = RouterHelper::normalizeUrl($path);
$assetFile = $this->skinPath . $path;
if (File::isFile($assetFile)) {
return $isPublic
? $this->publicSkinPath . $path
: $this->skinPath . $path;
}
else {
return $isPublic
? $this->defaultPublicSkinPath . $path
: $this->defaultSkinPath . $path;
}
}
/**
* Returns an array of paths where skin layouts can be found.
* @return array
*/
public function getLayoutPaths()
{
return [$this->skinPath.'/layouts', $this->defaultSkinPath.'/layouts'];
}
/**

View File

@ -1,78 +1,78 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="icon" type="image/png" href="<?= URL::asset('modules/backend/assets/images/favicon.png') ?>">
<link rel="icon" type="image/png" href="<?= Backend::skinAsset('assets/images/favicon.png') ?>">
<title data-title-template="<?= empty($this->pageTitleTemplate) ? '%s | October CMS' : e($this->pageTitleTemplate) ?>">
<?= $this->pageTitle ?> | October CMS
</title>
<link href="<?= URL::asset('modules/backend/assets/vendor/select2/select2.css') ?>" rel="stylesheet">
<link href="<?= URL::asset('modules/backend/assets/css/october.css') ?>?v<?= System\Models\Parameters::get('system::core.build', 1) ?>" rel="stylesheet">
<link href="<?= Backend::skinAsset('assets/vendor/select2/select2.css') ?>" rel="stylesheet">
<link href="<?= Backend::skinAsset('assets/css/october.css') ?>?v<?= System\Models\Parameters::get('system::core.build', 1) ?>" rel="stylesheet">
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery-2.0.3.min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.ui.widget.js') ?>"></script>
<script src="<?= URL::asset('modules/system/assets/js/framework.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/modernizr.min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/mousewheel.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.touchwipe.min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/moment.min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/raphael-min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.autoellipsis.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.waterfall.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/vendor/jquery.cookie.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery-2.0.3.min.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery.ui.widget.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/framework.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/modernizr.min.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/mousewheel.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery.touchwipe.min.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/moment.min.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/raphael-min.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery.autoellipsis.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery.waterfall.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/vendor/jquery.cookie.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/select2/select2.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/mustache/mustache.min.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/fileupload/jquery.fileupload.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/fileupload/jquery.iframe-transport.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/select2/select2.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/mustache/mustache.min.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/fileupload/jquery.fileupload.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/fileupload/jquery.iframe-transport.js') ?>"></script>
<script src="<?= URL::asset('modules/system/assets/vendor/bootstrap/js/tooltip.js') ?>"></script>
<script src="<?= URL::asset('modules/system/assets/vendor/bootstrap/js/modal.js') ?>"></script>
<script src="<?= URL::asset('modules/system/assets/vendor/bootstrap/js/tab.js') ?>"></script>
<script src="<?= URL::asset('modules/system/assets/vendor/bootstrap/js/transition.js') ?>"></script>
<script src="<?= URL::asset('modules/system/assets/vendor/bootstrap/js/dropdown.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/bootstrap/js/tooltip.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/bootstrap/js/modal.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/bootstrap/js/tab.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/bootstrap/js/transition.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/bootstrap/js/dropdown.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/flot/jquery.flot.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/flot/jquery.flot.tooltip.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/flot/jquery.flot.resize.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/vendor/flot/jquery.flot.time.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/flot/jquery.flot.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/flot/jquery.flot.tooltip.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/flot/jquery.flot.resize.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/vendor/flot/jquery.flot.time.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.controls.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.utils.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.triggerapi.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.dragscroll.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.toolbar.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.verticalmenu.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.navbar.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.sidenav.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.tab.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.popover.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.popup.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.goalmeter.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.scrollbar.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.filelist.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.hotkey.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.loadindicator.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.stripeloadindicator.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.flashmessage.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.inputpreset.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.layout.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.sidepaneltab.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.simplelist.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.sortable.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.inspector.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.dropdown.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.changemonitor.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.chartutils.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.chartpie.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.chartbar.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.chartline.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.balloonselector.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.rowlink.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.treelist.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.autocomplete.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.callout.js') ?>"></script>
<script src="<?= URL::asset('modules/backend/assets/js/october.sidenav-tree.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.controls.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.utils.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.triggerapi.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.dragscroll.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.toolbar.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.verticalmenu.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.navbar.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.sidenav.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.tab.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.popover.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.popup.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.goalmeter.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.scrollbar.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.filelist.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.hotkey.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.loadindicator.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.stripeloadindicator.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.flashmessage.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.inputpreset.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.layout.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.sidepaneltab.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.simplelist.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.sortable.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.inspector.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.dropdown.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.changemonitor.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.chartutils.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.chartpie.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.chartbar.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.chartline.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.balloonselector.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.rowlink.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.treelist.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.autocomplete.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.callout.js') ?>"></script>
<script src="<?= Backend::skinAsset('assets/js/october.sidenav-tree.js') ?>"></script>
<script>
<!--

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" class="no-js <?= $this->makeLayoutPartial('browser_detector') ?>">
<html lang="<?= Config::get('app.locale', 'en') ?>" class="no-js <?= $this->makeLayoutPartial('browser_detector') ?>">
<head>
<?= $this->makeLayoutPartial('head') ?>
</head>

View File

@ -1,9 +1,13 @@
<?php namespace Backend\Skins;
use File;
use Backend\Classes\Skin;
use October\Rain\Router\Helper as RouterHelper;
/**
* Standard skin information file
* Standard skin information file.
*
* This skin uses the default paths always, there is no lookup required.
*
* @package october\backend
* @author Alexey Bobkov, Samuel Georges
@ -12,6 +16,15 @@ use Backend\Classes\Skin;
class Standard extends Skin
{
/**
* {@inheritDoc}
*/
public function __construct()
{
$this->skinPath = $this->defaultSkinPath = PATH_BASE . '/modules/backend';
$this->publicSkinPath = $this->defaultPublicSkinPath = File::localToPublic($this->skinPath);
}
/**
* {@inheritDoc}
*/
@ -22,4 +35,24 @@ class Standard extends Skin
];
}
/**
* {@inheritDoc}
*/
public function getPath($path = null, $isPublic = false)
{
$path = RouterHelper::normalizeUrl($path);
return $isPublic
? $this->defaultPublicSkinPath . $path
: $this->defaultSkinPath . $path;
}
/**
* {@inheritDoc}
*/
public function getLayoutPaths()
{
return [$this->skinPath.'/layouts'];
}
}