2 namespace Civi\Angular
;
5 * The AngularLoader loads any JS/CSS/JSON resources
6 * required for setting up AngularJS.
8 * The AngularLoader stops short of bootstrapping AngularJS. You may
9 * need to `<div ng-app="..."></div>` or `angular.bootstrap(...)`.
12 * $loader = new AngularLoader();
13 * $loader->setPageName('civicrm/case/a');
14 * $loader->setModules(array('crmApp'));
18 * @link https://docs.angularjs.org/guide/bootstrap
23 * The weight to assign to any Angular JS module files.
25 const DEFAULT_MODULE_WEIGHT
= 200;
28 * The resource manager.
30 * Do not use publicly. Inject your own copy!
32 * @var \CRM_Core_Resources
37 * The Angular module manager.
39 * Do not use publicly. Inject your own copy!
41 * @var \Civi\Angular\Manager
46 * The region of the page into which JavaScript will be loaded.
60 * A list of modules to load.
67 protected $crmApp = NULL;
70 * AngularLoader constructor.
72 public function __construct() {
73 $this->res
= \CRM_Core_Resources
::singleton();
74 $this->angular
= \Civi
::service('angular');
75 $this->region
= \CRM_Utils_Request
::retrieve('snippet', 'String') ?
'ajax-snippet' : 'html-header';
76 $this->pageName
= $_GET['q'] ??
NULL;
81 * Register resources required by Angular.
83 * @return AngularLoader
85 public function load() {
86 $angular = $this->getAngular();
87 $res = $this->getRes();
89 if ($this->crmApp
!== NULL) {
90 $this->addModules($this->crmApp
['modules']);
91 $region = \CRM_Core_Region
::instance($this->crmApp
['region']);
92 $region->update('default', ['disabled' => TRUE]);
93 $region->add(['template' => $this->crmApp
['file'], 'weight' => 0]);
94 $this->res
->addSetting([
96 'defaultRoute' => $this->crmApp
['defaultRoute'],
100 // If trying to load an Angular page via AJAX, the route must be passed as a
101 // URL parameter, since the server doesn't receive information about
102 // URL fragments (i.e, what comes after the #).
103 $this->res
->addSetting([
104 'angularRoute' => $this->crmApp
['activeRoute'],
108 $moduleNames = $this->findActiveModules();
109 if (!$this->isAllModules($moduleNames)) {
110 $assetParams = ['modules' => implode(',', $moduleNames)];
113 // The module list will be "all modules that the user can see".
114 $assetParams = ['nonce' => md5(implode(',', $moduleNames))];
117 $res->addSettingsFactory(function () use (&$moduleNames, $angular, $res, $assetParams) {
118 // Merge static settings with the results of settingsFactory functions
119 $settingsByModule = $angular->getResources($moduleNames, 'settings', 'settings');
120 foreach ($angular->getResources($moduleNames, 'settingsFactory', 'settingsFactory') as $moduleName => $factory) {
121 $settingsByModule[$moduleName] = array_merge($settingsByModule[$moduleName] ??
[], $factory());
123 // Add clientside permissions
125 $toCheck = $angular->getResources($moduleNames, 'permissions', 'permissions');
126 foreach ($toCheck as $perms) {
127 foreach ((array) $perms as $perm) {
128 if (!isset($permissions[$perm])) {
129 $permissions[$perm] = \CRM_Core_Permission
::check($perm);
133 // TODO optimization; client-side caching
134 return array_merge($settingsByModule, ['permissions' => $permissions], [
135 'resourceUrls' => \CRM_Extension_System
::singleton()->getMapper()->getActiveModuleUrls(),
137 'modules' => $moduleNames,
138 'requires' => $angular->getResources($moduleNames, 'requires', 'requires'),
139 'cacheCode' => $res->getCacheCode(),
140 'bundleUrl' => \Civi
::service('asset_builder')->getUrl('angular-modules.json', $assetParams),
145 $res->addScriptFile('civicrm', 'bower_components/angular/angular.min.js', 100, $this->getRegion(), FALSE);
146 $res->addScriptFile('civicrm', 'js/crm.angular.js', 101, $this->getRegion(), FALSE);
149 $config = \CRM_Core_Config
::singleton();
150 if ($config->debug
) {
151 foreach ($moduleNames as $moduleName) {
152 foreach ($this->angular
->getResources($moduleName, 'css', 'cacheUrl') as $url) {
153 $res->addStyleUrl($url, self
::DEFAULT_MODULE_WEIGHT +
(++
$headOffset), $this->getRegion());
155 foreach ($this->angular
->getResources($moduleName, 'js', 'cacheUrl') as $url) {
156 $res->addScriptUrl($url, self
::DEFAULT_MODULE_WEIGHT +
(++
$headOffset), $this->getRegion());
157 // addScriptUrl() bypasses the normal string-localization of addScriptFile(),
158 // but that's OK because all Angular strings (JS+HTML) will load via crmResource.
163 // Note: addScriptUrl() bypasses the normal string-localization of addScriptFile(),
164 // but that's OK because all Angular strings (JS+HTML) will load via crmResource.
165 // $aggScriptUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=js&r=' . $res->getCacheCode(), FALSE, NULL, FALSE);
166 $aggScriptUrl = \Civi
::service('asset_builder')->getUrl('angular-modules.js', $assetParams);
167 $res->addScriptUrl($aggScriptUrl, 120, $this->getRegion());
169 // FIXME: The following CSS aggregator doesn't currently handle path-adjustments - which can break icons.
170 //$aggStyleUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=css&r=' . $res->getCacheCode(), FALSE, NULL, FALSE);
171 //$aggStyleUrl = \Civi::service('asset_builder')->getUrl('angular-modules.css', $assetParams);
172 //$res->addStyleUrl($aggStyleUrl, 120, $this->getRegion());
174 foreach ($this->angular
->getResources($moduleNames, 'css', 'cacheUrl') as $url) {
175 $res->addStyleUrl($url, self
::DEFAULT_MODULE_WEIGHT +
(++
$headOffset), $this->getRegion());
179 foreach ($this->angular
->getResources($moduleNames, 'bundles', 'bundles') as $bundles) {
180 $res->addBundle($bundles);
187 * Use Civi's generic "application" module.
189 * This is suitable for use on a basic, standalone Angular page
190 * like `civicrm/a`. (If you need to integrate Angular with pre-existing,
191 * non-Angular pages... then this probably won't help.)
193 * The Angular bootstrap process requires an HTML directive like
194 * `<div ng-app="foo">`.
196 * Calling useApp() will replace the page's main body with the
197 * `<div ng-app="crmApp">...</div>` and apply some configuration options
198 * for the `crmApp` module.
200 * @param array $settings
201 * A list of settings. Accepted values:
202 * - activeRoute: string, the route to open up immediately
204 * - defaultRoute: string, use this to redirect the default route (`/`) to another page
206 * - region: string, the place on the page where we should insert the angular app
208 * @return AngularLoader
209 * @link https://code.angularjs.org/1.5.11/docs/guide/bootstrap
211 public function useApp($settings = []) {
213 'modules' => ['crmApp'],
214 'activeRoute' => NULL,
215 'defaultRoute' => NULL,
216 'region' => 'page-body',
217 'file' => 'Civi/Angular/Page/Main.tpl',
219 $this->crmApp
= array_merge($defaults, $settings);
224 * Get a list of all Angular modules which should be activated on this
228 * List of module names.
229 * Ex: array('angularFileUpload', 'crmUi', 'crmUtil').
231 public function findActiveModules() {
232 return $this->angular
->resolveDependencies(array_merge(
234 $this->angular
->resolveDefaultModules($this->getPageName())
239 * @param $moduleNames
242 private function isAllModules($moduleNames) {
243 $allModuleNames = array_keys($this->angular
->getModules());
244 return count(array_diff($allModuleNames, $moduleNames)) === 0;
248 * @return \CRM_Core_Resources
250 public function getRes() {
255 * @param \CRM_Core_Resources $res
256 * @return AngularLoader
258 public function setRes($res) {
264 * @return \Civi\Angular\Manager
266 public function getAngular() {
267 return $this->angular
;
271 * @param \Civi\Angular\Manager $angular
272 * @return AngularLoader
274 public function setAngular($angular) {
275 $this->angular
= $angular;
282 public function getRegion() {
283 return $this->region
;
287 * @param string $region
288 * @return AngularLoader
290 public function setRegion($region) {
291 $this->region
= $region;
299 public function getPageName() {
300 return $this->pageName
;
304 * @param string $pageName
306 * @return AngularLoader
308 public function setPageName($pageName) {
309 $this->pageName
= $pageName;
314 * @param array|string $modules
315 * @return AngularLoader
317 public function addModules($modules) {
318 $modules = (array) $modules;
319 $this->modules
= array_unique(array_merge($this->modules
, $modules));
326 public function getModules() {
327 return $this->modules
;
331 * @param array $modules
332 * @return AngularLoader
334 public function setModules($modules) {
335 $this->modules
= $modules;