Merge pull request #17720 from alifrumin/5.27.0-releasenotes
[civicrm-core.git] / CRM / Custom / Form / ChangeFieldType.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 * $Id$
17 *
18 */
19
20 /**
21 * This class is to build the form for Deleting Group
22 */
23 class CRM_Custom_Form_ChangeFieldType extends CRM_Core_Form {
24
25 /**
26 * The field id
27 *
28 * @var int
29 */
30 protected $_id;
31
32 /**
33 * Array of custom field values
34 * @var array
35 */
36 protected $_values;
37
38 /**
39 * Mapper array of valid field type
40 * @var array
41 */
42 protected $_htmlTypeTransitions;
43
44 /**
45 * Set up variables to build the form.
46 *
47 * @return void
48 * @access protected
49 */
50 public function preProcess() {
51 $this->_id = CRM_Utils_Request::retrieve('id', 'Positive',
52 $this, TRUE
53 );
54
55 $this->_values = [];
56 $params = ['id' => $this->_id];
57 CRM_Core_BAO_CustomField::retrieve($params, $this->_values);
58
59 $this->_htmlTypeTransitions = self::fieldTypeTransitions(CRM_Utils_Array::value('data_type', $this->_values),
60 CRM_Utils_Array::value('html_type', $this->_values)
61 );
62
63 if (empty($this->_values) || empty($this->_htmlTypeTransitions)) {
64 CRM_Core_Error::fatal(ts("Invalid custom field or can't change input type of this custom field."));
65 }
66
67 $url = CRM_Utils_System::url('civicrm/admin/custom/group/field/update',
68 "action=update&reset=1&gid={$this->_values['custom_group_id']}&id={$this->_id}"
69 );
70 $session = CRM_Core_Session::singleton();
71 $session->pushUserContext($url);
72
73 CRM_Utils_System::setTitle(ts('Change Field Type: %1',
74 [1 => $this->_values['label']]
75 ));
76 }
77
78 /**
79 * Build the form object.
80 *
81 * @return void
82 */
83 public function buildQuickForm() {
84
85 $srcHtmlType = $this->add('select',
86 'src_html_type',
87 ts('Current HTML Type'),
88 [$this->_values['html_type'] => $this->_values['html_type']],
89 TRUE
90 );
91
92 $srcHtmlType->setValue($this->_values['html_type']);
93 $srcHtmlType->freeze();
94
95 $this->assign('srcHtmlType', $this->_values['html_type']);
96
97 $dstHtmlType = $this->add('select',
98 'dst_html_type',
99 ts('New HTML Type'),
100 [
101 '' => ts('- select -'),
102 ] + $this->_htmlTypeTransitions,
103 TRUE
104 );
105
106 $this->addButtons([
107 [
108 'type' => 'next',
109 'name' => ts('Change Field Type'),
110 'isDefault' => TRUE,
111 'js' => ['onclick' => 'return checkCustomDataField();'],
112 ],
113 [
114 'type' => 'cancel',
115 'name' => ts('Cancel'),
116 ],
117 ]);
118 }
119
120 /**
121 * Process the form when submitted.
122 *
123 * @return void
124 */
125 public function postProcess() {
126 $params = $this->controller->exportValues($this->_name);
127
128 $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup',
129 $this->_values['custom_group_id'],
130 'table_name'
131 );
132
133 $singleValueOps = [
134 'Text',
135 'Select',
136 'Radio',
137 'Autocomplete-Select',
138 ];
139
140 $mutliValueOps = [
141 'CheckBox',
142 'Multi-Select',
143 ];
144
145 $srcHtmlType = $this->_values['html_type'];
146 $dstHtmlType = $params['dst_html_type'];
147
148 $customField = new CRM_Core_DAO_CustomField();
149 $customField->id = $this->_id;
150 $customField->find(TRUE);
151
152 if ($dstHtmlType == 'Text' && in_array($srcHtmlType, [
153 'Select',
154 'Radio',
155 'Autocomplete-Select',
156 ])) {
157 $customField->option_group_id = "NULL";
158 CRM_Core_BAO_CustomField::checkOptionGroup($this->_values['option_group_id']);
159 }
160
161 if (in_array($srcHtmlType, $mutliValueOps) &&
162 in_array($dstHtmlType, $singleValueOps)) {
163 $this->flattenToFirstValue($tableName, $this->_values['column_name']);
164 }
165 elseif (in_array($srcHtmlType, $singleValueOps) &&
166 in_array($dstHtmlType, $mutliValueOps)) {
167 $this->firstValueToFlatten($tableName, $this->_values['column_name']);
168 }
169
170 $customField->html_type = $dstHtmlType;
171 $customField->save();
172
173 // Reset cache for custom fields
174 Civi::cache('fields')->flush();
175 // reset ACL and system caches.
176 CRM_Core_BAO_Cache::resetCaches();
177
178 CRM_Core_Session::setStatus(ts('Input type of custom field \'%1\' has been successfully changed to \'%2\'.',
179 [1 => $this->_values['label'], 2 => $dstHtmlType]
180 ), ts('Field Type Changed'), 'success');
181 }
182
183 /**
184 * @param $dataType
185 * @param $htmlType
186 *
187 * @return array|null
188 */
189 public static function fieldTypeTransitions($dataType, $htmlType) {
190 // Text field is single value field,
191 // can not be change to other single value option which contains option group
192 if ($htmlType == 'Text') {
193 return NULL;
194 }
195
196 $singleValueOps = [
197 'Text' => 'Text',
198 'Select' => 'Select',
199 'Radio' => 'Radio',
200 'Autocomplete-Select' => 'Autocomplete-Select',
201 ];
202
203 $mutliValueOps = [
204 'CheckBox' => 'CheckBox',
205 'Multi-Select' => 'Multi-Select',
206 ];
207
208 switch ($dataType) {
209 case 'String':
210 if (in_array($htmlType, array_keys($singleValueOps))) {
211 unset($singleValueOps[$htmlType]);
212 return array_merge($singleValueOps, $mutliValueOps);
213 }
214 elseif (in_array($htmlType, array_keys($mutliValueOps))) {
215 unset($singleValueOps['Text']);
216 foreach ($singleValueOps as $type => $label) {
217 $singleValueOps[$type] = "{$label} ( " . ts('Not Safe') . " )";
218 }
219 unset($mutliValueOps[$htmlType]);
220 return array_merge($mutliValueOps, $singleValueOps);
221 }
222 break;
223
224 case 'Int':
225 case 'Float':
226 case 'Int':
227 case 'Money':
228 if (in_array($htmlType, array_keys($singleValueOps))) {
229 unset($singleValueOps[$htmlType]);
230 return $singleValueOps;
231 }
232 break;
233
234 case 'Memo':
235 $ops = [
236 'TextArea' => 'TextArea',
237 'RichTextEditor' => 'RichTextEditor',
238 ];
239 if (in_array($htmlType, array_keys($ops))) {
240 unset($ops[$htmlType]);
241 return $ops;
242 }
243 break;
244 }
245
246 return NULL;
247 }
248
249 /**
250 * Take a single-value column (eg: a Radio or Select etc ) and convert
251 * value to the multi listed value (eg:"^Foo^")
252 *
253 * @param string $table
254 * @param string $column
255 */
256 public function firstValueToFlatten($table, $column) {
257 $selectSql = "SELECT id, $column FROM $table WHERE $column IS NOT NULL";
258 $updateSql = "UPDATE $table SET $column = %1 WHERE id = %2";
259 $dao = CRM_Core_DAO::executeQuery($selectSql);
260 while ($dao->fetch()) {
261 if (!$dao->{$column}) {
262 continue;
263 }
264 $value = CRM_Core_DAO::VALUE_SEPARATOR . $dao->{$column} . CRM_Core_DAO::VALUE_SEPARATOR;
265 $params = [
266 1 => [(string) $value, 'String'],
267 2 => [$dao->id, 'Integer'],
268 ];
269 CRM_Core_DAO::executeQuery($updateSql, $params);
270 }
271 }
272
273 /**
274 * Take a multi-value column (e.g. a Multi-Select or CheckBox column), and convert
275 * all values (of the form "^^" or "^Foo^" or "^Foo^Bar^") to the first listed value ("Foo")
276 *
277 * @param string $table
278 * @param string $column
279 */
280 public function flattenToFirstValue($table, $column) {
281 $selectSql = "SELECT id, $column FROM $table WHERE $column IS NOT NULL";
282 $updateSql = "UPDATE $table SET $column = %1 WHERE id = %2";
283 $dao = CRM_Core_DAO::executeQuery($selectSql);
284 while ($dao->fetch()) {
285 $values = self::explode($dao->{$column});
286 $params = [
287 1 => [(string) array_shift($values), 'String'],
288 2 => [$dao->id, 'Integer'],
289 ];
290 CRM_Core_DAO::executeQuery($updateSql, $params);
291 }
292 }
293
294 /**
295 * @param $str
296 *
297 * @return array
298 */
299 public static function explode($str) {
300 if (empty($str) || $str == CRM_Core_DAO::VALUE_SEPARATOR . CRM_Core_DAO::VALUE_SEPARATOR) {
301 return [];
302 }
303 else {
304 return explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($str, CRM_Core_DAO::VALUE_SEPARATOR));
305 }
306 }
307
308 }