Merge pull request #22532 from seamuslee001/dev_core_3034
[civicrm-core.git] / CRM / Event / Form / ManageEvent / Location.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 use Civi\Api4\Event;
13 use Civi\Api4\LocBlock;
14 use Civi\Api4\Email;
15 use Civi\Api4\Phone;
16 use Civi\Api4\Address;
17
18 /**
19 *
20 *
21 * @package CRM
22 * @copyright CiviCRM LLC https://civicrm.org/licensing
23 */
24
25 /**
26 * This class generates form components for processing Event Location
27 * civicrm_event_page.
28 */
29 class CRM_Event_Form_ManageEvent_Location extends CRM_Event_Form_ManageEvent {
30
31 /**
32 * @var \Civi\Api4\Generic\Result
33 */
34 protected $locationBlock;
35
36 /**
37 * How many locationBlocks should we display?
38 *
39 * @var int
40 * @const
41 */
42 const LOCATION_BLOCKS = 1;
43
44 /**
45 * The variable, for storing the location array
46 *
47 * @var array
48 */
49 protected $_locationIds = [];
50
51 /**
52 * The variable, for storing location block id with event
53 *
54 * @var int
55 */
56 protected $_oldLocBlockId = 0;
57
58 /**
59 * Get the db values for this form.
60 * @var array
61 */
62 public $_values = [];
63
64 /**
65 * Set variables up before form is built.
66 */
67 public function preProcess() {
68 parent::preProcess();
69 $this->setSelectedChild('location');
70
71 $this->_values = $this->get('values');
72 if ($this->_id && empty($this->_values)) {
73 //get location values.
74 $params = [
75 'entity_id' => $this->_id,
76 'entity_table' => 'civicrm_event',
77 ];
78 $this->_values = CRM_Core_BAO_Location::getValues($params);
79
80 //get event values.
81 $params = ['id' => $this->_id];
82 CRM_Event_BAO_Event::retrieve($params, $this->_values);
83 $this->set('values', $this->_values);
84 }
85
86 //location blocks.
87 CRM_Contact_Form_Location::preProcess($this);
88 }
89
90 /**
91 * Set default values for the form.
92 *
93 * Note that in edit/view mode the default values are retrieved from the database.
94 */
95 public function setDefaultValues() {
96 $defaults = $this->_values;
97
98 if (!empty($defaults['loc_block_id'])) {
99 $defaults['loc_event_id'] = $defaults['loc_block_id'];
100 $countLocUsed = CRM_Event_BAO_Event::countEventsUsingLocBlockId($defaults['loc_block_id']);
101 $this->assign('locUsed', $countLocUsed);
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 }
108
109 if (!isset($defaults['address'][1]['state_province_id'])) {
110 $defaults['address'][1]['state_province_id'] = $config->defaultContactStateProvince;
111 }
112
113 $defaults['location_option'] = $this->_oldLocBlockId ? 2 : 1;
114
115 return $defaults;
116 }
117
118 /**
119 * Add local and global form rules.
120 */
121 public function addRules() {
122 $this->addFormRule(['CRM_Event_Form_ManageEvent_Location', 'formRule']);
123 }
124
125 /**
126 * Global validation rules for the form.
127 *
128 * @param array $fields
129 * Posted values of the form.
130 *
131 * @return array
132 * list of errors to be posted back to the form
133 */
134 public static function formRule($fields) {
135 // check for state/country mapping
136 $errors = CRM_Contact_Form_Edit_Address::formRule($fields);
137
138 return empty($errors) ? TRUE : $errors;
139 }
140
141 /**
142 * Function to build location block.
143 */
144 public function buildQuickForm() {
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);
150
151 $this->applyFilter('__ALL__', 'trim');
152
153 //fix for CRM-1971
154 $this->assign('action', $this->_action);
155
156 if ($this->_id) {
157 $this->locationBlock = Event::get()
158 ->addWhere('id', '=', $this->_id)
159 ->setSelect(['loc_block_id.*', 'loc_block_id'])
160 ->execute()->first();
161 $this->_oldLocBlockId = $this->locationBlock['loc_block_id'];
162 }
163
164 // get the list of location blocks being used by other events
165
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
169 if (!empty($locationEvents[$this->_oldLocBlockId])) {
170 $possibleDuplicate = $locationEvents[$this->_oldLocBlockId];
171 $locationEvents = array_flip(array_unique($locationEvents));
172 if (!empty($locationEvents[$possibleDuplicate])) {
173 $locationEvents[$possibleDuplicate] = $this->_oldLocBlockId;
174 }
175 $locationEvents = array_flip($locationEvents);
176 }
177 else {
178 $locationEvents = array_unique($locationEvents);
179 }
180
181 if (!empty($locationEvents)) {
182 $this->assign('locEvents', TRUE);
183 $optionTypes = [
184 '1' => ts('Create new location'),
185 '2' => ts('Use existing location'),
186 ];
187
188 $this->addRadio('location_option', ts("Choose Location"), $optionTypes);
189
190 if (!isset($locationEvents[$this->_oldLocBlockId]) || (!$this->_oldLocBlockId)) {
191 $locationEvents = ['' => ts('- select -')] + $locationEvents;
192 }
193 $this->add('select', 'loc_event_id', ts('Use Location'), $locationEvents, FALSE, ['class' => 'crm-select2']);
194 }
195 $this->addElement('advcheckbox', 'is_show_location', ts('Show Location?'));
196 parent::buildQuickForm();
197 }
198
199 /**
200 * Process the form submission.
201 */
202 public function postProcess() {
203 $params = $this->exportValues();
204 $deleteOldBlock = FALSE;
205
206 // if 'use existing location' option is selected -
207 if (CRM_Utils_Array::value('location_option', $params) == 2 && !empty($params['loc_event_id']) &&
208 ($params['loc_event_id'] != $this->_oldLocBlockId)
209 ) {
210 // if new selected loc is different from old loc, update the loc_block_id
211 // so that loc update would affect the selected loc and not the old one.
212 $deleteOldBlock = TRUE;
213 CRM_Core_DAO::setFieldValue('CRM_Event_DAO_Event', $this->_id,
214 'loc_block_id', $params['loc_event_id']
215 );
216 }
217
218 // if 'create new loc' option is selected, set the loc_block_id for this event to null
219 // so that an update would result in creating a new loc.
220 if ($this->_oldLocBlockId && (CRM_Utils_Array::value('location_option', $params) == 1)) {
221 $deleteOldBlock = TRUE;
222 CRM_Core_DAO::setFieldValue('CRM_Event_DAO_Event', $this->_id,
223 'loc_block_id', 'null'
224 );
225 }
226
227 // if 'create new loc' option is selected OR selected new loc is different
228 // from old one, go ahead and delete the old loc provided thats not being
229 // used by any other event
230 if ($this->_oldLocBlockId && $deleteOldBlock) {
231 CRM_Event_BAO_Event::deleteEventLocBlock($this->_oldLocBlockId, $this->_id);
232 }
233
234 $isUpdateToExistingLocationBlock = !$deleteOldBlock && !empty($params['loc_event_id']) && (int) $params['loc_event_id'] === $this->locationBlock['loc_block_id'];
235 // It should be impossible for there to be no default location type. Consider removing this handling
236 $defaultLocationTypeID = CRM_Core_BAO_LocationType::getDefault()->id ?? 1;
237
238 foreach ([
239 'address' => $params['address'],
240 'phone' => $params['phone'],
241 'email' => $params['email'],
242 ] as $block => $locationEntities) {
243
244 $params[$block][1]['is_primary'] = 1;
245 foreach ($locationEntities as $index => $locationEntity) {
246 if (!$this->isLocationHasData($block, $locationEntity)) {
247 unset($params[$block][$index]);
248 continue;
249 }
250 $params[$block][$index]['location_type_id'] = $defaultLocationTypeID;
251 $fieldKey = (int) $index === 1 ? '_id' : '_2_id';
252 if ($isUpdateToExistingLocationBlock && !empty($this->locationBlock['loc_block_id.' . $block . $fieldKey])) {
253 $params[$block][$index]['id'] = $this->locationBlock['loc_block_id.' . $block . $fieldKey];
254 }
255 }
256 }
257 $addresses = empty($params['address']) ? [] : Address::save(FALSE)->setRecords($params['address'])->execute();
258 $emails = empty($params['email']) ? [] : Email::save(FALSE)->setRecords($params['email'])->execute();
259 $phones = empty($params['phone']) ? [] : Phone::save(FALSE)->setRecords($params['phone'])->execute();
260
261 $params['loc_block_id'] = LocBlock::save(FALSE)->setRecords([
262 [
263 'email_id' => $emails[0]['id'] ?? NULL,
264 'address_id' => $addresses[0]['id'] ?? NULL,
265 'phone_id' => $phones[0]['id'] ?? NULL,
266 'email_2_id' => $emails[1]['id'] ?? NULL,
267 'address_2_id' => $addresses[1]['id'] ?? NULL,
268 'phone_2_id' => $phones[1]['id'] ?? NULL,
269 ],
270 ])->execute()->first()['id'];
271
272 // finally update event params
273 $params['id'] = $this->_id;
274 CRM_Event_BAO_Event::add($params);
275
276 // Update tab "disabled" css class
277 $this->ajaxResponse['tabValid'] = TRUE;
278 parent::endPostProcess();
279 }
280
281 /**
282 * Return a descriptive name for the page, used in wizard header
283 *
284 * @return string
285 */
286 public function getTitle() {
287 return ts('Event Location');
288 }
289
290 /**
291 * Is there some data to save for the given entity
292 *
293 * @param string $block
294 * @param array $locationEntity
295 *
296 * @return bool
297 */
298 protected function isLocationHasData(string $block, array $locationEntity): bool {
299 if ($block === 'email') {
300 return !empty($locationEntity['email']);
301 }
302 if ($block === 'phone') {
303 return !empty($locationEntity['phone']);
304 }
305 foreach ($locationEntity as $value) {
306 if (!empty($value)) {
307 return TRUE;
308 }
309 }
310 return FALSE;
311 }
312
313 }