Skip to content

Commit

Permalink
CRM-16145 - Civi/Angular - Aggregate JS and CSS files
Browse files Browse the repository at this point in the history
CRM-16145 aims to setup a more intuitive file structure, but the file
structure has been constrained by the functional requirement to minimize
file-requests.  Aggregation frees us to split and reorg files with affecting
the number of file-requests.
  • Loading branch information
totten committed Apr 7, 2015
1 parent 7f616c0 commit 27a90ef
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 49 deletions.
60 changes: 33 additions & 27 deletions Civi/Angular/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public function getModules() {
);
$angularModules['crmUi'] = array(
'ext' => 'civicrm',
'js' => array('js/angular-crm-ui.js', 'packages/ckeditor/ckeditor.js'),
'js' => array('js/angular-crm-ui.js'),
'partials' => array('partials/crmUi'),
);
$angularModules['crmUtil'] = array(
Expand Down Expand Up @@ -255,36 +255,42 @@ public function getStrings($name) {
}

/**
* @param string $name
* Module name.
* Get resources for one or more modules.
*
* @param string|array $moduleNames
* List of module names.
* @param string $resType
* Type of resource ('js', 'css').
* @param string $refType
* Type of reference to the resource ('cacheUrl', 'rawUrl', 'path').
* @return array
* List of URLs.
* @throws \Exception
* List of URLs or paths.
* @throws \CRM_Core_Exception
*/
public function getScriptUrls($name) {
$module = $this->getModule($name);
public function getResources($moduleNames, $resType, $refType) {
$result = array();
if (isset($module['js'])) {
foreach ($module['js'] as $file) {
$result[] = $this->res->getUrl($module['ext'], $file, TRUE);
}
}
return $result;
}
$moduleNames = (array) $moduleNames;
foreach ($moduleNames as $moduleName) {
$module = $this->getModule($moduleName);
if (isset($module[$resType])) {
foreach ($module[$resType] as $file) {
switch ($refType) {
case 'path':
$result[] = $this->res->getPath($module['ext'], $file);
break;

/**
* @param string $name
* Module name.
* @return array
* List of URLs.
* @throws \Exception
*/
public function getStyleUrls($name) {
$module = $this->getModule($name);
$result = array();
if (isset($module['css'])) {
foreach ($module['css'] as $file) {
$result[] = $this->res->getUrl($module['ext'], $file, TRUE);
case 'rawUrl':
$result[] = $this->res->getUrl($module['ext'], $file);
break;

case 'cacheUrl':
$result[] = $this->res->getUrl($module['ext'], $file, TRUE);
break;

default:
throw new \CRM_Core_Exception("Unrecognized resource format");
}
}
}
}
return $result;
Expand Down
34 changes: 25 additions & 9 deletions Civi/Angular/Page/Main.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,33 @@ public function registerResources() {

$this->res->addScriptFile('civicrm', 'bower_components/angular/angular.min.js', 100, 'html-header', FALSE);
$this->res->addScriptFile('civicrm', 'bower_components/angular-route/angular-route.min.js', 110, 'html-header', FALSE);
$headOffset = 0;
foreach ($modules as $moduleName => $module) {
foreach ($this->angular->getStyleUrls($moduleName) as $url) {
$this->res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), 'html-header');
}
foreach ($this->angular->getScriptUrls($moduleName) as $url) {
$this->res->addScriptUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), 'html-header');
// addScriptUrl() bypasses the normal string-localization of addScriptFile(),
// but that's OK because all Angular strings (JS+HTML) will load via crmResource.

// FIXME: crmUi depends on loading ckeditor, but ckeditor doesn't work with this aggregation.
$this->res->addScriptFile('civicrm', 'packages/ckeditor/ckeditor.js', 100, 'html-header', FALSE);

$config = \CRM_Core_Config::singleton();
if ($config->debug) {
$headOffset = 0;
foreach ($modules as $moduleName => $module) {
foreach ($this->angular->getResources($moduleName, 'css', 'cacheUrl') as $url) {
$this->res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), 'html-header');
}
foreach ($this->angular->getResources($moduleName, 'js', 'cacheUrl') as $url) {
$this->res->addScriptUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), 'html-header');
// addScriptUrl() bypasses the normal string-localization of addScriptFile(),
// but that's OK because all Angular strings (JS+HTML) will load via crmResource.
}
}
}
else {
// Note: addScriptUrl() bypasses the normal string-localization of addScriptFile(),
// but that's OK because all Angular strings (JS+HTML) will load via crmResource.
$aggScriptUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=js&r=' . $page->res->getCacheCode(), FALSE, NULL, FALSE);
$this->res->addScriptUrl($aggScriptUrl, 120, 'html-header');

$aggStyleUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=css&r=' . $page->res->getCacheCode(), FALSE, NULL, FALSE);
$this->res->addStyleUrl($aggStyleUrl, 120, 'html-header');
}
}

}
97 changes: 84 additions & 13 deletions Civi/Angular/Page/Modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,120 @@
namespace Civi\Angular\Page;

/**
* This page returns HTML partials used by Angular.
* This page aggregates data from Angular modules.
*
* Example: Aggregate metadata about all modules in JSON format.
* civicrm/ajax/angular-modules?format=json
*
* Example: Aggregate metadata for crmUi and crmUtil modules.
* civicrm/ajax/angular-modules?format=json&modules=crmUi,crmUtil
*
* Example: Aggregate *.js files for all modules.
* civicrm/ajax/angular-modules?format=js
*
* Example: Aggregate *.css files for all modules.
* civicrm/ajax/angular-modules?format=css
*/
class Modules extends \CRM_Core_Page {

/**
* This page aggregates HTML partials used by Angular.
* See class description.
*/
public function run() {
//$config = \CRM_Core_Config::singleton();
//\CRM_Core_Page_AJAX::setJsHeaders($config->debug ? 30 : NULL);
\CRM_Core_Page_AJAX::setJsHeaders();

/**
* @var \Civi\Angular\Manager $angular
*/
$angular = \Civi\Core\Container::singleton()->get('angular');
$modules = $angular->getModules();
$moduleNames = $this->parseModuleNames(\CRM_Utils_Request::retrieve('modules', 'String'), $angular);

switch (\CRM_Utils_Request::retrieve('format', 'String')) {
case 'json':
case '':
$this->send(
'application/javascript',
json_encode($this->getMetadata($moduleNames, $angular))
);
break;

case 'js':
$this->send(
'application/javascript',
\CRM_Utils_File::concat($angular->getResources($moduleNames, 'js', 'path'), "\n")
);
break;

case 'css':
$this->send(
'text/css',
\CRM_Utils_File::concat($angular->getResources($moduleNames, 'css', 'path'), "\n")
);
break;

default:
\CRM_Core_Error::fatal("Unrecognized format");
}

\CRM_Utils_System::civiExit();
}

$modulesExpr = \CRM_Utils_Request::retrieve('modules', 'String');
/**
* @param string $modulesExpr
* Comma-separated list of module names.
* @param \Civi\Angular\Manager $angular
* @return array
* Any well-formed module names. All if moduleExpr is blank.
*/
public function parseModuleNames($modulesExpr, $angular) {
if ($modulesExpr) {
$moduleNames = preg_grep(
'/^[a-zA-Z0-9\-_\.]+$/',
explode(',', $modulesExpr)
);
return $moduleNames;
}
else {
$moduleNames = array_keys($modules);
$moduleNames = array_keys($angular->getModules());
return $moduleNames;
}
}

/**
* @param array $moduleNames
* List of module names.
* @param \Civi\Angular\Manager $angular
* @return array
*/
public function getMetadata($moduleNames, $angular) {
$modules = $angular->getModules();
$result = array();
foreach ($moduleNames as $moduleName) {
if (isset($modules[$moduleName])) {
$result[$moduleName] = array();
$result[$moduleName]['domain'] = $modules[$moduleName]['ext'];
$result[$moduleName]['js'] = $angular->getScriptUrls($moduleName);
$result[$moduleName]['css'] = $angular->getStyleUrls($moduleName);
$result[$moduleName]['js'] = $angular->getResources($moduleName, 'js', 'rawUrl');
$result[$moduleName]['css'] = $angular->getResources($moduleName, 'css', 'rawUrl');
$result[$moduleName]['partials'] = $angular->getPartials($moduleName);
$result[$moduleName]['strings'] = $angular->getTranslatedStrings($moduleName);
}
}
return $result;
}

echo json_encode($result);
\CRM_Utils_System::civiExit();
/**
* Send a response.
*
* @param string $type
* Content type.
* @param string $data
* Content.
*/
public function send($type, $data) {
// Encourage browsers to cache for a long time - 1 year
$ttl = 60 * 60 * 24 * 364;
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + $ttl));
header("Content-Type: $type");
header("Cache-Control: max-age=$ttl, public");
echo $data;
}

}

0 comments on commit 27a90ef

Please sign in to comment.