CRM-15855 - Add crmAutosave module
[civicrm-core.git] / Civi / Angular / Manager.php
1 <?php
2 namespace Civi\Angular;
3
4 /**
5 * Manage Angular resources.
6 *
7 * @package Civi\Angular
8 */
9 class 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
20 * The Civi extension which defines the Angular module.
21 * - js: array(string $relativeFilePath)
22 * List of JS files (relative to the extension).
23 * - css: array(string $relativeFilePath)
24 * List of CSS files (relative to the extension).
25 * - partials: array(string $relativeFilePath)
26 * A list of partial-HTML folders (relative to the extension).
27 * This will be mapped to "~/moduleName" by crmResource.
28 */
29 protected $modules = NULL;
30
31 /**
32 * @param \CRM_Core_Resources $res
33 * The resource manager.
34 */
35 public function __construct($res) {
36 $this->res = $res;
37 }
38
39 /**
40 * Get a list of AngularJS modules which should be autoloaded
41 *
42 * @return array
43 * Each item has some combination of these keys:
44 * - ext: string
45 * The Civi extension which defines the Angular module.
46 * - js: array(string $relativeFilePath)
47 * List of JS files (relative to the extension).
48 * - css: array(string $relativeFilePath)
49 * List of CSS files (relative to the extension).
50 * - partials: array(string $relativeFilePath)
51 * A list of partial-HTML folders (relative to the extension).
52 * This will be mapped to "~/moduleName" by crmResource.
53 */
54 public function getModules() {
55 if ($this->modules === NULL) {
56
57 $angularModules = array();
58 $angularModules['angularFileUpload'] = array(
59 'ext' => 'civicrm',
60 'js' => array('bower_components/angular-file-upload/angular-file-upload.min.js'),
61 );
62 $angularModules['crmApp'] = array(
63 'ext' => 'civicrm',
64 'js' => array('js/angular-crmApp.js'),
65 );
66 $angularModules['crmAttachment'] = array(
67 'ext' => 'civicrm',
68 'js' => array('js/angular-crmAttachment.js'),
69 'css' => array('css/angular-crmAttachment.css'),
70 'partials' => array('partials/crmAttachment'),
71 );
72 $angularModules['crmAutosave'] = array(
73 'ext' => 'civicrm',
74 'js' => array('js/angular-crmAutosave.js'),
75 );
76 $angularModules['crmResource'] = array(
77 'ext' => 'civicrm',
78 // 'js' => array('js/angular-crmResource/byModule.js'), // One HTTP request per module.
79 'js' => array('js/angular-crmResource/all.js'), // One HTTP request for all modules.
80 );
81 $angularModules['crmUi'] = array(
82 'ext' => 'civicrm',
83 'js' => array('js/angular-crm-ui.js', 'packages/ckeditor/ckeditor.js'),
84 'partials' => array('partials/crmUi'),
85 );
86 $angularModules['crmUtil'] = array(
87 'ext' => 'civicrm',
88 'js' => array('js/angular-crm-util.js'),
89 );
90 // https://github.com/jwstadler/angular-jquery-dialog-service
91 $angularModules['dialogService'] = array(
92 'ext' => 'civicrm',
93 'js' => array('bower_components/angular-jquery-dialog-service/dialog-service.js'),
94 );
95 $angularModules['ngSanitize'] = array(
96 'ext' => 'civicrm',
97 'js' => array('js/angular-sanitize.js'),
98 );
99 $angularModules['ui.utils'] = array(
100 'ext' => 'civicrm',
101 'js' => array('bower_components/angular-ui-utils/ui-utils.min.js'),
102 );
103 $angularModules['ui.sortable'] = array(
104 'ext' => 'civicrm',
105 'js' => array('bower_components/angular-ui-sortable/sortable.min.js'),
106 );
107 $angularModules['unsavedChanges'] = array(
108 'ext' => 'civicrm',
109 'js' => array('bower_components/angular-unsavedChanges/dist/unsavedChanges.min.js'),
110 );
111
112 foreach (\CRM_Core_Component::getEnabledComponents() as $component) {
113 $angularModules = array_merge($angularModules, $component->getAngularModules());
114 }
115 \CRM_Utils_Hook::angularModules($angularModules);
116 $this->modules = $this->resolvePatterns($angularModules);
117 }
118
119 return $this->modules;
120 }
121
122 /**
123 * Get the descriptor for an Angular module.
124 *
125 * @param string $name
126 * Module name.
127 * @return array
128 * Details about the module:
129 * - ext: string, the name of the Civi extension which defines the module
130 * - js: array(string $relativeFilePath).
131 * - css: array(string $relativeFilePath).
132 * - partials: array(string $relativeFilePath).
133 * @throws \Exception
134 */
135 public function getModule($name) {
136 $modules = $this->getModules();
137 if (!isset($modules[$name])) {
138 throw new \Exception("Unrecognized Angular module");
139 }
140 return $modules[$name];
141 }
142
143 /**
144 * Convert any globs in an Angular module to file names.
145 *
146 * @param array $modules
147 * List of Angular modules.
148 * @return array
149 * Updated list of Angular modules
150 */
151 protected function resolvePatterns($modules) {
152 $newModules = array();
153
154 foreach ($modules as $moduleKey => $module) {
155 foreach (array('js', 'css', 'partials') as $fileset) {
156 if (!isset($module[$fileset])) {
157 continue;
158 }
159 $module[$fileset] = $this->res->glob($module['ext'], $module[$fileset]);
160 }
161 $newModules[$moduleKey] = $module;
162 }
163
164 return $newModules;
165 }
166
167 /**
168 * Get the partial HTML documents for a module.
169 *
170 * @param string $name
171 * Angular module name.
172 * @return array
173 * Array(string $extFilePath => string $html)
174 * @throws \Exception
175 * Invalid partials configuration.
176 */
177 public function getPartials($name) {
178 $module = $this->getModule($name);
179 $result = array();
180 if (isset($module['partials'])) {
181 foreach ($module['partials'] as $partialDir) {
182 $partialDir = $this->res->getPath($module['ext']) . '/' . $partialDir;
183 $files = \CRM_Utils_File::findFiles($partialDir, '*.html', TRUE);
184 foreach ($files as $file) {
185 $filename = '~/' . $name . '/' . $file;
186 $result[$filename] = file_get_contents($partialDir . '/' . $file);
187 }
188 }
189 }
190 return $result;
191 }
192
193 /**
194 * Get list of translated strings for a module.
195 *
196 * @param string $name
197 * Angular module name.
198 * @return array
199 * Translated strings: array(string $orig => string $translated).
200 */
201 public function getTranslatedStrings($name) {
202 $module = $this->getModule($name);
203 $result = array();
204 $strings = $this->getStrings($name);
205 foreach ($strings as $string) {
206 // TODO: should we pass translation domain based on $module[ext] or $module[tsDomain]?
207 // It doesn't look like client side really supports the domain right now...
208 $translated = ts($string, array(
209 'domain' => array($module['ext'], NULL),
210 ));
211 if ($translated != $string) {
212 $result[$string] = $translated;
213 }
214 }
215 return $result;
216 }
217
218 /**
219 * Get list of translatable strings for a module.
220 *
221 * @param string $name
222 * Angular module name.
223 * @return array
224 * Translatable strings.
225 */
226 public function getStrings($name) {
227 $module = $this->getModule($name);
228 $result = array();
229 if (isset($module['js'])) {
230 foreach ($module['js'] as $file) {
231 $strings = $this->res->getStrings()->get(
232 $module['ext'],
233 $this->res->getPath($module['ext'], $file),
234 'text/javascript'
235 );
236 $result = array_unique(array_merge($result, $strings));
237 }
238 }
239 if (isset($module['partials'])) {
240 foreach ($module['partials'] as $partialDir) {
241 $partialDir = $this->res->getPath($module['ext']) . '/' . $partialDir;
242 $files = \CRM_Utils_File::findFiles($partialDir, '*.html');
243 foreach ($files as $file) {
244 $strings = $this->res->getStrings()->get(
245 $module['ext'],
246 $file,
247 'text/html'
248 );
249 $result = array_unique(array_merge($result, $strings));
250 }
251 }
252 }
253 return $result;
254 }
255
256 /**
257 * @param string $name
258 * Module name.
259 * @return array
260 * List of URLs.
261 * @throws \Exception
262 */
263 public function getScriptUrls($name) {
264 $module = $this->getModule($name);
265 $result = array();
266 if (isset($module['js'])) {
267 foreach ($module['js'] as $file) {
268 $result[] = $this->res->getUrl($module['ext'], $file, TRUE);
269 }
270 }
271 return $result;
272 }
273
274 /**
275 * @param string $name
276 * Module name.
277 * @return array
278 * List of URLs.
279 * @throws \Exception
280 */
281 public function getStyleUrls($name) {
282 $module = $this->getModule($name);
283 $result = array();
284 if (isset($module['css'])) {
285 foreach ($module['css'] as $file) {
286 $result[] = $this->res->getUrl($module['ext'], $file, TRUE);
287 }
288 }
289 return $result;
290 }
291
292 }