Let modules provide css/js assets to be used by other modules (#3961)
This commit is contained in:
parent
f98f988aff
commit
d699191629
|
@ -4,24 +4,23 @@
|
|||
namespace Icinga\Application\Modules;
|
||||
|
||||
use Exception;
|
||||
use Zend_Controller_Router_Route;
|
||||
use Zend_Controller_Router_Route_Abstract;
|
||||
use Zend_Controller_Router_Route_Regex;
|
||||
use Icinga\Application\ApplicationBootstrap;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Hook;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Application\Modules\DashboardContainer;
|
||||
use Icinga\Application\Modules\MenuItemContainer;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Module\Setup\SetupWizard;
|
||||
use Icinga\Util\File;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Web\Controller\Dispatcher;
|
||||
use Icinga\Application\Hook;
|
||||
use Icinga\Web\Navigation\Navigation;
|
||||
use Icinga\Web\Widget;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use Zend_Controller_Router_Route;
|
||||
use Zend_Controller_Router_Route_Abstract;
|
||||
use Zend_Controller_Router_Route_Regex;
|
||||
|
||||
/**
|
||||
* Module handling
|
||||
|
@ -44,6 +43,13 @@ class Module
|
|||
*/
|
||||
private $basedir;
|
||||
|
||||
/**
|
||||
* Directory for assets
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $assetDir;
|
||||
|
||||
/**
|
||||
* Directory for styles
|
||||
*
|
||||
|
@ -184,6 +190,34 @@ class Module
|
|||
*/
|
||||
protected $jsFiles = array();
|
||||
|
||||
/**
|
||||
* Globally provided CSS assets
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cssAssets = [];
|
||||
|
||||
/**
|
||||
* Globally provided JS assets
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $jsAssets = [];
|
||||
|
||||
/**
|
||||
* Required CSS assets
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cssRequires = [];
|
||||
|
||||
/**
|
||||
* Required JS assets
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $jsRequires = [];
|
||||
|
||||
/**
|
||||
* Routes to add to the route chain
|
||||
*
|
||||
|
@ -247,6 +281,7 @@ class Module
|
|||
$this->app = $app;
|
||||
$this->name = $name;
|
||||
$this->basedir = $basedir;
|
||||
$this->assetDir = $basedir . '/asset';
|
||||
$this->cssdir = $basedir . '/public/css';
|
||||
$this->jsdir = $basedir . '/public/js';
|
||||
$this->libdir = $basedir . '/library';
|
||||
|
@ -435,6 +470,7 @@ class Module
|
|||
return false;
|
||||
}
|
||||
$this->registerWebIntegration();
|
||||
$this->registerAssets();
|
||||
$this->registered = true;
|
||||
|
||||
return true;
|
||||
|
@ -468,7 +504,7 @@ class Module
|
|||
* @param string $name
|
||||
* @param bool $autoload
|
||||
*
|
||||
* @return mixed
|
||||
* @return self
|
||||
*
|
||||
* @throws ProgrammingError When the module is not yet loaded
|
||||
*/
|
||||
|
@ -484,6 +520,162 @@ class Module
|
|||
return $manager->getModule($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a static CSS asset which can be required by other modules
|
||||
*
|
||||
* @param string $path The path, relative to the module's base
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function provideCssAsset($path)
|
||||
{
|
||||
$fullPath = join(DIRECTORY_SEPARATOR, [$this->basedir, $path]);
|
||||
$this->cssAssets[] = $fullPath;
|
||||
$this->cssRequires[] = $fullPath; // A module should not have to require its own assets
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS assets provided by this module
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCssAssets()
|
||||
{
|
||||
return $this->cssAssets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require CSS from a different module
|
||||
*
|
||||
* @param string $path The file's path, relative to the module's asset or base directory
|
||||
* @param string $from The module's name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function requireCssFile($path, $from)
|
||||
{
|
||||
$module = self::get($from);
|
||||
$cssAssetDir = join(DIRECTORY_SEPARATOR, [$module->assetDir, 'css']);
|
||||
foreach ($module->getCssAssets() as $assetPath) {
|
||||
if (substr($assetPath, 0, strlen($cssAssetDir)) === $cssAssetDir) {
|
||||
$relativePath = ltrim(substr($assetPath, strlen($cssAssetDir)), '/\\');
|
||||
} else {
|
||||
$relativePath = ltrim(substr($assetPath, strlen($module->basedir)), '/\\');
|
||||
}
|
||||
|
||||
if ($path === $relativePath) {
|
||||
$this->cssRequires[] = $assetPath;
|
||||
break; // Exact match, won't match again..
|
||||
} elseif (fnmatch($path, $relativePath)) {
|
||||
$this->cssRequires[] = $assetPath;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this module requires CSS from a different module
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function requiresCss()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
return ! empty($this->cssRequires);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the CSS assets required by this module
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCssRequires()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
return $this->cssRequires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a static Javascript asset which can be required by other modules
|
||||
*
|
||||
* @param string $path The path, relative to the module's base
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function provideJsAsset($path)
|
||||
{
|
||||
$fullPath = join(DIRECTORY_SEPARATOR, [$this->basedir, $path]);
|
||||
$this->jsAssets[] = $fullPath;
|
||||
$this->jsRequires[] = $fullPath; // A module should not have to require its own assets
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Javascript assets provided by this module
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsAssets()
|
||||
{
|
||||
return $this->jsAssets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require Javascript from a different module
|
||||
*
|
||||
* @param string $path The file's path, relative to the module's asset or base directory
|
||||
* @param string $from The module's name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function requireJsFile($path, $from)
|
||||
{
|
||||
$module = self::get($from);
|
||||
$jsAssetDir = join(DIRECTORY_SEPARATOR, [$module->assetDir, 'js']);
|
||||
foreach ($module->getJsAssets() as $assetPath) {
|
||||
if (substr($assetPath, 0, strlen($jsAssetDir)) === $jsAssetDir) {
|
||||
$relativePath = ltrim(substr($assetPath, strlen($jsAssetDir)), '/\\');
|
||||
} else {
|
||||
$relativePath = ltrim(substr($assetPath, strlen($module->basedir)), '/\\');
|
||||
}
|
||||
|
||||
if ($path === $relativePath) {
|
||||
$this->jsRequires[] = $assetPath;
|
||||
break; // Exact match, won't match again..
|
||||
} elseif (fnmatch($path, $relativePath)) {
|
||||
$this->jsRequires[] = $assetPath;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this module requires Javascript from a different module
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function requiresJs()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
return ! empty($this->jsRequires);
|
||||
}
|
||||
|
||||
/**
|
||||
* List the Javascript assets required by this module
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJsRequires()
|
||||
{
|
||||
$this->launchConfigScript();
|
||||
return $this->jsRequires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an additional CSS/LESS file
|
||||
*
|
||||
|
@ -1095,6 +1287,40 @@ class Module
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this module's assets
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function registerAssets()
|
||||
{
|
||||
if ($this->app->isCli() || ! is_dir($this->assetDir)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$listAssets = function ($type) {
|
||||
$dir = join(DIRECTORY_SEPARATOR, [$this->assetDir, $type]);
|
||||
if (! is_dir($dir)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return new RecursiveIteratorIterator(new RecursiveDirectoryIterator(
|
||||
$dir,
|
||||
RecursiveDirectoryIterator::CURRENT_AS_PATHNAME | RecursiveDirectoryIterator::SKIP_DOTS
|
||||
));
|
||||
};
|
||||
|
||||
foreach ($listAssets('css') as $assetPath) {
|
||||
$this->provideCssAsset(ltrim(substr($assetPath, strlen($this->basedir)), '/\\'));
|
||||
}
|
||||
|
||||
foreach ($listAssets('js') as $assetPath) {
|
||||
$this->provideJsAsset(ltrim(substr($assetPath, strlen($this->basedir)), '/\\'));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind text domain for i18n
|
||||
*
|
||||
|
|
|
@ -76,6 +76,7 @@ class JavaScript
|
|||
$jsFiles[] = $basedir . '/' . $file;
|
||||
}
|
||||
|
||||
$sharedFiles = [];
|
||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $name => $module) {
|
||||
if ($module->hasJs()) {
|
||||
foreach ($module->getJsFiles() as $path) {
|
||||
|
@ -84,8 +85,16 @@ class JavaScript
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($module->requiresJs()) {
|
||||
foreach ($module->getJsRequires() as $path) {
|
||||
$sharedFiles[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
$files = array_merge($vendorFiles, $jsFiles);
|
||||
|
||||
$sharedFiles = array_unique($sharedFiles);
|
||||
$files = array_merge($vendorFiles, $jsFiles, $sharedFiles);
|
||||
|
||||
$request = Icinga::app()->getRequest();
|
||||
$noCache = $request->getHeader('Cache-Control') === 'no-cache' || $request->getHeader('Pragma') === 'no-cache';
|
||||
|
@ -116,6 +125,14 @@ class JavaScript
|
|||
$js .= file_get_contents($file) . "\n\n\n";
|
||||
}
|
||||
|
||||
foreach ($sharedFiles as $file) {
|
||||
if (substr($file, -7, 7) === '.min.js') {
|
||||
$out .= ';' . ltrim(trim(file_get_contents($file)), ';') . "\n";
|
||||
} else {
|
||||
$js .= file_get_contents($file) . "\n\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($minified) {
|
||||
require_once 'JShrink/Minifier.php';
|
||||
$out .= Minifier::minify($js, array('flaggedComments' => false));
|
||||
|
|
|
@ -36,6 +36,13 @@ class LessCompiler
|
|||
*/
|
||||
protected $moduleLessFiles = array();
|
||||
|
||||
/**
|
||||
* Array of module names indexed by LESS asset paths
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $moduleRequires = [];
|
||||
|
||||
/**
|
||||
* LESS source
|
||||
*
|
||||
|
@ -91,6 +98,20 @@ class LessCompiler
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a LESS asset requirement
|
||||
*
|
||||
* @param string $moduleName
|
||||
* @param string $lessFile
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addModuleRequire($moduleName, $lessFile)
|
||||
{
|
||||
$this->moduleRequires[$lessFile][] = $moduleName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of LESS files added to the compiler
|
||||
*
|
||||
|
@ -104,6 +125,8 @@ class LessCompiler
|
|||
$lessFiles = array_merge($lessFiles, $moduleLessFiles);
|
||||
}
|
||||
|
||||
$lessFiles = array_merge($lessFiles, array_keys($this->moduleRequires));
|
||||
|
||||
if ($this->theme !== null) {
|
||||
$lessFiles[] = $this->theme;
|
||||
}
|
||||
|
@ -149,6 +172,16 @@ class LessCompiler
|
|||
$this->source .= file_get_contents($lessFile);
|
||||
}
|
||||
|
||||
$requireCss = '';
|
||||
foreach ($this->moduleRequires as $requiredFile => $modules) {
|
||||
$containers = array_map(function ($name) {
|
||||
return '.icinga-module.module-' . $name;
|
||||
}, $modules);
|
||||
$requireCss .= join(',', $containers) . ' {' . file_get_contents($requiredFile) . '}';
|
||||
}
|
||||
|
||||
$this->source .= $requireCss;
|
||||
|
||||
$moduleCss = '';
|
||||
foreach ($this->moduleLessFiles as $moduleName => $moduleLessFiles) {
|
||||
$moduleCss .= '.icinga-module.module-' . $moduleName . ' {';
|
||||
|
|
|
@ -104,6 +104,12 @@ class StyleSheet
|
|||
$this->lessCompiler->addModuleLessFile($moduleName, $lessFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
if ($module->requiresCss()) {
|
||||
foreach ($module->getCssRequires() as $lessFilePath) {
|
||||
$this->lessCompiler->addModuleRequire($moduleName, $lessFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$themingConfig = $this->app->getConfig()->getSection('themes');
|
||||
|
|
Loading…
Reference in New Issue