CustomGroup - Ensure 'name' is always unique
[civicrm-core.git] / CRM / Upgrade / Incremental / php / FiveFortySeven.php
CommitLineData
9ea6ff40
C
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 * Upgrade logic for the 5.47.x series.
14 *
15 * Each minor version in the series is handled by either a `5.47.x.mysql.tpl` file,
16 * or a function in this class named `upgrade_5_47_x`.
17 * If only a .tpl file exists for a version, it will be run automatically.
18 * If the function exists, it must explicitly add the 'runSql' task if there is a corresponding .mysql.tpl.
19 *
68cc0e2e 20 * This class may also implement `setPreUpgradeMessage()` and `setPostUpgradeMessage()` functions.
9ea6ff40
C
21 */
22class CRM_Upgrade_Incremental_php_FiveFortySeven extends CRM_Upgrade_Incremental_Base {
23
fe8c33a3
EM
24 /**
25 * Compute any messages which should be displayed before upgrade.
26 *
27 * Note: This function is called iteratively for each incremental upgrade step.
28 * There must be a concrete step (eg 'X.Y.Z.mysql.tpl' or 'upgrade_X_Y_Z()').
29 *
30 * @param string $preUpgradeMessage
31 * @param string $rev
32 * a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'.
33 * @param null $currentVer
34 */
35 public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL): void {
36 if ($rev === '5.47.alpha1') {
37 $count = CRM_Core_DAO::singleValueQuery('SELECT count(*) FROM civicrm_contact WHERE preferred_mail_format != "Both"');
38 if ($count) {
39 $preUpgradeMessage .= '<p>' . ts('The contact field preferred mail format is being phased out. Modern email clients can handle receiving both formats so CiviCRM is moving towards always sending both and the field will be incrementally removed from the UI.')
40 . ' <a href="https://lab.civicrm.org/dev/core/-/issues/2866">' . ts('See the issue for more detail') . '</a></p>';
41 }
79b6ac2b
CW
42 // Check for custom groups with duplicate names
43 $dupes = CRM_Core_DAO::singleValueQuery('SELECT COUNT(g1.id) FROM civicrm_custom_group g1, civicrm_custom_group g2 WHERE g1.name = g2.name AND g1.id > g2.id');
44 if ($dupes) {
45 $preUpgradeMessage .= '<p>' .
46 ts('Your system has custom field groups with duplicate internal names. To ensure data integrity, the internal names will be automatically changed; user-facing titles will not be affected. Please review any custom code you may be using which relies on custom field group names.') .
47 '</p>';
48 }
fe8c33a3
EM
49 }
50 }
51
9ea6ff40
C
52 /**
53 * Upgrade step; adds tasks including 'runSql'.
54 *
55 * @param string $rev
56 * The version number matching this function name
57 */
58 public function upgrade_5_47_alpha1($rev): void {
59 $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
df014828 60 $this->addTask('Migrate CiviGrant component to an extension', 'migrateCiviGrant');
a0efcd4d
EM
61 $this->addTask('Add created_date to civicrm_relationship', 'addColumn', 'civicrm_relationship', 'created_date',
62 "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Relationship created date'"
63 );
64 $this->addTask('Add modified_date column to civicrm_relationship', 'addColumn',
65 'civicrm_relationship', 'modified_date',
66 "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Relationship last modified.'"
67 );
68 $this->addTask('Set initial value for relationship created_date and modified_date to start_date', 'updateRelationshipDates');
f61cf6ab
FW
69 $this->addTask('core-issue#2122 - Add timezone column to Events', 'addColumn',
70 'civicrm_event', 'event_tz', "text NULL DEFAULT NULL COMMENT 'Event\'s native time zone'"
71 );
72 $this->addTask('core-issue#2122 - Set the timezone to the default for existing Events', 'setEventTZDefault');
79b6ac2b
CW
73 $this->addTask('Drop CustomGroup UI_name_extends index', 'dropIndex', 'civicrm_custom_group', 'UI_name_extends');
74 $this->addTask('Add CustomGroup UI_name index', 'addIndex', 'civicrm_custom_group', ['name'], 'UI');
df014828
CW
75 }
76
77 /**
78 * @param \CRM_Queue_TaskContext $ctx
a0efcd4d
EM
79 *
80 * @return bool
81 */
82 public static function updateRelationshipDates(CRM_Queue_TaskContext $ctx): bool {
83 CRM_Core_DAO::executeQuery('
84 UPDATE civicrm_relationship SET created_date = start_date, modified_date = start_date
85 WHERE start_date IS NOT NULL AND start_date > "1970-01-01"
86 ');
87 return TRUE;
88 }
89
90 /**
91 * @param \CRM_Queue_TaskContext $ctx
92 *
df014828 93 * @return bool
a0efcd4d
EM
94 * @throws \API_Exception
95 * @throws \CRM_Core_Exception
96 * @throws \Civi\API\Exception\NotImplementedException
df014828
CW
97 */
98 public static function migrateCiviGrant(CRM_Queue_TaskContext $ctx): bool {
99 $civiGrantEnabled = in_array('CiviGrant', Civi::settings()->get('enable_components'), TRUE);
100 if ($civiGrantEnabled) {
101 CRM_Core_BAO_ConfigSetting::disableComponent('CiviGrant');
102 }
103 $civiGrantId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Component', 'CiviGrant', 'id', 'name');
104 if ($civiGrantId) {
105 foreach (['civicrm_menu', 'civicrm_option_value'] as $table) {
106 CRM_Core_DAO::executeQuery("UPDATE $table SET component_id = NULL WHERE component_id = $civiGrantId", [], TRUE, NULL, FALSE, FALSE);
107 }
108 CRM_Core_DAO::executeQuery("DELETE FROM civicrm_component WHERE name = 'CiviGrant'", [], TRUE, NULL, FALSE, FALSE);
109 }
110 $ext = new CRM_Core_DAO_Extension();
111 $ext->full_name = 'civigrant';
112 if (!$ext->find(TRUE)) {
113 $ext->type = 'module';
114 $ext->name = 'CiviGrant';
115 $ext->label = ts('CiviGrant');
116 $ext->file = 'civigrant';
117 $ext->is_active = (int) $civiGrantEnabled;
118 $ext->save();
119
120 $managedItems = [
121 'OptionGroup_advanced_search_options_OptionValue_CiviGrant' => [
122 'entity' => 'OptionValue',
123 'values' => [
124 'option_group_id:name' => 'advanced_search_options',
125 'name' => 'CiviGrant',
126 ],
127 ],
128 'OptionGroup_contact_view_options_OptionValue_CiviGrant' => [
129 'entity' => 'OptionValue',
130 'values' => [
131 'option_group_id:name' => 'contact_view_options',
132 'name' => 'CiviGrant',
133 ],
134 ],
135 'OptionGroup_mapping_type_OptionValue_Export Grant' => [
136 'entity' => 'OptionValue',
137 'values' => [
138 'option_group_id:name' => 'mapping_type',
139 'name' => 'Export Grant',
140 ],
141 ],
142 'OptionGroup_grant_status' => [
143 'entity' => 'OptionGroup',
144 'values' => [
145 'name' => 'grant_status',
146 ],
147 ],
148 'OptionGroup_grant_status_OptionValue_Submitted' => [
149 'entity' => 'OptionValue',
150 'values' => [
151 'option_group_id:name' => 'grant_status',
152 'name' => 'Submitted',
153 ],
154 ],
155 'OptionGroup_grant_status_OptionValue_Eligible' => [
156 'entity' => 'OptionValue',
157 'values' => [
158 'option_group_id:name' => 'grant_status',
159 'name' => 'Eligible',
160 ],
161 ],
162 'OptionGroup_grant_status_OptionValue_Ineligible' => [
163 'entity' => 'OptionValue',
164 'values' => [
165 'option_group_id:name' => 'grant_status',
166 'name' => 'Ineligible',
167 ],
168 ],
169 'OptionGroup_grant_status_OptionValue_Paid' => [
170 'entity' => 'OptionValue',
171 'values' => [
172 'option_group_id:name' => 'grant_status',
173 'name' => 'Paid',
174 ],
175 ],
176 'OptionGroup_grant_status_OptionValue_Awaiting Information' => [
177 'entity' => 'OptionValue',
178 'values' => [
179 'option_group_id:name' => 'grant_status',
180 'name' => 'Awaiting Information',
181 ],
182 ],
183 'OptionGroup_grant_status_OptionValue_Withdrawn' => [
184 'entity' => 'OptionValue',
185 'values' => [
186 'option_group_id:name' => 'grant_status',
187 'name' => 'Withdrawn',
188 ],
189 ],
190 'OptionGroup_grant_status_OptionValue_Approved for Payment' => [
191 'entity' => 'OptionValue',
192 'values' => [
193 'option_group_id:name' => 'grant_status',
194 'name' => 'Approved for Payment',
195 ],
196 ],
197 'OptionGroup_grant_type' => [
198 'entity' => 'OptionGroup',
199 'values' => [
200 'name' => 'grant_type',
201 ],
202 ],
203 'OptionGroup_grant_type_OptionValue_Emergency' => [
204 'entity' => 'OptionValue',
205 'values' => [
206 'option_group_id:name' => 'grant_type',
207 'name' => 'Emergency',
208 ],
209 ],
210 'OptionGroup_grant_type_OptionValue_Family Support' => [
211 'entity' => 'OptionValue',
212 'values' => [
213 'option_group_id:name' => 'grant_type',
214 'name' => 'Family Support',
215 ],
216 ],
217 'OptionGroup_grant_type_OptionValue_General Protection' => [
218 'entity' => 'OptionValue',
219 'values' => [
220 'option_group_id:name' => 'grant_type',
221 'name' => 'General Protection',
222 ],
223 ],
224 'OptionGroup_grant_type_OptionValue_Impunity' => [
225 'entity' => 'OptionValue',
226 'values' => [
227 'option_group_id:name' => 'grant_type',
228 'name' => 'Impunity',
229 ],
230 ],
231 'OptionGroup_report_template_OptionValue_CRM_Report_Form_Grant_Detail' => [
232 'entity' => 'OptionValue',
233 'values' => [
234 'option_group_id:name' => 'report_template',
235 'name' => 'CRM_Report_Form_Grant_Detail',
236 ],
237 ],
238 'OptionGroup_report_template_OptionValue_CRM_Report_Form_Grant_Statistics' => [
239 'entity' => 'OptionValue',
240 'values' => [
241 'option_group_id:name' => 'report_template',
242 'name' => 'CRM_Report_Form_Grant_Statistics',
243 ],
244 ],
245 'Navigation_Grants' => [
246 'entity' => 'Navigation',
247 'values' => [
248 'name' => 'Grants',
249 'domain_id' => 'current_domain',
250 ],
251 ],
252 'Navigation_Grants_Navigation_Dashboard' => [
253 'entity' => 'Navigation',
254 'values' => [
255 'name' => 'Dashboard',
256 'url' => 'civicrm/grant?reset=1',
257 'domain_id' => 'current_domain',
258 ],
259 ],
260 'Navigation_Grants_Navigation_New_Grant' => [
261 'entity' => 'Navigation',
262 'values' => [
263 'name' => 'New Grant',
264 'url' => 'civicrm/grant/add?reset=1&action=add&context=standalone',
265 'domain_id' => 'current_domain',
266 ],
267 ],
268 'Navigation_Grants_Navigation_Find_Grants' => [
269 'entity' => 'Navigation',
270 'values' => [
271 'name' => 'Find Grants',
272 'url' => 'civicrm/grant/search?reset=1',
273 'domain_id' => 'current_domain',
274 ],
275 ],
276 'Navigation_CiviGrant' => [
277 'entity' => 'Navigation',
278 'values' => [
279 'name' => 'CiviGrant',
280 'domain_id' => 'current_domain',
281 ],
282 ],
283 'Navigation_CiviGrant_Navigation_Grant_Types' => [
284 'entity' => 'Navigation',
285 'values' => [
286 'name' => 'Grant Types',
287 'domain_id' => 'current_domain',
288 ],
289 ],
290 'Navigation_CiviGrant_Navigation_Grant_Status' => [
291 'entity' => 'Navigation',
292 'values' => [
293 'name' => 'Grant Status',
294 'domain_id' => 'current_domain',
295 ],
296 ],
297 'Navigation_Grants_Navigation_Grant_Reports' => [
298 'entity' => 'Navigation',
299 'values' => [
300 'name' => 'Grant Reports',
301 'domain_id' => 'current_domain',
302 ],
303 ],
304 ];
305 // Create an entry in civicrm_managed for each existing record that will be managed by the extension
306 foreach ($managedItems as $name => $item) {
307 $params = ['checkPermissions' => FALSE];
308 foreach ($item['values'] as $k => $v) {
309 $params['where'][] = [$k, '=', $v];
310 }
311 $record = civicrm_api4($item['entity'], 'get', $params)->first();
312 if ($record) {
313 $mgd = new CRM_Core_DAO_Managed();
314 $mgd->name = $name;
315 $mgd->module = 'civigrant';
316 $mgd->entity_type = $item['entity'];
317 if (!$mgd->find(TRUE)) {
318 $mgd->entity_id = $record['id'];
319 $mgd->cleanup = 'unused';
320 $mgd->save();
321 }
322 // Disable record if CiviGrant disabled
323 if (!$civiGrantEnabled && !empty($record['is_active'])) {
324 civicrm_api4($item['entity'], 'update', [
325 'checkPermissions' => FALSE,
326 'values' => ['id' => $record['id'], 'is_active' => FALSE],
327 ]);
328 }
329 }
330 }
331 }
332 return TRUE;
9ea6ff40
C
333 }
334
f61cf6ab
FW
335 /**
336 * Set the timezone to the default for existing Events.
337 *
338 * @param \CRM_Queue_TaskContext $ctx
339 * @return bool
340 */
341 public static function setEventTZDefault(CRM_Queue_TaskContext $ctx) {
342 // Set default for CiviCRM Events to user system timezone (most reasonable default);
343 $defaultTZ = CRM_Core_Config::singleton()->userSystem->getTimeZoneString();
344 CRM_Core_DAO::executeQuery('UPDATE `civicrm_event` SET `event_tz` = %1 WHERE `event_tz` IS NULL;', [1 => [$defaultTZ, 'String']]);
345 return TRUE;
346 }
347
9ea6ff40 348}