3 * General csv import library.
6 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
11 * Utitily class for importing of CSV files.
13 class csv_import_reader
{
17 var $_columns; //cached columns
22 * @param int $iid import identifier
23 * @param string $type which script imports?
25 function csv_import_reader($iid, $type) {
32 * @param string &$content passed by ref for memory reasons, unset after return
33 * @param string $encoding content encoding
34 * @param string $delimiter_name separator (comma, semicolon, colon, cfg)
35 * @param string $column_validation name of function for columns validation, must have one param $columns
36 * @return false if error, count of data lines if ok; use get_error() to get error string
38 function load_csv_content(&$content, $encoding, $delimiter_name, $column_validation=null) {
44 $textlib = textlib_get_instance();
46 $content = $textlib->convert($content, $encoding, 'utf-8');
47 // remove Unicode BOM from first line
48 $content = $textlib->trim_utf8_bom($content);
49 // Fix mac/dos newlines
50 $content = preg_replace('!\r\n?!', "\n", $content);
51 // is there anyting in file?
52 $columns = strtok($content, "\n");
53 if ($columns === false) {
54 $this->_error
= get_string('csvemptyfile', 'error');
57 $csv_delimiter = csv_import_reader
::get_delimiter($delimiter_name);
58 $csv_encode = csv_import_reader
::get_encoded_delimiter($delimiter_name);
60 // process header - list of columns
61 $columns = explode($csv_delimiter, $columns);
62 $col_count = count($columns);
63 if ($col_count === 0) {
64 $this->_error
= get_string('csvemptyfile', 'error');
68 foreach ($columns as $key=>$value) {
69 $columns[$key] = str_replace($csv_encode, $csv_delimiter, trim($value));
71 if ($column_validation) {
72 $result = $column_validation($columns);
73 if ($result !== true) {
74 $this->_error
= $result;
78 $this->_columns
= $columns; // cached columns
80 // open file for writing
81 $filename = $CFG->dataroot
.'/temp/csvimport/'.$this->_type
.'/'.$USER->id
.'/'.$this->_iid
;
82 $fp = fopen($filename, "w");
83 fwrite($fp, serialize($columns)."\n");
85 // again - do we have any data for processing?
88 while ($line !== false) {
89 $line = explode($csv_delimiter, $line);
90 foreach ($line as $key=>$value) {
91 $line[$key] = str_replace($csv_encode, $csv_delimiter, trim($value));
93 if (count($line) !== $col_count) {
95 $this->_error
= get_string('csvweirdcolumns', 'error');
100 fwrite($fp, serialize($line)."\n");
102 $line = strtok("\n");
110 * Returns list of columns
113 function get_columns() {
114 if (isset($this->_columns
)) {
115 return $this->_columns
;
120 $filename = $CFG->dataroot
.'/temp/csvimport/'.$this->_type
.'/'.$USER->id
.'/'.$this->_iid
;
121 if (!file_exists($filename)) {
124 $fp = fopen($filename, "r");
127 if ($line === false) {
130 $this->_columns
= unserialize($line);
131 return $this->_columns
;
140 if (!empty($this->_fp
)) {
143 $filename = $CFG->dataroot
.'/temp/csvimport/'.$this->_type
.'/'.$USER->id
.'/'.$this->_iid
;
144 if (!file_exists($filename)) {
147 if (!$this->_fp
= fopen($filename, "r")) {
151 return (fgets($this->_fp
) !== false);
156 * @return array of values
159 if (empty($this->_fp
) or feof($this->_fp
)) {
162 if ($ser = fgets($this->_fp
)) {
163 return unserialize($ser);
170 * Release iteration related resources
173 if (!empty($this->_fp
)) {
181 * @return string error text of null if none
183 function get_error() {
184 return $this->_error
;
188 * Cleanup temporary data
189 * @static if $full=true
190 * @param boolean true menasdo a full cleanup - all sessions for current user, false only the active iid
192 function cleanup($full=false) {
196 @remove_dir
($CFG->dataroot
.'/temp/csvimport/'.$this->_type
.'/'.$USER->id
);
198 @unlink
($CFG->dataroot
.'/temp/csvimport/'.$this->_type
.'/'.$USER->id
.'/'.$this->_iid
);
203 * Get list of cvs delimiters
205 * @return array suitable for selection box
207 function get_delimiter_list() {
208 $delimiters = array('comma'=>',', 'semicolon'=>';', 'colon'=>':', 'tab'=>'\\t');
209 if (isset($CFG->CSV_DELIMITER
) and strlen($CFG->CSV_DELIMITER
) === 1 and !in_array($CFG->CSV_DELIMITER
, $delimiters)) {
210 $delimiters['cfg'] = $CFG->CSV_DELIMITER
;
216 * Get delimiter character
218 * @param string separator name
219 * @return char delimiter char
221 function get_delimiter($delimiter_name) {
222 switch ($delimiter_name) {
223 case 'colon': return ':';
224 case 'semicolon': return ';';
225 case 'tab': return "\t";
226 case 'cfg': if (isset($CFG->CSV_DELIMITER
)) { return $CFG->CSV_DELIMITER
; } // no break; fall back to comma
227 case 'comma': return ',';
232 * Get encoded delimiter character
234 * @param string separator name
235 * @return char encoded delimiter char
237 function get_encoded_delimiter($delimiter_name) {
239 if ($delimiter_name == 'cfg' and isset($CFG->CSV_ENCODE
)) {
240 return $CFG->CSV_ENCODE
;
242 $delimiter = csv_import_reader
::get_delimiter($delimiter_name);
243 return '&#'.ord($delimiter);
247 * Create new import id
249 * @param string who imports?
252 function get_new_iid($type) {
255 if (!$filename = make_upload_directory('temp/csvimport/'.$type.'/'.$USER->id
, false)) {
256 error('Can not create temporary upload directory - verify moodledata permissions!');
259 // use current (non-conflicting) time stamp
261 while (file_exists($filename.'/'.$iiid)) {