]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Import/Importer.php
Release 6.5.15
[Github/sugarcrm.git] / modules / Import / Importer.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
4 /*********************************************************************************
5  * SugarCRM Community Edition is a customer relationship management program developed by
6  * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
7  * 
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.
14  * 
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
18  * details.
19  * 
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
23  * 02110-1301 USA.
24  * 
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.
27  * 
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.
31  * 
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  ********************************************************************************/
38
39
40 require_once('modules/Import/ImportCacheFiles.php');
41 require_once('modules/Import/ImportFieldSanitize.php');
42 require_once('modules/Import/ImportDuplicateCheck.php');
43
44
45 class Importer
46 {
47     /**
48      * @var ImportFieldSanitizer
49      */
50     protected $ifs;
51
52     /**
53      * @var Currency
54      */
55     protected $defaultUserCurrency;
56
57     /**
58      * @var importColumns
59      */
60     protected $importColumns;
61
62     /**
63      * @var importSource
64      */
65     protected $importSource;
66
67     /**
68      * @var $isUpdateOnly
69      */
70     protected $isUpdateOnly;
71
72     /**
73      * @var  $bean
74      */
75     protected $bean;
76
77     /**
78      * @var sugarToExternalSourceFieldMap
79      */
80     protected $sugarToExternalSourceFieldMap = array();
81
82
83     public function __construct($importSource, $bean)
84     {
85         global $mod_strings, $sugar_config;
86
87         $this->importSource = $importSource;
88
89         //Vanilla copy of the bean object.
90         $this->bean = $bean;
91
92         // use our own error handler
93         set_error_handler(array('Importer','handleImportErrors'),E_ALL);
94
95          // Increase the max_execution_time since this step can take awhile
96         ini_set("max_execution_time", max($sugar_config['import_max_execution_time'],3600));
97
98         // stop the tracker
99         TrackerManager::getInstance()->pause();
100
101         // set the default locale settings
102         $this->ifs = $this->getFieldSanitizer();
103
104         //Get the default user currency
105         $this->defaultUserCurrency = new Currency();
106         $this->defaultUserCurrency->retrieve('-99');
107
108         //Get our import column definitions
109         $this->importColumns = $this->getImportColumns();
110         $this->isUpdateOnly = ( isset($_REQUEST['import_type']) && $_REQUEST['import_type'] == 'update' );
111     }
112
113     public function import()
114     {
115         foreach($this->importSource as $row)
116         {
117             $this->importRow($row);
118         }
119
120         // save mapping if requested
121         if ( isset($_REQUEST['save_map_as']) && $_REQUEST['save_map_as'] != '' )
122         {
123             $this->saveMappingFile();
124         }
125
126         $this->importSource->writeStatus();
127
128         //All done, remove file.
129     }
130
131
132     protected function importRow($row)
133     {
134         global $sugar_config, $mod_strings, $current_user;
135
136         $focus = clone $this->bean;
137         $focus->unPopulateDefaultValues();
138         $focus->save_from_post = false;
139         $focus->team_id = null;
140         $this->ifs->createdBeans = array();
141         $this->importSource->resetRowErrorCounter();
142         $do_save = true;
143
144         for ( $fieldNum = 0; $fieldNum < $_REQUEST['columncount']; $fieldNum++ )
145         {
146             // loop if this column isn't set
147             if ( !isset($this->importColumns[$fieldNum]) )
148                 continue;
149
150             // get this field's properties
151             $field           = $this->importColumns[$fieldNum];
152             $fieldDef        = $focus->getFieldDefinition($field);
153             $fieldTranslated = translate((isset($fieldDef['vname'])?$fieldDef['vname']:$fieldDef['name']), $focus->module_dir)." (".$fieldDef['name'].")";
154             $defaultRowValue = '';
155             // Bug 37241 - Don't re-import over a field we already set during the importing of another field
156             if ( !empty($focus->$field) )
157                 continue;
158
159             // translate strings
160             global $locale;
161             if(empty($locale))
162             {
163                 $locale = new Localization();
164             }
165             if ( isset($row[$fieldNum]) )
166             {
167                 $rowValue = $locale->translateCharset(strip_tags(trim($row[$fieldNum])),$this->importSource->importlocale_charset,$sugar_config['default_charset']);
168             }
169             else if( isset($this->sugarToExternalSourceFieldMap[$field]) && isset($row[$this->sugarToExternalSourceFieldMap[$field]]) )
170             {
171                 $rowValue = $locale->translateCharset(strip_tags(trim($row[$this->sugarToExternalSourceFieldMap[$field]])),$this->importSource->importlocale_charset,$sugar_config['default_charset']);
172             }
173             else
174             {
175                 $rowValue = '';
176             }
177
178             // If there is an default value then use it instead
179             if ( !empty($_REQUEST[$field]) )
180             {
181                 $defaultRowValue = $this->populateDefaultMapValue($field, $_REQUEST[$field], $fieldDef);
182
183
184                 if( empty($rowValue))
185                 {
186                     $rowValue = $defaultRowValue;
187                     //reset the default value to empty
188                     $defaultRowValue='';
189                 }
190             }
191
192             // Bug 22705 - Don't update the First Name or Last Name value if Full Name is set
193             if ( in_array($field, array('first_name','last_name')) && !empty($focus->full_name) )
194                 continue;
195
196             // loop if this value has not been set
197             if ( !isset($rowValue) )
198                 continue;
199
200             // If the field is required and blank then error out
201             if ( array_key_exists($field,$focus->get_import_required_fields()) && empty($rowValue) && $rowValue!='0')
202             {
203                 $this->importSource->writeError( $mod_strings['LBL_REQUIRED_VALUE'],$fieldTranslated,'NULL');
204                 $do_save = false;
205             }
206
207             // Handle the special case "Sync to Outlook"
208             if ( $focus->object_name == "Contact" && $field == 'sync_contact' )
209             {
210                 /**
211                  * Bug #41194 : if true used as value of sync_contact - add curent user to list to sync
212                  */
213                 if ( true == $rowValue || 'true' == strtolower($rowValue)) {
214                     $focus->sync_contact = $focus->id;
215                 } elseif (false == $rowValue || 'false' == strtolower($rowValue)) {
216                     $focus->sync_contact = '';
217                 } else {
218                     $bad_names = array();
219                     $returnValue = $this->ifs->synctooutlook($rowValue,$fieldDef,$bad_names);
220                     // try the default value on fail
221                     if ( !$returnValue && !empty($defaultRowValue) )
222                         $returnValue = $this->ifs->synctooutlook($defaultRowValue, $fieldDef, $bad_names);
223                     if ( !$returnValue )
224                     {
225                         $this->importSource->writeError($mod_strings['LBL_ERROR_SYNC_USERS'], $fieldTranslated, $bad_names);
226                         $do_save = 0;
227                     } else {
228                         $focus->sync_contact = $returnValue;
229                     }
230                 }
231             }
232
233             // Handle email field, if it's a semi-colon separated export
234             if ($field == 'email_addresses_non_primary' && !empty($rowValue))
235             {
236                 if (strpos($rowValue, ';') !== false)
237                 {
238                     $rowValue = explode(';', $rowValue);
239                 }
240                 else
241                 {
242                     $rowValue = array($rowValue);
243                 }
244             }
245
246             // Handle email1 and email2 fields ( these don't have the type of email )
247             if ( $field == 'email1' || $field == 'email2' )
248             {
249                 $returnValue = $this->ifs->email($rowValue, $fieldDef, $focus);
250                 // try the default value on fail
251                 if ( !$returnValue && !empty($defaultRowValue) )
252                     $returnValue = $this->ifs->email( $defaultRowValue, $fieldDef);
253                 if ( $returnValue === FALSE )
254                 {
255                     $do_save=0;
256                     $this->importSource->writeError( $mod_strings['LBL_ERROR_INVALID_EMAIL'], $fieldTranslated, $rowValue);
257                 }
258                 else
259                 {
260                     $rowValue = $returnValue;
261                     // check for current opt_out and invalid email settings for this email address
262                     // if we find any, set them now
263                     $emailres = $focus->db->query( "SELECT opt_out, invalid_email FROM email_addresses WHERE email_address = '".$focus->db->quote($rowValue)."'");
264                     if ( $emailrow = $focus->db->fetchByAssoc($emailres) )
265                     {
266                         $focus->email_opt_out = $emailrow['opt_out'];
267                         $focus->invalid_email = $emailrow['invalid_email'];
268                     }
269                 }
270             }
271
272             // Handle splitting Full Name into First and Last Name parts
273             if ( $field == 'full_name' && !empty($rowValue) )
274             {
275                 $this->ifs->fullname($rowValue,$fieldDef,$focus);
276             }
277
278             // to maintain 451 compatiblity
279             if(!isset($fieldDef['module']) && $fieldDef['type']=='relate')
280                 $fieldDef['module'] = ucfirst($fieldDef['table']);
281
282             if(isset($fieldDef['custom_type']) && !empty($fieldDef['custom_type']))
283                 $fieldDef['type'] = $fieldDef['custom_type'];
284
285             // If the field is empty then there is no need to check the data
286             if( !empty($rowValue) )
287             {
288                 // If it's an array of non-primary e-mails, check each mail
289                 if ($field == "email_addresses_non_primary" && is_array($rowValue))
290                 {
291                     foreach ($rowValue as $tempRow)
292                     {
293                         $tempRow = $this->sanitizeFieldValueByType($tempRow, $fieldDef, $defaultRowValue, $focus, $fieldTranslated);
294                         if ($tempRow === FALSE)
295                         {
296                             $rowValue = false;
297                             $do_save = false;
298                             break;
299                         }
300                     }
301                 }
302                 else
303                 {
304                     $rowValue = $this->sanitizeFieldValueByType($rowValue, $fieldDef, $defaultRowValue, $focus, $fieldTranslated);
305                 }
306
307                 if ($rowValue === false)
308                 {
309                                         /* BUG 51213 - jeff @ neposystems.com */
310                     $do_save = false;
311                     continue;
312                                 }
313             }
314
315             // if the parent type is in singular form, get the real module name for parent_type
316             if (isset($fieldDef['type']) && $fieldDef['type']=='parent_type') {
317                 $rowValue = get_module_from_singular($rowValue);
318             }
319
320             $focus->$field = $rowValue;
321             unset($defaultRowValue);
322         }
323
324         // Now try to validate flex relate fields
325         if ( isset($focus->field_defs['parent_name']) && isset($focus->parent_name) && ($focus->field_defs['parent_name']['type'] == 'parent') )
326         {
327             // populate values from the picker widget if the import file doesn't have them
328             $parent_idField = $focus->field_defs['parent_name']['id_name'];
329             if ( empty($focus->$parent_idField) && !empty($_REQUEST[$parent_idField]) )
330                 $focus->$parent_idField = $_REQUEST[$parent_idField];
331
332             $parent_typeField = $focus->field_defs['parent_name']['type_name'];
333
334             if ( empty($focus->$parent_typeField) && !empty($_REQUEST[$parent_typeField]) )
335                 $focus->$parent_typeField = $_REQUEST[$parent_typeField];
336             // now validate it
337             $returnValue = $this->ifs->parent($focus->parent_name,$focus->field_defs['parent_name'],$focus, empty($_REQUEST['parent_name']));
338             if ( !$returnValue && !empty($_REQUEST['parent_name']) )
339                 $returnValue = $this->ifs->parent( $_REQUEST['parent_name'],$focus->field_defs['parent_name'], $focus);
340         }
341
342         // check to see that the indexes being entered are unique.
343         if (isset($_REQUEST['enabled_dupes']) && $_REQUEST['enabled_dupes'] != "")
344         {
345             $toDecode = html_entity_decode  ($_REQUEST['enabled_dupes'], ENT_QUOTES);
346             $enabled_dupes = json_decode($toDecode);
347             $idc = new ImportDuplicateCheck($focus);
348
349             if ( $idc->isADuplicateRecord($enabled_dupes) )
350             {
351                 $this->importSource->markRowAsDuplicate($idc->_dupedFields);
352                 $this->_undoCreatedBeans($this->ifs->createdBeans);
353                 return;
354             }
355         }
356         //Allow fields to be passed in for dup check as well (used by external adapters)
357         else if( !empty($_REQUEST['enabled_dup_fields']) )
358         {
359             $toDecode = html_entity_decode  ($_REQUEST['enabled_dup_fields'], ENT_QUOTES);
360             $enabled_dup_fields = json_decode($toDecode);
361             $idc = new ImportDuplicateCheck($focus);
362             if ( $idc->isADuplicateRecordByFields($enabled_dup_fields) )
363             {
364                 $this->importSource->markRowAsDuplicate($idc->_dupedFields);
365                 $this->_undoCreatedBeans($this->ifs->createdBeans);
366                 return;
367             }
368         }
369
370         // if the id was specified
371         $newRecord = true;
372         if ( !empty($focus->id) )
373         {
374             $focus->id = $this->_convertId($focus->id);
375
376             // check if it already exists
377             $query = "SELECT * FROM {$focus->table_name} WHERE id='".$focus->db->quote($focus->id)."'";
378             $result = $focus->db->query($query)
379             or sugar_die("Error selecting sugarbean: ");
380
381             $dbrow = $focus->db->fetchByAssoc($result);
382
383             if (isset ($dbrow['id']) && $dbrow['id'] != -1)
384             {
385                 // if it exists but was deleted, just remove it
386                 if (isset ($dbrow['deleted']) && $dbrow['deleted'] == 1 && $this->isUpdateOnly ==false)
387                 {
388                     $this->removeDeletedBean($focus);
389                     $focus->new_with_id = true;
390                 }
391                 else
392                 {
393                     if( ! $this->isUpdateOnly )
394                     {
395                         $this->importSource->writeError($mod_strings['LBL_ID_EXISTS_ALREADY'],'ID',$focus->id);
396                         $this->_undoCreatedBeans($this->ifs->createdBeans);
397                         return;
398                     }
399
400                     $clonedBean = $this->cloneExistingBean($focus);
401                     if($clonedBean === FALSE)
402                     {
403                         $this->importSource->writeError($mod_strings['LBL_RECORD_CANNOT_BE_UPDATED'],'ID',$focus->id);
404                         $this->_undoCreatedBeans($this->ifs->createdBeans);
405                         return;
406                     }
407                     else
408                     {
409                         $focus = $clonedBean;
410                         $newRecord = FALSE;
411                     }
412                 }
413             }
414             else
415             {
416                 $focus->new_with_id = true;
417             }
418         }
419
420         if ($do_save)
421         {
422             $this->saveImportBean($focus, $newRecord);
423             // Update the created/updated counter
424             $this->importSource->markRowAsImported($newRecord);
425         }
426         else
427             $this->_undoCreatedBeans($this->ifs->createdBeans);
428
429         unset($defaultRowValue);
430
431     }
432
433
434     protected function sanitizeFieldValueByType($rowValue, $fieldDef, $defaultRowValue, $focus, $fieldTranslated)
435     {
436         global $mod_strings, $app_list_strings;
437         switch ($fieldDef['type'])
438         {
439             case 'enum':
440             case 'multienum':
441                 if ( isset($fieldDef['type']) && $fieldDef['type'] == "multienum" )
442                     $returnValue = $this->ifs->multienum($rowValue,$fieldDef);
443                 else
444                     $returnValue = $this->ifs->enum($rowValue,$fieldDef);
445                 // try the default value on fail
446                 if ( !$returnValue && !empty($defaultRowValue) )
447                 {
448                     if ( isset($fieldDef['type']) && $fieldDef['type'] == "multienum" )
449                         $returnValue = $this->ifs->multienum($defaultRowValue,$fieldDef);
450                     else
451                         $returnValue = $this->ifs->enum($defaultRowValue,$fieldDef);
452                 }
453                 if ( $returnValue === FALSE )
454                 {
455                     $this->importSource->writeError($mod_strings['LBL_ERROR_NOT_IN_ENUM'] . implode(",",$app_list_strings[$fieldDef['options']]), $fieldTranslated,$rowValue);
456                     return FALSE;
457                 }
458                 else
459                     return $returnValue;
460
461                 break;
462             case 'relate':
463             case 'parent':
464                 $returnValue = $this->ifs->relate($rowValue, $fieldDef, $focus, empty($defaultRowValue));
465                 if (!$returnValue && !empty($defaultRowValue))
466                     $returnValue = $this->ifs->relate($defaultRowValue,$fieldDef, $focus);
467                 // Bug 33623 - Set the id value found from the above method call as an importColumn
468                 if ($returnValue !== false)
469                     $this->importColumns[] = $fieldDef['id_name'];
470                 return $rowValue;
471                 break;
472             case 'teamset':
473                 $this->ifs->teamset($rowValue,$fieldDef,$focus);
474                 $this->importColumns[] = 'team_set_id';
475                 $this->importColumns[] = 'team_id';
476                 return $rowValue;
477                 break;
478             case 'fullname':
479                 return $rowValue;
480                 break;
481             default:
482                 $fieldtype = $fieldDef['type'];
483                 $returnValue = $this->ifs->$fieldtype($rowValue, $fieldDef, $focus);
484                 // try the default value on fail
485                 if ( !$returnValue && !empty($defaultRowValue) )
486                     $returnValue = $this->ifs->$fieldtype($defaultRowValue,$fieldDef, $focus);
487                 if ( !$returnValue )
488                 {
489                     $this->importSource->writeError($mod_strings['LBL_ERROR_INVALID_'.strtoupper($fieldDef['type'])],$fieldTranslated,$rowValue,$focus);
490                     return FALSE;
491                 }
492                 return $returnValue;
493         }
494     }
495
496     protected function cloneExistingBean($focus)
497     {
498         $existing_focus = clone $this->bean;
499         if ( !( $existing_focus->retrieve($focus->id) instanceOf SugarBean ) )
500         {
501             return FALSE;
502         }
503         else
504         {
505             $newData = $focus->toArray();
506             foreach ( $newData as $focus_key => $focus_value )
507                 if ( in_array($focus_key,$this->importColumns) )
508                     $existing_focus->$focus_key = $focus_value;
509
510             return $existing_focus;
511         }
512     }
513
514     protected function removeDeletedBean($focus)
515     {
516         global $mod_strings;
517
518         $query2 = "DELETE FROM {$focus->table_name} WHERE id='".$focus->db->quote($focus->id)."'";
519         $result2 = $focus->db->query($query2) or sugar_die($mod_strings['LBL_ERROR_DELETING_RECORD']." ".$focus->id);
520         if ($focus->hasCustomFields())
521         {
522             $query3 = "DELETE FROM {$focus->table_name}_cstm WHERE id_c='".$focus->db->quote($focus->id)."'";
523             $result2 = $focus->db->query($query3);
524         }
525     }
526
527     protected function saveImportBean($focus, $newRecord)
528     {
529         global $timedate, $current_user;
530
531         // Populate in any default values to the bean
532         $focus->populateDefaultValues();
533
534         if ( !isset($focus->assigned_user_id) || $focus->assigned_user_id == '' && $newRecord )
535         {
536             $focus->assigned_user_id = $current_user->id;
537         }
538         /*
539         * Bug 34854: Added all conditions besides the empty check on date modified.
540         */
541         if ( ( !empty($focus->new_with_id) && !empty($focus->date_modified) ) ||
542              ( empty($focus->new_with_id) && $timedate->to_db($focus->date_modified) != $timedate->to_db($timedate->to_display_date_time($focus->fetched_row['date_modified'])) )
543         ) 
544             $focus->update_date_modified = false;
545
546         // Bug 53636 - Allow update of "Date Created"
547         if (!empty($focus->date_entered)) {
548                 $focus->update_date_entered = true;
549         }
550             
551         $focus->optimistic_lock = false;
552         if ( $focus->object_name == "Contact" && isset($focus->sync_contact) )
553         {
554             //copy the potential sync list to another varible
555             $list_of_users=$focus->sync_contact;
556             //and set it to false for the save
557             $focus->sync_contact=false;
558         }
559         else if($focus->object_name == "User" && !empty($current_user) && $focus->is_admin && !is_admin($current_user) && is_admin_for_module($current_user, 'Users')) {
560             sugar_die($GLOBALS['mod_strings']['ERR_IMPORT_SYSTEM_ADMININSTRATOR']);
561         }
562         //bug# 46411 importing Calls will not populate Leads or Contacts Subpanel
563         if (!empty($focus->parent_type) && !empty($focus->parent_id))
564         {
565             foreach ($focus->relationship_fields as $key => $val)
566             {
567                 if ($val == strtolower($focus->parent_type))
568                 {
569                     $focus->$key = $focus->parent_id;
570                 }
571             }
572         }                                       
573         //bug# 40260 setting it true as the module in focus is involved in an import
574         $focus->in_import=true;
575         // call any logic needed for the module preSave
576         $focus->beforeImportSave();
577
578         // Bug51192: check if there are any changes in the imported data
579         $hasDataChanges = false;
580         $dataChanges=$focus->db->getAuditDataChanges($focus);
581         
582         if(!empty($dataChanges)) {
583             foreach($dataChanges as $field=>$fieldData) {
584                 if($fieldData['data_type'] != 'date' || strtotime($fieldData['before']) != strtotime($fieldData['after'])) {
585                     $hasDataChanges = true;
586                     break;
587                 }
588             }
589         }
590         
591         // if modified_user_id is set, set the flag to false so SugarBEan will not reset it
592         if (isset($focus->modified_user_id) && $focus->modified_user_id && !$hasDataChanges) {
593             $focus->update_modified_by = false;
594         }
595         // if created_by is set, set the flag to false so SugarBEan will not reset it
596         if (isset($focus->created_by) && $focus->created_by) {
597             $focus->set_created_by = false;
598         }
599
600         if ( $focus->object_name == "Contact" && isset($list_of_users) )
601             $focus->process_sync_to_outlook($list_of_users);
602
603         $focus->save(false);
604
605         //now that save is done, let's make sure that parent and related id's were saved as relationships
606         //this takes place before the afterImportSave()
607         $this->checkRelatedIDsAfterSave($focus);
608
609         // call any logic needed for the module postSave
610         $focus->afterImportSave();
611
612         // Add ID to User's Last Import records
613         if ( $newRecord )
614             $this->importSource->writeRowToLastImport( $_REQUEST['import_module'],($focus->object_name == 'Case' ? 'aCase' : $focus->object_name),$focus->id);
615
616     }
617
618     protected function saveMappingFile()
619     {
620         global $current_user;
621
622         $firstrow    = unserialize(base64_decode($_REQUEST['firstrow']));
623         $mappingValsArr = $this->importColumns;
624         $mapping_file = new ImportMap();
625         if ( isset($_REQUEST['has_header']) && $_REQUEST['has_header'] == 'on')
626         {
627             $header_to_field = array ();
628             foreach ($this->importColumns as $pos => $field_name)
629             {
630                 if (isset($firstrow[$pos]) && isset($field_name))
631                 {
632                     $header_to_field[$firstrow[$pos]] = $field_name;
633                 }
634             }
635
636             $mappingValsArr = $header_to_field;
637         }
638         //get array of values to save for duplicate and locale settings
639         $advMapping = $this->retrieveAdvancedMapping();
640
641         //merge with mappingVals array
642         if(!empty($advMapping) && is_array($advMapping))
643         {
644             $mappingValsArr = $advMapping + $mappingValsArr;
645         }
646
647         //set mapping
648         $mapping_file->setMapping($mappingValsArr);
649
650         // save default fields
651         $defaultValues = array();
652         for ( $i = 0; $i < $_REQUEST['columncount']; $i++ )
653         {
654             if (isset($this->importColumns[$i]) && !empty($_REQUEST[$this->importColumns[$i]]))
655             {
656                 $field = $this->importColumns[$i];
657                 $fieldDef = $this->bean->getFieldDefinition($field);
658                 if(!empty($fieldDef['custom_type']) && $fieldDef['custom_type'] == 'teamset')
659                 {
660                     require_once('include/SugarFields/Fields/Teamset/SugarFieldTeamset.php');
661                     $sugar_field = new SugarFieldTeamset('Teamset');
662                     $teams = $sugar_field->getTeamsFromRequest($field);
663                     if(isset($_REQUEST['primary_team_name_collection']))
664                     {
665                         $primary_index = $_REQUEST['primary_team_name_collection'];
666                     }
667
668                     //If primary_index was selected, ensure that the first Array entry is the primary team
669                     if(isset($primary_index))
670                     {
671                         $count = 0;
672                         $new_teams = array();
673                         foreach($teams as $id=>$name)
674                         {
675                             if($primary_index == $count++)
676                             {
677                                 $new_teams[$id] = $name;
678                                 unset($teams[$id]);
679                                 break;
680                             }
681                         }
682
683                         foreach($teams as $id=>$name)
684                         {
685                             $new_teams[$id] = $name;
686                         }
687                         $teams = $new_teams;
688                     } //if
689
690                     $json = getJSONobj();
691                     $defaultValues[$field] = $json->encode($teams);
692                 }
693                 else
694                 {
695                     $defaultValues[$field] = $_REQUEST[$this->importColumns[$i]];
696                 }
697             }
698         }
699         $mapping_file->setDefaultValues($defaultValues);
700         $result = $mapping_file->save( $current_user->id,  $_REQUEST['save_map_as'], $_REQUEST['import_module'], $_REQUEST['source'],
701             ( isset($_REQUEST['has_header']) && $_REQUEST['has_header'] == 'on'), $_REQUEST['custom_delimiter'], html_entity_decode($_REQUEST['custom_enclosure'],ENT_QUOTES)
702         );
703     }
704
705
706     protected function populateDefaultMapValue($field, $fieldValue, $fieldDef)
707     {
708         global $timedate, $current_user;
709
710         if ( is_array($fieldValue) )
711             $defaultRowValue = encodeMultienumValue($fieldValue);
712         else
713             $defaultRowValue = $_REQUEST[$field];
714         // translate default values to the date/time format for the import file
715         if( $fieldDef['type'] == 'date' && $this->ifs->dateformat != $timedate->get_date_format() )
716             $defaultRowValue = $timedate->swap_formats($defaultRowValue, $this->ifs->dateformat, $timedate->get_date_format());
717
718         if( $fieldDef['type'] == 'time' && $this->ifs->timeformat != $timedate->get_time_format() )
719             $defaultRowValue = $timedate->swap_formats($defaultRowValue, $this->ifs->timeformat, $timedate->get_time_format());
720
721         if( ($fieldDef['type'] == 'datetime' || $fieldDef['type'] == 'datetimecombo') && $this->ifs->dateformat.' '.$this->ifs->timeformat != $timedate->get_date_time_format() )
722             $defaultRowValue = $timedate->swap_formats($defaultRowValue, $this->ifs->dateformat.' '.$this->ifs->timeformat,$timedate->get_date_time_format());
723
724         if ( in_array($fieldDef['type'],array('currency','float','int','num')) && $this->ifs->num_grp_sep != $current_user->getPreference('num_grp_sep') )
725             $defaultRowValue = str_replace($current_user->getPreference('num_grp_sep'), $this->ifs->num_grp_sep,$defaultRowValue);
726
727         if ( in_array($fieldDef['type'],array('currency','float')) && $this->ifs->dec_sep != $current_user->getPreference('dec_sep') )
728             $defaultRowValue = str_replace($current_user->getPreference('dec_sep'), $this->ifs->dec_sep,$defaultRowValue);
729
730         $user_currency_symbol = $this->defaultUserCurrency->symbol;
731         if ( $fieldDef['type'] == 'currency' && $this->ifs->currency_symbol != $user_currency_symbol )
732             $defaultRowValue = str_replace($user_currency_symbol, $this->ifs->currency_symbol,$defaultRowValue);
733
734         return $defaultRowValue;
735     }
736
737     protected function getImportColumns()
738     {
739         $importable_fields = $this->bean->get_importable_fields();
740         $importColumns = array();
741         foreach ($_REQUEST as $name => $value)
742         {
743             // only look for var names that start with "fieldNum"
744             if (strncasecmp($name, "colnum_", 7) != 0)
745                 continue;
746
747             // pull out the column position for this field name
748             $pos = substr($name, 7);
749
750             if ( isset($importable_fields[$value]) )
751             {
752                 // now mark that we've seen this field
753                 $importColumns[$pos] = $value;
754             }
755         }
756
757         return $importColumns;
758     }
759
760     protected function getFieldSanitizer()
761     {
762         $ifs = new ImportFieldSanitize();
763         $copyFields = array('dateformat','timeformat','timezone','default_currency_significant_digits','num_grp_sep','dec_sep','default_locale_name_format');
764         foreach($copyFields as $field)
765         {
766             $fieldKey = "importlocale_$field";
767             $ifs->$field = $this->importSource->$fieldKey;
768         }
769
770         $currency = new Currency();
771         $currency->retrieve($this->importSource->importlocale_currency);
772         $ifs->currency_symbol = $currency->symbol;
773
774         return $ifs;
775     }
776
777     /**
778      * Sets a translation map from sugar field key to external source key used while importing a row.  This allows external sources
779      * to return a data set that is an associative array rather than numerically indexed.
780      *
781      * @param  $translator
782      * @return void
783      */
784     public function setFieldKeyTranslator($translator)
785     {
786         $this->sugarToExternalSourceFieldMap = $translator;
787     }
788
789     /**
790      * If a bean save is not done for some reason, this method will undo any of the beans that were created
791      *
792      * @param array $ids ids of user_last_import records created
793      */
794     protected function _undoCreatedBeans( array $ids )
795     {
796         $focus = new UsersLastImport();
797         foreach ($ids as $id)
798             $focus->undoById($id);
799     }
800
801     /**
802      * clean id's when being imported
803      *
804      * @param  string $string
805      * @return string
806      */
807     protected function _convertId($string)
808     {
809         return preg_replace_callback(
810             '|[^A-Za-z0-9\-\_]|',
811             create_function(
812             // single quotes are essential here,
813             // or alternative escape all $ as \$
814             '$matches',
815             'return ord($matches[0]);'
816                  ) ,
817             $string);
818     }
819
820     public function retrieveAdvancedMapping()
821     {
822         $advancedMappingSettings = array();
823
824         //harvest the dupe index settings
825         if( isset($_REQUEST['enabled_dupes']) )
826         {
827             $toDecode = html_entity_decode  ($_REQUEST['enabled_dupes'], ENT_QUOTES);
828             $dupe_ind = json_decode($toDecode);
829
830             foreach($dupe_ind as $dupe)
831             {
832                 $advancedMappingSettings['dupe_'.$dupe] = $dupe;
833             }
834         }
835
836         foreach($_REQUEST as $rk=>$rv)
837         {
838             //harvest the import locale settings
839             if(strpos($rk,'portlocale_')>0)
840             {
841                 $advancedMappingSettings[$rk] = $rv;
842             }
843
844         }
845         return $advancedMappingSettings;
846     }
847
848     public static function getImportableModules()
849     {
850         global $beanList;
851         $importableModules = array();
852         foreach ($beanList as $moduleName => $beanName)
853         {
854             if( class_exists($beanName) )
855             {
856                 $tmp = new $beanName();
857                 if( isset($tmp->importable) && $tmp->importable )
858                 {
859                     $label = isset($GLOBALS['app_list_strings']['moduleList'][$moduleName]) ? $GLOBALS['app_list_strings']['moduleList'][$moduleName] : $moduleName;
860                     $importableModules[$moduleName] = $label;
861                 }
862             }
863         }
864
865         asort($importableModules);
866         return $importableModules;
867     }
868
869
870     /**
871      * Replaces PHP error handler in Step4
872      *
873      * @param int    $errno
874      * @param string $errstr
875      * @param string $errfile
876      * @param string $errline
877      */
878     public static function handleImportErrors($errno, $errstr, $errfile, $errline)
879     {
880         $GLOBALS['log']->fatal("Caught error: $errstr");
881
882         if ( !defined('E_DEPRECATED') )
883             define('E_DEPRECATED','8192');
884         if ( !defined('E_USER_DEPRECATED') )
885             define('E_USER_DEPRECATED','16384');
886
887         $isFatal = false;
888         switch ($errno)
889         {
890             case E_USER_ERROR:
891                 $message = "ERROR: [$errno] $errstr on line $errline in file $errfile<br />\n";
892                 $isFatal = true;
893                 break;
894             case E_USER_WARNING:
895             case E_WARNING:
896                 $message = "WARNING: [$errno] $errstr on line $errline in file $errfile<br />\n";
897                 break;
898             case E_USER_NOTICE:
899             case E_NOTICE:
900                 $message = "NOTICE: [$errno] $errstr on line $errline in file $errfile<br />\n";
901                 break;
902             case E_STRICT:
903             case E_DEPRECATED:
904             case E_USER_DEPRECATED:
905                 // don't worry about these
906                 // $message = "STRICT ERROR: [$errno] $errstr on line $errline in file $errfile<br />\n";
907                 $message = "";
908                 break;
909             default:
910                 $message = "Unknown error type: [$errno] $errstr on line $errline in file $errfile<br />\n";
911                 break;
912         }
913
914         // check to see if current reporting level should be included based upon error_reporting() setting, if not
915         // then just return
916         if (error_reporting() & $errno)
917         {
918             echo $message;
919         }
920
921         if ($isFatal)
922         {
923             exit(1);
924         }
925     }
926
927
928     /**
929          * upon bean save, the relationships are saved by SugarBean->save_relationship_changes() method, but those values depend on
930      * the request object and is not reliable during import.  This function makes sure any defined related or parent id's are processed
931          * and their relationship saved.
932          */
933     public function checkRelatedIDsAfterSave($focus)
934     {
935         if(empty($focus)){
936             return false;
937         }
938
939         //check relationship fields first
940         if(!empty($focus->parent_id) && !empty($focus->parent_type)){
941             $relParentName = strtolower($focus->parent_type);
942             $relParentID = strtolower($focus->parent_id);
943         }
944         if(!empty($focus->related_id) && !empty($focus->related_type)){
945             $relName = strtolower($focus->related_type);
946             $relID = strtolower($focus->related_id);
947         }
948
949         //now refresh the bean and process for parent relationship
950         $focus->retrieve($focus->id);
951         if(!empty($relParentName) && !empty($relParentID)){
952
953             //grab the relationship and any available ids
954             if(!empty($focus->$relParentName)){
955                 $rel_ids=array();
956                 $focus->load_relationship($relParentName);
957                 $rel_ids = $focus->$relParentName->get();
958
959                 //if the current parent_id is not part of the stored rels, then add it
960                 if(!in_array($relParentID, $rel_ids)){
961                     $focus->$relParentName->add($relParentID);
962                 }
963             }
964         }
965
966         //now lets process any related fields
967         if(!empty($relName) && !empty($relID)){
968             if(!empty($focus->$relName)){
969                 $rel_ids=array();
970                 $focus->load_relationship($relName);
971                 $rel_ids = $focus->$relName->get();
972
973                 //if the related_id is not part of the stored rels, then add it
974                 if(!in_array($relID, $rel_ids)){
975                     $focus->$relName->add($relID);
976                 }
977             }
978         }
979     }
980
981
982 }