Fixes for Activity import
[civicrm-core.git] / CRM / Activity / Import / Form / MapField.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_Activity_Import_Form_MapField extends CRM_Import_Form_MapField {
22
23 /**
24 * @var bool
25 */
26 public $submitOnce = TRUE;
27
28 /**
29 * Build the form object.
30 *
31 * @throws \CiviCRM_API3_Exception
32 */
33 public function buildQuickForm() {
34 $savedMappingID = (int) $this->getSubmittedValue('savedMapping');
35 $this->buildSavedMappingFields($savedMappingID);
36 $this->addFormRule(['CRM_Activity_Import_Form_MapField', 'formRule']);
37
38 //-------- end of saved mapping stuff ---------
39
40 $defaults = [];
41 $headerPatterns = $this->getHeaderPatterns();
42 $dataPatterns = $this->getDataPatterns();
43 $fieldMappings = $this->getFieldMappings();
44 $columnHeaders = $this->getColumnHeaders();
45 $hasHeaders = $this->getSubmittedValue('skipColumnHeader');
46
47 $sel1 = $this->_mapperFields;
48
49 $js = "<script type='text/javascript'>\n";
50 $formName = 'document.forms.' . $this->_name;
51
52 foreach ($columnHeaders as $i => $columnHeader) {
53 $sel = &$this->addElement('hierselect', "mapper[$i]", ts('Mapper for Field %1', [1 => $i]), NULL);
54 $jsSet = FALSE;
55 if ($this->getSubmittedValue('savedMapping')) {
56 $fieldMapping = $fieldMappings[$i] ?? NULL;
57 if (isset($fieldMappings[$i])) {
58 if ($fieldMapping['name'] !== ts('do_not_import')) {
59 $js .= "{$formName}['mapper[$i][3]'].style.display = 'none';\n";
60 $defaults["mapper[$i]"] = [$fieldMapping['name']];
61 $jsSet = TRUE;
62 }
63 else {
64 $defaults["mapper[$i]"] = [];
65 }
66 if (!$jsSet) {
67 for ($k = 1; $k < 4; $k++) {
68 $js .= "{$formName}['mapper[$i][$k]'].style.display = 'none';\n";
69 }
70 }
71 }
72 else {
73 // this load section to help mapping if we ran out of saved columns when doing Load Mapping
74 $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n";
75
76 if ($hasHeaders) {
77 $defaults["mapper[$i]"] = [$this->defaultFromHeader($columnHeader, $headerPatterns)];
78 }
79 else {
80 $defaults["mapper[$i]"] = [$this->defaultFromData($dataPatterns, $i)];
81 }
82 }
83 // End of load mapping.
84 }
85 else {
86 $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n";
87 if ($hasHeaders) {
88 // Infer the default from the skipped headers if we have them
89 $defaults["mapper[$i]"] = [
90 $this->defaultFromHeader($columnHeader, $headerPatterns),
91 0,
92 ];
93 }
94 else {
95 // Otherwise guess the default from the form of the data
96 $defaults["mapper[$i]"] = [$this->defaultFromData($dataPatterns, $i), 0];
97 }
98 }
99
100 $sel->setOptions([
101 $sel1,
102 ['' => NULL],
103 ]);
104 }
105 $js .= "</script>\n";
106 $this->assign('initHideBoxes', $js);
107
108 $this->setDefaults($defaults);
109
110 $this->addButtons([
111 [
112 'type' => 'back',
113 'name' => ts('Previous'),
114 ],
115 [
116 'type' => 'next',
117 'name' => ts('Continue'),
118 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
119 'isDefault' => TRUE,
120 ],
121 [
122 'type' => 'cancel',
123 'name' => ts('Cancel'),
124 ],
125 ]
126 );
127 }
128
129 /**
130 * Global validation rules for the form.
131 *
132 * @param array $fields
133 * Posted values of the form.
134 *
135 * @return array
136 * list of errors to be posted back to the form
137 */
138 public static function formRule($fields) {
139 $errors = [];
140 // define so we avoid notices below
141 $errors['_qf_default'] = '';
142
143 $fieldMessage = NULL;
144 if (!array_key_exists('savedMapping', $fields)) {
145 $importKeys = [];
146 foreach ($fields['mapper'] as $mapperPart) {
147 $importKeys[] = $mapperPart[0];
148 }
149 // FIXME: should use the schema titles, not redeclare them
150 $requiredFields = [
151 'target_contact_id' => ts('Contact ID'),
152 'activity_date_time' => ts('Activity Date'),
153 'activity_subject' => ts('Activity Subject'),
154 'activity_type_id' => ts('Activity Type ID'),
155 ];
156
157 $contactFieldsBelowWeightMessage = self::validateRequiredContactMatchFields('Individual', $importKeys);
158 foreach ($requiredFields as $field => $title) {
159 if (!in_array($field, $importKeys)) {
160 if ($field === 'target_contact_id') {
161 if (!$contactFieldsBelowWeightMessage || in_array('external_identifier', $importKeys)) {
162 continue;
163 }
164 else {
165 $errors['_qf_default'] .= ts('Missing required contact matching fields.')
166 . $contactFieldsBelowWeightMessage
167 . '<br />';
168 }
169 }
170 elseif ($field === 'activity_type_id') {
171 if (in_array('activity_label', $importKeys)) {
172 continue;
173 }
174 else {
175 $errors['_qf_default'] .= ts('Missing required field: Provide %1 or %2',
176 [
177 1 => $title,
178 2 => 'Activity Type Label',
179 ]) . '<br />';
180 }
181 }
182 else {
183 $errors['_qf_default'] .= ts('Missing required field: %1', [1 => $title]) . '<br />';
184 }
185 }
186 }
187 }
188
189 if (!empty($fields['saveMapping'])) {
190 $nameField = $fields['saveMappingName'] ?? NULL;
191 if (empty($nameField)) {
192 $errors['saveMappingName'] = ts('Name is required to save Import Mapping');
193 }
194 else {
195 if (CRM_Core_BAO_Mapping::checkMapping($nameField, CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Activity'))) {
196 $errors['saveMappingName'] = ts('Duplicate Import Mapping Name');
197 }
198 }
199 }
200
201 if (empty($errors['_qf_default'])) {
202 unset($errors['_qf_default']);
203 }
204 if (!empty($errors)) {
205 if (!empty($errors['saveMappingName'])) {
206 $_flag = 1;
207 $assignError = new CRM_Core_Page();
208 $assignError->assign('mappingDetailsError', $_flag);
209 }
210 return $errors;
211 }
212
213 return TRUE;
214 }
215
216 /**
217 * @return CRM_Activity_Import_Parser_Activity
218 */
219 protected function getParser(): CRM_Activity_Import_Parser_Activity {
220 if (!$this->parser) {
221 $this->parser = new CRM_Activity_Import_Parser_Activity();
222 $this->parser->setUserJobID($this->getUserJobID());
223 $this->parser->init();
224 }
225 return $this->parser;
226 }
227
228 protected function getHighlightedFields(): array {
229 $highlightedFields = [];
230 $requiredFields = [
231 'activity_date_time',
232 'activity_type_id',
233 'target_contact_id',
234 'activity_subject',
235 ];
236 foreach ($requiredFields as $val) {
237 $highlightedFields[] = $val;
238 }
239 return $highlightedFields;
240 }
241
242 public function getImportType(): string {
243 return 'Import Activity';
244 }
245
246 /**
247 * Get the mapping name per the civicrm_mapping_field.type_id option group.
248 *
249 * @return string
250 */
251 public function getMappingTypeName(): string {
252 return 'Import Participant';
253 }
254
255 }