CRM-20600 - PartialSyntaxTest - Ensure that Angular partials are well-formed
[civicrm-core.git] / Civi / Angular / Manager.php
CommitLineData
16072ce1
TO
1<?php
2namespace Civi\Angular;
3
4/**
5 * Manage Angular resources.
6 *
7 * @package Civi\Angular
8 */
9class Manager {
10
11 /**
12 * @var \CRM_Core_Resources
13 */
14 protected $res = NULL;
15
16 /**
17 * @var array|NULL
18 * Each item has some combination of these keys:
19 * - ext: string
8671b4f2 20 * The Civi extension which defines the Angular module.
16072ce1 21 * - js: array(string $relativeFilePath)
8671b4f2 22 * List of JS files (relative to the extension).
16072ce1 23 * - css: array(string $relativeFilePath)
8671b4f2 24 * List of CSS files (relative to the extension).
16072ce1 25 * - partials: array(string $relativeFilePath)
8671b4f2
TO
26 * A list of partial-HTML folders (relative to the extension).
27 * This will be mapped to "~/moduleName" by crmResource.
1da632e0
TO
28 * - settings: array(string $key => mixed $value)
29 * List of settings to preload.
16072ce1
TO
30 */
31 protected $modules = NULL;
32
33 /**
34 * @param \CRM_Core_Resources $res
35 * The resource manager.
36 */
37 public function __construct($res) {
38 $this->res = $res;
39 }
40
41 /**
fe482240 42 * Get a list of AngularJS modules which should be autoloaded.
16072ce1
TO
43 *
44 * @return array
8671b4f2
TO
45 * Each item has some combination of these keys:
46 * - ext: string
47 * The Civi extension which defines the Angular module.
48 * - js: array(string $relativeFilePath)
49 * List of JS files (relative to the extension).
50 * - css: array(string $relativeFilePath)
51 * List of CSS files (relative to the extension).
52 * - partials: array(string $relativeFilePath)
53 * A list of partial-HTML folders (relative to the extension).
54 * This will be mapped to "~/moduleName" by crmResource.
1da632e0
TO
55 * - settings: array(string $key => mixed $value)
56 * List of settings to preload.
16072ce1
TO
57 */
58 public function getModules() {
59 if ($this->modules === NULL) {
1da632e0 60 $config = \CRM_Core_Config::singleton();
8456e727 61 global $civicrm_root;
16072ce1 62
8456e727
TO
63 // Note: It would be nice to just glob("$civicrm_root/ang/*.ang.php"), but at time
64 // of writing CiviMail and CiviCase have special conditionals.
16072ce1 65
8456e727
TO
66 $angularModules = array();
67 $angularModules['angularFileUpload'] = include "$civicrm_root/ang/angularFileUpload.ang.php";
68 $angularModules['crmApp'] = include "$civicrm_root/ang/crmApp.ang.php";
69 $angularModules['crmAttachment'] = include "$civicrm_root/ang/crmAttachment.ang.php";
70 $angularModules['crmAutosave'] = include "$civicrm_root/ang/crmAutosave.ang.php";
71 $angularModules['crmCxn'] = include "$civicrm_root/ang/crmCxn.ang.php";
72 // $angularModules['crmExample'] = include "$civicrm_root/ang/crmExample.ang.php";
73 $angularModules['crmResource'] = include "$civicrm_root/ang/crmResource.ang.php";
74 $angularModules['crmUi'] = include "$civicrm_root/ang/crmUi.ang.php";
75 $angularModules['crmUtil'] = include "$civicrm_root/ang/crmUtil.ang.php";
76 $angularModules['dialogService'] = include "$civicrm_root/ang/dialogService.ang.php";
77 $angularModules['ngRoute'] = include "$civicrm_root/ang/ngRoute.ang.php";
78 $angularModules['ngSanitize'] = include "$civicrm_root/ang/ngSanitize.ang.php";
79 $angularModules['ui.utils'] = include "$civicrm_root/ang/ui.utils.ang.php";
80 $angularModules['ui.sortable'] = include "$civicrm_root/ang/ui.sortable.ang.php";
81 $angularModules['unsavedChanges'] = include "$civicrm_root/ang/unsavedChanges.ang.php";
82 $angularModules['statuspage'] = include "$civicrm_root/ang/crmStatusPage.ang.php";
c0f7f681 83
16072ce1
TO
84 foreach (\CRM_Core_Component::getEnabledComponents() as $component) {
85 $angularModules = array_merge($angularModules, $component->getAngularModules());
86 }
87 \CRM_Utils_Hook::angularModules($angularModules);
88 $this->modules = $this->resolvePatterns($angularModules);
89 }
90
91 return $this->modules;
92 }
93
94 /**
95 * Get the descriptor for an Angular module.
96 *
97 * @param string $name
98 * Module name.
99 * @return array
100 * Details about the module:
101 * - ext: string, the name of the Civi extension which defines the module
102 * - js: array(string $relativeFilePath).
103 * - css: array(string $relativeFilePath).
104 * - partials: array(string $relativeFilePath).
105 * @throws \Exception
106 */
107 public function getModule($name) {
108 $modules = $this->getModules();
109 if (!isset($modules[$name])) {
110 throw new \Exception("Unrecognized Angular module");
111 }
112 return $modules[$name];
113 }
114
115 /**
116 * Convert any globs in an Angular module to file names.
117 *
118 * @param array $modules
119 * List of Angular modules.
120 * @return array
121 * Updated list of Angular modules
122 */
123 protected function resolvePatterns($modules) {
124 $newModules = array();
125
126 foreach ($modules as $moduleKey => $module) {
127 foreach (array('js', 'css', 'partials') as $fileset) {
128 if (!isset($module[$fileset])) {
129 continue;
130 }
131 $module[$fileset] = $this->res->glob($module['ext'], $module[$fileset]);
132 }
133 $newModules[$moduleKey] = $module;
134 }
135
136 return $newModules;
137 }
138
139 /**
140 * Get the partial HTML documents for a module.
141 *
142 * @param string $name
143 * Angular module name.
144 * @return array
145 * Array(string $extFilePath => string $html)
a2dc0f82
TO
146 * @throws \Exception
147 * Invalid partials configuration.
16072ce1
TO
148 */
149 public function getPartials($name) {
150 $module = $this->getModule($name);
151 $result = array();
152 if (isset($module['partials'])) {
a2dc0f82
TO
153 foreach ($module['partials'] as $partialDir) {
154 $partialDir = $this->res->getPath($module['ext']) . '/' . $partialDir;
155 $files = \CRM_Utils_File::findFiles($partialDir, '*.html', TRUE);
156 foreach ($files as $file) {
157 $filename = '~/' . $name . '/' . $file;
158 $result[$filename] = file_get_contents($partialDir . '/' . $file);
159 }
16072ce1
TO
160 }
161 }
162 return $result;
163 }
164
16072ce1
TO
165 /**
166 * Get list of translated strings for a module.
167 *
168 * @param string $name
169 * Angular module name.
170 * @return array
171 * Translated strings: array(string $orig => string $translated).
172 */
173 public function getTranslatedStrings($name) {
e3d90d6c 174 $module = $this->getModule($name);
16072ce1
TO
175 $result = array();
176 $strings = $this->getStrings($name);
177 foreach ($strings as $string) {
178 // TODO: should we pass translation domain based on $module[ext] or $module[tsDomain]?
179 // It doesn't look like client side really supports the domain right now...
e3d90d6c
TO
180 $translated = ts($string, array(
181 'domain' => array($module['ext'], NULL),
182 ));
16072ce1
TO
183 if ($translated != $string) {
184 $result[$string] = $translated;
185 }
186 }
187 return $result;
188 }
189
190 /**
191 * Get list of translatable strings for a module.
192 *
193 * @param string $name
194 * Angular module name.
195 * @return array
196 * Translatable strings.
197 */
198 public function getStrings($name) {
199 $module = $this->getModule($name);
200 $result = array();
201 if (isset($module['js'])) {
202 foreach ($module['js'] as $file) {
203 $strings = $this->res->getStrings()->get(
204 $module['ext'],
205 $this->res->getPath($module['ext'], $file),
206 'text/javascript'
207 );
208 $result = array_unique(array_merge($result, $strings));
209 }
210 }
211 if (isset($module['partials'])) {
a2dc0f82
TO
212 foreach ($module['partials'] as $partialDir) {
213 $partialDir = $this->res->getPath($module['ext']) . '/' . $partialDir;
214 $files = \CRM_Utils_File::findFiles($partialDir, '*.html');
215 foreach ($files as $file) {
216 $strings = $this->res->getStrings()->get(
217 $module['ext'],
218 $file,
219 'text/html'
220 );
221 $result = array_unique(array_merge($result, $strings));
222 }
16072ce1
TO
223 }
224 }
225 return $result;
226 }
227
228 /**
27a90ef6
TO
229 * Get resources for one or more modules.
230 *
231 * @param string|array $moduleNames
232 * List of module names.
233 * @param string $resType
1da632e0 234 * Type of resource ('js', 'css', 'settings').
27a90ef6 235 * @param string $refType
1da632e0 236 * Type of reference to the resource ('cacheUrl', 'rawUrl', 'path', 'settings').
16072ce1 237 * @return array
27a90ef6
TO
238 * List of URLs or paths.
239 * @throws \CRM_Core_Exception
16072ce1 240 */
27a90ef6 241 public function getResources($moduleNames, $resType, $refType) {
16072ce1 242 $result = array();
27a90ef6
TO
243 $moduleNames = (array) $moduleNames;
244 foreach ($moduleNames as $moduleName) {
245 $module = $this->getModule($moduleName);
246 if (isset($module[$resType])) {
247 foreach ($module[$resType] as $file) {
248 switch ($refType) {
249 case 'path':
250 $result[] = $this->res->getPath($module['ext'], $file);
251 break;
16072ce1 252
27a90ef6
TO
253 case 'rawUrl':
254 $result[] = $this->res->getUrl($module['ext'], $file);
255 break;
256
257 case 'cacheUrl':
258 $result[] = $this->res->getUrl($module['ext'], $file, TRUE);
259 break;
260
1da632e0
TO
261 case 'settings':
262 if (!empty($module[$resType])) {
263 $result[$moduleName] = $module[$resType];
264 }
265 break;
266
27a90ef6
TO
267 default:
268 throw new \CRM_Core_Exception("Unrecognized resource format");
269 }
270 }
16072ce1
TO
271 }
272 }
273 return $result;
274 }
8671b4f2 275
16072ce1 276}