Merge pull request #23938 from seamuslee001/brick_math_update
[civicrm-core.git] / CRM / Export / Form / Select.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 */
17
18 /**
19 * This class gets the name of the file to upload
20 */
21 class CRM_Export_Form_Select extends CRM_Core_Form_Task {
22
23 /**
24 * Various Contact types.
25 */
26 const
27 EXPORT_ALL = 1,
28 EXPORT_SELECTED = 2,
29 EXPORT_MERGE_DO_NOT_MERGE = 0,
30 EXPORT_MERGE_SAME_ADDRESS = 1,
31 EXPORT_MERGE_HOUSEHOLD = 2;
32
33 /**
34 * Export modes.
35 */
36 const
37 CONTACT_EXPORT = 1,
38 CONTRIBUTE_EXPORT = 2,
39 MEMBER_EXPORT = 3,
40 EVENT_EXPORT = 4,
41 PLEDGE_EXPORT = 5,
42 CASE_EXPORT = 6,
43 GRANT_EXPORT = 7,
44 ACTIVITY_EXPORT = 8;
45
46 /**
47 * Current export mode.
48 *
49 * @var int
50 */
51 public $_exportMode;
52
53 public $_componentTable;
54
55 /**
56 * Use the form name to create the tpl file name.
57 *
58 * @return string
59 */
60 public function getTemplateFileName() {
61 return 'CRM/Export/Form/Select.tpl';
62 }
63
64 /**
65 * Build all the data structures needed to build the form.
66 *
67 * @param
68 *
69 * @return void
70 */
71 public function preProcess() {
72 $this->preventAjaxSubmit();
73
74 //special case for custom search, directly give option to download csv file
75 $customSearchID = $this->get('customSearchID');
76 if ($customSearchID) {
77 CRM_Export_BAO_Export::exportCustom($this->get('customSearchClass'),
78 $this->get('formValues'),
79 $this->get(CRM_Utils_Sort::SORT_ORDER)
80 );
81 }
82
83 $this->_selectAll = FALSE;
84 $this->_exportMode = self::CONTACT_EXPORT;
85 $this->_componentIds = [];
86 $this->_componentClause = NULL;
87
88 // FIXME: This should use a modified version of CRM_Contact_Form_Search::getModeValue but it doesn't have all the contexts
89 // FIXME: Or better still, use CRM_Core_DAO_AllCoreTables::getBriefName($daoName) to get the $entityShortName
90 $entityShortname = $this->getEntityShortName();
91
92 if (!in_array($entityShortname, ['Contact', 'Contribute', 'Member', 'Event', 'Pledge', 'Case', 'Grant', 'Activity'], TRUE)) {
93 // This is never reached - the exception here is just to clarify that entityShortName MUST be one of the above
94 // to save future refactorers & reviewers from asking that question.
95 throw new CRM_Core_Exception('Unreachable code');
96 }
97 $this->_exportMode = constant('CRM_Export_Form_Select::' . strtoupper($entityShortname) . '_EXPORT');
98
99 $this::$entityShortname = strtolower($entityShortname);
100 $values = $this->getSearchFormValues();
101
102 $count = 0;
103 $this->_matchingContacts = FALSE;
104 if (CRM_Utils_Array::value('radio_ts', $values) == 'ts_sel') {
105 foreach ($values as $key => $value) {
106 if (strstr($key, 'mark_x')) {
107 $count++;
108 }
109 if ($count > 2) {
110 $this->_matchingContacts = TRUE;
111 break;
112 }
113 }
114 }
115
116 $this->callPreProcessing();
117
118 // $component is used on CRM/Export/Form/Select.tpl to display extra information for contact export
119 ($this->_exportMode == self::CONTACT_EXPORT) ? $component = FALSE : $component = TRUE;
120 $this->assign('component', $component);
121
122 $this->assign('isShowMergeOptions', $this->isShowContactMergeOptions());
123
124 if ($this->_componentTable) {
125 $query = "
126 SELECT count(*)
127 FROM {$this->_componentTable}
128 ";
129 $totalSelectedRecords = CRM_Core_DAO::singleValueQuery($query);
130 }
131 else {
132 $totalSelectedRecords = count($this->_componentIds);
133 }
134 $this->assign('totalSelectedRecords', $totalSelectedRecords);
135
136 // all records actions = save a search
137 if (($values['radio_ts'] == 'ts_all') || ($this->_task == CRM_Contact_Task::SAVE_SEARCH)) {
138 $this->_selectAll = TRUE;
139 $rowCount = $this->get('rowCount');
140 if ($rowCount > 2) {
141 $this->_matchingContacts = TRUE;
142 }
143 $this->assign('totalSelectedRecords', $rowCount);
144 }
145
146 $this->assign('matchingContacts', $this->_matchingContacts);
147 $this->set('componentIds', $this->_componentIds);
148 $this->set('selectAll', $this->_selectAll);
149 $this->set('exportMode', $this->_exportMode);
150 $this->set('componentClause', $this->_componentClause);
151 $this->set('componentTable', $this->getTableName());
152 }
153
154 /**
155 * Get the name of the table for the relevant entity.
156 */
157 public function getTableName() {
158 throw new CRM_Core_Exception('should be over-riden');
159 }
160
161 /**
162 * Build the form object.
163 *
164 * @return void
165 */
166 public function buildQuickForm() {
167 //export option
168 $exportOptions = $exportOptionsJS = $mergeOptions = $mergeOptionsJS = $postalMailing = [];
169 $exportOptions[self::EXPORT_ALL] = ts('Export PRIMARY fields');
170 $exportOptions[self::EXPORT_SELECTED] = ts('Select fields for export');
171 $mergeOptions[self::EXPORT_MERGE_DO_NOT_MERGE] = ts('Do not merge');
172 $mergeOptions[self::EXPORT_MERGE_SAME_ADDRESS] = ts('Merge All Contacts with the Same Address');
173 $mergeOptions[self::EXPORT_MERGE_HOUSEHOLD] = ts('Merge Household Members into their Households');
174 foreach (array_keys($exportOptions) as $key) {
175 $exportOptionsJS[$key] = ['onClick' => 'showMappingOption( );'];
176 }
177 foreach (array_keys($mergeOptions) as $key) {
178 $mergeOptionsJS[$key] = ['onclick' => 'showGreetingOptions( );'];
179 }
180 $this->addRadio('exportOption', ts('Export Type'), $exportOptions, [], '<br/>', FALSE, $exportOptionsJS);
181 $postalMailing[] = $this->createElement('advcheckbox',
182 'postal_mailing_export',
183 NULL,
184 NULL
185 );
186
187 if ($this->_matchingContacts) {
188 $this->_greetingOptions = self::getGreetingOptions();
189
190 foreach ($this->_greetingOptions as $key => $value) {
191 $fieldLabel = ts('%1 (when merging contacts)', [1 => ucwords(str_replace('_', ' ', $key))]);
192 $this->addElement('select', $key, $fieldLabel,
193 $value, ['onchange' => "showOther(this);"]
194 );
195 $this->addElement('text', "{$key}_other", '');
196 }
197 }
198
199 if ($this->_exportMode == self::CONTACT_EXPORT) {
200 $this->addRadio('mergeOption', ts('Merge Options'), $mergeOptions, [], '<br/>', FALSE, $mergeOptionsJS);
201 $this->addGroup($postalMailing, 'postal_mailing_export', ts('Postal Mailing Export'), '<br/>');
202
203 $this->addElement('select', 'additional_group', ts('Additional Group for Export'),
204 ['' => ts('- select group -')] + CRM_Core_PseudoConstant::nestedGroup(),
205 ['class' => 'crm-select2 huge']
206 );
207 }
208
209 $this->buildMapping();
210
211 $this->setDefaults([
212 'exportOption' => self::EXPORT_ALL,
213 'mergeOption' => self::EXPORT_MERGE_DO_NOT_MERGE,
214 ]);
215
216 $this->addButtons([
217 [
218 'type' => 'next',
219 'name' => ts('Continue'),
220 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
221 'isDefault' => TRUE,
222 ],
223 [
224 'type' => 'cancel',
225 'name' => ts('Cancel'),
226 ],
227 ]);
228
229 $this->addFormRule(['CRM_Export_Form_Select', 'formRule'], $this);
230 }
231
232 /**
233 * Validation.
234 *
235 * @param array $params
236 * (ref.) an assoc array of name/value pairs.
237 *
238 * @param $files
239 * @param self $self
240 *
241 * @return bool|array
242 * mixed true or array of errors
243 */
244 public static function formRule($params, $files, $self) {
245 $errors = [];
246
247 if (CRM_Utils_Array::value('mergeOption', $params) == self::EXPORT_MERGE_SAME_ADDRESS &&
248 $self->_matchingContacts
249 ) {
250 $greetings = [
251 'postal_greeting' => 'postal_greeting_other',
252 'addressee' => 'addressee_other',
253 ];
254
255 foreach ($greetings as $key => $value) {
256 $otherOption = $params[$key] ?? NULL;
257
258 if ((CRM_Utils_Array::value($otherOption, $self->_greetingOptions[$key]) == ts('Other')) && empty($params[$value])) {
259
260 $label = ucwords(str_replace('_', ' ', $key));
261 $errors[$value] = ts('Please enter a value for %1 (when merging contacts), or select a pre-configured option from the list.', [1 => $label]);
262 }
263 }
264 }
265
266 return empty($errors) ? TRUE : $errors;
267 }
268
269 /**
270 * Process the uploaded file.
271 *
272 * @throws \CRM_Core_Exception
273 */
274 public function postProcess() {
275 $params = $this->controller->exportValues($this->_name);
276 $exportOption = $params['exportOption'];
277 $mergeSameAddress = CRM_Utils_Array::value('mergeOption', $params) == self::EXPORT_MERGE_SAME_ADDRESS ? 1 : 0;
278 $mergeSameHousehold = CRM_Utils_Array::value('mergeOption', $params) == self::EXPORT_MERGE_HOUSEHOLD ? 1 : 0;
279
280 $this->set('mergeSameAddress', $mergeSameAddress);
281 $this->set('mergeSameHousehold', $mergeSameHousehold);
282
283 // instead of increasing the number of arguments to exportComponents function, we
284 // will send $exportParams as another argument, which is an array and suppose to contain
285 // all submitted options or any other argument
286 $exportParams = $params;
287
288 $mappingId = $params['mapping'] ?? NULL;
289 if ($mappingId) {
290 $this->set('mappingId', $mappingId);
291 }
292 else {
293 $this->set('mappingId', NULL);
294 }
295
296 if ($exportOption == self::EXPORT_ALL) {
297 CRM_Export_BAO_Export::exportComponents($this->_selectAll,
298 $this->_componentIds,
299 (array) $this->get('queryParams'),
300 $this->get(CRM_Utils_Sort::SORT_ORDER),
301 NULL,
302 $this->get('returnProperties'),
303 $this->_exportMode,
304 $this->_componentClause,
305 $this->_componentTable,
306 $mergeSameAddress,
307 $mergeSameHousehold,
308 $exportParams,
309 $this->get('queryOperator')
310 );
311 }
312
313 //reset map page
314 $this->controller->resetPage('Map');
315 }
316
317 /**
318 * Return a descriptive name for the page, used in wizard header
319 *
320 * @return string
321 */
322 public function getTitle() {
323 return ts('Export Options');
324 }
325
326 /**
327 * Build mapping form element.
328 */
329 public function buildMapping() {
330 switch ($this->_exportMode) {
331 case CRM_Export_Form_Select::CONTACT_EXPORT:
332 $exportType = 'Export Contact';
333 break;
334
335 case CRM_Export_Form_Select::CONTRIBUTE_EXPORT:
336 $exportType = 'Export Contribution';
337 break;
338
339 case CRM_Export_Form_Select::MEMBER_EXPORT:
340 $exportType = 'Export Membership';
341 break;
342
343 case CRM_Export_Form_Select::EVENT_EXPORT:
344 $exportType = 'Export Participant';
345 break;
346
347 case CRM_Export_Form_Select::PLEDGE_EXPORT:
348 $exportType = 'Export Pledge';
349 break;
350
351 case CRM_Export_Form_Select::CASE_EXPORT:
352 $exportType = 'Export Case';
353 break;
354
355 case CRM_Export_Form_Select::GRANT_EXPORT:
356 $exportType = 'Export Grant';
357 break;
358
359 case CRM_Export_Form_Select::ACTIVITY_EXPORT:
360 $exportType = 'Export Activity';
361 break;
362 }
363
364 $this->set('mappingTypeId', CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', $exportType));
365
366 $mappings = CRM_Core_BAO_Mapping::getMappings($exportType, TRUE);
367 if (!empty($mappings)) {
368 $this->add('select2', 'mapping', ts('Use Saved Field Mapping'), $mappings, FALSE, ['placeholder' => ts('- select -')]);
369 }
370 }
371
372 /**
373 * @return array
374 */
375 public static function getGreetingOptions() {
376 $options = [];
377 $greetings = [
378 'postal_greeting' => 'postal_greeting_other',
379 'addressee' => 'addressee_other',
380 ];
381
382 foreach ($greetings as $key => $value) {
383 $params = [];
384 $optionGroupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $key, 'id', 'name');
385
386 CRM_Core_DAO::commonRetrieveAll('CRM_Core_DAO_OptionValue', 'option_group_id', $optionGroupId,
387 $params, ['label', 'filter']
388 );
389
390 $greetingCount = 1;
391 $options[$key] = ["$greetingCount" => ts('List of names')];
392
393 foreach ($params as $id => $field) {
394 if (CRM_Utils_Array::value('filter', $field) == 4) {
395 $options[$key][++$greetingCount] = $field['label'];
396 }
397 }
398
399 $options[$key][++$greetingCount] = ts('Other');
400 }
401
402 return $options;
403 }
404
405 /**
406 * Get the query mode (eg. CRM_Contact_BAO_Query::MODE_CASE)
407 *
408 * @return int
409 */
410 public function getQueryMode() {
411 return (int) ($this->queryMode ?: $this->controller->get('component_mode'));
412 }
413
414 /**
415 * Call the pre-processing function.
416 */
417 protected function callPreProcessing(): void {
418 throw new CRM_Core_Exception('This must be over-ridden');
419 }
420
421 /**
422 * Assign the title of the task to the tpl.
423 */
424 protected function isShowContactMergeOptions() {
425 throw new CRM_Core_Exception('This must be over-ridden');
426 }
427
428 /**
429 * Get the name of the component.
430 *
431 * @return array
432 */
433 protected function getComponentName(): string {
434 // CRM_Export_Controller_Standalone has this method
435 if (method_exists($this->controller, 'getComponent')) {
436 return $this->controller->getComponent();
437 }
438 // For others, just guess based on the name of the controller
439 $formName = CRM_Utils_System::getClassName($this->controller->getStateMachine());
440 $componentName = explode('_', $formName);
441 return $componentName[1];
442 }
443
444 /**
445 * Get the DAO name for the given export.
446 *
447 * @return string
448 */
449 protected function getDAOName(): string {
450 switch ($this->getQueryMode()) {
451 case CRM_Contact_BAO_Query::MODE_CONTRIBUTE:
452 return 'Contribute';
453
454 case CRM_Contact_BAO_Query::MODE_MEMBER:
455 return 'Membership';
456
457 case CRM_Contact_BAO_Query::MODE_EVENT:
458 return 'Event';
459
460 case CRM_Contact_BAO_Query::MODE_PLEDGE:
461 return 'Pledge';
462
463 case CRM_Contact_BAO_Query::MODE_CASE:
464 return 'Case';
465
466 case CRM_Contact_BAO_Query::MODE_GRANT:
467 return 'Grant';
468
469 case CRM_Contact_BAO_Query::MODE_ACTIVITY:
470 return 'Activity';
471
472 default:
473 return $this->controller->get('entity') ?? $this->getComponentName();
474 }
475 }
476
477 /**
478 * Get the entity short name for a given export.
479 *
480 * @return string
481 */
482 protected function getEntityShortName(): string {
483 switch ($this->getQueryMode()) {
484 case CRM_Contact_BAO_Query::MODE_CONTRIBUTE:
485 return 'Contribute';
486
487 case CRM_Contact_BAO_Query::MODE_MEMBER:
488 return 'Member';
489
490 case CRM_Contact_BAO_Query::MODE_EVENT:
491 return 'Event';
492
493 case CRM_Contact_BAO_Query::MODE_PLEDGE:
494 return 'Pledge';
495
496 case CRM_Contact_BAO_Query::MODE_CASE:
497 return 'Case';
498
499 case CRM_Contact_BAO_Query::MODE_GRANT:
500 return 'Grant';
501
502 case CRM_Contact_BAO_Query::MODE_ACTIVITY:
503 return 'Activity';
504
505 default:
506 return $this->getComponentName();
507 }
508 }
509
510 }