Merge pull request #19510 from eileenmcnaughton/msg_tpl
[civicrm-core.git] / ext / afform / core / afform.php
CommitLineData
66aa0f5e
TO
1<?php
2
3require_once 'afform.civix.php';
4use CRM_Afform_ExtensionUtil as E;
fb388832 5use Civi\Api4\Action\Afform\Submit;
66aa0f5e 6
bc3b7c5b
TO
7/**
8 * Filter the content of $params to only have supported afform fields.
9 *
10 * @param array $params
11 * @return array
12 */
13function _afform_fields_filter($params) {
5591cfbf 14 $result = [];
e38db494 15 $fields = \Civi\Api4\Afform::getfields()->setCheckPermissions(FALSE)->setAction('create')->execute()->indexBy('name');
50868e8d
CW
16 foreach ($fields as $fieldName => $field) {
17 if (isset($params[$fieldName])) {
18 $result[$fieldName] = $params[$fieldName];
d1ec770c 19
e38db494
CW
20 if ($field['data_type'] === 'Boolean' && !is_bool($params[$fieldName])) {
21 $result[$fieldName] = CRM_Utils_String::strtobool($params[$fieldName]);
d1ec770c
TO
22 }
23 }
bc3b7c5b
TO
24 }
25 return $result;
26}
27
8f4a0ee9 28/**
5591cfbf 29 * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
8f4a0ee9
TO
30 */
31function afform_civicrm_container($container) {
77dccccb 32 $container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
8f4a0ee9
TO
33 $container->setDefinition('afform_scanner', new \Symfony\Component\DependencyInjection\Definition(
34 'CRM_Afform_AfformScanner',
5591cfbf 35 []
b53fe171 36 ))->setPublic(TRUE);
8f4a0ee9
TO
37}
38
66aa0f5e
TO
39/**
40 * Implements hook_civicrm_config().
41 *
42 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
43 */
44function afform_civicrm_config(&$config) {
45 _afform_civix_civicrm_config($config);
77dccccb
TO
46
47 if (isset(Civi::$statics[__FUNCTION__])) {
48 return;
49 }
50 Civi::$statics[__FUNCTION__] = 1;
51
e1aca853 52 Civi::dispatcher()->addListener(Submit::EVENT_NAME, [Submit::class, 'processContacts'], 500);
fb388832 53 Civi::dispatcher()->addListener(Submit::EVENT_NAME, [Submit::class, 'processGenericEntity'], -1000);
5803f22e 54 Civi::dispatcher()->addListener('hook_civicrm_angularModules', ['\Civi\Afform\AngularDependencyMapper', 'autoReq'], -1000);
a92d81a1 55 Civi::dispatcher()->addListener('hook_civicrm_alterAngular', ['\Civi\Afform\AfformMetadataInjector', 'preprocess']);
66aa0f5e
TO
56}
57
58/**
59 * Implements hook_civicrm_xmlMenu().
60 *
61 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_xmlMenu
62 */
63function afform_civicrm_xmlMenu(&$files) {
64 _afform_civix_civicrm_xmlMenu($files);
65}
66
67/**
68 * Implements hook_civicrm_install().
69 *
70 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
71 */
72function afform_civicrm_install() {
73 _afform_civix_civicrm_install();
74}
75
76/**
77 * Implements hook_civicrm_postInstall().
78 *
79 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
80 */
81function afform_civicrm_postInstall() {
82 _afform_civix_civicrm_postInstall();
83}
84
85/**
86 * Implements hook_civicrm_uninstall().
87 *
88 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
89 */
90function afform_civicrm_uninstall() {
91 _afform_civix_civicrm_uninstall();
92}
93
94/**
95 * Implements hook_civicrm_enable().
96 *
97 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
98 */
99function afform_civicrm_enable() {
100 _afform_civix_civicrm_enable();
101}
102
103/**
104 * Implements hook_civicrm_disable().
105 *
106 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
107 */
108function afform_civicrm_disable() {
109 _afform_civix_civicrm_disable();
110}
111
112/**
113 * Implements hook_civicrm_upgrade().
114 *
115 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
116 */
117function afform_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
118 return _afform_civix_civicrm_upgrade($op, $queue);
119}
120
121/**
122 * Implements hook_civicrm_managed().
123 *
124 * Generate a list of entities to create/deactivate/delete when this module
125 * is installed, disabled, uninstalled.
126 *
127 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
128 */
129function afform_civicrm_managed(&$entities) {
130 _afform_civix_civicrm_managed($entities);
521b232a
TO
131
132 /** @var \CRM_Afform_AfformScanner $scanner */
133 if (\Civi::container()->has('afform_scanner')) {
134 $scanner = \Civi::service('afform_scanner');
135 }
136 else {
137 // This might happen at oddballs points - e.g. while you're in the middle of re-enabling the ext.
138 // This AfformScanner instance only lives during this method call, and it feeds off the regular cache.
139 $scanner = new CRM_Afform_AfformScanner();
140 }
141
142 foreach ($scanner->getMetas() as $afform) {
143 if (empty($afform['is_dashlet']) || empty($afform['name'])) {
144 continue;
145 }
146 $entities[] = [
147 'module' => E::LONG_NAME,
148 'name' => 'afform_dashlet_' . $afform['name'],
149 'entity' => 'Dashboard',
150 'update' => 'always',
151 // ideal cleanup policy might be to (a) deactivate if used and (b) remove if unused
152 'cleanup' => 'always',
153 'params' => [
154 'version' => 3,
155 // Q: Should we loop through all domains?
156 'domain_id' => CRM_Core_BAO_Domain::getDomain()->id,
157 'is_active' => TRUE,
158 'name' => $afform['name'],
159 'label' => $afform['title'] ?? ts('(Untitled)'),
160 'directive' => _afform_angular_module_name($afform['name'], 'dash'),
161 'permission' => "@afform:" . $afform['name'],
162 ],
163 ];
164 }
66aa0f5e
TO
165}
166
167/**
168 * Implements hook_civicrm_caseTypes().
169 *
170 * Generate a list of case-types.
171 *
172 * Note: This hook only runs in CiviCRM 4.4+.
173 *
174 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_caseTypes
175 */
176function afform_civicrm_caseTypes(&$caseTypes) {
177 _afform_civix_civicrm_caseTypes($caseTypes);
178}
179
180/**
181 * Implements hook_civicrm_angularModules().
182 *
e1aca853 183 * Generate a list of Afform Angular modules.
66aa0f5e
TO
184 */
185function afform_civicrm_angularModules(&$angularModules) {
186 _afform_civix_civicrm_angularModules($angularModules);
bb56ac78 187
5e04a2d4 188 $afforms = \Civi\Api4\Afform::get(FALSE)
e38db494 189 ->setSelect(['name', 'requires', 'module_name', 'directive_name'])
2d4bfef1
CW
190 ->execute();
191
192 foreach ($afforms as $afform) {
e38db494 193 $angularModules[$afform['module_name']] = [
bb56ac78 194 'ext' => E::LONG_NAME,
2d4bfef1
CW
195 'js' => ['assetBuilder://afform.js?name=' . urlencode($afform['name'])],
196 'requires' => $afform['requires'],
bb56ac78 197 'basePages' => [],
aa6abb77 198 'partialsCallback' => '_afform_get_partials',
2d4bfef1 199 '_afform' => $afform['name'],
77dccccb 200 'exports' => [
04417491 201 $afform['directive_name'] => 'E',
77dccccb 202 ],
bb56ac78 203 ];
bb56ac78 204 }
66aa0f5e
TO
205}
206
aa6abb77 207/**
2d4bfef1
CW
208 * Callback to retrieve partials for a given afform/angular module.
209 *
210 * @see afform_civicrm_angularModules
aa6abb77
TO
211 *
212 * @param string $moduleName
213 * The module name.
214 * @param array $module
215 * The module definition.
216 * @return array
217 * Array(string $filename => string $html).
2d4bfef1 218 * @throws API_Exception
aa6abb77
TO
219 */
220function _afform_get_partials($moduleName, $module) {
2d4bfef1
CW
221 $afform = civicrm_api4('Afform', 'get', [
222 'where' => [['name', '=', $module['_afform']]],
223 'select' => ['layout'],
224 'layoutFormat' => 'html',
225 'checkPermissions' => FALSE,
226 ], 0);
aa6abb77 227 return [
2d4bfef1 228 "~/$moduleName/$moduleName.aff.html" => $afform['layout'],
aa6abb77
TO
229 ];
230}
231
66aa0f5e
TO
232/**
233 * Implements hook_civicrm_alterSettingsFolders().
234 *
235 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_alterSettingsFolders
236 */
237function afform_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
238 _afform_civix_civicrm_alterSettingsFolders($metaDataFolders);
239}
240
241/**
242 * Implements hook_civicrm_entityTypes().
243 *
244 * Declare entity types provided by this module.
245 *
246 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_entityTypes
247 */
248function afform_civicrm_entityTypes(&$entityTypes) {
249 _afform_civix_civicrm_entityTypes($entityTypes);
250}
251
98f4a7cb
TO
252/**
253 * Implements hook_civicrm_themes().
254 */
255function afform_civicrm_themes(&$themes) {
256 _afform_civix_civicrm_themes($themes);
257}
258
bb56ac78
TO
259/**
260 * Implements hook_civicrm_buildAsset().
261 */
262function afform_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
263 if ($asset !== 'afform.js') {
264 return;
265 }
266
267 if (empty($params['name'])) {
268 throw new RuntimeException("Missing required parameter: afform.js?name=NAME");
269 }
270
2d4bfef1 271 $moduleName = _afform_angular_module_name($params['name'], 'camel');
bb56ac78
TO
272 $smarty = CRM_Core_Smarty::singleton();
273 $smarty->assign('afform', [
aa6abb77 274 'camel' => $moduleName,
2d4bfef1 275 'meta' => ['name' => $params['name']],
aa6abb77 276 'templateUrl' => "~/$moduleName/$moduleName.aff.html",
bb56ac78
TO
277 ]);
278 $mimeType = 'text/javascript';
9ec944f2 279 $content = $smarty->fetch('afform/AfformAngularModule.tpl');
bb56ac78
TO
280}
281
8775c48a
TO
282/**
283 * Implements hook_civicrm_alterMenu().
284 */
285function afform_civicrm_alterMenu(&$items) {
8f4a0ee9
TO
286 if (Civi::container()->has('afform_scanner')) {
287 $scanner = Civi::service('afform_scanner');
288 }
289 else {
290 // During installation...
291 $scanner = new CRM_Afform_AfformScanner();
292 }
8775c48a
TO
293 foreach ($scanner->getMetas() as $name => $meta) {
294 if (!empty($meta['server_route'])) {
295 $items[$meta['server_route']] = [
296 'page_callback' => 'CRM_Afform_Page_AfformBase',
297 'page_arguments' => 'afform=' . urlencode($name),
13bdd6d2 298 'title' => $meta['title'] ?? '',
f16b2aee 299 'access_arguments' => [["@afform:$name"], 'and'],
254f01f0 300 'is_public' => $meta['is_public'],
8775c48a
TO
301 ];
302 }
303 }
f16b2aee
TO
304}
305
306/**
307 * Implements hook_civicrm_permission_check().
308 *
586344a7
TO
309 * This extends the list of permissions available in `CRM_Core_Permission:check()`
310 * by introducing virtual-permissions named `@afform:myForm`. The evaluation
311 * of these virtual-permissions is dependent on the settings for `myForm`.
312 * `myForm` may be exposed/integrated through multiple subsystems (routing,
313 * nav-menu, API, etc), and the use of virtual-permissions makes easy to enforce
314 * consistent permissions across any relevant subsystems.
315 *
f16b2aee
TO
316 * @see CRM_Utils_Hook::permission_check()
317 */
318function afform_civicrm_permission_check($permission, &$granted, $contactId) {
14b26ac5 319 if ($permission[0] !== '@') {
f16b2aee
TO
320 // Micro-optimization - this function may get hit a lot.
321 return;
322 }
323
324 if (preg_match('/^@afform:(.*)/', $permission, $m)) {
325 $name = $m[1];
326
2d4bfef1
CW
327 $afform = \Civi\Api4\Afform::get()
328 ->setCheckPermissions(FALSE)
329 ->addWhere('name', '=', $name)
330 ->setSelect(['permission'])
331 ->execute()
332 ->first();
333 if ($afform) {
334 $granted = CRM_Core_Permission::check($afform['permission'], $contactId);
335 }
f16b2aee 336 }
8775c48a
TO
337}
338
c4e6b413
TO
339/**
340 * Implements hook_civicrm_permissionList().
341 *
342 * @see CRM_Utils_Hook::permissionList()
343 */
344function afform_civicrm_permissionList(&$permissions) {
345 $scanner = Civi::service('afform_scanner');
346 foreach ($scanner->getMetas() as $name => $meta) {
347 $permissions['@afform:' . $name] = [
348 'group' => 'afform',
349 'title' => ts('Afform: Inherit permission of %1', [
350 1 => $name,
351 ]),
352 ];
353 }
354}
355
74f862e4
TO
356/**
357 * Clear any local/in-memory caches based on afform data.
358 */
359function _afform_clear() {
360 $container = \Civi::container();
361 $container->get('afform_scanner')->clear();
76b9562a 362 $container->get('angular')->clear();
74f862e4
TO
363}
364
bb56ac78 365/**
87dde5eb
TO
366 * @param string $fileBaseName
367 * Ex: foo-bar
841850b1
TO
368 * @param string $format
369 * 'camel' or 'dash'.
bb56ac78 370 * @return string
841850b1 371 * Ex: 'FooBar' or 'foo-bar'.
3cd5c38b 372 * @throws \Exception
bb56ac78 373 */
87dde5eb 374function _afform_angular_module_name($fileBaseName, $format = 'camel') {
841850b1
TO
375 switch ($format) {
376 case 'camel':
87dde5eb 377 $camelCase = '';
9c84a124 378 foreach (preg_split('/[-_ ]/', $fileBaseName, NULL, PREG_SPLIT_NO_EMPTY) as $shortNamePart) {
87dde5eb
TO
379 $camelCase .= ucfirst($shortNamePart);
380 }
14b26ac5 381 return strtolower($camelCase[0]) . substr($camelCase, 1);
841850b1
TO
382
383 case 'dash':
9c84a124 384 return strtolower(implode('-', preg_split('/[-_ ]|(?=[A-Z])/', $fileBaseName, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
841850b1
TO
385
386 default:
387 throw new \Exception("Unrecognized format");
388 }
bb56ac78 389}