414 lines
14 KiB
PHP
414 lines
14 KiB
PHP
|
<?php
|
||
|
namespace Civi\Angular;
|
||
|
|
||
|
/**
|
||
|
* Manage Angular resources.
|
||
|
*
|
||
|
* @package Civi\Angular
|
||
|
*/
|
||
|
class Manager {
|
||
|
|
||
|
/**
|
||
|
* @var \CRM_Core_Resources
|
||
|
*/
|
||
|
protected $res = NULL;
|
||
|
|
||
|
/**
|
||
|
* @var array|NULL
|
||
|
* Each item has some combination of these keys:
|
||
|
* - ext: string
|
||
|
* The Civi extension which defines the Angular module.
|
||
|
* - js: array(string $relativeFilePath)
|
||
|
* List of JS files (relative to the extension).
|
||
|
* - css: array(string $relativeFilePath)
|
||
|
* List of CSS files (relative to the extension).
|
||
|
* - partials: array(string $relativeFilePath)
|
||
|
* A list of partial-HTML folders (relative to the extension).
|
||
|
* This will be mapped to "~/moduleName" by crmResource.
|
||
|
* - settings: array(string $key => mixed $value)
|
||
|
* List of settings to preload.
|
||
|
*/
|
||
|
protected $modules = NULL;
|
||
|
|
||
|
/**
|
||
|
* @var \CRM_Utils_Cache_Interface
|
||
|
*/
|
||
|
protected $cache;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
* Array(string $name => ChangeSet $change).
|
||
|
*/
|
||
|
protected $changeSets = NULL;
|
||
|
|
||
|
/**
|
||
|
* @param \CRM_Core_Resources $res
|
||
|
* The resource manager.
|
||
|
*/
|
||
|
public function __construct($res, \CRM_Utils_Cache_Interface $cache = NULL) {
|
||
|
$this->res = $res;
|
||
|
$this->cache = $cache ? $cache : new \CRM_Utils_Cache_Arraycache(array());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a list of AngularJS modules which should be autoloaded.
|
||
|
*
|
||
|
* @return array
|
||
|
* Each item has some combination of these keys:
|
||
|
* - ext: string
|
||
|
* The Civi extension which defines the Angular module.
|
||
|
* - js: array(string $relativeFilePath)
|
||
|
* List of JS files (relative to the extension).
|
||
|
* - css: array(string $relativeFilePath)
|
||
|
* List of CSS files (relative to the extension).
|
||
|
* - partials: array(string $relativeFilePath)
|
||
|
* A list of partial-HTML folders (relative to the extension).
|
||
|
* This will be mapped to "~/moduleName" by crmResource.
|
||
|
* - settings: array(string $key => mixed $value)
|
||
|
* List of settings to preload.
|
||
|
*/
|
||
|
public function getModules() {
|
||
|
if ($this->modules === NULL) {
|
||
|
$config = \CRM_Core_Config::singleton();
|
||
|
global $civicrm_root;
|
||
|
|
||
|
// Note: It would be nice to just glob("$civicrm_root/ang/*.ang.php"), but at time
|
||
|
// of writing CiviMail and CiviCase have special conditionals.
|
||
|
|
||
|
$angularModules = array();
|
||
|
$angularModules['angularFileUpload'] = include "$civicrm_root/ang/angularFileUpload.ang.php";
|
||
|
$angularModules['crmApp'] = include "$civicrm_root/ang/crmApp.ang.php";
|
||
|
$angularModules['crmAttachment'] = include "$civicrm_root/ang/crmAttachment.ang.php";
|
||
|
$angularModules['crmAutosave'] = include "$civicrm_root/ang/crmAutosave.ang.php";
|
||
|
$angularModules['crmCxn'] = include "$civicrm_root/ang/crmCxn.ang.php";
|
||
|
// $angularModules['crmExample'] = include "$civicrm_root/ang/crmExample.ang.php";
|
||
|
$angularModules['crmResource'] = include "$civicrm_root/ang/crmResource.ang.php";
|
||
|
$angularModules['crmUi'] = include "$civicrm_root/ang/crmUi.ang.php";
|
||
|
$angularModules['crmUtil'] = include "$civicrm_root/ang/crmUtil.ang.php";
|
||
|
$angularModules['dialogService'] = include "$civicrm_root/ang/dialogService.ang.php";
|
||
|
$angularModules['ngRoute'] = include "$civicrm_root/ang/ngRoute.ang.php";
|
||
|
$angularModules['ngSanitize'] = include "$civicrm_root/ang/ngSanitize.ang.php";
|
||
|
$angularModules['ui.utils'] = include "$civicrm_root/ang/ui.utils.ang.php";
|
||
|
$angularModules['ui.bootstrap'] = include "$civicrm_root/ang/ui.bootstrap.ang.php";
|
||
|
$angularModules['ui.sortable'] = include "$civicrm_root/ang/ui.sortable.ang.php";
|
||
|
$angularModules['unsavedChanges'] = include "$civicrm_root/ang/unsavedChanges.ang.php";
|
||
|
$angularModules['statuspage'] = include "$civicrm_root/ang/crmStatusPage.ang.php";
|
||
|
|
||
|
foreach (\CRM_Core_Component::getEnabledComponents() as $component) {
|
||
|
$angularModules = array_merge($angularModules, $component->getAngularModules());
|
||
|
}
|
||
|
\CRM_Utils_Hook::angularModules($angularModules);
|
||
|
foreach (array_keys($angularModules) as $module) {
|
||
|
if (!isset($angularModules[$module]['basePages'])) {
|
||
|
$angularModules[$module]['basePages'] = array('civicrm/a');
|
||
|
}
|
||
|
}
|
||
|
$this->modules = $this->resolvePatterns($angularModules);
|
||
|
}
|
||
|
|
||
|
return $this->modules;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the descriptor for an Angular module.
|
||
|
*
|
||
|
* @param string $name
|
||
|
* Module name.
|
||
|
* @return array
|
||
|
* Details about the module:
|
||
|
* - ext: string, the name of the Civi extension which defines the module
|
||
|
* - js: array(string $relativeFilePath).
|
||
|
* - css: array(string $relativeFilePath).
|
||
|
* - partials: array(string $relativeFilePath).
|
||
|
* @throws \Exception
|
||
|
*/
|
||
|
public function getModule($name) {
|
||
|
$modules = $this->getModules();
|
||
|
if (!isset($modules[$name])) {
|
||
|
throw new \Exception("Unrecognized Angular module");
|
||
|
}
|
||
|
return $modules[$name];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Resolve a full list of Angular dependencies.
|
||
|
*
|
||
|
* @param array $names
|
||
|
* List of Angular modules.
|
||
|
* Ex: array('crmMailing').
|
||
|
* @return array
|
||
|
* List of Angular modules, include all dependencies.
|
||
|
* Ex: array('crmMailing', 'crmUi', 'crmUtil', 'ngRoute').
|
||
|
*/
|
||
|
public function resolveDependencies($names) {
|
||
|
$allModules = $this->getModules();
|
||
|
$visited = array();
|
||
|
$result = $names;
|
||
|
while (($missingModules = array_diff($result, array_keys($visited))) && !empty($missingModules)) {
|
||
|
foreach ($missingModules as $module) {
|
||
|
$visited[$module] = 1;
|
||
|
if (!isset($allModules[$module])) {
|
||
|
\Civi::log()->warning('Unrecognized Angular module {name}. Please ensure that all Angular modules are declared.', array(
|
||
|
'name' => $module,
|
||
|
'civi.tag' => 'deprecated',
|
||
|
));
|
||
|
}
|
||
|
elseif (isset($allModules[$module]['requires'])) {
|
||
|
$result = array_unique(array_merge($result, $allModules[$module]['requires']));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
sort($result);
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a list of Angular modules that should be loaded on the given
|
||
|
* base-page.
|
||
|
*
|
||
|
* @param string $basePage
|
||
|
* The name of the base-page for which we want a list of moudles.
|
||
|
* @return array
|
||
|
* List of Angular modules.
|
||
|
* Ex: array('crmMailing', 'crmUi', 'crmUtil', 'ngRoute').
|
||
|
*/
|
||
|
public function resolveDefaultModules($basePage) {
|
||
|
$modules = $this->getModules();
|
||
|
$result = array();
|
||
|
foreach ($modules as $moduleName => $module) {
|
||
|
if (in_array($basePage, $module['basePages']) || in_array('*', $module['basePages'])) {
|
||
|
$result[] = $moduleName;
|
||
|
}
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Convert any globs in an Angular module to file names.
|
||
|
*
|
||
|
* @param array $modules
|
||
|
* List of Angular modules.
|
||
|
* @return array
|
||
|
* Updated list of Angular modules
|
||
|
*/
|
||
|
protected function resolvePatterns($modules) {
|
||
|
$newModules = array();
|
||
|
|
||
|
foreach ($modules as $moduleKey => $module) {
|
||
|
foreach (array('js', 'css', 'partials') as $fileset) {
|
||
|
if (!isset($module[$fileset])) {
|
||
|
continue;
|
||
|
}
|
||
|
$module[$fileset] = $this->res->glob($module['ext'], $module[$fileset]);
|
||
|
}
|
||
|
$newModules[$moduleKey] = $module;
|
||
|
}
|
||
|
|
||
|
return $newModules;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the partial HTML documents for a module (unfiltered).
|
||
|
*
|
||
|
* @param string $name
|
||
|
* Angular module name.
|
||
|
* @return array
|
||
|
* Array(string $extFilePath => string $html)
|
||
|
* @throws \Exception
|
||
|
* Invalid partials configuration.
|
||
|
*/
|
||
|
public function getRawPartials($name) {
|
||
|
$module = $this->getModule($name);
|
||
|
$result = array();
|
||
|
if (isset($module['partials'])) {
|
||
|
foreach ($module['partials'] as $partialDir) {
|
||
|
$partialDir = $this->res->getPath($module['ext']) . '/' . $partialDir;
|
||
|
$files = \CRM_Utils_File::findFiles($partialDir, '*.html', TRUE);
|
||
|
foreach ($files as $file) {
|
||
|
$filename = '~/' . $name . '/' . $file;
|
||
|
$result[$filename] = file_get_contents($partialDir . '/' . $file);
|
||
|
}
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the partial HTML documents for a module.
|
||
|
*
|
||
|
* @param string $name
|
||
|
* Angular module name.
|
||
|
* @return array
|
||
|
* Array(string $extFilePath => string $html)
|
||
|
* @throws \Exception
|
||
|
* Invalid partials configuration.
|
||
|
*/
|
||
|
public function getPartials($name) {
|
||
|
$cacheKey = "angular-partials::$name";
|
||
|
$cacheValue = $this->cache->get($cacheKey);
|
||
|
if ($cacheValue === NULL) {
|
||
|
$cacheValue = ChangeSet::applyResourceFilters($this->getChangeSets(), 'partials', $this->getRawPartials($name));
|
||
|
$this->cache->set($cacheKey, $cacheValue);
|
||
|
}
|
||
|
return $cacheValue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get list of translated strings for a module.
|
||
|
*
|
||
|
* @param string $name
|
||
|
* Angular module name.
|
||
|
* @return array
|
||
|
* Translated strings: array(string $orig => string $translated).
|
||
|
*/
|
||
|
public function getTranslatedStrings($name) {
|
||
|
$module = $this->getModule($name);
|
||
|
$result = array();
|
||
|
$strings = $this->getStrings($name);
|
||
|
foreach ($strings as $string) {
|
||
|
// TODO: should we pass translation domain based on $module[ext] or $module[tsDomain]?
|
||
|
// It doesn't look like client side really supports the domain right now...
|
||
|
$translated = ts($string, array(
|
||
|
'domain' => array($module['ext'], NULL),
|
||
|
));
|
||
|
if ($translated != $string) {
|
||
|
$result[$string] = $translated;
|
||
|
}
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get list of translatable strings for a module.
|
||
|
*
|
||
|
* @param string $name
|
||
|
* Angular module name.
|
||
|
* @return array
|
||
|
* Translatable strings.
|
||
|
*/
|
||
|
public function getStrings($name) {
|
||
|
$module = $this->getModule($name);
|
||
|
$result = array();
|
||
|
if (isset($module['js'])) {
|
||
|
foreach ($module['js'] as $file) {
|
||
|
$strings = $this->res->getStrings()->get(
|
||
|
$module['ext'],
|
||
|
$this->res->getPath($module['ext'], $file),
|
||
|
'text/javascript'
|
||
|
);
|
||
|
$result = array_unique(array_merge($result, $strings));
|
||
|
}
|
||
|
}
|
||
|
$partials = $this->getPartials($name);
|
||
|
foreach ($partials as $partial) {
|
||
|
$result = array_unique(array_merge($result, \CRM_Utils_JS::parseStrings($partial)));
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get resources for one or more modules.
|
||
|
*
|
||
|
* @param string|array $moduleNames
|
||
|
* List of module names.
|
||
|
* @param string $resType
|
||
|
* Type of resource ('js', 'css', 'settings').
|
||
|
* @param string $refType
|
||
|
* Type of reference to the resource ('cacheUrl', 'rawUrl', 'path', 'settings').
|
||
|
* @return array
|
||
|
* List of URLs or paths.
|
||
|
* @throws \CRM_Core_Exception
|
||
|
*/
|
||
|
public function getResources($moduleNames, $resType, $refType) {
|
||
|
$result = array();
|
||
|
$moduleNames = (array) $moduleNames;
|
||
|
foreach ($moduleNames as $moduleName) {
|
||
|
$module = $this->getModule($moduleName);
|
||
|
if (isset($module[$resType])) {
|
||
|
foreach ($module[$resType] as $file) {
|
||
|
$refTypeSuffix = '';
|
||
|
if (is_string($file) && preg_match(';^(assetBuilder|ext)://;', $file)) {
|
||
|
$refTypeSuffix = '-' . parse_url($file, PHP_URL_SCHEME);
|
||
|
}
|
||
|
|
||
|
switch ($refType . $refTypeSuffix) {
|
||
|
case 'path':
|
||
|
$result[] = $this->res->getPath($module['ext'], $file);
|
||
|
break;
|
||
|
|
||
|
case 'rawUrl':
|
||
|
$result[] = $this->res->getUrl($module['ext'], $file);
|
||
|
break;
|
||
|
|
||
|
case 'cacheUrl':
|
||
|
$result[] = $this->res->getUrl($module['ext'], $file, TRUE);
|
||
|
break;
|
||
|
|
||
|
case 'path-assetBuilder':
|
||
|
$assetName = parse_url($file, PHP_URL_HOST) . parse_url($file, PHP_URL_PATH);
|
||
|
$assetParams = array();
|
||
|
parse_str('' . parse_url($file, PHP_URL_QUERY), $assetParams);
|
||
|
$result[] = \Civi::service('asset_builder')->getPath($assetName, $assetParams);
|
||
|
break;
|
||
|
|
||
|
case 'rawUrl-assetBuilder':
|
||
|
case 'cacheUrl-assetBuilder':
|
||
|
$assetName = parse_url($file, PHP_URL_HOST) . parse_url($file, PHP_URL_PATH);
|
||
|
$assetParams = array();
|
||
|
parse_str('' . parse_url($file, PHP_URL_QUERY), $assetParams);
|
||
|
$result[] = \Civi::service('asset_builder')->getUrl($assetName, $assetParams);
|
||
|
break;
|
||
|
|
||
|
case 'path-ext':
|
||
|
$result[] = $this->res->getPath(parse_url($file, PHP_URL_HOST), ltrim(parse_url($file, PHP_URL_PATH), '/'));
|
||
|
break;
|
||
|
|
||
|
case 'rawUrl-ext':
|
||
|
$result[] = $this->res->getUrl(parse_url($file, PHP_URL_HOST), ltrim(parse_url($file, PHP_URL_PATH), '/'));
|
||
|
break;
|
||
|
|
||
|
case 'cacheUrl-ext':
|
||
|
$result[] = $this->res->getUrl(parse_url($file, PHP_URL_HOST), ltrim(parse_url($file, PHP_URL_PATH), '/'), TRUE);
|
||
|
break;
|
||
|
|
||
|
case 'settings':
|
||
|
case 'requires':
|
||
|
if (!empty($module[$resType])) {
|
||
|
$result[$moduleName] = $module[$resType];
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
throw new \CRM_Core_Exception("Unrecognized resource format");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ChangeSet::applyResourceFilters($this->getChangeSets(), $resType, $result);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
* Array(string $name => ChangeSet $changeSet).
|
||
|
*/
|
||
|
public function getChangeSets() {
|
||
|
if ($this->changeSets === NULL) {
|
||
|
$this->changeSets = array();
|
||
|
\CRM_Utils_Hook::alterAngular($this);
|
||
|
}
|
||
|
return $this->changeSets;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param ChangeSet $changeSet
|
||
|
* @return \Civi\Angular\Manager
|
||
|
*/
|
||
|
public function add($changeSet) {
|
||
|
$this->changeSets[$changeSet->getName()] = $changeSet;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
}
|