Merge pull request #9973 from lcdservices/CRM-19469
[civicrm-core.git] / Civi / Angular / AngularLoader.php
1 <?php
2 namespace Civi\Angular;
3
4 /**
5 * The AngularLoader loads any JS/CSS/JSON resources
6 * required for setting up AngularJS.
7 *
8 * The AngularLoader stops short of bootstrapping AngularJS. You may
9 * need to `<div ng-app="..."></div>` or `angular.bootstrap(...)`.
10 *
11 * @code
12 * $loader = new AngularLoader();
13 * $loader->setPageName('civicrm/case/a');
14 * $loader->setModules(array('crmApp'));
15 * $loader->load();
16 * @endCode
17 *
18 * @link https://docs.angularjs.org/guide/bootstrap
19 */
20 class AngularLoader {
21
22 /**
23 * The weight to assign to any Angular JS module files.
24 */
25 const DEFAULT_MODULE_WEIGHT = 200;
26
27 /**
28 * The resource manager.
29 *
30 * Do not use publicly. Inject your own copy!
31 *
32 * @var \CRM_Core_Resources
33 */
34 protected $res;
35
36 /**
37 * The Angular module manager.
38 *
39 * Do not use publicly. Inject your own copy!
40 *
41 * @var \Civi\Angular\Manager
42 */
43 protected $angular;
44
45 /**
46 * The region of the page into which JavaScript will be loaded.
47 *
48 * @var string
49 */
50 protected $region;
51
52 /**
53 * @var string
54 * Ex: 'civicrm/a'.
55 */
56 protected $pageName;
57
58 /**
59 * @var array
60 * A list of modules to load.
61 */
62 protected $modules;
63
64 /**
65 * AngularLoader constructor.
66 */
67 public function __construct() {
68 $this->res = \CRM_Core_Resources::singleton();
69 $this->angular = \Civi::service('angular');
70 $this->region = \CRM_Utils_Request::retrieve('snippet', 'String') ? 'ajax-snippet' : 'html-header';
71 $this->pageName = isset($_GET['q']) ? $_GET['q'] : NULL;
72 $this->modules = array();
73 }
74
75 /**
76 * Register resources required by Angular.
77 */
78 public function load() {
79 $angular = $this->getAngular();
80 $res = $this->getRes();
81
82 $moduleNames = $this->findActiveModules();
83 if (!$this->isAllModules($moduleNames)) {
84 $assetParams = array('modules' => implode(',', $moduleNames));
85 }
86 else {
87 // The module list will be "all modules that the user can see".
88 $assetParams = array('nonce' => md5(implode(',', $moduleNames)));
89 }
90
91 $res->addSettingsFactory(function () use (&$moduleNames, $angular, $res, $assetParams) {
92 // TODO optimization; client-side caching
93 $result = array_merge($angular->getResources($moduleNames, 'settings', 'settings'), array(
94 'resourceUrls' => \CRM_Extension_System::singleton()->getMapper()->getActiveModuleUrls(),
95 'angular' => array(
96 'modules' => $moduleNames,
97 'requires' => $angular->getResources($moduleNames, 'requires', 'requires'),
98 'cacheCode' => $res->getCacheCode(),
99 'bundleUrl' => \Civi::service('asset_builder')->getUrl('angular-modules.json', $assetParams),
100 ),
101 ));
102 return $result;
103 });
104
105 $res->addScriptFile('civicrm', 'bower_components/angular/angular.min.js', 100, $this->getRegion(), FALSE);
106 $res->addScriptFile('civicrm', 'js/crm.angular.js', 101, $this->getRegion(), FALSE);
107
108 $headOffset = 0;
109 $config = \CRM_Core_Config::singleton();
110 if ($config->debug) {
111 foreach ($moduleNames as $moduleName) {
112 foreach ($this->angular->getResources($moduleName, 'css', 'cacheUrl') as $url) {
113 $res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->getRegion());
114 }
115 foreach ($this->angular->getResources($moduleName, 'js', 'cacheUrl') as $url) {
116 $res->addScriptUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->getRegion());
117 // addScriptUrl() bypasses the normal string-localization of addScriptFile(),
118 // but that's OK because all Angular strings (JS+HTML) will load via crmResource.
119 }
120 }
121 }
122 else {
123 // Note: addScriptUrl() bypasses the normal string-localization of addScriptFile(),
124 // but that's OK because all Angular strings (JS+HTML) will load via crmResource.
125 // $aggScriptUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=js&r=' . $res->getCacheCode(), FALSE, NULL, FALSE);
126 $aggScriptUrl = \Civi::service('asset_builder')->getUrl('angular-modules.js', $assetParams);
127 $res->addScriptUrl($aggScriptUrl, 120, $this->getRegion());
128
129 // FIXME: The following CSS aggregator doesn't currently handle path-adjustments - which can break icons.
130 //$aggStyleUrl = \CRM_Utils_System::url('civicrm/ajax/angular-modules', 'format=css&r=' . $res->getCacheCode(), FALSE, NULL, FALSE);
131 //$aggStyleUrl = \Civi::service('asset_builder')->getUrl('angular-modules.css', $assetParams);
132 //$res->addStyleUrl($aggStyleUrl, 120, $this->getRegion());
133
134 foreach ($this->angular->getResources($moduleNames, 'css', 'cacheUrl') as $url) {
135 $res->addStyleUrl($url, self::DEFAULT_MODULE_WEIGHT + (++$headOffset), $this->getRegion());
136 }
137 }
138 }
139
140 /**
141 * Get a list of all Angular modules which should be activated on this
142 * page.
143 *
144 * @return array
145 * List of module names.
146 * Ex: array('angularFileUpload', 'crmUi', 'crmUtil').
147 */
148 public function findActiveModules() {
149 return $this->angular->resolveDependencies(array_merge(
150 $this->getModules(),
151 $this->angular->resolveDefaultModules($this->getPageName())
152 ));
153 }
154
155 /**
156 * @param $moduleNames
157 * @return int
158 */
159 private function isAllModules($moduleNames) {
160 $allModuleNames = array_keys($this->angular->getModules());
161 return count(array_diff($allModuleNames, $moduleNames)) === 0;
162 }
163
164 /**
165 * @return \CRM_Core_Resources
166 */
167 public function getRes() {
168 return $this->res;
169 }
170
171 /**
172 * @param \CRM_Core_Resources $res
173 */
174 public function setRes($res) {
175 $this->res = $res;
176 }
177
178 /**
179 * @return \Civi\Angular\Manager
180 */
181 public function getAngular() {
182 return $this->angular;
183 }
184
185 /**
186 * @param \Civi\Angular\Manager $angular
187 */
188 public function setAngular($angular) {
189 $this->angular = $angular;
190 }
191
192 /**
193 * @return string
194 */
195 public function getRegion() {
196 return $this->region;
197 }
198
199 /**
200 * @param string $region
201 */
202 public function setRegion($region) {
203 $this->region = $region;
204 }
205
206 /**
207 * @return string
208 * Ex: 'civicrm/a'.
209 */
210 public function getPageName() {
211 return $this->pageName;
212 }
213
214 /**
215 * @param string $pageName
216 * Ex: 'civicrm/a'.
217 */
218 public function setPageName($pageName) {
219 $this->pageName = $pageName;
220 }
221
222 /**
223 * @return array
224 */
225 public function getModules() {
226 return $this->modules;
227 }
228
229 /**
230 * @param array $modules
231 */
232 public function setModules($modules) {
233 $this->modules = $modules;
234 }
235
236 }