Helpdesk da PluGzOne, baseado no osTicket
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

209 lines
7.0 KiB

<?php
/*********************************************************************
class.import.php
Utilities for importing objects and data (usually via CSV)
Peter Rotich <peter@osticket.com>
Jared Hancock <jared@osticket.com>
Copyright (c) 2006-2015 osTicket
http://www.osticket.com
Released under the GNU General Public License WITHOUT ANY WARRANTY.
See LICENSE.TXT for details.
vim: expandtab sw=4 ts=4 sts=4:
**********************************************************************/
class ImportError extends Exception {}
class ImportDataError extends ImportError {}
class CsvImporter {
// Bytes sequence of common BOM
const BOMs = array(
'UTF8' => "\xEF\xBB\xBF",
'UTF16_BE' => "\xFE\xFF",
'UTF16_LE' => "\xFF\xFE",
'UTF32_BE' => "\x00\x00\xFE\xFF",
'UTF32_LE' => "\xFF\xFE\x00\x00");
var $stream;
function __construct($stream) {
// File upload
if (is_array($stream) && !$stream['error']) {
// Properly detect Macintosh style line endings
ini_set('auto_detect_line_endings', true);
$this->stream = fopen($stream['tmp_name'], 'r');
// Skip Byte Order Mark (BOM) if present
if (!self::isBOM(fgets($this->stream, 4)))
rewind($this->stream);
}
// Open file
elseif (is_resource($stream)) {
$this->stream = $stream;
}
// Text from standard-in
elseif (is_string($stream)) {
$this->stream = fopen('php://temp', 'w+');
fwrite($this->stream, $stream);
rewind($this->stream);
}
else {
throw new ImportError(__('Unable to parse submitted csv: ').print_r(Format::htmlchars($stream), true));
}
}
function __destruct() {
fclose($this->stream);
}
function importCsv($all_fields=array(), $defaults=array()) {
$named_fields = array();
$has_header = true;
foreach ($all_fields as $f)
if ($f->get('name'))
$named_fields[$f->get('name')] = $f;
// Read the first row and see if it is a header or not
if (!($data = fgetcsv($this->stream, 1000, ",")))
throw new ImportError(__('Whoops. Perhaps you meant to send some CSV records'));
$headers = array();
foreach (Format::htmlchars($data) as $h) {
$h = trim($h);
$found = false;
foreach ($all_fields as $f) {
if (in_array(mb_strtolower($h), array(
mb_strtolower($f->get('name')), mb_strtolower($f->get('label'))))) {
$found = true;
if (!$f->get('name'))
throw new ImportError(sprintf(__(
'%s: Field must have `variable` set to be imported'), Format::htmlchars($h)));
$headers[$f->get('name')] = $f->get('label');
break;
}
}
if (!$found) {
$has_header = false;
if (count($data) == count($named_fields)) {
// Number of fields in the user form matches the number
// of fields in the data. Assume things line up
$headers = array();
foreach ($named_fields as $f)
$headers[$f->get('name')] = $f->get('label');
break;
}
else {
throw new ImportError(sprintf(
__('%s: Unable to map header to the object field'), Format::htmlchars($h)));
}
}
}
if (!$has_header)
fseek($this->stream, 0);
$objects = $fields = array();
foreach ($headers as $h => $label) {
if (!isset($named_fields[$h]))
continue;
$f = $named_fields[$h];
$name = $f->get('name');
$fields[$name] = $f;
}
// Add default fields (org_id, etc).
foreach ($defaults as $key => $val) {
// Don't apply defaults which are also being imported
if (isset($header[$key]))
unset($defaults[$key]);
}
// Avoid reading the entire CSV before yielding the first record.
// Use an iterator. This will also allow row-level errors to be
// continuable such that the rows with errors can be handled and the
// iterator can continue with the next row.
return new CsvImportIterator($this->stream, $headers, $fields, $defaults);
}
// Check if a string matches a BOM
static function isBOM($str) {
return in_array($str, self::BOMs);
}
}
class CsvImportIterator
implements Iterator {
var $stream;
var $start = 0;
var $headers;
var $fields;
var $defaults;
var $current = true;
var $row = 0;
function __construct($stream, $headers, $fields, $defaults) {
$this->stream = $stream;
$this->start = ftell($stream);
$this->headers = $headers;
$this->fields = $fields;
$this->defaults = $defaults;
}
// Iterator interface -------------------------------------
function rewind() {
@fseek($this->stream, $this->start);
if (ftell($this->stream) != $this->start)
throw new RuntimeException('Stream cannot be rewound');
$this->row = 0;
$this->next();
}
function valid() {
return $this->current;
}
function current() {
return $this->current;
}
function key() {
return $this->row;
}
function next() {
do {
if (($csv = fgetcsv($this->stream, 4096, ",")) === false) {
// Read error
$this->current = false;
break;
}
if (count($csv) == 1 && $csv[0] == null)
// Skip empty rows
continue;
elseif (count($csv) != count($this->headers))
throw new ImportDataError(sprintf(__('Bad data. Expected: %s'),
implode(', ', $this->headers)));
// Validate according to field configuration
$i = 0;
$this->current = $this->defaults;
foreach ($this->headers as $h => $label) {
$f = $this->fields[$h];
$f->_errors = array();
$T = $f->parse($csv[$i]);
if ($f->validateEntry($T) && $f->errors())
throw new ImportDataError(sprintf(__(
/* 1 will be a field label, and 2 will be error messages */
'%1$s: Invalid data: %2$s'),
$label, implode(', ', $f->errors())));
// Convert to database format
$this->current[$h] = $f->to_database($T);
$i++;
}
}
// Use the do-loop only for the empty line skipping
while (false);
$this->row++;
}
}