2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
4 /*********************************************************************************
5 * SugarCRM is a customer relationship management program developed by
6 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Affero General Public License version 3 as published by the
10 * Free Software Foundation with the addition of the following permission added
11 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
12 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
13 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
20 * You should have received a copy of the GNU Affero General Public License along with
21 * this program; if not, see http://www.gnu.org/licenses or write to the Free
22 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
26 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
28 * The interactive user interfaces in modified source and object code versions
29 * of this program must display Appropriate Legal Notices, as required under
30 * Section 5 of the GNU Affero General Public License version 3.
32 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
33 * these Appropriate Legal Notices must retain the display of the "Powered by
34 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
35 * technical reasons, the Appropriate Legal Notices must display the words
36 * "Powered by SugarCRM".
37 ********************************************************************************/
39 /*********************************************************************************
41 * Description: view handler for step 4 of the import process
42 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
43 * All Rights Reserved.
44 ********************************************************************************/
46 require_once('include/MVC/View/SugarView.php');
47 require_once('modules/Import/ImportFile.php');
48 require_once('modules/Import/ImportFileSplitter.php');
49 require_once('modules/Import/ImportCacheFiles.php');
50 require_once('modules/Import/ImportFieldSanitize.php');
51 require_once('modules/Import/ImportDuplicateCheck.php');
53 class ImportViewStep4 extends SugarView
56 * @see SugarView::display()
58 public function display()
62 // Increase the max_execution_time since this step can take awhile
63 ini_set("max_execution_time", max($sugar_config['import_max_execution_time'],3600));
66 TrackerManager::getInstance()->pause();
68 // use our own error handler
69 set_error_handler('handleImportErrors',E_ALL);
71 global $mod_strings, $app_strings, $current_user, $import_bean_map;
72 global $app_list_strings, $timedate;
74 $update_only = ( isset($_REQUEST['import_type']) && $_REQUEST['import_type'] == 'update' );
75 $firstrow = unserialize(base64_decode($_REQUEST['firstrow']));
77 // All the Look Up Caches are initialized here
78 $enum_lookup_cache=array();
80 // Let's try and load the import bean
81 $focus = loadImportBean($_REQUEST['import_module']);
83 trigger_error($mod_strings['LBL_ERROR_IMPORTS_NOT_SET_UP'],E_USER_ERROR);
86 // setup the importable fields array.
87 $importable_fields = $focus->get_importable_fields();
89 // loop through all request variables
90 $importColumns = array();
91 foreach ($_REQUEST as $name => $value) {
92 // only look for var names that start with "fieldNum"
93 if (strncasecmp($name, "colnum_", 7) != 0) {
97 // pull out the column position for this field name
98 $pos = substr($name, 7);
100 if ( isset($importable_fields[$value]) ) {
101 // now mark that we've seen this field
102 $importColumns[$pos] = $value;
107 // set the default locale settings
108 $ifs = new ImportFieldSanitize();
109 $ifs->dateformat = $_REQUEST['importlocale_dateformat'];
110 $ifs->timeformat = $_REQUEST['importlocale_timeformat'];
111 $ifs->timezone = $_REQUEST['importlocale_timezone'];
112 $currency = new Currency();
113 $currency->retrieve($_REQUEST['importlocale_currency']);
114 $ifs->currency_symbol = $currency->symbol;
115 $ifs->default_currency_significant_digits
116 = $_REQUEST['importlocale_default_currency_significant_digits'];
118 = $_REQUEST['importlocale_num_grp_sep'];
119 $ifs->dec_sep = $_REQUEST['importlocale_dec_sep'];
120 $ifs->default_locale_name_format
121 = $_REQUEST['importlocale_default_locale_name_format'];
123 // Check to be sure we are getting an import file that is in the right place
124 if ( realpath(dirname($_REQUEST['tmp_file']).'/') != realpath($sugar_config['upload_dir']) )
125 trigger_error($mod_strings['LBL_CANNOT_OPEN'],E_USER_ERROR);
127 // Open the import file
128 $importFile = new ImportFile(
129 $_REQUEST['tmp_file'],
130 $_REQUEST['custom_delimiter'],
131 html_entity_decode($_REQUEST['custom_enclosure'],ENT_QUOTES)
134 if ( !$importFile->fileExists() ) {
135 trigger_error($mod_strings['LBL_CANNOT_OPEN'],E_USER_ERROR);
138 $fieldDefs = $focus->getFieldDefinitions();
142 while ( $row = $importFile->getNextRow() ) {
143 $focus = loadImportBean($_REQUEST['import_module']);
144 $focus->unPopulateDefaultValues();
145 $focus->save_from_post = false;
146 $focus->team_id = null;
147 $ifs->createdBeans = array();
151 for ( $fieldNum = 0; $fieldNum < $_REQUEST['columncount']; $fieldNum++ ) {
152 // loop if this column isn't set
153 if ( !isset($importColumns[$fieldNum]) ) {
157 // get this field's properties
158 $field = $importColumns[$fieldNum];
159 $fieldDef = $focus->getFieldDefinition($field);
160 $fieldTranslated = translate((isset($fieldDef['vname'])?$fieldDef['vname']:$fieldDef['name']),
161 $_REQUEST['module'])." (".$fieldDef['name'].")";
163 // Bug 37241 - Don't re-import over a field we already set during the importing of another field
164 if ( !empty($focus->$field) ) {
168 //DETERMINE WHETHER OR NOT $fieldDef['name'] IS DATE_MODIFIED AND SET A VAR, USE DOWN BELOW
173 $locale = new Localization();
175 if ( isset($row[$fieldNum]) )
176 $rowValue = $locale->translateCharset(
177 strip_tags(trim($row[$fieldNum])),
178 $_REQUEST['importlocale_charset'],
179 $sugar_config['default_charset']
184 // If there is an default value then use it instead
185 if ( !empty($_REQUEST[$field]) ) {
186 if ( is_array($_REQUEST[$field]) )
187 $defaultRowValue = encodeMultienumValue($_REQUEST[$field]);
189 $defaultRowValue = $_REQUEST[$field];
190 // translate default values to the date/time format for the import file
191 if ( $fieldDef['type'] == 'date'
192 && $ifs->dateformat != $timedate->get_date_format() )
193 $defaultRowValue = $timedate->swap_formats(
194 $defaultRowValue, $ifs->dateformat, $timedate->get_date_format());
195 if ( $fieldDef['type'] == 'time'
196 && $ifs->timeformat != $timedate->get_time_format() )
197 $defaultRowValue = $timedate->swap_formats(
198 $defaultRowValue, $ifs->timeformat, $timedate->get_time_format());
199 if ( ($fieldDef['type'] == 'datetime' || $fieldDef['type'] == 'datetimecombo')
200 && $ifs->dateformat.' '.$ifs->timeformat != $timedate->get_date_time_format() )
201 $defaultRowValue = $timedate->swap_formats(
202 $defaultRowValue, $ifs->dateformat.' '.$ifs->timeformat,
203 $timedate->get_date_time_format());
204 if ( in_array($fieldDef['type'],array('currency','float','int','num'))
205 && $ifs->num_grp_sep != $current_user->getPreference('num_grp_sep') )
206 $defaultRowValue = str_replace($current_user->getPreference('num_grp_sep'),
207 $ifs->num_grp_sep,$defaultRowValue);
208 if ( in_array($fieldDef['type'],array('currency','float'))
209 && $ifs->dec_sep != $current_user->getPreference('dec_sep') )
210 $defaultRowValue = str_replace($current_user->getPreference('dec_sep'),
211 $ifs->dec_sep,$defaultRowValue);
212 $currency->retrieve('-99');
213 $user_currency_symbol = $currency->symbol;
214 if ( $fieldDef['type'] == 'currency'
215 && $ifs->currency_symbol != $user_currency_symbol )
216 $defaultRowValue = str_replace($user_currency_symbol,
217 $ifs->currency_symbol,$defaultRowValue);
220 if ( empty($rowValue) ) {
221 $rowValue = $defaultRowValue;
222 unset($defaultRowValue);
226 // Bug 22705 - Don't update the First Name or Last Name value if Full Name is set
227 if ( in_array($field, array('first_name','last_name')) && !empty($focus->full_name) )
230 // loop if this value has not been set
231 if ( !isset($rowValue) )
234 // If the field is required and blank then error out
235 if ( array_key_exists($field,$focus->get_import_required_fields())
238 $importFile->writeError(
239 $mod_strings['LBL_REQUIRED_VALUE'],
246 // Handle the special case "Sync to Outlook"
247 if ( $focus->object_name == "Contacts" && $field == 'sync_contact' ) {
248 $bad_names = array();
249 $returnValue = $ifs->synctooutlook(
253 // try the default value on fail
254 if ( !$returnValue && !empty($defaultRowValue) )
255 $returnValue = $ifs->synctooutlook(
259 if ( !$returnValue ) {
260 $importFile->writeError(
261 $mod_strings['LBL_ERROR_SYNC_USERS'],
263 explode(",",$bad_names));
268 // Handle email1 and email2 fields ( these don't have the type of email )
269 if ( $field == 'email1' || $field == 'email2' ) {
270 $returnValue = $ifs->email($rowValue, $fieldDef);
271 // try the default value on fail
272 if ( !$returnValue && !empty($defaultRowValue) )
273 $returnValue = $ifs->email(
276 if ( $returnValue === FALSE ) {
278 $importFile->writeError(
279 $mod_strings['LBL_ERROR_INVALID_EMAIL'],
284 $rowValue = $returnValue;
285 // check for current opt_out and invalid email settings for this email address
286 // if we find any, set them now
287 $emailres = $focus->db->query(
288 "SELECT opt_out, invalid_email FROM email_addresses
289 WHERE email_address = '".$focus->db->quote($rowValue)."'");
290 if ( $emailrow = $focus->db->fetchByAssoc($emailres) ) {
291 $focus->email_opt_out = $emailrow['opt_out'];
292 $focus->invalid_email = $emailrow['invalid_email'];
297 // Handle splitting Full Name into First and Last Name parts
298 if ( $field == 'full_name' && !empty($rowValue) ) {
305 // to maintain 451 compatiblity
306 if(!isset($fieldDef['module']) && $fieldDef['type']=='relate')
307 $fieldDef['module'] = ucfirst($fieldDef['table']);
309 if(isset($fieldDef['custom_type']) && !empty($fieldDef['custom_type']))
310 $fieldDef['type'] = $fieldDef['custom_type'];
312 // If the field is empty then there is no need to check the data
313 if( !empty($rowValue) ) {
314 switch ($fieldDef['type']) {
317 if ( isset($fieldDef['type']) && $fieldDef['type'] == "multienum" )
318 $returnValue = $ifs->multienum($rowValue,$fieldDef);
320 $returnValue = $ifs->enum($rowValue,$fieldDef);
321 // try the default value on fail
322 if ( !$returnValue && !empty($defaultRowValue) )
323 if ( isset($fieldDef['type']) && $fieldDef['type'] == "multienum" )
324 $returnValue = $ifs->multienum($defaultRowValue,$fieldDef);
326 $returnValue = $ifs->enum($defaultRowValue,$fieldDef);
327 if ( $returnValue === FALSE ) {
328 $importFile->writeError(
329 $mod_strings['LBL_ERROR_NOT_IN_ENUM']
330 . implode(",",$app_list_strings[$fieldDef['options']]),
336 $rowValue = $returnValue;
341 $returnValue = $ifs->relate(
345 empty($defaultRowValue));
346 if ( !$returnValue && !empty($defaultRowValue) )
347 $returnValue = $ifs->relate(
351 // Bug 33623 - Set the id value found from the above method call as an importColumn
352 if ( $returnValue !== false )
353 $importColumns[] = $fieldDef['id_name'];
356 $returnValue = $ifs->teamset(
360 $importColumns[] = 'team_set_id';
361 $importColumns[] = 'team_id';
366 if ( method_exists('ImportFieldSanitize',$fieldDef['type']) ) {
367 $fieldtype = $fieldDef['type'];
368 $returnValue = $ifs->$fieldtype($rowValue, $fieldDef);
369 // try the default value on fail
370 if ( !$returnValue && !empty($defaultRowValue) )
371 $returnValue = $ifs->$fieldtype(
374 if ( !$returnValue ) {
376 $importFile->writeError(
377 $mod_strings['LBL_ERROR_INVALID_'.strtoupper($fieldDef['type'])],
382 $rowValue = $returnValue;
386 $focus->$field = $rowValue;
387 unset($defaultRowValue);
390 // Now try to validate flex relate fields
391 if ( isset($focus->field_defs['parent_name'])
392 && isset($focus->parent_name)
393 && ($focus->field_defs['parent_name']['type'] == 'parent') ) {
394 // populate values from the picker widget if the import file doesn't have them
395 $parent_idField = $focus->field_defs['parent_name']['id_name'];
396 if ( empty($focus->$parent_idField) && !empty($_REQUEST[$parent_idField]) )
397 $focus->$parent_idField = $_REQUEST[$parent_idField];
398 $parent_typeField = $focus->field_defs['parent_name']['type_name'];
399 if ( empty($focus->$parent_typeField) && !empty($_REQUEST[$parent_typeField]) )
400 $focus->$parent_typeField = $_REQUEST[$parent_typeField];
402 $returnValue = $ifs->parent(
404 $focus->field_defs['parent_name'],
406 empty($_REQUEST['parent_name']));
407 if ( !$returnValue && !empty($_REQUEST['parent_name']) )
408 $returnValue = $ifs->parent(
409 $_REQUEST['parent_name'],
410 $focus->field_defs['parent_name'],
414 // check to see that the indexes being entered are unique.
415 if (isset($_REQUEST['display_tabs_def']) && $_REQUEST['display_tabs_def'] != ""){
416 $idc = new ImportDuplicateCheck($focus);
417 if ( $idc->isADuplicateRecord(explode('&', $_REQUEST['display_tabs_def'])) ){
418 $importFile->markRowAsDuplicate();
419 $this->_undoCreatedBeans($ifs->createdBeans);
424 // if the id was specified
426 if ( !empty($focus->id) ) {
427 $focus->id = $this->_convertId($focus->id);
429 // check if it already exists
430 $query = "SELECT * FROM {$focus->table_name} WHERE id='".$focus->db->quote($focus->id)."'";
431 $result = $focus->db->query($query)
432 or sugar_die("Error selecting sugarbean: ");
434 $dbrow = $focus->db->fetchByAssoc($result);
436 if (isset ($dbrow['id']) && $dbrow['id'] != -1) {
437 // if it exists but was deleted, just remove it
438 if (isset ($dbrow['deleted']) && $dbrow['deleted'] == 1 && $update_only==false) {
439 $query2 = "DELETE FROM {$focus->table_name} WHERE id='".$focus->db->quote($focus->id)."'";
440 $result2 = $focus->db->query($query2) or sugar_die($mod_strings['LBL_ERROR_DELETING_RECORD']." ".$focus->id);
441 if ($focus->hasCustomFields()) {
442 $query3 = "DELETE FROM {$focus->table_name}_cstm WHERE id_c='".$focus->db->quote($focus->id)."'";
443 $result2 = $focus->db->query($query3);
445 $focus->new_with_id = true;
448 if( !$update_only ) {
450 $importFile->writeError($mod_strings['LBL_ID_EXISTS_ALREADY'],'ID',$focus->id);
451 $this->_undoCreatedBeans($ifs->createdBeans);
454 $existing_focus = loadImportBean($_REQUEST['import_module']);
456 if ( !( $existing_focus->retrieve($dbrow['id']) instanceOf SugarBean ) ) {
458 $importFile->writeError($mod_strings['LBL_RECORD_CANNOT_BE_UPDATED'],'ID',$focus->id);
459 $this->_undoCreatedBeans($ifs->createdBeans);
463 $newData = $focus->toArray();
464 foreach ( $newData as $focus_key => $focus_value )
465 if ( in_array($focus_key,$importColumns) )
466 $existing_focus->$focus_key = $focus_value;
468 $focus = $existing_focus;
470 unset($existing_focus);
474 $focus->new_with_id = true;
479 // Populate in any default values to the bean
480 $focus->populateDefaultValues();
482 if ( !isset($focus->assigned_user_id) || $focus->assigned_user_id == '' && $newRecord ) {
483 $focus->assigned_user_id = $current_user->id;
486 * Bug 34854: Added all conditions besides the empty check on date modified. Currently, if
487 * we do an update to a record, it doesn't update the date_modified value.
488 * Hack note: I'm doing a to_display and back to_db on the fetched row to make sure that any truncating that happens
489 * when $focus->date_modified goes to_display and back to_db also happens on the fetched db value. Otherwise,
490 * in some cases we truncate the seconds on one and not the other, and the comparison fails when it should pass
492 if ( ( !empty($focus->new_with_id) && !empty($focus->date_modified) ) ||
493 ( empty($focus->new_with_id) && $timedate->to_db($focus->date_modified) != $timedate->to_db($timedate->to_display_date_time($focus->fetched_row['date_modified'])) )
495 $focus->update_date_modified = false;
497 $focus->optimistic_lock = false;
498 if ( $focus->object_name == "Contacts" && isset($focus->sync_contact) ) {
499 //copy the potential sync list to another varible
500 $list_of_users=$focus->sync_contact;
501 //and set it to false for the save
502 $focus->sync_contact=false;
503 } else if($focus->object_name == "User" && !empty($current_user) && $focus->is_admin && !is_admin($current_user) && is_admin_for_module($current_user, 'Users')) {
504 sugar_die($GLOBALS['mod_strings']['ERR_IMPORT_SYSTEM_ADMININSTRATOR']);
506 //bug# 40260 setting it true as the module in focus is involved in an import
507 $focus->in_import=true;
508 // call any logic needed for the module preSave
509 $focus->beforeImportSave();
514 // call any logic needed for the module postSave
515 $focus->afterImportSave();
517 if ( $focus->object_name == "Contacts" && isset($list_of_users) )
518 $focus->process_sync_to_outlook($list_of_users);
520 // Update the created/updated counter
521 $importFile->markRowAsImported($newRecord);
523 // Add ID to User's Last Import records
525 ImportFile::writeRowToLastImport(
526 $_REQUEST['import_module'],
527 ($focus->object_name == 'Case' ? 'aCase' : $focus->object_name),
531 $this->_undoCreatedBeans($ifs->createdBeans);
534 // save mapping if requested
535 if ( isset($_REQUEST['save_map_as']) && $_REQUEST['save_map_as'] != '' ) {
536 $mapping_file = new ImportMap();
537 if ( isset($_REQUEST['has_header']) && $_REQUEST['has_header'] == 'on') {
538 $header_to_field = array ();
539 foreach ($importColumns as $pos => $field_name) {
540 if (isset($firstrow[$pos]) && isset($field_name)) {
541 $header_to_field[$firstrow[$pos]] = $field_name;
544 $mapping_file->setMapping($header_to_field);
547 $mapping_file->setMapping($importColumns);
550 // save default fields
551 $defaultValues = array();
552 for ( $i = 0; $i < $_REQUEST['columncount']; $i++ )
554 if (isset($importColumns[$i]) && !empty($_REQUEST[$importColumns[$i]])) {
555 $field = $importColumns[$i];
556 $fieldDef = $focus->getFieldDefinition($field);
557 if(!empty($fieldDef['custom_type']) && $fieldDef['custom_type'] == 'teamset') {
558 require_once('include/SugarFields/Fields/Teamset/SugarFieldTeamset.php');
559 $sugar_field = new SugarFieldTeamset('Teamset');
560 $teams = $sugar_field->getTeamsFromRequest($field);
561 if(isset($_REQUEST['primary_team_name_collection'])) {
562 $primary_index = $_REQUEST['primary_team_name_collection'];
565 //If primary_index was selected, ensure that the first Array entry is the primary team
566 if(isset($primary_index)) {
568 $new_teams = array();
569 foreach($teams as $id=>$name) {
570 if($primary_index == $count++) {
571 $new_teams[$id] = $name;
577 foreach($teams as $id=>$name) {
578 $new_teams[$id] = $name;
583 $json = getJSONobj();
584 $defaultValues[$field] = $json->encode($teams);
586 $defaultValues[$field] = $_REQUEST[$importColumns[$i]];
590 $mapping_file->setDefaultValues($defaultValues);
591 $result = $mapping_file->save(
593 $_REQUEST['save_map_as'],
594 $_REQUEST['import_module'],
596 ( isset($_REQUEST['has_header']) && $_REQUEST['has_header'] == 'on'),
597 $_REQUEST['custom_delimiter'],
598 html_entity_decode($_REQUEST['custom_enclosure'],ENT_QUOTES)
602 $importFile->writeStatus();
606 * If a bean save is not done for some reason, this method will undo any of the beans that were created
608 * @param array $ids ids of user_last_import records created
610 protected function _undoCreatedBeans(
614 $focus = new UsersLastImport();
615 foreach ($ids as $id)
616 $focus->undoById($id);
620 * clean id's when being imported
622 * @param string $string
625 protected function _convertId(
629 return preg_replace_callback(
632 // single quotes are essential here,
633 // or alternative escape all $ as \$
635 'return ord($matches[0]);'