[REF] [Import] Remove last instance of passing fieldTypes through get->set
[civicrm-core.git] / CRM / Contact / Import / Form / DataSource.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 delegates to the chosen DataSource to grab the data to be imported.
20 */
21 class CRM_Contact_Import_Form_DataSource extends CRM_Import_Forms {
22
23 /**
24 * Get any smarty elements that may not be present in the form.
25 *
26 * To make life simpler for smarty we ensure they are set to null
27 * rather than unset. This is done at the last minute when $this
28 * is converted to an array to be assigned to the form.
29 *
30 * @return array
31 */
32 public function getOptionalQuickFormElements(): array {
33 return ['disableUSPS'];
34 }
35
36 /**
37 * Set variables up before form is built.
38 *
39 * @throws \CRM_Core_Exception
40 */
41 public function preProcess() {
42 $results = [];
43 $config = CRM_Core_Config::singleton();
44 $handler = opendir($config->uploadDir);
45 $errorFiles = ['sqlImport.errors', 'sqlImport.conflicts', 'sqlImport.duplicates', 'sqlImport.mismatch'];
46
47 // check for post max size avoid when called twice
48 $snippet = $_GET['snippet'] ?? 0;
49 if (empty($snippet)) {
50 CRM_Utils_Number::formatUnitSize(ini_get('post_max_size'), TRUE);
51 }
52
53 while ($file = readdir($handler)) {
54 if ($file !== '.' && $file !== '..' &&
55 in_array($file, $errorFiles) && !is_writable($config->uploadDir . $file)
56 ) {
57 $results[] = $file;
58 }
59 }
60 closedir($handler);
61 if (!empty($results)) {
62 $this->invalidConfig(ts('<b>%1</b> file(s) in %2 directory are not writable. Listed file(s) might be used during the import to log the errors occurred during Import process. Contact your site administrator for assistance.', [
63 1 => implode(', ', $results),
64 2 => $config->uploadDir,
65 ]));
66 }
67 }
68
69 /**
70 * Build the form object.
71 *
72 * @throws \CRM_Core_Exception
73 */
74 public function buildQuickForm() {
75
76 $this->assign('urlPath', 'civicrm/import/datasource');
77 $this->assign('urlPathVar', 'snippet=4&user_job_id=' . $this->get('user_job_id'));
78
79 $this->add('select', 'dataSource', ts('Data Source'), $this->getDataSources(), TRUE,
80 ['onchange' => 'buildDataSourceFormBlock(this.value);']
81 );
82
83 // duplicate handling options
84 $this->addRadio('onDuplicate', ts('For Duplicate Contacts'), [
85 CRM_Import_Parser::DUPLICATE_SKIP => ts('Skip'),
86 CRM_Import_Parser::DUPLICATE_UPDATE => ts('Update'),
87 CRM_Import_Parser::DUPLICATE_FILL => ts('Fill'),
88 CRM_Import_Parser::DUPLICATE_NOCHECK => ts('No Duplicate Checking'),
89 ]);
90
91 $mappingArray = CRM_Core_BAO_Mapping::getMappings('Import Contact');
92
93 $this->assign('savedMapping', $mappingArray);
94 $this->addElement('select', 'savedMapping', ts('Saved Field Mapping'), ['' => ts('- select -')] + $mappingArray);
95
96 $js = ['onClick' => "buildSubTypes();buildDedupeRules();"];
97 // contact types option
98 $contactTypeOptions = $contactTypeAttributes = [];
99 if (CRM_Contact_BAO_ContactType::isActive('Individual')) {
100 $contactTypeOptions[CRM_Import_Parser::CONTACT_INDIVIDUAL] = ts('Individual');
101 $contactTypeAttributes[CRM_Import_Parser::CONTACT_INDIVIDUAL] = $js;
102 }
103 if (CRM_Contact_BAO_ContactType::isActive('Household')) {
104 $contactTypeOptions[CRM_Import_Parser::CONTACT_HOUSEHOLD] = ts('Household');
105 $contactTypeAttributes[CRM_Import_Parser::CONTACT_HOUSEHOLD] = $js;
106 }
107 if (CRM_Contact_BAO_ContactType::isActive('Organization')) {
108 $contactTypeOptions[CRM_Import_Parser::CONTACT_ORGANIZATION] = ts('Organization');
109 $contactTypeAttributes[CRM_Import_Parser::CONTACT_ORGANIZATION] = $js;
110 }
111 $this->addRadio('contactType', ts('Contact Type'), $contactTypeOptions, [], NULL, FALSE, $contactTypeAttributes);
112
113 $this->addElement('select', 'contactSubType', ts('Subtype'));
114 $this->addElement('select', 'dedupe_rule_id', ts('Dedupe Rule'));
115
116 CRM_Core_Form_Date::buildAllowedDateFormats($this);
117
118 $geoCode = FALSE;
119 if (CRM_Utils_GeocodeProvider::getUsableClassName()) {
120 $geoCode = TRUE;
121 $this->addElement('checkbox', 'doGeocodeAddress', ts('Geocode addresses during import?'));
122 }
123 $this->assign('geoCode', $geoCode);
124
125 $this->addElement('text', 'fieldSeparator', ts('Import Field Separator'), ['size' => 2]);
126
127 if (Civi::settings()->get('address_standardization_provider') === 'USPS') {
128 $this->addElement('checkbox', 'disableUSPS', ts('Disable USPS address validation during import?'));
129 }
130 $this->buildDataSourceFields();
131
132 $this->addButtons([
133 [
134 'type' => 'upload',
135 'name' => ts('Continue'),
136 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
137 'isDefault' => TRUE,
138 ],
139 [
140 'type' => 'cancel',
141 'name' => ts('Cancel'),
142 ],
143 ]);
144 }
145
146 /**
147 * Set the default values of various form elements.
148 *
149 * @return array
150 * reference to the array of default values
151 */
152 public function setDefaultValues() {
153 $defaults = [
154 'dataSource' => $this->getDefaultDataSource(),
155 'onDuplicate' => CRM_Import_Parser::DUPLICATE_SKIP,
156 'contactType' => CRM_Import_Parser::CONTACT_INDIVIDUAL,
157 'fieldSeparator' => CRM_Core_Config::singleton()->fieldSeparator,
158 ];
159
160 if ($this->get('loadedMapping')) {
161 $defaults['savedMapping'] = $this->get('loadedMapping');
162 }
163
164 return $defaults;
165 }
166
167 /**
168 * Call the DataSource's postProcess method.
169 *
170 * @throws \CRM_Core_Exception
171 * @throws \API_Exception
172 */
173 public function postProcess() {
174 $this->controller->resetPage('MapField');
175 if (!$this->getUserJobID()) {
176 $this->createUserJob();
177 }
178 else {
179 $this->flushDataSource();
180 $this->updateUserJobMetadata('submitted_values', $this->getSubmittedValues());
181 }
182 // Setup the params array
183 $this->_params = $this->controller->exportValues($this->_name);
184
185 // @todo - this params are being set here because they were / possibly still
186 // are in some places being accessed by forms later in the flow
187 // ie CRM_Contact_Import_Form_MapField, CRM_Contact_Import_Form_Preview
188 // or CRM_Contact_Import_Form_Summary using `$this->get()
189 // which was the old way of saving values submitted on this form such that
190 // the other forms could access them. Now they should use
191 // `getSubmittedValue` or simply not get them if the only
192 // reason is to pass to the Parser which can itself
193 // call 'getSubmittedValue'
194 // Once the mentioned forms no longer call $this->get() all this 'setting'
195 // is obsolete.
196 $storeParams = [
197 'onDuplicate' => $this->getSubmittedValue('onDuplicate'),
198 'dedupe' => $this->getSubmittedValue('dedupe_rule_id'),
199 'contactType' => $this->getSubmittedValue('contactType'),
200 'dateFormats' => $this->getSubmittedValue('dateFormats'),
201 'savedMapping' => $this->getSubmittedValue('savedMapping'),
202 ];
203
204 foreach ($storeParams as $storeName => $value) {
205 $this->set($storeName, $value);
206 }
207 $this->set('disableUSPS', $this->getSubmittedValue('disableUSPS'));
208 $this->set('dataSource', $this->getSubmittedValue('dataSource'));
209 $this->set('skipColumnHeader', CRM_Utils_Array::value('skipColumnHeader', $this->_params));
210
211 CRM_Core_Session::singleton()->set('dateTypes', $storeParams['dateFormats']);
212
213 $this->instantiateDataSource();
214
215 $mapper = [];
216
217 $parser = new CRM_Contact_Import_Parser_Contact($mapper);
218 $parser->setMaxLinesToProcess(100);
219 $parser->setUserJobID($this->getUserJobID());
220 $parser->run(
221 [],
222 CRM_Import_Parser::MODE_MAPFIELD
223 );
224
225 }
226
227 /**
228 * Instantiate the datasource.
229 *
230 * This gives the datasource a chance to do any table creation etc.
231 *
232 * @throws \API_Exception
233 * @throws \CRM_Core_Exception
234 */
235 private function instantiateDataSource(): void {
236 $this->getDataSourceObject()->initialize();
237 }
238
239 /**
240 * General function for handling invalid configuration.
241 *
242 * I was going to statusBounce them all but when I tested I was 'bouncing' to weird places
243 * whereas throwing an exception gave no behaviour change. So, I decided to centralise
244 * and we can 'flip the switch' later.
245 *
246 * @param $message
247 *
248 * @throws \CRM_Core_Exception
249 */
250 protected function invalidConfig($message) {
251 throw new CRM_Core_Exception($message);
252 }
253
254 /**
255 * Return a descriptive name for the page, used in wizard header
256 *
257 * @return string
258 */
259 public function getTitle(): string {
260 return ts('Choose Data Source');
261 }
262
263 }