Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
39de6fd5 | 4 | | CiviCRM version 4.6 | |
6a488035 | 5 | +--------------------------------------------------------------------+ |
06b69b18 | 6 | | Copyright CiviCRM LLC (c) 2004-2014 | |
6a488035 TO |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
10 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
11 | | under the terms of the GNU Affero General Public License | | |
12 | | Version 3, 19 November 2009. | | |
13 | | | | |
14 | | CiviCRM is distributed in the hope that it will be useful, but | | |
15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
17 | | See the GNU Affero General Public License for more details. | | |
18 | | | | |
19 | | You should have received a copy of the GNU Affero General Public | | |
20 | | License and the CiviCRM Licensing Exception along | | |
21 | | with this program; if not, contact CiviCRM LLC | | |
22 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
23 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
25 | +--------------------------------------------------------------------+ | |
26 | */ | |
27 | ||
28 | /** | |
29 | * | |
30 | * @package CRM | |
06b69b18 | 31 | * @copyright CiviCRM LLC (c) 2004-2014 |
6a488035 TO |
32 | * $Id$ |
33 | * | |
34 | */ | |
35 | ||
36 | /** | |
37 | * This class delegates to the chosen DataSource to grab the data to be | |
38 | * imported. | |
39 | */ | |
719a6fec | 40 | class CRM_Contact_Import_Form_DataSource extends CRM_Core_Form { |
6a488035 TO |
41 | |
42 | private $_dataSource; | |
43 | ||
44 | private $_dataSourceIsValid = FALSE; | |
45 | ||
46 | private $_dataSourceClassFile; | |
47 | ||
b0b2638a DL |
48 | private $_dataSourceClass; |
49 | ||
6a488035 | 50 | /** |
100fef9d | 51 | * Set variables up before form is built |
6a488035 TO |
52 | * |
53 | * @return void | |
6a488035 TO |
54 | */ |
55 | public function preProcess() { | |
56 | ||
57 | //Test database user privilege to create table(Temporary) CRM-4725 | |
6a4257d4 | 58 | $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); |
6a488035 TO |
59 | $daoTestPrivilege = new CRM_Core_DAO; |
60 | $daoTestPrivilege->query("CREATE TEMPORARY TABLE import_job_permission_one(test int) ENGINE=InnoDB"); | |
61 | $daoTestPrivilege->query("CREATE TEMPORARY TABLE import_job_permission_two(test int) ENGINE=InnoDB"); | |
62 | $daoTestPrivilege->query("DROP TABLE IF EXISTS import_job_permission_one, import_job_permission_two"); | |
6a4257d4 | 63 | unset($errorScope); |
6a488035 TO |
64 | |
65 | if ($daoTestPrivilege->_lastError) { | |
66 | CRM_Core_Error::fatal(ts('Database Configuration Error: Insufficient permissions. Import requires that the CiviCRM database user has permission to create temporary tables. Contact your site administrator for assistance.')); | |
67 | } | |
68 | ||
69 | $results = array(); | |
70 | $config = CRM_Core_Config::singleton(); | |
71 | $handler = opendir($config->uploadDir); | |
72 | $errorFiles = array('sqlImport.errors', 'sqlImport.conflicts', 'sqlImport.duplicates', 'sqlImport.mismatch'); | |
73 | ||
f4a17080 | 74 | // check for post max size avoid when called twice |
75 | $snippet = CRM_Utils_Array::value('snippet', $_GET, 0); | |
76 | if (empty($snippet)) { | |
77 | CRM_Core_Config_Defaults::formatUnitSize(ini_get('post_max_size'), TRUE); | |
78 | } | |
66dc6009 | 79 | |
6a488035 TO |
80 | while ($file = readdir($handler)) { |
81 | if ($file != '.' && $file != '..' && | |
82 | in_array($file, $errorFiles) && !is_writable($config->uploadDir . $file) | |
83 | ) { | |
84 | $results[] = $file; | |
85 | } | |
86 | } | |
87 | closedir($handler); | |
88 | if (!empty($results)) { | |
89 | CRM_Core_Error::fatal(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.', array(1 => implode(', ', $results), 2 => $config->uploadDir))); | |
90 | } | |
91 | ||
92 | $this->_dataSourceIsValid = FALSE; | |
0a11d4d8 DL |
93 | $this->_dataSource = CRM_Utils_Request::retrieve( |
94 | 'dataSource', | |
95 | 'String', | |
96 | CRM_Core_DAO::$_nullObject, | |
97 | FALSE, | |
98 | NULL, | |
99 | 'GET' | |
6a488035 TO |
100 | ); |
101 | ||
102 | $this->_params = $this->controller->exportValues($this->_name); | |
103 | if (!$this->_dataSource) { | |
104 | //considering dataSource as base criteria instead of hidden_dataSource. | |
105 | $this->_dataSource = CRM_Utils_Array::value('dataSource', | |
106 | $_POST, | |
107 | CRM_Utils_Array::value('dataSource', | |
108 | $this->_params | |
109 | ) | |
110 | ); | |
111 | $this->assign('showOnlyDataSourceFormPane', FALSE); | |
112 | } | |
113 | else { | |
114 | $this->assign('showOnlyDataSourceFormPane', TRUE); | |
115 | } | |
116 | ||
117 | if (strpos($this->_dataSource, 'CRM_Import_DataSource_') === 0) { | |
118 | $this->_dataSourceIsValid = TRUE; | |
119 | $this->assign('showDataSourceFormPane', TRUE); | |
120 | $dataSourcePath = explode('_', $this->_dataSource); | |
719a6fec | 121 | $templateFile = "CRM/Contact/Import/Form/" . $dataSourcePath[3] . ".tpl"; |
6a488035 TO |
122 | $this->assign('dataSourceFormTemplateFile', $templateFile); |
123 | } | |
124 | } | |
125 | ||
126 | /** | |
c490a46a | 127 | * Build the form object |
6a488035 | 128 | * |
355ba699 | 129 | * @return void |
6a488035 TO |
130 | */ |
131 | ||
132 | public function buildQuickForm() { | |
133 | ||
134 | // If there's a dataSource in the query string, we need to load | |
135 | // the form from the chosen DataSource class | |
136 | if ($this->_dataSourceIsValid) { | |
137 | $this->_dataSourceClassFile = str_replace('_', '/', $this->_dataSource) . ".php"; | |
138 | require_once $this->_dataSourceClassFile; | |
b0b2638a DL |
139 | $this->_dataSourceClass = new $this->_dataSource; |
140 | $this->_dataSourceClass->buildQuickForm( $this ); | |
6a488035 TO |
141 | } |
142 | ||
143 | // Get list of data sources and display them as options | |
144 | $dataSources = $this->_getDataSources(); | |
145 | ||
146 | $this->assign('urlPath', "civicrm/import"); | |
147 | $this->assign('urlPathVar', 'snippet=4'); | |
148 | ||
149 | $this->add('select', 'dataSource', ts('Data Source'), $dataSources, TRUE, | |
150 | array('onchange' => 'buildDataSourceFormBlock(this.value);') | |
151 | ); | |
152 | ||
153 | // duplicate handling options | |
154 | $duplicateOptions = array(); | |
155 | $duplicateOptions[] = $this->createElement('radio', | |
a05662ef | 156 | NULL, NULL, ts('Skip'), CRM_Import_Parser::DUPLICATE_SKIP |
6a488035 TO |
157 | ); |
158 | $duplicateOptions[] = $this->createElement('radio', | |
a05662ef | 159 | NULL, NULL, ts('Update'), CRM_Import_Parser::DUPLICATE_UPDATE |
6a488035 TO |
160 | ); |
161 | $duplicateOptions[] = $this->createElement('radio', | |
a05662ef | 162 | NULL, NULL, ts('Fill'), CRM_Import_Parser::DUPLICATE_FILL |
6a488035 TO |
163 | ); |
164 | $duplicateOptions[] = $this->createElement('radio', | |
a05662ef | 165 | NULL, NULL, ts('No Duplicate Checking'), CRM_Import_Parser::DUPLICATE_NOCHECK |
6a488035 TO |
166 | ); |
167 | ||
168 | $this->addGroup($duplicateOptions, 'onDuplicate', | |
169 | ts('For Duplicate Contacts') | |
170 | ); | |
171 | ||
172 | $mappingArray = CRM_Core_BAO_Mapping::getMappings(CRM_Core_OptionGroup::getValue('mapping_type', | |
173 | 'Import Contact', | |
174 | 'name' | |
175 | )); | |
176 | ||
177 | $this->assign('savedMapping', $mappingArray); | |
178 | $this->addElement('select', 'savedMapping', ts('Mapping Option'), array('' => ts('- select -')) + $mappingArray); | |
179 | ||
180 | ||
181 | $js = array('onClick' => "buildSubTypes();buildDedupeRules();"); | |
182 | // contact types option | |
183 | $contactOptions = array(); | |
184 | if (CRM_Contact_BAO_ContactType::isActive('Individual')) { | |
185 | $contactOptions[] = $this->createElement('radio', | |
a05662ef | 186 | NULL, NULL, ts('Individual'), CRM_Import_Parser::CONTACT_INDIVIDUAL, $js |
6a488035 TO |
187 | ); |
188 | } | |
189 | if (CRM_Contact_BAO_ContactType::isActive('Household')) { | |
190 | $contactOptions[] = $this->createElement('radio', | |
a05662ef | 191 | NULL, NULL, ts('Household'), CRM_Import_Parser::CONTACT_HOUSEHOLD, $js |
6a488035 TO |
192 | ); |
193 | } | |
194 | if (CRM_Contact_BAO_ContactType::isActive('Organization')) { | |
195 | $contactOptions[] = $this->createElement('radio', | |
a05662ef | 196 | NULL, NULL, ts('Organization'), CRM_Import_Parser::CONTACT_ORGANIZATION, $js |
6a488035 TO |
197 | ); |
198 | } | |
199 | ||
200 | $this->addGroup($contactOptions, 'contactType', | |
201 | ts('Contact Type') | |
202 | ); | |
203 | ||
204 | $this->addElement('select', 'subType', ts('Subtype')); | |
205 | $this->addElement('select', 'dedupe', ts('Dedupe Rule')); | |
206 | ||
207 | CRM_Core_Form_Date::buildAllowedDateFormats($this); | |
208 | ||
209 | $config = CRM_Core_Config::singleton(); | |
210 | $geoCode = FALSE; | |
211 | if (!empty($config->geocodeMethod)) { | |
212 | $geoCode = TRUE; | |
213 | $this->addElement('checkbox', 'doGeocodeAddress', ts('Lookup mapping info during import?')); | |
214 | } | |
215 | $this->assign('geoCode', $geoCode); | |
216 | ||
217 | $this->addElement('text', 'fieldSeparator', ts('Import Field Separator'), array('size' => 2)); | |
218 | ||
219 | $this->addButtons(array( | |
220 | array( | |
221 | 'type' => 'upload', | |
222 | 'name' => ts('Continue >>'), | |
223 | 'spacing' => ' ', | |
224 | 'isDefault' => TRUE, | |
225 | ), | |
226 | array( | |
227 | 'type' => 'cancel', | |
228 | 'name' => ts('Cancel'), | |
229 | ), | |
230 | ) | |
231 | ); | |
232 | } | |
233 | ||
86538308 EM |
234 | /** |
235 | * This virtual function is used to set the default values of | |
236 | * various form elements | |
237 | * | |
238 | * access public | |
239 | * | |
240 | * @return array reference to the array of default values | |
241 | * | |
242 | */ | |
243 | /** | |
244 | * @return array | |
245 | */ | |
00be9182 | 246 | public function setDefaultValues() { |
6a488035 TO |
247 | $config = CRM_Core_Config::singleton(); |
248 | $defaults = array( | |
249 | 'dataSource' => 'CRM_Import_DataSource_CSV', | |
a05662ef CW |
250 | 'onDuplicate' => CRM_Import_Parser::DUPLICATE_SKIP, |
251 | 'contactType' => CRM_Import_Parser::CONTACT_INDIVIDUAL, | |
6a488035 TO |
252 | 'fieldSeparator' => $config->fieldSeparator, |
253 | ); | |
254 | ||
255 | if ($loadeMapping = $this->get('loadedMapping')) { | |
256 | $this->assign('loadedMapping', $loadeMapping); | |
257 | $defaults['savedMapping'] = $loadeMapping; | |
258 | } | |
259 | ||
260 | return $defaults; | |
261 | } | |
262 | ||
86538308 EM |
263 | /** |
264 | * @return array | |
265 | * @throws Exception | |
266 | */ | |
6a488035 TO |
267 | private function _getDataSources() { |
268 | // Open the data source dir and scan it for class files | |
269 | $config = CRM_Core_Config::singleton(); | |
270 | $dataSourceDir = $config->importDataSourceDir; | |
271 | $dataSources = array(); | |
272 | if (!is_dir($dataSourceDir)) { | |
273 | CRM_Core_Error::fatal("Import DataSource directory $dataSourceDir does not exist"); | |
274 | } | |
275 | if (!$dataSourceHandle = opendir($dataSourceDir)) { | |
276 | CRM_Core_Error::fatal("Unable to access DataSource directory $dataSourceDir"); | |
277 | } | |
278 | ||
279 | while (($dataSourceFile = readdir($dataSourceHandle)) !== FALSE) { | |
280 | $fileType = filetype($dataSourceDir . $dataSourceFile); | |
281 | $matches = array(); | |
282 | if (($fileType == 'file' || $fileType == 'link') && | |
283 | preg_match('/^(.+)\.php$/', $dataSourceFile, $matches) | |
284 | ) { | |
285 | $dataSourceClass = "CRM_Import_DataSource_" . $matches[1]; | |
286 | require_once $dataSourceDir . DIRECTORY_SEPARATOR . $dataSourceFile; | |
b0b2638a DL |
287 | $object = new $dataSourceClass; |
288 | $info = $object->getInfo(); | |
6a488035 TO |
289 | $dataSources[$dataSourceClass] = $info['title']; |
290 | } | |
291 | } | |
292 | closedir($dataSourceHandle); | |
293 | return $dataSources; | |
294 | } | |
295 | ||
296 | /** | |
297 | * Call the DataSource's postProcess method to take over | |
298 | * and then setup some common data structures for the next step | |
299 | * | |
300 | * @return void | |
6a488035 TO |
301 | */ |
302 | public function postProcess() { | |
303 | $this->controller->resetPage('MapField'); | |
304 | ||
305 | if ($this->_dataSourceIsValid) { | |
306 | // Setup the params array | |
307 | $this->_params = $this->controller->exportValues($this->_name); | |
308 | ||
309 | $storeParams = array( | |
310 | 'onDuplicate' => 'onDuplicate', | |
311 | 'dedupe' => 'dedupe', | |
312 | 'contactType' => 'contactType', | |
313 | 'contactSubType' => 'subType', | |
314 | 'dateFormats' => 'dateFormats', | |
315 | 'savedMapping' => 'savedMapping', | |
316 | ); | |
317 | ||
318 | foreach ($storeParams as $storeName => $storeValueName) { | |
319 | $$storeName = $this->exportValue($storeValueName); | |
320 | $this->set($storeName, $$storeName); | |
321 | } | |
322 | ||
323 | $this->set('dataSource', $this->_params['dataSource']); | |
324 | $this->set('skipColumnHeader', CRM_Utils_Array::value('skipColumnHeader', $this->_params)); | |
325 | ||
326 | $session = CRM_Core_Session::singleton(); | |
327 | $session->set('dateTypes', $dateFormats); | |
328 | ||
329 | // Get the PEAR::DB object | |
330 | $dao = new CRM_Core_DAO(); | |
331 | $db = $dao->getDatabaseConnection(); | |
332 | ||
333 | //hack to prevent multiple tables. | |
334 | $this->_params['import_table_name'] = $this->get('importTableName'); | |
335 | if (!$this->_params['import_table_name']) { | |
336 | $this->_params['import_table_name'] = 'civicrm_import_job_' . md5(uniqid(rand(), TRUE)); | |
337 | } | |
338 | ||
b0b2638a | 339 | $this->_dataSourceClass->postProcess( $this->_params, $db, $this ); |
6a488035 TO |
340 | |
341 | // We should have the data in the DB now, parse it | |
342 | $importTableName = $this->get('importTableName'); | |
343 | $fieldNames = $this->_prepareImportTable($db, $importTableName); | |
344 | $mapper = array(); | |
345 | ||
719a6fec | 346 | $parser = new CRM_Contact_Import_Parser_Contact($mapper); |
6a488035 TO |
347 | $parser->setMaxLinesToProcess(100); |
348 | $parser->run($importTableName, | |
349 | $mapper, | |
a05662ef | 350 | CRM_Import_Parser::MODE_MAPFIELD, |
6a488035 TO |
351 | $contactType, |
352 | $fieldNames['pk'], | |
353 | $fieldNames['status'], | |
a05662ef | 354 | CRM_Import_Parser::DUPLICATE_SKIP, |
6a488035 | 355 | NULL, NULL, FALSE, |
719a6fec | 356 | CRM_Contact_Import_Parser::DEFAULT_TIMEOUT, |
6a488035 TO |
357 | $contactSubType, |
358 | $dedupe | |
359 | ); | |
360 | ||
361 | // add all the necessary variables to the form | |
362 | $parser->set($this); | |
363 | } | |
364 | else { | |
365 | CRM_Core_Error::fatal("Invalid DataSource on form post. This shouldn't happen!"); | |
366 | } | |
367 | } | |
368 | ||
369 | /** | |
370 | * Add a PK and status column to the import table so we can track our progress | |
371 | * Returns the name of the primary key and status columns | |
372 | * | |
77b97be7 | 373 | * @param $db |
100fef9d | 374 | * @param string $importTableName |
77b97be7 | 375 | * |
6a488035 | 376 | * @return array |
6a488035 TO |
377 | */ |
378 | private function _prepareImportTable($db, $importTableName) { | |
379 | /* TODO: Add a check for an existing _status field; | |
380 | * if it exists, create __status instead and return that | |
381 | */ | |
382 | ||
383 | $statusFieldName = '_status'; | |
384 | $primaryKeyName = '_id'; | |
385 | ||
386 | $this->set('primaryKeyName', $primaryKeyName); | |
387 | $this->set('statusFieldName', $statusFieldName); | |
388 | ||
389 | /* Make sure the PK is always last! We rely on this later. | |
390 | * Should probably stop doing that at some point, but it | |
391 | * would require moving to associative arrays rather than | |
392 | * relying on numerical order of the fields. This could in | |
393 | * turn complicate matters for some DataSources, which | |
394 | * would also not be good. Decisions, decisions... | |
395 | */ | |
396 | ||
397 | $alterQuery = "ALTER TABLE $importTableName | |
398 | ADD COLUMN $statusFieldName VARCHAR(32) | |
399 | DEFAULT 'NEW' NOT NULL, | |
400 | ADD COLUMN ${statusFieldName}Msg TEXT, | |
401 | ADD COLUMN $primaryKeyName INT PRIMARY KEY NOT NULL | |
402 | AUTO_INCREMENT"; | |
403 | $db->query($alterQuery); | |
404 | ||
405 | return array('status' => $statusFieldName, 'pk' => $primaryKeyName); | |
406 | } | |
407 | ||
408 | /** | |
409 | * Return a descriptive name for the page, used in wizard header | |
410 | * | |
411 | * | |
412 | * @return string | |
6a488035 TO |
413 | */ |
414 | public function getTitle() { | |
415 | return ts('Choose Data Source'); | |
416 | } | |
417 | } |