Merge pull request #23127 from civicrm/5.48
[civicrm-core.git] / CRM / Event / Form / ManageEvent / Location.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035 11
9b9335ed 12use Civi\Api4\Event;
aa06ad4a 13use Civi\Api4\LocBlock;
14use Civi\Api4\Email;
15use Civi\Api4\Phone;
16use Civi\Api4\Address;
9b9335ed 17
6a488035
TO
18/**
19 *
20 *
21 * @package CRM
ca5cec67 22 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
23 */
24
25/**
26 * This class generates form components for processing Event Location
27 * civicrm_event_page.
28 */
29class CRM_Event_Form_ManageEvent_Location extends CRM_Event_Form_ManageEvent {
30
9b9335ed 31 /**
32 * @var \Civi\Api4\Generic\Result
33 */
34 protected $locationBlock;
35
6a488035 36 /**
100fef9d 37 * How many locationBlocks should we display?
6a488035
TO
38 *
39 * @var int
40 * @const
41 */
7da04cde 42 const LOCATION_BLOCKS = 1;
6a488035
TO
43
44 /**
100fef9d 45 * The variable, for storing the location array
6a488035
TO
46 *
47 * @var array
48 */
be2fb01f 49 protected $_locationIds = [];
6a488035
TO
50
51 /**
100fef9d 52 * The variable, for storing location block id with event
6a488035
TO
53 *
54 * @var int
55 */
56 protected $_oldLocBlockId = 0;
57
58 /**
66f9e52b 59 * Get the db values for this form.
90b461f1 60 * @var array
6a488035 61 */
be2fb01f 62 public $_values = [];
6a488035
TO
63
64 /**
66f9e52b 65 * Set variables up before form is built.
6a488035 66 */
00be9182 67 public function preProcess() {
6a488035 68 parent::preProcess();
e4b857f8 69 $this->setSelectedChild('location');
6a488035
TO
70
71 $this->_values = $this->get('values');
72 if ($this->_id && empty($this->_values)) {
6a488035 73 //get location values.
be2fb01f 74 $params = [
6a488035
TO
75 'entity_id' => $this->_id,
76 'entity_table' => 'civicrm_event',
be2fb01f 77 ];
6a488035
TO
78 $this->_values = CRM_Core_BAO_Location::getValues($params);
79
80 //get event values.
be2fb01f 81 $params = ['id' => $this->_id];
6a488035
TO
82 CRM_Event_BAO_Event::retrieve($params, $this->_values);
83 $this->set('values', $this->_values);
84 }
be84b210 85
86 //location blocks.
87 CRM_Contact_Form_Location::preProcess($this);
6a488035
TO
88 }
89
90 /**
3bdf1f3a 91 * Set default values for the form.
6a488035 92 *
3bdf1f3a 93 * Note that in edit/view mode the default values are retrieved from the database.
6a488035 94 */
00be9182 95 public function setDefaultValues() {
6a488035
TO
96 $defaults = $this->_values;
97
5d92a7e7 98 if (!empty($defaults['loc_block_id'])) {
6a488035
TO
99 $defaults['loc_event_id'] = $defaults['loc_block_id'];
100 $countLocUsed = CRM_Event_BAO_Event::countEventsUsingLocBlockId($defaults['loc_block_id']);
62a0f5a1 101 $this->assign('locUsed', $countLocUsed);
6a488035
TO
102 }
103
104 $config = CRM_Core_Config::singleton();
105 if (!isset($defaults['address'][1]['country_id'])) {
106 $defaults['address'][1]['country_id'] = $config->defaultContactCountry;
107 }
03e04002 108
6a488035
TO
109 if (!isset($defaults['address'][1]['state_province_id'])) {
110 $defaults['address'][1]['state_province_id'] = $config->defaultContactStateProvince;
111 }
112
6a488035
TO
113 $defaults['location_option'] = $this->_oldLocBlockId ? 2 : 1;
114
115 return $defaults;
116 }
117
118 /**
66f9e52b 119 * Add local and global form rules.
6a488035 120 */
00be9182 121 public function addRules() {
be2fb01f 122 $this->addFormRule(['CRM_Event_Form_ManageEvent_Location', 'formRule']);
6a488035
TO
123 }
124
125 /**
66f9e52b 126 * Global validation rules for the form.
6a488035 127 *
d4dd1e85
TO
128 * @param array $fields
129 * Posted values of the form.
6a488035 130 *
a6c01b45
CW
131 * @return array
132 * list of errors to be posted back to the form
6a488035 133 */
00be9182 134 public static function formRule($fields) {
6a488035 135 // check for state/country mapping
1273d77c 136 $errors = CRM_Contact_Form_Edit_Address::formRule($fields);
6a488035
TO
137
138 return empty($errors) ? TRUE : $errors;
139 }
140
141 /**
3bdf1f3a 142 * Function to build location block.
6a488035
TO
143 */
144 public function buildQuickForm() {
59f8bad6
CW
145 CRM_Contact_Form_Edit_Address::buildQuickForm($this, 1);
146 CRM_Contact_Form_Edit_Email::buildQuickForm($this, 1);
147 CRM_Contact_Form_Edit_Email::buildQuickForm($this, 2);
148 CRM_Contact_Form_Edit_Phone::buildQuickForm($this, 1);
149 CRM_Contact_Form_Edit_Phone::buildQuickForm($this, 2);
6a488035
TO
150
151 $this->applyFilter('__ALL__', 'trim');
152
6a488035
TO
153 //fix for CRM-1971
154 $this->assign('action', $this->_action);
155
156 if ($this->_id) {
9b9335ed 157 $this->locationBlock = Event::get()
158 ->addWhere('id', '=', $this->_id)
84ad7693 159 ->setSelect(['loc_block_id.*', 'loc_block_id'])
9b9335ed 160 ->execute()->first();
161 $this->_oldLocBlockId = $this->locationBlock['loc_block_id'];
6a488035
TO
162 }
163
164 // get the list of location blocks being used by other events
03e04002 165
6a488035
TO
166 $locationEvents = CRM_Event_BAO_Event::getLocationEvents();
167 // remove duplicates and make sure that the duplicate entry with key as
168 // loc_block_id of this event (this->_id) is preserved
a7488080 169 if (!empty($locationEvents[$this->_oldLocBlockId])) {
6a488035
TO
170 $possibleDuplicate = $locationEvents[$this->_oldLocBlockId];
171 $locationEvents = array_flip(array_unique($locationEvents));
a7488080 172 if (!empty($locationEvents[$possibleDuplicate])) {
6a488035
TO
173 $locationEvents[$possibleDuplicate] = $this->_oldLocBlockId;
174 }
175 $locationEvents = array_flip($locationEvents);
176 }
177 else {
178 $locationEvents = array_unique($locationEvents);
179 }
180
6a488035
TO
181 if (!empty($locationEvents)) {
182 $this->assign('locEvents', TRUE);
be2fb01f 183 $optionTypes = [
353ffa53 184 '1' => ts('Create new location'),
6a488035 185 '2' => ts('Use existing location'),
be2fb01f 186 ];
6a488035 187
62a0f5a1 188 $this->addRadio('location_option', ts("Choose Location"), $optionTypes);
6a488035
TO
189
190 if (!isset($locationEvents[$this->_oldLocBlockId]) || (!$this->_oldLocBlockId)) {
be2fb01f 191 $locationEvents = ['' => ts('- select -')] + $locationEvents;
6a488035 192 }
be2fb01f 193 $this->add('select', 'loc_event_id', ts('Use Location'), $locationEvents, FALSE, ['class' => 'crm-select2']);
6a488035
TO
194 }
195 $this->addElement('advcheckbox', 'is_show_location', ts('Show Location?'));
196 parent::buildQuickForm();
197 }
198
199 /**
66f9e52b 200 * Process the form submission.
6a488035
TO
201 */
202 public function postProcess() {
203 $params = $this->exportValues();
204 $deleteOldBlock = FALSE;
205
51d78c5d
CW
206 // If 'Use existing location' is selected.
207 if (CRM_Utils_Array::value('location_option', $params) == 2) {
208
209 /*
210 * If there is an existing LocBlock and the selected LocBlock is different,
211 * flag the existing LocBlock for deletion.
212 */
213 if ($this->_oldLocBlockId && !empty($params['loc_event_id']) &&
214 ($params['loc_event_id'] != $this->_oldLocBlockId)
215 ) {
216 $deleteOldBlock = TRUE;
217 }
218
219 /*
220 * Always update the loc_block_id in this Event so that LocBlock update
221 * affects the selected LocBlock and not the previous one - whether or not
222 * there is a previous LocBlock.
223 */
6a488035
TO
224 CRM_Core_DAO::setFieldValue('CRM_Event_DAO_Event', $this->_id,
225 'loc_block_id', $params['loc_event_id']
226 );
51d78c5d 227
6a488035
TO
228 }
229
51d78c5d
CW
230 /*
231 * If there is an existing LocBlock and 'Create new location' is selected,
232 * set the loc_block_id for this Event to null so that an update results in
233 * creating a new LocBlock.
234 */
6a488035
TO
235 if ($this->_oldLocBlockId && (CRM_Utils_Array::value('location_option', $params) == 1)) {
236 $deleteOldBlock = TRUE;
237 CRM_Core_DAO::setFieldValue('CRM_Event_DAO_Event', $this->_id,
238 'loc_block_id', 'null'
239 );
240 }
241
51d78c5d
CW
242 /*
243 * If there is a previous LocBlock and we have determined that it should be
244 * deleted, go ahead and do so now. The method that is called will only delete
245 * the LocBlock if it is not being used by another Event.
246 */
6a488035
TO
247 if ($this->_oldLocBlockId && $deleteOldBlock) {
248 CRM_Event_BAO_Event::deleteEventLocBlock($this->_oldLocBlockId, $this->_id);
249 }
250
51d78c5d
CW
251 // Assume a new LocBlock is needed.
252 $isUpdateToExistingLocationBlock = FALSE;
253
254 /*
255 * If there is a previous LocBlock and it was not deleted, check if the new
256 * LocBlock ID matches the previous one. If so, then it needs to be updated.
257 */
258 if (!empty($this->locationBlock['loc_block_id']) && !$deleteOldBlock) {
259 if (!empty($params['loc_event_id']) && (int) $params['loc_event_id'] === $this->locationBlock['loc_block_id']) {
260 $isUpdateToExistingLocationBlock = TRUE;
261 }
262 }
263
264 /*
265 * If 'Use existing location' is selected and there isn't a previous LocBlock
266 * but a LocBlock has been selected, then that LocBlock should be updated.
267 * In order to do so, the IDs of the Address, Phone and Email "Blocks" have
268 * to be retrieved and added in to the elements in the $params array.
269 */
270 if (CRM_Utils_Array::value('location_option', $params) == 2) {
271 if (empty($this->locationBlock['loc_block_id']) && !empty($params['loc_event_id'])) {
272 $isUpdateToExistingLocationBlock = TRUE;
273 $existingLocBlock = LocBlock::get()
274 ->addWhere('id', '=', (int) $params['loc_event_id'])
275 ->setCheckPermissions(FALSE)
276 ->execute()->first();
277 }
278 }
279
280 /*
281 * It should be impossible for there to be no default location type.
282 * Consider removing this handling.
283 */
9b9335ed 284 $defaultLocationTypeID = CRM_Core_BAO_LocationType::getDefault()->id ?? 1;
aa06ad4a 285
be2fb01f 286 foreach ([
9b9335ed 287 'address' => $params['address'],
288 'phone' => $params['phone'],
289 'email' => $params['email'],
290 ] as $block => $locationEntities) {
531b6c9e 291
9b9335ed 292 $params[$block][1]['is_primary'] = 1;
293 foreach ($locationEntities as $index => $locationEntity) {
51d78c5d
CW
294
295 $fieldKey = (int) $index === 1 ? '_id' : '_2_id';
296
297 // Assume there's no Block ID.
298 $blockId = FALSE;
299
300 // Check the existing LocBlock for an ID.
301 if (!empty($this->locationBlock['loc_block_id.' . $block . $fieldKey])) {
302 $blockId = $this->locationBlock['loc_block_id.' . $block . $fieldKey];
303 }
304 else {
305 // Check the queried LocBlock for an ID.
306 if (!empty($existingLocBlock[$block . $fieldKey])) {
307 $blockId = $existingLocBlock[$block . $fieldKey];
308 }
309 }
310
311 /*
312 * Unsetting the array element excludes the Block from being updated and
313 * removes it from the LocBlock. However, the intention of clearing a Block
314 * is presumably to delete it.
315 */
7af49a64 316 if (!$this->isLocationHasData($block, $locationEntity)) {
317 unset($params[$block][$index]);
51d78c5d
CW
318 if (!empty($blockId)) {
319 // The Block can be deleted here.
320 }
7af49a64 321 continue;
322 }
51d78c5d 323
9b9335ed 324 $params[$block][$index]['location_type_id'] = $defaultLocationTypeID;
51d78c5d
CW
325
326 // Assign the existing Block ID if an update is needed.
327 if ($isUpdateToExistingLocationBlock && !empty($blockId)) {
328 $params[$block][$index]['id'] = $blockId;
045bc380 329 }
6a488035 330 }
51d78c5d 331
6a488035 332 }
51d78c5d
CW
333
334 // Update the Blocks.
7af49a64 335 $addresses = empty($params['address']) ? [] : Address::save(FALSE)->setRecords($params['address'])->execute();
336 $emails = empty($params['email']) ? [] : Email::save(FALSE)->setRecords($params['email'])->execute();
337 $phones = empty($params['phone']) ? [] : Phone::save(FALSE)->setRecords($params['phone'])->execute();
aa06ad4a 338
51d78c5d
CW
339 // Build the LocBlock record.
340 $record = [
341 'email_id' => $emails[0]['id'] ?? NULL,
342 'address_id' => $addresses[0]['id'] ?? NULL,
343 'phone_id' => $phones[0]['id'] ?? NULL,
344 'email_2_id' => $emails[1]['id'] ?? NULL,
345 'address_2_id' => $addresses[1]['id'] ?? NULL,
346 'phone_2_id' => $phones[1]['id'] ?? NULL,
347 ];
348
349 // Maybe trigger LocBlock update.
350 if ($isUpdateToExistingLocationBlock) {
351 $record['id'] = (int) $params['loc_event_id'];
352 }
353
354 // Update the LocBlock.
355 $params['loc_block_id'] = LocBlock::save(FALSE)->setRecords([$record])->execute()->first()['id'];
356
357 // Finally update Event params.
6a488035
TO
358 $params['id'] = $this->_id;
359 CRM_Event_BAO_Event::add($params);
360
51d78c5d 361 // Update tab "disabled" CSS class.
5d92a7e7 362 $this->ajaxResponse['tabValid'] = TRUE;
6a488035
TO
363 parent::endPostProcess();
364 }
6a488035
TO
365
366 /**
367 * Return a descriptive name for the page, used in wizard header
368 *
369 * @return string
6a488035
TO
370 */
371 public function getTitle() {
372 return ts('Event Location');
373 }
96025800 374
7af49a64 375 /**
376 * Is there some data to save for the given entity
377 *
378 * @param string $block
379 * @param array $locationEntity
380 *
381 * @return bool
382 */
383 protected function isLocationHasData(string $block, array $locationEntity): bool {
384 if ($block === 'email') {
385 return !empty($locationEntity['email']);
386 }
387 if ($block === 'phone') {
388 return !empty($locationEntity['phone']);
389 }
390 foreach ($locationEntity as $value) {
391 if (!empty($value)) {
392 return TRUE;
393 }
394 }
395 return FALSE;
396 }
397
6a488035 398}