Merge pull request #15821 from seamuslee001/dev_core_183_custom_group
[civicrm-core.git] / CRM / Core / Form / EntityFormTrait.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17 trait CRM_Core_Form_EntityFormTrait {
18
19 /**
20 * The entity subtype ID (eg. for Relationship / Activity)
21 *
22 * @var int
23 */
24 protected $_entitySubTypeId;
25
26 /**
27 * Get entity fields for the entity to be added to the form.
28 *
29 * @return array
30 */
31 public function getEntityFields() {
32 return $this->entityFields;
33 }
34
35 /**
36 * Explicitly declare the form context.
37 */
38 public function getDefaultContext() {
39 return 'create';
40 }
41
42 /**
43 * Get entity fields for the entity to be added to the form.
44 *
45 * @return string
46 */
47 public function getDeleteMessage() {
48 return $this->deleteMessage;
49 }
50
51 /**
52 * Set the delete message.
53 *
54 * We do this from the constructor in order to do a translation.
55 */
56 public function setDeleteMessage() {
57 }
58
59 /**
60 * Set entity fields to be assigned to the form.
61 */
62 protected function setEntityFields() {
63 }
64
65 /**
66 * Get the entity id being edited.
67 *
68 * @return int|null
69 */
70 public function getEntityId() {
71 return $this->_id;
72 }
73
74 /**
75 * Should custom data be suppressed on this form.
76 *
77 * @return bool
78 */
79 protected function isSuppressCustomData() {
80 return FALSE;
81 }
82
83 /**
84 * Get the entity subtype ID being edited
85 *
86 * @param $subTypeId
87 *
88 * @return int|null
89 */
90 public function getEntitySubTypeId($subTypeId) {
91 if ($subTypeId) {
92 return $subTypeId;
93 }
94 return $this->_entitySubTypeId;
95 }
96
97 /**
98 * If the custom data is in the submitted data (eg. added via ajax loaded form) add to form.
99 */
100 public function addCustomDataToForm() {
101 if ($this->isSuppressCustomData()) {
102 return TRUE;
103 }
104 $customisableEntities = CRM_Core_SelectValues::customGroupExtends();
105 if (isset($customisableEntities[$this->getDefaultEntity()])) {
106 CRM_Custom_Form_CustomData::addToForm($this);
107 }
108 }
109
110 /**
111 * Build the form object.
112 */
113 public function buildQuickEntityForm() {
114 if ($this->isDeleteContext()) {
115 $this->buildDeleteForm();
116 return;
117 }
118 $this->applyFilter('__ALL__', 'trim');
119 $this->addEntityFieldsToTemplate();
120 $this->assign('entityFields', $this->entityFields);
121 $this->assign('entityID', $this->getEntityId());
122 $this->assign('entityInClassFormat', strtolower(str_replace('_', '-', $this->getDefaultEntity())));
123 $this->assign('entityTable', CRM_Core_DAO_AllCoreTables::getTableForClass(CRM_Core_DAO_AllCoreTables::getFullName($this->getDefaultEntity())));
124 $this->addCustomDataToForm();
125 $this->addFormButtons();
126
127 if ($this->isViewContext()) {
128 $this->freeze();
129 }
130 }
131
132 /**
133 * Build the form for any deletion.
134 */
135 protected function buildDeleteForm() {
136 $this->assign('deleteMessage', $this->getDeleteMessage());
137 $this->addFormButtons();
138 }
139
140 /**
141 * Add relevant buttons to the form.
142 */
143 protected function addFormButtons() {
144 if ($this->isViewContext() || $this->_action & CRM_Core_Action::PREVIEW) {
145 $this->addButtons([
146 [
147 'type' => 'cancel',
148 'name' => ts('Done'),
149 'isDefault' => TRUE,
150 ],
151 ]);
152 }
153 else {
154 $this->addButtons([
155 [
156 'type' => 'next',
157 'name' => $this->isDeleteContext() ? ts('Delete') : ts('Save'),
158 'isDefault' => TRUE,
159 ],
160 [
161 'type' => 'cancel',
162 'name' => ts('Cancel'),
163 ],
164 ]);
165 }
166 }
167
168 /**
169 * Get the defaults for the entity.
170 */
171 protected function getEntityDefaults() {
172 $defaults = $moneyFields = [];
173
174 if (!$this->isDeleteContext() &&
175 $this->getEntityId()) {
176 $params = ['id' => $this->getEntityId()];
177 $baoName = $this->_BAOName;
178 $baoName::retrieve($params, $defaults);
179 }
180 foreach ($this->entityFields as $entityFieldName => $fieldSpec) {
181 $value = CRM_Utils_Request::retrieveValue($fieldSpec['name'], $this->getValidationTypeForField($fieldSpec['name']));
182 if ($value !== FALSE && $value !== NULL) {
183 $defaults[$fieldSpec['name']] = $value;
184 }
185 // Store a list of fields with money formatters
186 if (CRM_Utils_Array::value('formatter', $fieldSpec) == 'crmMoney') {
187 $moneyFields[] = $entityFieldName;
188 }
189 }
190 if (!empty($defaults['currency'])) {
191 // If we have a money formatter we need to pass the specified currency or it will render as the default
192 foreach ($moneyFields as $entityFieldName) {
193 $this->entityFields[$entityFieldName]['formatterParam'] = $defaults['currency'];
194 }
195 }
196
197 // Assign again as we may have modified above
198 $this->assign('entityFields', $this->entityFields);
199 return $defaults;
200 }
201
202 /**
203 * Get the validation rule to apply to a function.
204 *
205 * Alphanumeric is designed to always be safe & for now we just return
206 * that but in future we can use tighter rules for types like int, bool etc.
207 *
208 * @param string $fieldName
209 *
210 * @return string|int|bool
211 */
212 protected function getValidationTypeForField($fieldName) {
213 switch ($this->metadata[$fieldName]['type']) {
214 case CRM_Utils_Type::T_BOOLEAN:
215 return 'Boolean';
216
217 default:
218 return 'Alphanumeric';
219 }
220 }
221
222 /**
223 * Set translated fields.
224 *
225 * This function is called from the class constructor, allowing us to set
226 * fields on the class that can't be set as properties due to need for
227 * translation or other non-input specific handling.
228 */
229 protected function setTranslatedFields() {
230 $this->setEntityFields();
231 $this->setDeleteMessage();
232 $metadata = civicrm_api3($this->getDefaultEntity(), 'getfields', ['action' => 'create']);
233 $this->metadata = $metadata['values'];
234 foreach ($this->metadata as $fieldName => $spec) {
235 if (isset($this->entityFields[$fieldName])) {
236 if ($spec['localizable']) {
237 $this->entityFields[$fieldName]['is_add_translate_dialog'] = TRUE;
238 }
239 if (empty($spec['html'])) {
240 $this->entityFields[$fieldName]['not-auto-addable'] = TRUE;
241 }
242 }
243 }
244 }
245
246 /**
247 * Add defined entity field to template.
248 */
249 protected function addEntityFieldsToTemplate() {
250 foreach ($this->getEntityFields() as $fieldSpec) {
251 if (empty($fieldSpec['not-auto-addable'])) {
252 $element = $this->addField($fieldSpec['name'], [], CRM_Utils_Array::value('required', $fieldSpec), FALSE);
253 if (!empty($fieldSpec['is_freeze'])) {
254 $element->freeze();
255 }
256 }
257 }
258 }
259
260 /**
261 * Is the form being used in the context of a deletion.
262 *
263 * (For some reason rather than having separate forms Civi overloads one form).
264 *
265 * @return bool
266 */
267 protected function isDeleteContext() {
268 return ($this->_action & CRM_Core_Action::DELETE);
269 }
270
271 /**
272 * Is the form being used in the context of a view.
273 *
274 * @return bool
275 */
276 protected function isViewContext() {
277 return ($this->_action & CRM_Core_Action::VIEW);
278 }
279
280 protected function setEntityFieldsMetadata() {
281 foreach ($this->entityFields as $field => &$props) {
282 if (!empty($props['not-auto-addable'])) {
283 // We can't load this field using metadata
284 continue;
285 }
286 if ($field != 'id' && $this->isDeleteContext()) {
287 // Delete forms don't generally present any fields to edit
288 continue;
289 }
290 // Resolve action.
291 if (empty($props['action'])) {
292 $props['action'] = $this->getApiAction();
293 }
294 $fieldSpec = civicrm_api3($this->getDefaultEntity(), 'getfield', $props);
295 $fieldSpec = $fieldSpec['values'];
296 if (!isset($props['description']) && isset($fieldSpec['description'])) {
297 $props['description'] = $fieldSpec['description'];
298 }
299 }
300 }
301
302 }