Importer::saveImportBean()   F
last analyzed

Complexity

Conditions 33
Paths 3072

Size

Total Lines 90
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 1122
Metric Value
cc 33
eloc 40
nc 3072
nop 2
dl 0
loc 90
ccs 0
cts 61
cp 0
crap 1122
rs 2.1214

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
9
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
10
 *
11
 * This program is free software; you can redistribute it and/or modify it under
12
 * the terms of the GNU Affero General Public License version 3 as published by the
13
 * Free Software Foundation with the addition of the following permission added
14
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
15
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
16
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
17
 *
18
 * This program is distributed in the hope that it will be useful, but WITHOUT
19
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
21
 * details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License along with
24
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
25
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26
 * 02110-1301 USA.
27
 *
28
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
29
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
30
 *
31
 * The interactive user interfaces in modified source and object code versions
32
 * of this program must display Appropriate Legal Notices, as required under
33
 * Section 5 of the GNU Affero General Public License version 3.
34
 *
35
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
36
 * these Appropriate Legal Notices must retain the display of the "Powered by
37
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
38
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
39
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
40
 ********************************************************************************/
41
42
43
require_once('modules/Import/ImportCacheFiles.php');
44
require_once('modules/Import/ImportFieldSanitize.php');
45
require_once('modules/Import/ImportDuplicateCheck.php');
46
47
48
class Importer
49
{
50
    /**
51
     * @var ImportFieldSanitizer
52
     */
53
    protected $ifs;
54
55
    /**
56
     * @var Currency
57
     */
58
    protected $defaultUserCurrency;
59
60
    /**
61
     * @var importColumns
62
     */
63
    protected $importColumns;
64
65
    /**
66
     * @var importSource
67
     */
68
    protected $importSource;
69
70
    /**
71
     * @var $isUpdateOnly
72
     */
73
    protected $isUpdateOnly;
74
75
    /**
76
     * @var  $bean
77
     */
78
    protected $bean;
79
80
    /**
81
     * @var sugarToExternalSourceFieldMap
82
     */
83
    protected $sugarToExternalSourceFieldMap = array();
84
85
86
    public function __construct($importSource, $bean)
87
    {
88
        global $mod_strings, $sugar_config;
89
90
        $this->importSource = $importSource;
91
92
        //Vanilla copy of the bean object.
93
        $this->bean = $bean;
94
95
        // use our own error handler
96
        set_error_handler(array('Importer','handleImportErrors'),E_ALL);
97
98
         // Increase the max_execution_time since this step can take awhile
99
        ini_set("max_execution_time", max($sugar_config['import_max_execution_time'],3600));
100
101
        // stop the tracker
102
        TrackerManager::getInstance()->pause();
103
104
        // set the default locale settings
105
        $this->ifs = $this->getFieldSanitizer();
106
107
        //Get the default user currency
108
        $this->defaultUserCurrency = new Currency();
109
        $this->defaultUserCurrency->retrieve('-99');
110
111
        //Get our import column definitions
112
        $this->importColumns = $this->getImportColumns();
113
        $this->isUpdateOnly = ( isset($_REQUEST['import_type']) && $_REQUEST['import_type'] == 'update' );
114
    }
115
116
    public function import()
117
    {
118
        foreach($this->importSource as $row)
119
        {
120
            $this->importRow($row);
121
        }
122
123
        // save mapping if requested
124
        if ( isset($_REQUEST['save_map_as']) && $_REQUEST['save_map_as'] != '' )
125
        {
126
            $this->saveMappingFile();
127
        }
128
129
        $this->importSource->writeStatus();
130
131
        //All done, remove file.
132
    }
133
134
135
    protected function importRow($row)
136
    {
137
        global $sugar_config, $mod_strings, $current_user;
138
139
        $focus = clone $this->bean;
140
        $focus->unPopulateDefaultValues();
141
        $focus->save_from_post = false;
142
        $focus->team_id = null;
143
        $this->ifs->createdBeans = array();
144
        $this->importSource->resetRowErrorCounter();
145
        $do_save = true;
146
147
        for ( $fieldNum = 0; $fieldNum < $_REQUEST['columncount']; $fieldNum++ )
148
        {
149
            // loop if this column isn't set
150
            if ( !isset($this->importColumns[$fieldNum]) )
151
                continue;
152
153
            // get this field's properties
154
            $field           = $this->importColumns[$fieldNum];
155
            $fieldDef        = $focus->getFieldDefinition($field);
156
            $fieldTranslated = translate((isset($fieldDef['vname'])?$fieldDef['vname']:$fieldDef['name']), $focus->module_dir)." (".$fieldDef['name'].")";
157
            $defaultRowValue = '';
158
            // Bug 37241 - Don't re-import over a field we already set during the importing of another field
159
            if ( !empty($focus->$field) )
160
                continue;
161
162
            // translate strings
163
            global $locale;
164
            if(empty($locale))
165
            {
166
                $locale = new Localization();
167
            }
168
            if ( isset($row[$fieldNum]) )
169
            {
170
                $rowValue = $locale->translateCharset(strip_tags(trim($row[$fieldNum])),$this->importSource->importlocale_charset,$sugar_config['default_charset']);
171
            }
172
            else if( isset($this->sugarToExternalSourceFieldMap[$field]) && isset($row[$this->sugarToExternalSourceFieldMap[$field]]) )
173
            {
174
                $rowValue = $locale->translateCharset(strip_tags(trim($row[$this->sugarToExternalSourceFieldMap[$field]])),$this->importSource->importlocale_charset,$sugar_config['default_charset']);
175
            }
176
            else
177
            {
178
                $rowValue = '';
179
            }
180
181
            // If there is an default value then use it instead
182
            if ( !empty($_REQUEST[$field]) )
183
            {
184
                $defaultRowValue = $this->populateDefaultMapValue($field, $_REQUEST[$field], $fieldDef);
185
186
187
                if( empty($rowValue))
188
                {
189
                    $rowValue = $defaultRowValue;
190
                    //reset the default value to empty
191
                    $defaultRowValue='';
192
                }
193
            }
194
195
            // Bug 22705 - Don't update the First Name or Last Name value if Full Name is set
196
            if ( in_array($field, array('first_name','last_name')) && !empty($focus->full_name) )
197
                continue;
198
199
            // loop if this value has not been set
200
            if ( !isset($rowValue) )
201
                continue;
202
203
            // If the field is required and blank then error out
204
            if ( array_key_exists($field,$focus->get_import_required_fields()) && empty($rowValue) && $rowValue!='0')
205
            {
206
                $this->importSource->writeError( $mod_strings['LBL_REQUIRED_VALUE'],$fieldTranslated,'NULL');
207
                $do_save = false;
208
            }
209
210
            // Handle the special case "Sync to Outlook"
211
            if ( $focus->object_name == "Contact" && $field == 'sync_contact' )
212
            {
213
                /**
214
                 * Bug #41194 : if true used as value of sync_contact - add curent user to list to sync
215
                 */
216
                if ( true == $rowValue || 'true' == strtolower($rowValue)) {
217
                    $focus->sync_contact = $focus->id;
218
                } elseif (false == $rowValue || 'false' == strtolower($rowValue)) {
219
                    $focus->sync_contact = '';
220
                } else {
221
                    $bad_names = array();
222
                    $returnValue = $this->ifs->synctooutlook($rowValue,$fieldDef,$bad_names);
223
                    // try the default value on fail
224
                    if ( !$returnValue && !empty($defaultRowValue) )
225
                        $returnValue = $this->ifs->synctooutlook($defaultRowValue, $fieldDef, $bad_names);
226
                    if ( !$returnValue )
227
                    {
228
                        $this->importSource->writeError($mod_strings['LBL_ERROR_SYNC_USERS'], $fieldTranslated, $bad_names);
229
                        $do_save = 0;
230
                    } else {
231
                        $focus->sync_contact = $returnValue;
232
                    }
233
                }
234
            }
235
236
            // Handle email field, if it's a semi-colon separated export
237
            if ($field == 'email_addresses_non_primary' && !empty($rowValue))
238
            {
239
                if (strpos($rowValue, ';') !== false)
240
                {
241
                    $rowValue = explode(';', $rowValue);
242
                }
243
                else
244
                {
245
                    $rowValue = array($rowValue);
246
                }
247
            }
248
249
            // Handle email1 and email2 fields ( these don't have the type of email )
250
            if ( $field == 'email1' || $field == 'email2' )
251
            {
252
                $returnValue = $this->ifs->email($rowValue, $fieldDef, $focus);
253
                // try the default value on fail
254
                if ( !$returnValue && !empty($defaultRowValue) )
255
                    $returnValue = $this->ifs->email( $defaultRowValue, $fieldDef);
256
                if ( $returnValue === FALSE )
257
                {
258
                    $do_save=0;
259
                    $this->importSource->writeError( $mod_strings['LBL_ERROR_INVALID_EMAIL'], $fieldTranslated, $rowValue);
260
                }
261
                else
262
                {
263
                    $rowValue = $returnValue;
264
                    // check for current opt_out and invalid email settings for this email address
265
                    // if we find any, set them now
266
                    $emailres = $focus->db->query( "SELECT opt_out, invalid_email FROM email_addresses WHERE email_address = '".$focus->db->quote($rowValue)."'");
267
                    if ( $emailrow = $focus->db->fetchByAssoc($emailres) )
268
                    {
269
                        $focus->email_opt_out = $emailrow['opt_out'];
270
                        $focus->invalid_email = $emailrow['invalid_email'];
271
                    }
272
                }
273
            }
274
275
            // Handle splitting Full Name into First and Last Name parts
276
            if ( $field == 'full_name' && !empty($rowValue) )
277
            {
278
                $this->ifs->fullname($rowValue,$fieldDef,$focus);
279
            }
280
281
            // to maintain 451 compatiblity
282
            if(!isset($fieldDef['module']) && $fieldDef['type']=='relate')
283
                $fieldDef['module'] = ucfirst($fieldDef['table']);
284
285
            if(isset($fieldDef['custom_type']) && !empty($fieldDef['custom_type']))
286
                $fieldDef['type'] = $fieldDef['custom_type'];
287
288
            // If the field is empty then there is no need to check the data
289
            if( !empty($rowValue) )
290
            {
291
                // If it's an array of non-primary e-mails, check each mail
292
                if ($field == "email_addresses_non_primary" && is_array($rowValue))
293
                {
294
                    foreach ($rowValue as $tempRow)
295
                    {
296
                        $tempRow = $this->sanitizeFieldValueByType($tempRow, $fieldDef, $defaultRowValue, $focus, $fieldTranslated);
297
                        if ($tempRow === FALSE)
298
                        {
299
                            $rowValue = false;
300
                            $do_save = false;
301
                            break;
302
                        }
303
                    }
304
                }
305
                else
306
                {
307
                    $rowValue = $this->sanitizeFieldValueByType($rowValue, $fieldDef, $defaultRowValue, $focus, $fieldTranslated);
308
                }
309
310
                if ($rowValue === false)
311
                {
312
					/* BUG 51213 - jeff @ neposystems.com */
313
                    $do_save = false;
314
                    continue;
315
				}
316
            }
317
318
            // if the parent type is in singular form, get the real module name for parent_type
319
            if (isset($fieldDef['type']) && $fieldDef['type']=='parent_type') {
320
                $rowValue = get_module_from_singular($rowValue);
321
            }
322
323
            $focus->$field = $rowValue;
324
            unset($defaultRowValue);
325
        }
326
327
        // Now try to validate flex relate fields
328
        if ( isset($focus->field_defs['parent_name']) && isset($focus->parent_name) && ($focus->field_defs['parent_name']['type'] == 'parent') )
329
        {
330
            // populate values from the picker widget if the import file doesn't have them
331
            $parent_idField = $focus->field_defs['parent_name']['id_name'];
332
            if ( empty($focus->$parent_idField) && !empty($_REQUEST[$parent_idField]) )
333
                $focus->$parent_idField = $_REQUEST[$parent_idField];
334
335
            $parent_typeField = $focus->field_defs['parent_name']['type_name'];
336
337
            if ( empty($focus->$parent_typeField) && !empty($_REQUEST[$parent_typeField]) )
338
                $focus->$parent_typeField = $_REQUEST[$parent_typeField];
339
            // now validate it
340
            $returnValue = $this->ifs->parent($focus->parent_name,$focus->field_defs['parent_name'],$focus, empty($_REQUEST['parent_name']));
341
            if ( !$returnValue && !empty($_REQUEST['parent_name']) )
342
                $returnValue = $this->ifs->parent( $_REQUEST['parent_name'],$focus->field_defs['parent_name'], $focus);
343
        }
344
345
        // check to see that the indexes being entered are unique.
346
        if (isset($_REQUEST['enabled_dupes']) && $_REQUEST['enabled_dupes'] != "")
347
        {
348
            $toDecode = html_entity_decode  ($_REQUEST['enabled_dupes'], ENT_QUOTES);
349
            $enabled_dupes = json_decode($toDecode);
350
            $idc = new ImportDuplicateCheck($focus);
351
352
            if ( $idc->isADuplicateRecord($enabled_dupes) )
353
            {
354
                $this->importSource->markRowAsDuplicate($idc->_dupedFields);
355
                $this->_undoCreatedBeans($this->ifs->createdBeans);
356
                return;
357
            }
358
        }
359
        //Allow fields to be passed in for dup check as well (used by external adapters)
360
        else if( !empty($_REQUEST['enabled_dup_fields']) )
361
        {
362
            $toDecode = html_entity_decode  ($_REQUEST['enabled_dup_fields'], ENT_QUOTES);
363
            $enabled_dup_fields = json_decode($toDecode);
364
            $idc = new ImportDuplicateCheck($focus);
365
            if ( $idc->isADuplicateRecordByFields($enabled_dup_fields) )
366
            {
367
                $this->importSource->markRowAsDuplicate($idc->_dupedFields);
368
                $this->_undoCreatedBeans($this->ifs->createdBeans);
369
                return;
370
            }
371
        }
372
373
        // if the id was specified
374
        $newRecord = true;
375
        if ( !empty($focus->id) )
376
        {
377
            $focus->id = $this->_convertId($focus->id);
378
379
            // check if it already exists
380
            $query = "SELECT * FROM {$focus->table_name} WHERE id='".$focus->db->quote($focus->id)."'";
381
            $result = $focus->db->query($query)
382
            or sugar_die("Error selecting sugarbean: ");
383
384
            $dbrow = $focus->db->fetchByAssoc($result);
385
386
            if (isset ($dbrow['id']) && $dbrow['id'] != -1)
387
            {
388
                // if it exists but was deleted, just remove it
389
                if (isset ($dbrow['deleted']) && $dbrow['deleted'] == 1 && $this->isUpdateOnly ==false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
390
                {
391
                    $this->removeDeletedBean($focus);
392
                    $focus->new_with_id = true;
393
                }
394
                else
395
                {
396
                    if( ! $this->isUpdateOnly )
397
                    {
398
                        $this->importSource->writeError($mod_strings['LBL_ID_EXISTS_ALREADY'],'ID',$focus->id);
399
                        $this->_undoCreatedBeans($this->ifs->createdBeans);
400
                        return;
401
                    }
402
403
                    $clonedBean = $this->cloneExistingBean($focus);
404
                    if($clonedBean === FALSE)
405
                    {
406
                        $this->importSource->writeError($mod_strings['LBL_RECORD_CANNOT_BE_UPDATED'],'ID',$focus->id);
407
                        $this->_undoCreatedBeans($this->ifs->createdBeans);
408
                        return;
409
                    }
410
                    else
411
                    {
412
                        $focus = $clonedBean;
413
                        $newRecord = FALSE;
414
                    }
415
                }
416
            }
417
            else
418
            {
419
                $focus->new_with_id = true;
420
            }
421
        }
422
423
        if ($do_save)
424
        {
425
            $this->saveImportBean($focus, $newRecord);
426
            // Update the created/updated counter
427
            $this->importSource->markRowAsImported($newRecord);
428
        }
429
        else
430
            $this->_undoCreatedBeans($this->ifs->createdBeans);
431
432
        unset($defaultRowValue);
433
434
    }
435
436
437
    protected function sanitizeFieldValueByType($rowValue, $fieldDef, $defaultRowValue, $focus, $fieldTranslated)
438
    {
439
        global $mod_strings, $app_list_strings;
440
        switch ($fieldDef['type'])
441
        {
442
            case 'enum':
443
            case 'multienum':
444
                if ( isset($fieldDef['type']) && $fieldDef['type'] == "multienum" )
445
                    $returnValue = $this->ifs->multienum($rowValue,$fieldDef);
446
                else
447
                    $returnValue = $this->ifs->enum($rowValue,$fieldDef);
448
                // try the default value on fail
449
                if ( !$returnValue && !empty($defaultRowValue) )
450
                {
451
                    if ( isset($fieldDef['type']) && $fieldDef['type'] == "multienum" )
452
                        $returnValue = $this->ifs->multienum($defaultRowValue,$fieldDef);
453
                    else
454
                        $returnValue = $this->ifs->enum($defaultRowValue,$fieldDef);
455
                }
456
                if ( $returnValue === FALSE )
457
                {
458
                    $this->importSource->writeError($mod_strings['LBL_ERROR_NOT_IN_ENUM'] . implode(",",$app_list_strings[$fieldDef['options']]), $fieldTranslated,$rowValue);
459
                    return FALSE;
460
                }
461
                else
462
                    return $returnValue;
463
464
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
465
            case 'relate':
466
            case 'parent':
467
                $returnValue = $this->ifs->relate($rowValue, $fieldDef, $focus, empty($defaultRowValue));
468
                if (!$returnValue && !empty($defaultRowValue))
469
                    $returnValue = $this->ifs->relate($defaultRowValue,$fieldDef, $focus);
470
                // Bug 33623 - Set the id value found from the above method call as an importColumn
471
                if ($returnValue !== false)
472
                    $this->importColumns[] = $fieldDef['id_name'];
473
                return $rowValue;
474
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
475
            case 'teamset':
476
                $this->ifs->teamset($rowValue,$fieldDef,$focus);
477
                $this->importColumns[] = 'team_set_id';
478
                $this->importColumns[] = 'team_id';
479
                return $rowValue;
480
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
481
            case 'fullname':
482
                return $rowValue;
483
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
484
            default:
485
                $fieldtype = $fieldDef['type'];
486
                $returnValue = $this->ifs->$fieldtype($rowValue, $fieldDef, $focus);
487
                // try the default value on fail
488
                if ( !$returnValue && !empty($defaultRowValue) )
489
                    $returnValue = $this->ifs->$fieldtype($defaultRowValue,$fieldDef, $focus);
490
                if ( !$returnValue )
491
                {
492
                    $this->importSource->writeError($mod_strings['LBL_ERROR_INVALID_'.strtoupper($fieldDef['type'])],$fieldTranslated,$rowValue,$focus);
493
                    return FALSE;
494
                }
495
                return $returnValue;
496
        }
497
    }
498
499
    protected function cloneExistingBean($focus)
500
    {
501
        $existing_focus = clone $this->bean;
502
        if ( !( $existing_focus->retrieve($focus->id) instanceOf SugarBean ) )
503
        {
504
            return FALSE;
505
        }
506
        else
507
        {
508
            $newData = $focus->toArray();
509
            foreach ( $newData as $focus_key => $focus_value )
510
                if ( in_array($focus_key,$this->importColumns) )
511
                    $existing_focus->$focus_key = $focus_value;
512
513
            return $existing_focus;
514
        }
515
    }
516
517
    protected function removeDeletedBean($focus)
518
    {
519
        global $mod_strings;
520
521
        $query2 = "DELETE FROM {$focus->table_name} WHERE id='".$focus->db->quote($focus->id)."'";
522
        $result2 = $focus->db->query($query2) or sugar_die($mod_strings['LBL_ERROR_DELETING_RECORD']." ".$focus->id);
523
        if ($focus->hasCustomFields())
524
        {
525
            $query3 = "DELETE FROM {$focus->table_name}_cstm WHERE id_c='".$focus->db->quote($focus->id)."'";
526
            $result2 = $focus->db->query($query3);
527
        }
528
    }
529
530
    protected function saveImportBean($focus, $newRecord)
531
    {
532
        global $timedate, $current_user;
533
534
        // Populate in any default values to the bean
535
        $focus->populateDefaultValues();
536
537
        if ( !isset($focus->assigned_user_id) || $focus->assigned_user_id == '' && $newRecord )
538
        {
539
            $focus->assigned_user_id = $current_user->id;
540
        }
541
        /*
542
        * Bug 34854: Added all conditions besides the empty check on date modified.
543
        */
544
        if ( ( !empty($focus->new_with_id) && !empty($focus->date_modified) ) ||
545
             ( empty($focus->new_with_id) && $timedate->to_db($focus->date_modified) != $timedate->to_db($timedate->to_display_date_time($focus->fetched_row['date_modified'])) )
546
        ) 
547
            $focus->update_date_modified = false;
548
549
        // Bug 53636 - Allow update of "Date Created"
550
        if (!empty($focus->date_entered)) {
551
        	$focus->update_date_entered = true;
552
        }
553
            
554
        $focus->optimistic_lock = false;
555
        if ( $focus->object_name == "Contact" && isset($focus->sync_contact) )
556
        {
557
            //copy the potential sync list to another varible
558
            $list_of_users=$focus->sync_contact;
559
            //and set it to false for the save
560
            $focus->sync_contact=false;
561
        }
562
        else if($focus->object_name == "User" && !empty($current_user) && $focus->is_admin && !is_admin($current_user) && is_admin_for_module($current_user, 'Users')) {
563
            sugar_die($GLOBALS['mod_strings']['ERR_IMPORT_SYSTEM_ADMININSTRATOR']);
564
        }
565
        //bug# 46411 importing Calls will not populate Leads or Contacts Subpanel
566
        if (!empty($focus->parent_type) && !empty($focus->parent_id))
567
        {
568
            foreach ($focus->relationship_fields as $key => $val)
569
            {
570
                if ($val == strtolower($focus->parent_type))
571
                {
572
                    $focus->$key = $focus->parent_id;
573
                }
574
            }
575
        }					
576
        //bug# 40260 setting it true as the module in focus is involved in an import
577
        $focus->in_import=true;
578
        // call any logic needed for the module preSave
579
        $focus->beforeImportSave();
580
581
        // Bug51192: check if there are any changes in the imported data
582
        $hasDataChanges = false;
583
        $dataChanges=$focus->db->getAuditDataChanges($focus);
584
        
585
        if(!empty($dataChanges)) {
586
            foreach($dataChanges as $field=>$fieldData) {
587
                if($fieldData['data_type'] != 'date' || strtotime($fieldData['before']) != strtotime($fieldData['after'])) {
588
                    $hasDataChanges = true;
589
                    break;
590
                }
591
            }
592
        }
593
        
594
        // if modified_user_id is set, set the flag to false so SugarBEan will not reset it
595
        if (isset($focus->modified_user_id) && $focus->modified_user_id && !$hasDataChanges) {
596
            $focus->update_modified_by = false;
597
        }
598
        // if created_by is set, set the flag to false so SugarBEan will not reset it
599
        if (isset($focus->created_by) && $focus->created_by) {
600
            $focus->set_created_by = false;
601
        }
602
603
        if ( $focus->object_name == "Contact" && isset($list_of_users) )
604
            $focus->process_sync_to_outlook($list_of_users);
605
606
        $focus->save(false);
607
608
        //now that save is done, let's make sure that parent and related id's were saved as relationships
609
        //this takes place before the afterImportSave()
610
        $this->checkRelatedIDsAfterSave($focus);
611
612
        // call any logic needed for the module postSave
613
        $focus->afterImportSave();
614
615
        // Add ID to User's Last Import records
616
        if ( $newRecord )
617
            $this->importSource->writeRowToLastImport( $_REQUEST['import_module'],($focus->object_name == 'Case' ? 'aCase' : $focus->object_name),$focus->id);
618
619
    }
620
621
    protected function saveMappingFile()
622
    {
623
        global $current_user;
624
625
        $firstrow    = sugar_unserialize(base64_decode($_REQUEST['firstrow']));
626
        $mappingValsArr = $this->importColumns;
627
        $mapping_file = new ImportMap();
628
        if ( isset($_REQUEST['has_header']) && $_REQUEST['has_header'] == 'on')
629
        {
630
            $header_to_field = array ();
631
            foreach ($this->importColumns as $pos => $field_name)
632
            {
633
                if (isset($firstrow[$pos]) && isset($field_name))
634
                {
635
                    $header_to_field[$firstrow[$pos]] = $field_name;
636
                }
637
            }
638
639
            $mappingValsArr = $header_to_field;
640
        }
641
        //get array of values to save for duplicate and locale settings
642
        $advMapping = $this->retrieveAdvancedMapping();
643
644
        //merge with mappingVals array
645
        if(!empty($advMapping) && is_array($advMapping))
646
        {
647
            $mappingValsArr = $advMapping + $mappingValsArr;
648
        }
649
650
        //set mapping
651
        $mapping_file->setMapping($mappingValsArr);
652
653
        // save default fields
654
        $defaultValues = array();
655
        for ( $i = 0; $i < $_REQUEST['columncount']; $i++ )
656
        {
657
            if (isset($this->importColumns[$i]) && !empty($_REQUEST[$this->importColumns[$i]]))
658
            {
659
                $field = $this->importColumns[$i];
660
                $fieldDef = $this->bean->getFieldDefinition($field);
661
                if(!empty($fieldDef['custom_type']) && $fieldDef['custom_type'] == 'teamset')
662
                {
663
                    require_once('include/SugarFields/Fields/Teamset/SugarFieldTeamset.php');
664
                    $sugar_field = new SugarFieldTeamset('Teamset');
665
                    $teams = $sugar_field->getTeamsFromRequest($field);
666
                    if(isset($_REQUEST['primary_team_name_collection']))
667
                    {
668
                        $primary_index = $_REQUEST['primary_team_name_collection'];
669
                    }
670
671
                    //If primary_index was selected, ensure that the first Array entry is the primary team
672
                    if(isset($primary_index))
673
                    {
674
                        $count = 0;
675
                        $new_teams = array();
676
                        foreach($teams as $id=>$name)
677
                        {
678
                            if($primary_index == $count++)
679
                            {
680
                                $new_teams[$id] = $name;
681
                                unset($teams[$id]);
682
                                break;
683
                            }
684
                        }
685
686
                        foreach($teams as $id=>$name)
687
                        {
688
                            $new_teams[$id] = $name;
689
                        }
690
                        $teams = $new_teams;
691
                    } //if
692
693
                    $json = getJSONobj();
694
                    $defaultValues[$field] = $json->encode($teams);
695
                }
696
                else
697
                {
698
                    $defaultValues[$field] = $_REQUEST[$this->importColumns[$i]];
699
                }
700
            }
701
        }
702
        $mapping_file->setDefaultValues($defaultValues);
703
        $result = $mapping_file->save( $current_user->id,  $_REQUEST['save_map_as'], $_REQUEST['import_module'], $_REQUEST['source'],
704
            ( isset($_REQUEST['has_header']) && $_REQUEST['has_header'] == 'on'), $_REQUEST['custom_delimiter'], html_entity_decode($_REQUEST['custom_enclosure'],ENT_QUOTES)
705
        );
706
    }
707
708
709
    protected function populateDefaultMapValue($field, $fieldValue, $fieldDef)
710
    {
711
        global $timedate, $current_user;
712
713
        if ( is_array($fieldValue) )
714
            $defaultRowValue = encodeMultienumValue($fieldValue);
715
        else
716
            $defaultRowValue = $_REQUEST[$field];
717
        // translate default values to the date/time format for the import file
718
        if( $fieldDef['type'] == 'date' && $this->ifs->dateformat != $timedate->get_date_format() )
719
            $defaultRowValue = $timedate->swap_formats($defaultRowValue, $this->ifs->dateformat, $timedate->get_date_format());
720
721
        if( $fieldDef['type'] == 'time' && $this->ifs->timeformat != $timedate->get_time_format() )
722
            $defaultRowValue = $timedate->swap_formats($defaultRowValue, $this->ifs->timeformat, $timedate->get_time_format());
723
724
        if( ($fieldDef['type'] == 'datetime' || $fieldDef['type'] == 'datetimecombo') && $this->ifs->dateformat.' '.$this->ifs->timeformat != $timedate->get_date_time_format() )
725
            $defaultRowValue = $timedate->swap_formats($defaultRowValue, $this->ifs->dateformat.' '.$this->ifs->timeformat,$timedate->get_date_time_format());
726
727
        if ( in_array($fieldDef['type'],array('currency','float','int','num')) && $this->ifs->num_grp_sep != $current_user->getPreference('num_grp_sep') )
728
            $defaultRowValue = str_replace($current_user->getPreference('num_grp_sep'), $this->ifs->num_grp_sep,$defaultRowValue);
729
730
        if ( in_array($fieldDef['type'],array('currency','float')) && $this->ifs->dec_sep != $current_user->getPreference('dec_sep') )
731
            $defaultRowValue = str_replace($current_user->getPreference('dec_sep'), $this->ifs->dec_sep,$defaultRowValue);
732
733
        $user_currency_symbol = $this->defaultUserCurrency->symbol;
734
        if ( $fieldDef['type'] == 'currency' && $this->ifs->currency_symbol != $user_currency_symbol )
735
            $defaultRowValue = str_replace($user_currency_symbol, $this->ifs->currency_symbol,$defaultRowValue);
736
737
        return $defaultRowValue;
738
    }
739
740
    protected function getImportColumns()
741
    {
742
        $importable_fields = $this->bean->get_importable_fields();
743
        $importColumns = array();
744
        foreach ($_REQUEST as $name => $value)
745
        {
746
            // only look for var names that start with "fieldNum"
747
            if (strncasecmp($name, "colnum_", 7) != 0)
748
                continue;
749
750
            // pull out the column position for this field name
751
            $pos = substr($name, 7);
752
753
            if ( isset($importable_fields[$value]) )
754
            {
755
                // now mark that we've seen this field
756
                $importColumns[$pos] = $value;
757
            }
758
        }
759
760
        return $importColumns;
761
    }
762
763
    protected function getFieldSanitizer()
764
    {
765
        $ifs = new ImportFieldSanitize();
766
        $copyFields = array('dateformat','timeformat','timezone','default_currency_significant_digits','num_grp_sep','dec_sep','default_locale_name_format');
767
        foreach($copyFields as $field)
768
        {
769
            $fieldKey = "importlocale_$field";
770
            $ifs->$field = $this->importSource->$fieldKey;
771
        }
772
773
        $currency = new Currency();
774
        $currency->retrieve($this->importSource->importlocale_currency);
775
        $ifs->currency_symbol = $currency->symbol;
776
777
        return $ifs;
778
    }
779
780
    /**
781
     * Sets a translation map from sugar field key to external source key used while importing a row.  This allows external sources
782
     * to return a data set that is an associative array rather than numerically indexed.
783
     *
784
     * @param  $translator
785
     * @return void
786
     */
787
    public function setFieldKeyTranslator($translator)
788
    {
789
        $this->sugarToExternalSourceFieldMap = $translator;
790
    }
791
792
    /**
793
     * If a bean save is not done for some reason, this method will undo any of the beans that were created
794
     *
795
     * @param array $ids ids of user_last_import records created
796
     */
797
    protected function _undoCreatedBeans( array $ids )
798
    {
799
        $focus = new UsersLastImport();
800
        foreach ($ids as $id)
801
            $focus->undoById($id);
802
    }
803
804
    /**
805
     * clean id's when being imported
806
     *
807
     * @param  string $string
808
     * @return string
809
     */
810
    protected function _convertId($string)
811
    {
812
        return preg_replace_callback(
813
            '|[^A-Za-z0-9\-\_]|',
814
            create_function(
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
815
            // single quotes are essential here,
816
            // or alternative escape all $ as \$
817
            '$matches',
818
            'return ord($matches[0]);'
819
                 ) ,
820
            $string);
821
    }
822
823
    public function retrieveAdvancedMapping()
824
    {
825
        $advancedMappingSettings = array();
826
827
        //harvest the dupe index settings
828
        if( isset($_REQUEST['enabled_dupes']) )
829
        {
830
            $toDecode = html_entity_decode  ($_REQUEST['enabled_dupes'], ENT_QUOTES);
831
            $dupe_ind = json_decode($toDecode);
832
833
            foreach($dupe_ind as $dupe)
834
            {
835
                $advancedMappingSettings['dupe_'.$dupe] = $dupe;
836
            }
837
        }
838
839
        foreach($_REQUEST as $rk=>$rv)
840
        {
841
            //harvest the import locale settings
842
            if(strpos($rk,'portlocale_')>0)
843
            {
844
                $advancedMappingSettings[$rk] = $rv;
845
            }
846
847
        }
848
        return $advancedMappingSettings;
849
    }
850
851
    public static function getImportableModules()
852
    {
853
        global $beanList;
854
        $importableModules = array();
855
        foreach ($beanList as $moduleName => $beanName)
856
        {
857
            if( class_exists($beanName) )
858
            {
859
                $tmp = new $beanName();
860
                if( isset($tmp->importable) && $tmp->importable )
861
                {
862
                    $label = isset($GLOBALS['app_list_strings']['moduleList'][$moduleName]) ? $GLOBALS['app_list_strings']['moduleList'][$moduleName] : $moduleName;
863
                    $importableModules[$moduleName] = $label;
864
                }
865
            }
866
        }
867
868
        asort($importableModules);
869
        return $importableModules;
870
    }
871
872
873
    /**
874
     * Replaces PHP error handler in Step4
875
     *
876
     * @param int    $errno
877
     * @param string $errstr
878
     * @param string $errfile
879
     * @param string $errline
880
     */
881
    public static function handleImportErrors($errno, $errstr, $errfile, $errline)
882
    {
883
        $GLOBALS['log']->fatal("Caught error: $errstr");
884
885
        if ( !defined('E_DEPRECATED') )
886
            define('E_DEPRECATED','8192');
887
        if ( !defined('E_USER_DEPRECATED') )
888
            define('E_USER_DEPRECATED','16384');
889
890
        $isFatal = false;
891
        switch ($errno)
892
        {
893
            case E_USER_ERROR:
894
                $message = "ERROR: [$errno] $errstr on line $errline in file $errfile<br />\n";
895
                $isFatal = true;
896
                break;
897
            case E_USER_WARNING:
898
            case E_WARNING:
899
                $message = "WARNING: [$errno] $errstr on line $errline in file $errfile<br />\n";
900
                break;
901
            case E_USER_NOTICE:
902
            case E_NOTICE:
903
                $message = "NOTICE: [$errno] $errstr on line $errline in file $errfile<br />\n";
904
                break;
905
            case E_STRICT:
906
            case E_DEPRECATED:
907
            case E_USER_DEPRECATED:
908
                // don't worry about these
909
                // $message = "STRICT ERROR: [$errno] $errstr on line $errline in file $errfile<br />\n";
910
                $message = "";
911
                break;
912
            default:
913
                $message = "Unknown error type: [$errno] $errstr on line $errline in file $errfile<br />\n";
914
                break;
915
        }
916
917
        // check to see if current reporting level should be included based upon error_reporting() setting, if not
918
        // then just return
919
        if (error_reporting() & $errno)
920
        {
921
            echo $message;
922
        }
923
924
        if ($isFatal)
925
        {
926
            exit(1);
927
        }
928
    }
929
930
931
    /**
932
	 * upon bean save, the relationships are saved by SugarBean->save_relationship_changes() method, but those values depend on
933
     * the request object and is not reliable during import.  This function makes sure any defined related or parent id's are processed
934
	 * and their relationship saved.
935
	 */
936
    public function checkRelatedIDsAfterSave($focus)
937
    {
938
        if(empty($focus)){
939
            return false;
940
        }
941
942
        //check relationship fields first
943
        if(!empty($focus->parent_id) && !empty($focus->parent_type)){
944
            $relParentName = strtolower($focus->parent_type);
945
            $relParentID = strtolower($focus->parent_id);
946
        }
947
        if(!empty($focus->related_id) && !empty($focus->related_type)){
948
            $relName = strtolower($focus->related_type);
949
            $relID = strtolower($focus->related_id);
950
        }
951
952
        //now refresh the bean and process for parent relationship
953
        $focus->retrieve($focus->id);
954
        if(!empty($relParentName) && !empty($relParentID)){
955
956
            //grab the relationship and any available ids
957
            if(!empty($focus->$relParentName)){
958
                $rel_ids=array();
959
                $focus->load_relationship($relParentName);
960
                $rel_ids = $focus->$relParentName->get();
961
962
                //if the current parent_id is not part of the stored rels, then add it
963
                if(!in_array($relParentID, $rel_ids)){
964
                    $focus->$relParentName->add($relParentID);
965
                }
966
            }
967
        }
968
969
        //now lets process any related fields
970
        if(!empty($relName) && !empty($relID)){
971
            if(!empty($focus->$relName)){
972
                $rel_ids=array();
973
                $focus->load_relationship($relName);
974
                $rel_ids = $focus->$relName->get();
975
976
                //if the related_id is not part of the stored rels, then add it
977
                if(!in_array($relID, $rel_ids)){
978
                    $focus->$relName->add($relID);
979
                }
980
            }
981
        }
982
    }
983
984
985
}
986