]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Import/ImportFile.php
Release 6.1.5
[Github/sugarcrm.git] / modules / Import / ImportFile.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
4 /*********************************************************************************
5  * SugarCRM is a customer relationship management program developed by
6  * SugarCRM, Inc. Copyright (C) 2004-2011 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
41  * Description: Class to handle processing an import file
42  * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
43  * All Rights Reserved.
44  ********************************************************************************/
45  
46 require_once('modules/Import/ImportCacheFiles.php');
47
48 class ImportFile
49 {
50     /**
51      * Delimiter string we are using (i.e. , or ;)
52      */
53     private $_delimiter;
54     
55     /**
56      * Enclosure string we are using (i.e. ' or ")
57      */
58     private $_enclosure;
59     
60     /**
61      * Stores whether or not we are deleting the import file in the destructor
62      */
63     private $_deleteFile;
64     
65     /**
66      * File pointer returned from fopen() call
67      */
68     private $_fp;
69     
70     /**
71      * Filename of file we are importing
72      */
73     private $_filename;
74     
75     /**
76      * Array of the values in the current array we are in
77      */
78     private $_currentRow;
79     
80     /**
81      * Count of rows processed
82      */
83     private $_rowsCount = 0;
84     
85     /**
86      * Count of rows with errors
87      */
88     private $_errorCount = 0;
89     
90     /**
91      * Count of duplicate rows
92      */
93     private $_dupeCount = 0;
94     
95     /**
96      * Count of newly created rows
97      */
98     private $_createdCount = 0;
99     
100     /**
101      * Count of updated rows
102      */
103     private $_updatedCount = 0;
104     
105     /**
106      * True if the current row has already had an error it in, so we don't increase the $_errorCount
107      */
108     private $_rowCountedForErrors = false;
109     
110     /**
111      * Constructor
112      *
113      * @param string $filename
114      * @param string $delimiter
115      * @param string $enclosure
116      * @param bool   $deleteFile
117      */
118     public function __construct( 
119         $filename, 
120         $delimiter  = ',',
121         $enclosure  = '',
122         $deleteFile = true
123         )
124     {
125         if ( !is_file($filename) || !is_readable($filename) ) {
126             return false;
127         }
128         
129         // turn on auto-detection of line endings to fix bug #10770
130         ini_set('auto_detect_line_endings', '1');
131         
132         $this->_fp         = sugar_fopen($filename,'r');
133         $this->_filename   = $filename;
134         $this->_deleteFile = $deleteFile;
135         $this->_delimiter  = ( empty($delimiter) ? ',' : $delimiter );
136         $this->_enclosure  = ( empty($enclosure) ? '' : trim($enclosure) );
137
138         // Bug 39494 - Remove the BOM (Byte Order Mark) from the beginning of the import row if it exists
139         $bomCheck = fread($this->_fp, 3); 
140         if($bomCheck != pack("CCC",0xef,0xbb,0xbf)) {
141             rewind($this->_fp);
142         }
143     }
144     
145     /**
146      * Destructor
147      *
148      * Deletes $_importFile if $_deleteFile is true
149      */
150     public function __destruct()
151     {
152         if ( $this->_deleteFile && $this->fileExists() ) {
153             fclose($this->_fp);
154             //Make sure the file exists before unlinking
155             if(file_exists($this->_filename)) {
156                unlink($this->_filename);
157             }
158         }
159         
160         ini_restore('auto_detect_line_endings');
161     }
162     
163     /**
164      * Returns true if the filename given exists and is readable
165      *
166      * @return bool
167      */
168     public function fileExists()
169     {
170         return !$this->_fp ? false : true;
171     }
172     
173     /**
174      * Gets the next row from $_importFile
175      *
176      * @return array current row of file
177      */
178     public function getNextRow()
179     {
180         if (!$this->fileExists()) 
181             return false;
182         
183         // explode on delimiter instead if enclosure is an empty string
184         if ( empty($this->_enclosure) ) {
185             $row = explode($this->_delimiter,rtrim(fgets($this->_fp, 8192),"\r\n"));
186             if ($row !== false && !( count($row) == 1 && trim($row[0]) == '') )
187                 $this->_currentRow = $row;
188             else
189                 return false;
190         }
191         else {
192             $row = fgetcsv($this->_fp, 8192, $this->_delimiter, $this->_enclosure);
193             if ($row !== false && $row != array(null))
194                 $this->_currentRow = $row;
195             else
196                 return false;
197         }
198         
199         // Bug 26219 - Convert all line endings to the same style as PHP_EOL
200         foreach ( $this->_currentRow as $key => $value ) {
201             // use preg_replace instead of str_replace as str_replace may cause extra lines on Windows
202             $this->_currentRow[$key] = preg_replace("[\r\n|\n|\r]", PHP_EOL, $value);
203         }
204             
205         $this->_rowsCount++;
206         $this->_rowCountedForErrors = false;
207         
208         return $this->_currentRow;
209     }
210     
211     /**
212      * Returns the number of fields in the current row
213      *
214      * @return int count of fiels in the current row
215      */
216     public function getFieldCount()
217     {
218         return count($this->_currentRow);
219     }
220     
221     /**
222      * Writes the row out to the ImportCacheFiles::getDuplicateFileName() file
223      */
224     public function markRowAsDuplicate()
225     {
226         $fp = sugar_fopen(ImportCacheFiles::getDuplicateFileName(),'a');
227         if ( empty($this->_enclosure) )
228             fputs($fp,implode($this->_delimiter,$this->_currentRow).PHP_EOL);
229         else
230             fputcsv($fp,$this->_currentRow, $this->_delimiter, $this->_enclosure);
231         fclose($fp);
232         
233         $this->_dupeCount++;
234     }
235     
236     /**
237      * Writes the row out to the ImportCacheFiles::getErrorFileName() file
238      *
239      * @param $error string
240      * @param $fieldName string
241      * @param $fieldValue mixed
242      */
243     public function writeError(
244         $error,
245         $fieldName,
246         $fieldValue
247         )
248     {
249         $fp = sugar_fopen(ImportCacheFiles::getErrorFileName(),'a');
250         fputcsv($fp,array($error,$fieldName,$fieldValue,$this->_rowsCount));
251         fclose($fp);
252         
253         if ( !$this->_rowCountedForErrors ) {
254             $this->_errorCount++;
255             $this->_rowCountedForErrors = true;
256             $this->writeErrorRecord();
257         }
258     }
259     
260     /**
261      * Writes the row out to the ImportCacheFiles::getErrorRecordsFileName() file
262      */
263     public function writeErrorRecord()
264     {
265         $fp = sugar_fopen(ImportCacheFiles::getErrorRecordsFileName(),'a');
266         if ( empty($this->_enclosure) )
267             fputs($fp,implode($this->_delimiter,$this->_currentRow).PHP_EOL);
268         else
269             fputcsv($fp,$this->_currentRow, $this->_delimiter, $this->_enclosure);
270         fclose($fp);
271     }
272     
273     /**
274      * Writes the totals and filename out to the ImportCacheFiles::getStatusFileName() file
275      */
276     public function writeStatus()
277     {
278         $fp = sugar_fopen(ImportCacheFiles::getStatusFileName(),'a');
279         fputcsv($fp,array($this->_rowsCount,$this->_errorCount,$this->_dupeCount,
280             $this->_createdCount,$this->_updatedCount,$this->_filename));
281         fclose($fp);
282     }
283     
284     /**
285      * Add this row to the UsersLastImport table
286      *
287      * @param string $import_module name of the module we are doing the import into
288      * @param string $module        name of the bean we are creating for this import
289      * @param string $id            id of the recorded created in the $module
290      */
291     public static function writeRowToLastImport(
292         $import_module,
293         $module,
294         $id
295         )
296     {
297         // cache $last_import instance
298         static $last_import;
299         
300         if ( !($last_import instanceof UsersLastImport) ) 
301             $last_import = new UsersLastImport();
302         
303         $last_import->id = null;
304         $last_import->deleted = null;
305         $last_import->assigned_user_id = $GLOBALS['current_user']->id;
306         $last_import->import_module = $import_module;
307         if ( $module == 'Case' ) {
308             $module = 'aCase';
309         }
310         $last_import->bean_type = $module;
311         $last_import->bean_id = $id;
312         return $last_import->save();
313     }
314     
315     /**
316      * Marks whether this row created a new record or not
317      *
318      * @param $createdRecord bool true if record is created, false if it is just updated 
319      */
320     public function markRowAsImported(
321         $createdRecord = true
322         )
323     {
324         if ( $createdRecord )
325             ++$this->_createdCount;
326         else
327             ++$this->_updatedCount;
328     }
329 }