Commit | Line | Data |
---|---|---|
66aa0f5e TO |
1 | <?php |
2 | ||
3 | require_once 'afform.civix.php'; | |
4 | use CRM_Afform_ExtensionUtil as E; | |
fb388832 | 5 | use 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 | */ | |
13 | function _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 | */ |
31 | function 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 | */ | |
44 | function 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 | */ | |
63 | function 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 | */ | |
72 | function 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 | */ | |
81 | function 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 | */ | |
90 | function 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 | */ | |
99 | function 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 | */ | |
108 | function 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 | */ | |
117 | function 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 | */ | |
129 | function 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 | */ | |
176 | function 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 | */ |
185 | function 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 | */ |
220 | function _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 | */ | |
237 | function 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 | */ | |
248 | function afform_civicrm_entityTypes(&$entityTypes) { | |
249 | _afform_civix_civicrm_entityTypes($entityTypes); | |
250 | } | |
251 | ||
98f4a7cb TO |
252 | /** |
253 | * Implements hook_civicrm_themes(). | |
254 | */ | |
255 | function afform_civicrm_themes(&$themes) { | |
256 | _afform_civix_civicrm_themes($themes); | |
257 | } | |
258 | ||
bb56ac78 TO |
259 | /** |
260 | * Implements hook_civicrm_buildAsset(). | |
261 | */ | |
262 | function 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 | */ | |
285 | function 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 | */ | |
318 | function 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 | */ | |
344 | function 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 | */ | |
359 | function _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 | 374 | function _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 | } |