Afform - Add field types + pseudoconstant metadata to AfformSubmission entity
[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;
5
bc3b7c5b
TO
6/**
7 * Filter the content of $params to only have supported afform fields.
8 *
9 * @param array $params
10 * @return array
11 */
12function _afform_fields_filter($params) {
5591cfbf 13 $result = [];
67d666c6 14 $fields = \Civi\Api4\Afform::getfields(FALSE)->setAction('create')->execute()->indexBy('name');
50868e8d
CW
15 foreach ($fields as $fieldName => $field) {
16 if (isset($params[$fieldName])) {
17 $result[$fieldName] = $params[$fieldName];
d1ec770c 18
e38db494
CW
19 if ($field['data_type'] === 'Boolean' && !is_bool($params[$fieldName])) {
20 $result[$fieldName] = CRM_Utils_String::strtobool($params[$fieldName]);
d1ec770c
TO
21 }
22 }
bc3b7c5b
TO
23 }
24 return $result;
25}
26
8f4a0ee9 27/**
5591cfbf 28 * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
8f4a0ee9
TO
29 */
30function afform_civicrm_container($container) {
77dccccb 31 $container->addResource(new \Symfony\Component\Config\Resource\FileResource(__FILE__));
8f4a0ee9
TO
32 $container->setDefinition('afform_scanner', new \Symfony\Component\DependencyInjection\Definition(
33 'CRM_Afform_AfformScanner',
5591cfbf 34 []
b53fe171 35 ))->setPublic(TRUE);
8f4a0ee9
TO
36}
37
66aa0f5e
TO
38/**
39 * Implements hook_civicrm_config().
40 *
41 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
42 */
43function afform_civicrm_config(&$config) {
44 _afform_civix_civicrm_config($config);
77dccccb
TO
45
46 if (isset(Civi::$statics[__FUNCTION__])) {
47 return;
48 }
49 Civi::$statics[__FUNCTION__] = 1;
50
b7edd04e 51 $dispatcher = Civi::dispatcher();
07a1bc45
A
52 $dispatcher->addListener('civi.afform.submit', ['\Civi\Api4\Action\Afform\Submit', 'processGenericEntity'], 0);
53 $dispatcher->addListener('civi.afform.submit', ['\Civi\Api4\Action\Afform\Submit', 'preprocessContact'], 10);
b7edd04e
TO
54 $dispatcher->addListener('hook_civicrm_angularModules', ['\Civi\Afform\AngularDependencyMapper', 'autoReq'], -1000);
55 $dispatcher->addListener('hook_civicrm_alterAngular', ['\Civi\Afform\AfformMetadataInjector', 'preprocess']);
bdceaf62 56 $dispatcher->addListener('hook_civicrm_check', ['\Civi\Afform\StatusChecks', 'hook_civicrm_check']);
b7edd04e
TO
57
58 // Register support for email tokens
bdceaf62
TO
59 if (CRM_Extension_System::singleton()->getMapper()->isActiveModule('authx')) {
60 $dispatcher->addListener('hook_civicrm_alterMailContent', ['\Civi\Afform\Tokens', 'applyCkeditorWorkaround']);
61 $dispatcher->addListener('hook_civicrm_tokens', ['\Civi\Afform\Tokens', 'hook_civicrm_tokens']);
62 $dispatcher->addListener('hook_civicrm_tokenValues', ['\Civi\Afform\Tokens', 'hook_civicrm_tokenValues']);
63 }
66aa0f5e
TO
64}
65
66aa0f5e
TO
66/**
67 * Implements hook_civicrm_install().
68 *
69 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
70 */
71function afform_civicrm_install() {
72 _afform_civix_civicrm_install();
73}
74
75/**
76 * Implements hook_civicrm_postInstall().
77 *
78 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall
79 */
80function afform_civicrm_postInstall() {
81 _afform_civix_civicrm_postInstall();
82}
83
84/**
85 * Implements hook_civicrm_uninstall().
86 *
87 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall
88 */
89function afform_civicrm_uninstall() {
90 _afform_civix_civicrm_uninstall();
91}
92
93/**
94 * Implements hook_civicrm_enable().
95 *
96 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
97 */
98function afform_civicrm_enable() {
99 _afform_civix_civicrm_enable();
100}
101
102/**
103 * Implements hook_civicrm_disable().
104 *
105 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable
106 */
107function afform_civicrm_disable() {
108 _afform_civix_civicrm_disable();
109}
110
111/**
112 * Implements hook_civicrm_upgrade().
113 *
114 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade
115 */
116function afform_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
117 return _afform_civix_civicrm_upgrade($op, $queue);
118}
119
120/**
121 * Implements hook_civicrm_managed().
122 *
123 * Generate a list of entities to create/deactivate/delete when this module
124 * is installed, disabled, uninstalled.
125 *
126 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
127 */
128function afform_civicrm_managed(&$entities) {
521b232a
TO
129 /** @var \CRM_Afform_AfformScanner $scanner */
130 if (\Civi::container()->has('afform_scanner')) {
131 $scanner = \Civi::service('afform_scanner');
132 }
133 else {
134 // This might happen at oddballs points - e.g. while you're in the middle of re-enabling the ext.
135 // This AfformScanner instance only lives during this method call, and it feeds off the regular cache.
136 $scanner = new CRM_Afform_AfformScanner();
137 }
138
139 foreach ($scanner->getMetas() as $afform) {
140 if (empty($afform['is_dashlet']) || empty($afform['name'])) {
141 continue;
142 }
143 $entities[] = [
144 'module' => E::LONG_NAME,
145 'name' => 'afform_dashlet_' . $afform['name'],
146 'entity' => 'Dashboard',
147 'update' => 'always',
148 // ideal cleanup policy might be to (a) deactivate if used and (b) remove if unused
149 'cleanup' => 'always',
150 'params' => [
9bed436a
CW
151 'version' => 4,
152 'values' => [
153 // Q: Should we loop through all domains?
154 'domain_id' => 'current_domain',
155 'is_active' => TRUE,
156 'name' => $afform['name'],
157 'label' => $afform['title'] ?? E::ts('(Untitled)'),
158 'directive' => _afform_angular_module_name($afform['name'], 'dash'),
159 'permission' => "@afform:" . $afform['name'],
160 'url' => NULL,
161 ],
521b232a
TO
162 ],
163 ];
164 }
66aa0f5e
TO
165}
166
167/**
c63f20d3 168 * Implements hook_civicrm_tabset().
66aa0f5e 169 *
c63f20d3
CW
170 * Adds afforms as contact summary tabs.
171 */
172function afform_civicrm_tabset($tabsetName, &$tabs, $context) {
173 if ($tabsetName !== 'civicrm/contact/view') {
174 return;
175 }
176 $scanner = \Civi::service('afform_scanner');
177 $weight = 111;
178 foreach ($scanner->getMetas() as $afform) {
179 if (!empty($afform['contact_summary']) && $afform['contact_summary'] === 'tab') {
180 $module = _afform_angular_module_name($afform['name']);
181 $tabs[] = [
182 'id' => $afform['name'],
183 'title' => $afform['title'],
184 'weight' => $weight++,
185 'icon' => 'crm-i fa-list-alt',
186 'is_active' => TRUE,
187 'template' => 'afform/contactSummary/AfformTab.tpl',
188 'module' => $module,
189 'directive' => _afform_angular_module_name($afform['name'], 'dash'),
190 ];
191 // If this is the real contact summary page (and not a callback from ContactLayoutEditor), load module.
192 if (empty($context['caller'])) {
193 Civi::service('angularjs.loader')->addModules($module);
194 }
195 }
196 }
197}
198
199/**
200 * Implements hook_civicrm_pageRun().
66aa0f5e 201 *
c63f20d3
CW
202 * Adds afforms as contact summary blocks.
203 */
204function afform_civicrm_pageRun(&$page) {
205 if (get_class($page) !== 'CRM_Contact_Page_View_Summary') {
206 return;
207 }
208 $scanner = \Civi::service('afform_scanner');
209 $cid = $page->get('cid');
210 $side = 'left';
211 foreach ($scanner->getMetas() as $afform) {
212 if (!empty($afform['contact_summary']) && $afform['contact_summary'] === 'block') {
213 $module = _afform_angular_module_name($afform['name']);
214 $block = [
215 'module' => $module,
216 'directive' => _afform_angular_module_name($afform['name'], 'dash'),
217 ];
218 $content = CRM_Core_Smarty::singleton()->fetchWith('afform/contactSummary/AfformBlock.tpl', ['contactId' => $cid, 'block' => $block]);
219 CRM_Core_Region::instance("contact-basic-info-$side")->add([
220 'markup' => '<div class="crm-summary-block">' . $content . '</div>',
221 'weight' => 1,
222 ]);
223 Civi::service('angularjs.loader')->addModules($module);
224 $side = $side === 'left' ? 'right' : 'left';
225 }
226 }
227}
228
229/**
230 * Implements hook_civicrm_contactSummaryBlocks().
66aa0f5e 231 *
c63f20d3 232 * @link https://github.com/civicrm/org.civicrm.contactlayout
66aa0f5e 233 */
c63f20d3 234function afform_civicrm_contactSummaryBlocks(&$blocks) {
4f664e9c
CW
235 $afforms = \Civi\Api4\Afform::get(FALSE)
236 ->setSelect(['name', 'title', 'directive_name', 'module_name', 'type', 'type:icon', 'type:label'])
237 ->addWhere('contact_summary', '=', 'block')
238 ->execute();
4096e4b0 239 foreach ($afforms as $index => $afform) {
4f664e9c
CW
240 // Create a group per afform type
241 $blocks += [
242 "afform_{$afform['type']}" => [
243 'title' => $afform['type:label'],
244 'icon' => $afform['type:icon'],
245 'blocks' => [],
246 ],
247 ];
248 $blocks["afform_{$afform['type']}"]['blocks'][$afform['name']] = [
249 'title' => $afform['title'],
250 'tpl_file' => 'afform/contactSummary/AfformBlock.tpl',
251 'module' => $afform['module_name'],
252 'directive' => $afform['directive_name'],
253 'sample' => [
254 $afform['type:label'],
255 ],
256 'edit' => 'civicrm/admin/afform#/edit/' . $afform['name'],
4096e4b0 257 'system_default' => [0, $index % 2],
4f664e9c 258 ];
c63f20d3 259 }
66aa0f5e
TO
260}
261
262/**
263 * Implements hook_civicrm_angularModules().
264 *
e1aca853 265 * Generate a list of Afform Angular modules.
66aa0f5e
TO
266 */
267function afform_civicrm_angularModules(&$angularModules) {
5e04a2d4 268 $afforms = \Civi\Api4\Afform::get(FALSE)
e38db494 269 ->setSelect(['name', 'requires', 'module_name', 'directive_name'])
2d4bfef1
CW
270 ->execute();
271
272 foreach ($afforms as $afform) {
e38db494 273 $angularModules[$afform['module_name']] = [
bb56ac78 274 'ext' => E::LONG_NAME,
2d4bfef1
CW
275 'js' => ['assetBuilder://afform.js?name=' . urlencode($afform['name'])],
276 'requires' => $afform['requires'],
bb56ac78 277 'basePages' => [],
aa6abb77 278 'partialsCallback' => '_afform_get_partials',
2d4bfef1 279 '_afform' => $afform['name'],
292054ac
CW
280 // TODO: Allow afforms to declare their own theming requirements
281 'bundles' => ['bootstrap3'],
77dccccb 282 'exports' => [
04417491 283 $afform['directive_name'] => 'E',
77dccccb 284 ],
bb56ac78 285 ];
bb56ac78 286 }
66aa0f5e
TO
287}
288
aa6abb77 289/**
2d4bfef1
CW
290 * Callback to retrieve partials for a given afform/angular module.
291 *
292 * @see afform_civicrm_angularModules
aa6abb77
TO
293 *
294 * @param string $moduleName
295 * The module name.
296 * @param array $module
297 * The module definition.
298 * @return array
299 * Array(string $filename => string $html).
2d4bfef1 300 * @throws API_Exception
aa6abb77
TO
301 */
302function _afform_get_partials($moduleName, $module) {
2d4bfef1
CW
303 $afform = civicrm_api4('Afform', 'get', [
304 'where' => [['name', '=', $module['_afform']]],
305 'select' => ['layout'],
306 'layoutFormat' => 'html',
307 'checkPermissions' => FALSE,
308 ], 0);
aa6abb77 309 return [
2d4bfef1 310 "~/$moduleName/$moduleName.aff.html" => $afform['layout'],
aa6abb77
TO
311 ];
312}
313
66aa0f5e
TO
314/**
315 * Implements hook_civicrm_entityTypes().
316 *
317 * Declare entity types provided by this module.
318 *
319 * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_entityTypes
320 */
321function afform_civicrm_entityTypes(&$entityTypes) {
322 _afform_civix_civicrm_entityTypes($entityTypes);
323}
324
bb56ac78
TO
325/**
326 * Implements hook_civicrm_buildAsset().
327 */
328function afform_civicrm_buildAsset($asset, $params, &$mimeType, &$content) {
329 if ($asset !== 'afform.js') {
330 return;
331 }
332
333 if (empty($params['name'])) {
334 throw new RuntimeException("Missing required parameter: afform.js?name=NAME");
335 }
336
2d4bfef1 337 $moduleName = _afform_angular_module_name($params['name'], 'camel');
b6e13973
SL
338 $formMetaData = (array) civicrm_api4('Afform', 'get', [
339 'checkPermissions' => FALSE,
340 'select' => ['redirect', 'name'],
341 'where' => [['name', '=', $params['name']]],
342 ], 0);
bb56ac78
TO
343 $smarty = CRM_Core_Smarty::singleton();
344 $smarty->assign('afform', [
aa6abb77 345 'camel' => $moduleName,
b6e13973 346 'meta' => $formMetaData,
aa6abb77 347 'templateUrl' => "~/$moduleName/$moduleName.aff.html",
bb56ac78
TO
348 ]);
349 $mimeType = 'text/javascript';
9ec944f2 350 $content = $smarty->fetch('afform/AfformAngularModule.tpl');
bb56ac78
TO
351}
352
8775c48a
TO
353/**
354 * Implements hook_civicrm_alterMenu().
355 */
356function afform_civicrm_alterMenu(&$items) {
8f4a0ee9
TO
357 if (Civi::container()->has('afform_scanner')) {
358 $scanner = Civi::service('afform_scanner');
359 }
360 else {
361 // During installation...
362 $scanner = new CRM_Afform_AfformScanner();
363 }
8775c48a
TO
364 foreach ($scanner->getMetas() as $name => $meta) {
365 if (!empty($meta['server_route'])) {
366 $items[$meta['server_route']] = [
367 'page_callback' => 'CRM_Afform_Page_AfformBase',
368 'page_arguments' => 'afform=' . urlencode($name),
13bdd6d2 369 'title' => $meta['title'] ?? '',
f16b2aee 370 'access_arguments' => [["@afform:$name"], 'and'],
254f01f0 371 'is_public' => $meta['is_public'],
8775c48a
TO
372 ];
373 }
374 }
f16b2aee
TO
375}
376
377/**
378 * Implements hook_civicrm_permission_check().
379 *
586344a7
TO
380 * This extends the list of permissions available in `CRM_Core_Permission:check()`
381 * by introducing virtual-permissions named `@afform:myForm`. The evaluation
382 * of these virtual-permissions is dependent on the settings for `myForm`.
383 * `myForm` may be exposed/integrated through multiple subsystems (routing,
384 * nav-menu, API, etc), and the use of virtual-permissions makes easy to enforce
385 * consistent permissions across any relevant subsystems.
386 *
f16b2aee
TO
387 * @see CRM_Utils_Hook::permission_check()
388 */
389function afform_civicrm_permission_check($permission, &$granted, $contactId) {
14b26ac5 390 if ($permission[0] !== '@') {
f16b2aee
TO
391 // Micro-optimization - this function may get hit a lot.
392 return;
393 }
394
395 if (preg_match('/^@afform:(.*)/', $permission, $m)) {
396 $name = $m[1];
397
2d4bfef1
CW
398 $afform = \Civi\Api4\Afform::get()
399 ->setCheckPermissions(FALSE)
400 ->addWhere('name', '=', $name)
401 ->setSelect(['permission'])
402 ->execute()
403 ->first();
404 if ($afform) {
405 $granted = CRM_Core_Permission::check($afform['permission'], $contactId);
406 }
f16b2aee 407 }
8775c48a
TO
408}
409
c4e6b413
TO
410/**
411 * Implements hook_civicrm_permissionList().
412 *
413 * @see CRM_Utils_Hook::permissionList()
414 */
415function afform_civicrm_permissionList(&$permissions) {
416 $scanner = Civi::service('afform_scanner');
417 foreach ($scanner->getMetas() as $name => $meta) {
418 $permissions['@afform:' . $name] = [
419 'group' => 'afform',
67d666c6 420 'title' => E::ts('Afform: Inherit permission of %1', [
c4e6b413
TO
421 1 => $name,
422 ]),
423 ];
424 }
425}
426
74f862e4
TO
427/**
428 * Clear any local/in-memory caches based on afform data.
429 */
430function _afform_clear() {
431 $container = \Civi::container();
432 $container->get('afform_scanner')->clear();
76b9562a 433 $container->get('angular')->clear();
74f862e4
TO
434}
435
bb56ac78 436/**
87dde5eb
TO
437 * @param string $fileBaseName
438 * Ex: foo-bar
841850b1
TO
439 * @param string $format
440 * 'camel' or 'dash'.
bb56ac78 441 * @return string
841850b1 442 * Ex: 'FooBar' or 'foo-bar'.
3cd5c38b 443 * @throws \Exception
bb56ac78 444 */
87dde5eb 445function _afform_angular_module_name($fileBaseName, $format = 'camel') {
841850b1
TO
446 switch ($format) {
447 case 'camel':
87dde5eb 448 $camelCase = '';
9c84a124 449 foreach (preg_split('/[-_ ]/', $fileBaseName, NULL, PREG_SPLIT_NO_EMPTY) as $shortNamePart) {
87dde5eb
TO
450 $camelCase .= ucfirst($shortNamePart);
451 }
14b26ac5 452 return strtolower($camelCase[0]) . substr($camelCase, 1);
841850b1
TO
453
454 case 'dash':
9c84a124 455 return strtolower(implode('-', preg_split('/[-_ ]|(?=[A-Z])/', $fileBaseName, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
841850b1
TO
456
457 default:
458 throw new \Exception("Unrecognized format");
459 }
bb56ac78 460}
355881ac
SL
461
462/**
463 * Implements hook_civicrm_alterApiRoutePermissions().
464 *
465 * @see CRM_Utils_Hook::alterApiRoutePermissions
466 */
467function afform_civicrm_alterApiRoutePermissions(&$permissions, $entity, $action) {
f362c531 468 if ($entity == 'Afform') {
db393580
CW
469 // These actions should be accessible to anonymous users; permissions are checked internally
470 $allowedActions = ['prefill', 'submit', 'submitFile', 'getOptions'];
471 if (in_array($action, $allowedActions, TRUE)) {
f362c531
TO
472 $permissions = CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION;
473 }
355881ac
SL
474 }
475}
f228ad74
CW
476
477/**
478 * Implements hook_civicrm_preProcess().
479 *
480 * Wordpress only: Adds Afforms to the shortcode dialog (when editing pages/posts).
481 */
482function afform_civicrm_preProcess($formName, &$form) {
483 if ($formName === 'CRM_Core_Form_ShortCode') {
484 $form->components['afform'] = [
485 'label' => E::ts('Form Builder'),
486 'select' => [
487 'key' => 'name',
488 'entity' => 'Afform',
489 'select' => ['minimumInputLength' => 0],
490 'api' => [
491 'params' => ['type' => ['IN' => ['form', 'search']]],
492 ],
493 ],
494 ];
495 }
496}
497
1c3d1f8d
CW
498/**
499 * Implements hook_civicrm_pre().
500 */
501function afform_civicrm_pre($op, $entity, $id, &$params) {
502 // When deleting a searchDisplay, also delete any Afforms the display is embedded within
503 if ($entity === 'SearchDisplay' && $op === 'delete') {
504 $display = \Civi\Api4\SearchDisplay::get(FALSE)
505 ->addSelect('saved_search_id.name', 'name')
506 ->addWhere('id', '=', $id)
507 ->execute()->first();
508 \Civi\Api4\Afform::revert(FALSE)
509 ->addWhere('search_displays', 'CONTAINS', $display['saved_search_id.name'] . ".{$display['name']}")
510 ->execute();
511 }
04309075
CW
512 // When deleting a savedSearch, delete any Afforms which use the default display
513 if ($entity === 'SearchDisplay' && $op === 'delete') {
514 $search = \Civi\Api4\SavedSearch::get(FALSE)
515 ->addSelect('name')
516 ->addWhere('id', '=', $id)
517 ->execute()->first();
518 \Civi\Api4\Afform::revert(FALSE)
519 ->addWhere('search_displays', 'CONTAINS', $search['name'])
520 ->execute();
521 }
522}
523
524/**
525 * Implements hook_civicrm_referenceCounts().
526 */
527function afform_civicrm_referenceCounts($dao, &$counts) {
528 // Count afforms which contain a search display
529 if (is_a($dao, 'CRM_Search_DAO_SearchDisplay') && $dao->id) {
530 if (empty($dao->saved_search_id) || empty($dao->name)) {
531 $dao->find(TRUE);
532 }
533 $search = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $dao->saved_search_id);
534 $afforms = \Civi\Api4\Afform::get(FALSE)
535 ->selectRowCount()
536 ->addWhere('search_displays', 'CONTAINS', "$search.$dao->name")
537 ->execute();
538 if ($afforms->count()) {
539 $counts[] = [
540 'name' => 'Afform',
541 'type' => 'Afform',
542 'count' => $afforms->count(),
543 ];
544 }
545 }
546 // Count afforms which contain any displays from a SavedSearch (including the default display)
547 elseif (is_a($dao, 'CRM_Contact_DAO_SavedSearch') && $dao->id) {
548 if (empty($dao->name)) {
549 $dao->find(TRUE);
550 }
551 $clauses = [
552 ['search_displays', 'CONTAINS', $dao->name],
553 ];
554 try {
555 $displays = civicrm_api4('SearchDisplay', 'get', [
556 'where' => [['saved_search_id', '=', $dao->id]],
557 'select' => 'name',
558 ], ['name']);
559 foreach ($displays as $displayName) {
560 $clauses[] = ['search_displays', 'CONTAINS', $dao->name . '.' . $displayName];
561 }
562 }
563 catch (Exception $e) {
564 // In case SearchKit is not installed, the api call would fail
565 }
566 $afforms = \Civi\Api4\Afform::get(FALSE)
567 ->selectRowCount()
568 ->addClause('OR', $clauses)
569 ->execute();
570 if ($afforms->count()) {
571 $counts[] = [
572 'name' => 'Afform',
573 'type' => 'Afform',
574 'count' => $afforms->count(),
575 ];
576 }
577 }
1c3d1f8d
CW
578}
579
f228ad74
CW
580// Wordpress only: Register callback for rendering shortcodes
581if (function_exists('add_filter')) {
582 add_filter('civicrm_shortcode_get_markup', 'afform_shortcode_content', 10, 4);
583}
584
585/**
586 * Wordpress only: Render Afform content for shortcodes.
587 *
588 * @param string $content
589 * HTML Markup
590 * @param array $atts
591 * Shortcode attributes.
592 * @param array $args
593 * Existing shortcode arguments.
594 * @param string $context
595 * How many shortcodes are present on the page: 'single' or 'multiple'.
596 * @return string
597 * Modified markup.
598 */
599function afform_shortcode_content($content, $atts, $args, $context) {
600 if ($atts['component'] === 'afform') {
601 $afform = civicrm_api4('Afform', 'get', [
602 'select' => ['directive_name', 'module_name'],
603 'where' => [['name', '=', $atts['name']]],
604 ])->first();
605 if ($afform) {
606 Civi::service('angularjs.loader')->addModules($afform['module_name']);
607 $content = "
608 <div class='crm-container' id='bootstrap-theme'>
609 <crm-angular-js modules='{$afform['module_name']}'>
610 <{$afform['directive_name']}></{$afform['directive_name']}>
611 </crm-angular-js>
612 </div>";
613 }
614 }
615 return $content;
616}