SearchKit - Expose default display to the UI
[civicrm-core.git] / ext / afform / core / Civi / Api4 / Action / Afform / Get.php
1 <?php
2
3 namespace Civi\Api4\Action\Afform;
4
5 use Civi\Api4\CustomField;
6 use Civi\Api4\CustomGroup;
7 use CRM_Afform_ExtensionUtil as E;
8
9 /**
10 * @inheritDoc
11 * @package Civi\Api4\Action\Afform
12 */
13 class Get extends \Civi\Api4\Generic\BasicGetAction {
14
15 use \Civi\Api4\Utils\AfformFormatTrait;
16
17 public function getRecords() {
18 /** @var \CRM_Afform_AfformScanner $scanner */
19 $scanner = \Civi::service('afform_scanner');
20 $getComputed = $this->_isFieldSelected('has_local', 'has_base');
21 $getLayout = $this->_isFieldSelected('layout');
22 $getSearchDisplays = $this->_isFieldSelected('search_displays');
23 $values = [];
24
25 // This helps optimize lookups by file/module/directive name
26 $getNames = array_filter([
27 'name' => $this->_itemsToGet('name'),
28 'module_name' => $this->_itemsToGet('module_name'),
29 'directive_name' => $this->_itemsToGet('directive_name'),
30 ]);
31 $getTypes = $this->_itemsToGet('type');
32
33 $names = $getNames['name'] ?? array_keys($scanner->findFilePaths());
34
35 // Get autogenerated blocks if type block is not excluded
36 if (!$getTypes || in_array('block', $getTypes, TRUE)) {
37 $values = $this->getAutoGenerated($names, $getNames, $getLayout);
38 }
39
40 if ($this->checkPermissions) {
41 $names = array_filter($names, [$this, 'checkPermission']);
42 }
43
44 foreach ($names as $name) {
45 $info = [
46 'name' => $name,
47 'module_name' => _afform_angular_module_name($name, 'camel'),
48 'directive_name' => _afform_angular_module_name($name, 'dash'),
49 ];
50 // Skip if afform does not match requested name
51 foreach ($getNames as $key => $names) {
52 if (!in_array($info[$key], $names)) {
53 continue 2;
54 }
55 }
56 $record = $scanner->getMeta($name);
57 // Skip if afform does not exist or is not of requested type(s)
58 if (
59 (!$record && !isset($values[$name])) ||
60 ($getTypes && isset($record['type']) && !in_array($record['type'], $getTypes, TRUE))
61 ) {
62 continue;
63 }
64 $values[$name] = array_merge($values[$name] ?? [], $record ?? [], $info);
65 if ($getComputed) {
66 $scanner->addComputedFields($values[$name]);
67 }
68 if ($getLayout || $getSearchDisplays) {
69 // Autogenerated layouts will already be in values but can be overridden; scanner takes priority
70 $values[$name]['layout'] = $scanner->getLayout($name) ?? $values[$name]['layout'] ?? '';
71 }
72 if ($getSearchDisplays) {
73 $values[$name]['search_displays'] = $this->getSearchDisplays($values[$name]['layout']);
74 }
75 }
76
77 if ($getLayout && $this->layoutFormat !== 'html') {
78 foreach ($values as $name => $record) {
79 $values[$name]['layout'] = $this->convertHtmlToOutput($record['layout']);
80 }
81 }
82
83 return $values;
84 }
85
86 /**
87 * Assert that a form is authorized.
88 *
89 * @return bool
90 */
91 protected function checkPermission($name) {
92 return \CRM_Core_Permission::check("@afform:$name");
93 }
94
95 /**
96 * Generates afform blocks from custom field sets.
97 *
98 * @param array $names
99 * @param array $getNames
100 * @param bool $getLayout
101 * @return array
102 * @throws \API_Exception
103 */
104 protected function getAutoGenerated(&$names, $getNames, $getLayout) {
105 $values = $groupNames = [];
106 foreach ($getNames['name'] ?? [] as $name) {
107 if (strpos($name, 'afblockCustom_') === 0 && strlen($name) > 13) {
108 $groupNames[] = substr($name, 14);
109 }
110 }
111 // Early return if this api call is fetching afforms by name and those names are not custom-related
112 if ((!empty($getNames['name']) && !$groupNames)
113 || (!empty($getNames['module_name']) && !strstr(implode(' ', $getNames['module_name']), 'afblockCustom'))
114 || (!empty($getNames['directive_name']) && !strstr(implode(' ', $getNames['directive_name']), 'afblock-custom'))
115 ) {
116 return $values;
117 }
118 $customApi = CustomGroup::get(FALSE)
119 ->addSelect('name', 'title', 'help_pre', 'help_post', 'extends', 'max_multiple')
120 ->addWhere('is_multiple', '=', 1)
121 ->addWhere('is_active', '=', 1);
122 if ($groupNames) {
123 $customApi->addWhere('name', 'IN', $groupNames);
124 }
125 if ($getLayout) {
126 $customApi->addSelect('help_pre', 'help_post');
127 $customApi->addChain('fields', CustomField::get(FALSE)
128 ->addSelect('name')
129 ->addWhere('custom_group_id', '=', '$id')
130 ->addWhere('is_active', '=', 1)
131 ->addOrderBy('weight', 'ASC')
132 );
133 }
134 foreach ($customApi->execute() as $custom) {
135 $name = 'afblockCustom_' . $custom['name'];
136 if (!in_array($name, $names)) {
137 $names[] = $name;
138 }
139 $item = [
140 'name' => $name,
141 'type' => 'block',
142 'requires' => [],
143 'title' => E::ts('%1 block', [1 => $custom['title']]),
144 'description' => '',
145 'is_dashlet' => FALSE,
146 'is_public' => FALSE,
147 'is_token' => FALSE,
148 'permission' => 'access CiviCRM',
149 'join_entity' => 'Custom_' . $custom['name'],
150 'entity_type' => $custom['extends'],
151 'has_base' => TRUE,
152 ];
153 if ($getLayout) {
154 $item['layout'] = ($custom['help_pre'] ? '<div class="af-markup">' . $custom['help_pre'] . "</div>\n" : '');
155 foreach ($custom['fields'] as $field) {
156 $item['layout'] .= "<af-field name=\"{$field['name']}\" />\n";
157 }
158 $item['layout'] .= ($custom['help_post'] ? '<div class="af-markup">' . $custom['help_post'] . "</div>\n" : '');
159 }
160 $values[$name] = $item;
161 }
162 return $values;
163 }
164
165 /**
166 * Find search display tags in afform markup
167 *
168 * @param string $html
169 * @return string[]
170 */
171 private function getSearchDisplays(string $html) {
172 $tags = $searchDisplays = [];
173 preg_match_all('/<\\s*crm-search-display[^>]+>/', $html, $tags);
174 foreach ($tags[0] ?? [] as $tag) {
175 $searchName = $displayName = [];
176 preg_match('/search-name\\s*=\\s*[\'"]([^\'"]+)[\'"]/', $tag, $searchName);
177 // Note: display name will be blank when using the default (autogenerated) display
178 preg_match('/display-name\\s*=\\s*[\'"]([^\'"]+)[\'"]/', $tag, $displayName);
179 if (!empty($searchName[1])) {
180 $searchDisplays[] = $searchName[1] . (empty($displayName[1]) ? '' : '.' . $displayName[1]);
181 }
182 }
183 return $searchDisplays;
184 }
185
186 }