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