From: Michael M Slusarz Date: Wed, 12 May 2010 21:34:26 +0000 (-0600) Subject: Update Horde_Data to H4 X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=76771a86d2919834c03e512bcb8e36595771080e;p=horde.git Update Horde_Data to H4 Remove dependency on Core. This library still is way too much of a UI/widget-y class and not enough of a standalone library. --- diff --git a/fima/data.php b/fima/data.php index 70f976eaf..69198e0ea 100644 --- a/fima/data.php +++ b/fima/data.php @@ -6,16 +6,14 @@ * did not receive this file, see http://www.horde.org/licenses/asl.php. */ -function _cleanup() +function _cleanupData() { - global $import_step; - $import_step = 1; - return IMPORT_FILE; + $GLOBALS['import_step'] = 1; + return Horde_Data::IMPORT_FILE; } @define('FIMA_BASE', dirname(__FILE__)); require_once FIMA_BASE . '/lib/base.php'; -require_once 'Horde/Data.php'; $ledger = Fima::getActiveLedger(); @@ -25,11 +23,11 @@ $file_types = array('csv' => _("CSV"), /* Templates for the different import steps. */ $templates = array( - IMPORT_CSV => array($registry->get('templates', 'horde') . '/data/csvinfo.inc'), - IMPORT_TSV => array($registry->get('templates', 'horde') . '/data/tsvinfo.inc'), - IMPORT_MAPPED => array($registry->get('templates', 'horde') . '/data/csvmap.inc'), - IMPORT_DATETIME => array($registry->get('templates', 'horde') . '/data/datemap.inc'), - IMPORT_FILE => array(FIMA_TEMPLATES . '/data/import.inc', FIMA_TEMPLATES . '/data/export.inc'), + Horde_Data::IMPORT_CSV => array($registry->get('templates', 'horde') . '/data/csvinfo.inc'), + Horde_Data::IMPORT_TSV => array($registry->get('templates', 'horde') . '/data/tsvinfo.inc'), + Horde_Data::IMPORT_MAPPED => array($registry->get('templates', 'horde') . '/data/csvmap.inc'), + Horde_Data::IMPORT_DATETIME => array($registry->get('templates', 'horde') . '/data/datemap.inc'), + Horde_Data::IMPORT_FILE => array(FIMA_TEMPLATES . '/data/import.inc', FIMA_TEMPLATES . '/data/export.inc'), ); /* Field/clear name mapping. */ @@ -48,7 +46,7 @@ $param = array('time_fields' => $time_fields, 'file_types' => $file_types); $import_format = Horde_Util::getFormData('import_format', ''); $import_step = Horde_Util::getFormData('import_step', 0) + 1; -$next_step = IMPORT_FILE; +$next_step = Horde_Data::IMPORT_FILE; $actionID = Horde_Util::getFormData('actionID'); $error = false; @@ -56,7 +54,7 @@ $error = false; switch ($actionID) { case 'export': $data = array(); - + /* Create a Fima storage instance. */ $storage = &Fima_Driver::singleton($ledger); if (is_a($storage, 'PEAR_Error')) { @@ -65,13 +63,13 @@ case 'export': break; } $params = $storage->getParams(); - + $filters = array(array('type', $prefs->getValue('active_postingtype'))); /* Get accounts and postings. */ $accounts = Fima::listAccounts(); $postings = Fima::listPostings($filters); - + foreach ($postings as $postingId => $posting) { $row = array(); foreach ($posting as $key => $value) { @@ -105,18 +103,16 @@ case 'export': switch (Horde_Util::getFormData('exportID')) { case EXPORT_CSV: - $csv = &Horde_Data::singleton('csv'); - $csv->exportFile(_("postings.csv"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Csv', array('cleanup' => '_cleanupData'))->exportFile(_("postings.csv"), $data, true); exit; case EXPORT_TSV: - $tsv = &Horde_Data::singleton('tsv'); - $tsv->exportFile(_("postings.tsv"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Tsv', array('cleanup' => '_cleanupData'))->exportFile(_("postings.tsv"), $data, true); exit; } break; -case IMPORT_FILE: +case Horde_Data::IMPORT_FILE: $storage = &Fima_Driver::singleton($ledger); if (is_a($storage, 'PEAR_Error')) { $notification->push(sprintf(_("Failed to access the ledger: %s"), $storage->getMessage()), 'horde.error'); @@ -130,15 +126,16 @@ case IMPORT_FILE: } if (!$error) { - $data = &Horde_Data::singleton($import_format); - if (is_a($data, 'PEAR_Error')) { - $notification->push(_("This file format is not supported."), 'horde.error'); - $next_step = IMPORT_FILE; - } else { + try { + $data = $injector->getInstance('Horde_Data')->getOb($import_format, array('cleanup' => '_cleanupData')); $next_step = $data->nextStep($actionID, $param); - if (is_a($next_step, 'PEAR_Error')) { - $notification->push($next_step->getMessage(), 'horde.error'); + } catch (Horde_Data_Exception $e) { + if ($data) { + $notification->push($e, 'horde.error'); $next_step = $data->cleanup(); + } else { + $notification->push(_("This file format is not supported."), 'horde.error'); + $next_step = Horde_Data::IMPORT_FILE; } } } @@ -198,9 +195,6 @@ if (is_array($next_step)) { if (!count($next_step)) { $notification->push(sprintf(_("The %s file didn't contain any postings."), $file_types[$_SESSION['import_data']['format']]), 'horde.error'); - } elseif (is_a($result, 'PEAR_Error')) { - $notification->push(sprintf(_("There was an error importing the data: %s"), - $result->getMessage()), 'horde.error'); } else { $notification->push(sprintf(_("%s successfully imported"), $file_types[$_SESSION['import_data']['format']]), 'horde.success'); @@ -212,7 +206,7 @@ $title = _("Import/Export Postings"); require FIMA_TEMPLATES . '/common-header.inc'; require FIMA_TEMPLATES . '/menu.inc'; -if ($next_step == IMPORT_FILE) { +if ($next_step == Horde_Data::IMPORT_FILE) { /* Build the charset options. */ $charsets = Horde_Nls::$config['encodings']; $all_charsets = Horde_Nls::$config['charsets']; diff --git a/framework/Core/lib/Horde/Core/Binder/Data.php b/framework/Core/lib/Horde/Core/Binder/Data.php new file mode 100644 index 000000000..83d0df8a9 --- /dev/null +++ b/framework/Core/lib/Horde/Core/Binder/Data.php @@ -0,0 +1,17 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Core + */ + +/** + * A Horde_Injector:: based Horde_Data:: factory. + * + * Copyright 2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @category Horde + * @package Core + * @author Michael Slusarz + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Core + */ +class Horde_Core_Factory_Data +{ + /** + * The injector. + * + * @var Horde_Injector + */ + private $_injector; + + /** + * Constructor. + * + * @param Horde_Injector $injector The injector to use. + */ + public function __construct(Horde_Injector $injector) + { + $this->_injector = $injector; + } + + /** + * Return the Horde_Data:: instance. + * + * @param string $driver The driver. + * @param string $params Driver parameters. + * + * @return Horde_Data_Driver The instance. + * @throws Horde_Data_Exception + */ + public function getOb($driver, array $params = array()) + { + $params['browser'] = $this->_injector->getInstance('Horde_Browser'); + $params['vars'] = Horde_Variables::getDefaultVariables(); + + return Horde_Data::factory($driver, $params); + } + +} diff --git a/framework/Core/lib/Horde/Registry.php b/framework/Core/lib/Horde/Registry.php index 671aa50ba..cf8112103 100644 --- a/framework/Core/lib/Horde/Registry.php +++ b/framework/Core/lib/Horde/Registry.php @@ -236,6 +236,7 @@ class Horde_Registry 'Horde_Alarm' => new Horde_Core_Binder_Alarm(), // 'Horde_Browser' - initialized below 'Horde_Cache' => new Horde_Core_Binder_Cache(), + 'Horde_Data' => new Horde_Core_Binder_Data(), 'Horde_Db_Adapter_Base' => new Horde_Core_Binder_Db(), 'Horde_Db_Pear' => new Horde_Core_Binder_DbPear(), 'Horde_Editor' => new Horde_Core_Binder_Editor(), diff --git a/framework/Core/package.xml b/framework/Core/package.xml index 29e78358e..6290539cb 100644 --- a/framework/Core/package.xml +++ b/framework/Core/package.xml @@ -67,6 +67,7 @@ Application Framework. + @@ -88,6 +89,7 @@ Application Framework. + @@ -224,6 +226,7 @@ Application Framework. + @@ -243,6 +246,7 @@ Application Framework. + diff --git a/framework/Data/Data.php b/framework/Data/Data.php deleted file mode 100644 index f75343f2c..000000000 --- a/framework/Data/Data.php +++ /dev/null @@ -1,417 +0,0 @@ - - * @author Chuck Hagenbuch - * @package Horde_Data - */ -class Horde_Data extends PEAR { - -// Import constants -/** Import already mapped csv data. */ const IMPORT_MAPPED = 1; -/** Map date and time entries of csv data. */ const IMPORT_DATETIME = 2; -/** Import generic CSV data. */ const IMPORT_CSV = 3; -/** Import MS Outlook data. */ const IMPORT_OUTLOOK = 4; -/** Import vCalendar/iCalendar data. */ const IMPORT_ICALENDAR = 5; -/** Import vCards. */ const IMPORT_VCARD = 6; -/** Import generic tsv data. */ const IMPORT_TSV = 7; -/** Import Mulberry address book data */ const IMPORT_MULBERRY = 8; -/** Import Pine address book data. */ const IMPORT_PINE = 9; -/** Import file. */ const IMPORT_FILE = 11; -/** Import data. */ const IMPORT_DATA = 12; - -// Export constants -/** Export generic CSV data. */ const EXPORT_CSV = 100; -/** Export iCalendar data. */ const EXPORT_ICALENDAR = 101; -/** Export vCards. */ const EXPORT_VCARD = 102; -/** Export TSV data. */ const EXPORT_TSV = 103; -/** Export Outlook CSV data. */ const EXPORT_OUTLOOKCSV = 104; - - /** - * File extension. - * - * @var string - */ - var $_extension; - - /** - * MIME content type. - * - * @var string - */ - var $_contentType = 'text/plain'; - - /** - * A list of warnings raised during the last operation. - * - * @var array - */ - var $_warnings = array(); - - /** - * Stub to import passed data. - */ - function importData() - { - } - - /** - * Stub to return exported data. - */ - function exportData() - { - } - - /** - * Stub to import a file. - */ - function importFile($filename, $header = false) - { - $data = file_get_contents($filename); - return $this->importData($data, $header); - } - - /** - * Stub to export data to a file. - */ - function exportFile() - { - } - - /** - * Tries to determine the expected newline character based on the - * platform information passed by the browser's agent header. - * - * @return string The guessed expected newline characters, either \n, \r - * or \r\n. - */ - function getNewline() - { - switch ($GLOBALS['browser']->getPlatform()) { - case 'win': - return "\r\n"; - - case 'mac': - return "\r"; - - case 'unix': - default: - return "\n"; - } - } - - /** - * Returns the full filename including the basename and extension. - * - * @param string $basename Basename for the file. - * - * @return string The file name. - */ - function getFilename($basename) - { - return $basename . '.' . $this->_extension; - } - - /** - * Returns the content type. - * - * @return string The content type. - */ - function getContentType() - { - return $this->_contentType; - } - - /** - * Returns a list of warnings that have been raised during the last - * operation. - * - * @return array A (possibly empty) list of warnings. - */ - function warnings() - { - return $this->_warnings; - } - - /** - * Attempts to return a concrete Horde_Data instance based on $format. - * - * @param mixed $format The type of concrete Horde_Data subclass to - * return. If $format is an array, then we will look - * in $format[0]/lib/Data/ for the subclass - * implementation named $format[1].php. - * - * @return Horde_Data The newly created concrete Horde_Data instance, or - * false on an error. - */ - function &factory($format) - { - if (is_array($format)) { - $app = $format[0]; - $format = $format[1]; - } - - $format = basename($format); - - if (empty($format) || (strcmp($format, 'none') == 0)) { - $data = new Horde_Data(); - return $data; - } - - if (!empty($app)) { - require_once $GLOBALS['registry']->get('fileroot', $app) . '/lib/Data/' . $format . '.php'; - } else { - require_once 'Horde/Data/' . $format . '.php'; - } - $class = 'Horde_Data_' . $format; - if (class_exists($class)) { - $data = new $class(); - } else { - $data = PEAR::raiseError('Class definition of ' . $class . ' not found.'); - } - - return $data; - } - - /** - * Attempts to return a reference to a concrete Horde_Data instance - * based on $format. It will only create a new instance if no Horde_Data - * instance with the same parameters currently exists. - * - * This should be used if multiple data sources (and, thus, multiple - * Horde_Data instances) are required. - * - * This method must be invoked as: $var = &Horde_Data::singleton() - * - * @param string $format The type of concrete Horde_Data subclass to - * return. - * - * @return Horde_Data The concrete Horde_Data reference, or false on an - * error. - */ - function &singleton($format) - { - static $instances = array(); - - $signature = serialize($format); - if (!isset($instances[$signature])) { - $instances[$signature] = &Horde_Data::factory($format); - } - - return $instances[$signature]; - } - - /** - * Maps a date/time string to an associative array. - * - * The method signature has changed in Horde 3.1.3. - * - * @access private - * - * @param string $date The date. - * @param string $type One of 'date', 'time' or 'datetime'. - * @param array $params Two-dimensional array with additional information - * about the formatting. Possible keys are: - * - delimiter - The character that seperates the - * different date/time parts. - * - format - If 'ampm' and $date contains a time we - * assume that it is in AM/PM format. - * - order - If $type is 'datetime' the order of the - * day and time parts: -1 (timestamp), 0 - * (day/time), 1 (time/day). - * @param integer $key The key to use for $params. - * - * @return string The date or time in ISO format. - */ - function mapDate($date, $type, $params, $key) - { - switch ($type) { - case 'date': - case 'monthday': - case 'monthdayyear': - $dates = explode($params['delimiter'][$key], $date); - if (count($dates) != 3) { - return $date; - } - $index = array_flip(explode('/', $params['format'][$key])); - return $dates[$index['year']] . '-' . $dates[$index['month']] . '-' . $dates[$index['mday']]; - - case 'time': - $dates = explode($params['delimiter'][$key], $date); - if (count($dates) < 2 || count($dates) > 3) { - return $date; - } - if ($params['format'][$key] == 'ampm') { - if (strpos(strtolower($dates[count($dates)-1]), 'pm') !== false) { - if ($dates[0] !== '12') { - $dates[0] += 12; - } - } elseif ($dates[0] == '12') { - $dates[0] = '0'; - } - $dates[count($dates) - 1] = sprintf('%02d', $dates[count($dates)-1]); - } - return $dates[0] . ':' . $dates[1] . (count($dates) == 3 ? (':' . $dates[2]) : ':00'); - - case 'datetime': - switch ($params['order'][$key]) { - case -1: - return (string)(int)$date == $date - ? date('Y-m-d H:i:s', $date) - : $date; - case 0: - list($day, $time) = explode(' ', $date, 2); - break; - case 1: - list($time, $day) = explode(' ', $date, 2); - break; - } - $date = $this->mapDate($day, 'date', - array('delimiter' => $params['day_delimiter'], - 'format' => $params['day_format']), - $key); - $time = $this->mapDate($time, 'time', - array('delimiter' => $params['time_delimiter'], - 'format' => $params['time_format']), - $key); - return $date . ' ' . $time; - - } - } - - /** - * Takes all necessary actions for the given import step, parameters and - * form values and returns the next necessary step. - * - * @param integer $action The current step. One of the IMPORT_* constants. - * @param array $param An associative array containing needed - * parameters for the current step. - * - * @return mixed Either the next step as an integer constant or imported - * data set after the final step. - */ - function nextStep($action, $param = array()) - { - /* First step. */ - if (is_null($action)) { - $_SESSION['import_data'] = array(); - return self::IMPORT_FILE; - } - - switch ($action) { - case self::IMPORT_FILE: - /* Sanitize uploaded file. */ - $import_format = Horde_Util::getFormData('import_format'); - try { - $GLOBALS['browser']->wasFileUploaded('import_file', $param['file_types'][$import_format]); - } catch (Horde_Exception $e) { - PEAR::raiseError($e->getMessage()); - } - if ($_FILES['import_file']['size'] <= 0) { - return PEAR::raiseError(_("The file contained no data.")); - } - $_SESSION['import_data']['format'] = $import_format; - break; - - case self::IMPORT_MAPPED: - $dataKeys = Horde_Util::getFormData('dataKeys', ''); - $appKeys = Horde_Util::getFormData('appKeys', ''); - if (empty($dataKeys) || empty($appKeys)) { - global $registry; - return PEAR::raiseError(sprintf(_("You didn't map any fields from the imported file to the corresponding fields in %s."), - $registry->get('name'))); - } - $dataKeys = explode("\t", $dataKeys); - $appKeys = explode("\t", $appKeys); - $map = array(); - $dates = array(); - foreach ($appKeys as $key => $app) { - $map[$dataKeys[$key]] = $app; - if (isset($param['time_fields']) && - isset($param['time_fields'][$app])) { - $dates[$dataKeys[$key]]['type'] = $param['time_fields'][$app]; - $dates[$dataKeys[$key]]['values'] = array(); - $i = 0; - /* Build an example array of up to 10 date/time fields. */ - while ($i < count($_SESSION['import_data']['data']) && count($dates[$dataKeys[$key]]['values']) < 10) { - if (!empty($_SESSION['import_data']['data'][$i][$dataKeys[$key]])) { - $dates[$dataKeys[$key]]['values'][] = $_SESSION['import_data']['data'][$i][$dataKeys[$key]]; - } - $i++; - } - } - } - $_SESSION['import_data']['map'] = $map; - if (count($dates) > 0) { - foreach ($dates as $key => $data) { - if (count($data['values'])) { - $_SESSION['import_data']['dates'] = $dates; - return self::IMPORT_DATETIME; - } - } - } - return $this->nextStep(self::IMPORT_DATA, $param); - - case self::IMPORT_DATETIME: - case self::IMPORT_DATA: - if ($action == self::IMPORT_DATETIME) { - $params = array('delimiter' => Horde_Util::getFormData('delimiter'), - 'format' => Horde_Util::getFormData('format'), - 'order' => Horde_Util::getFormData('order'), - 'day_delimiter' => Horde_Util::getFormData('day_delimiter'), - 'day_format' => Horde_Util::getFormData('day_format'), - 'time_delimiter' => Horde_Util::getFormData('time_delimiter'), - 'time_format' => Horde_Util::getFormData('time_format')); - } - if (!isset($_SESSION['import_data']['data'])) { - return PEAR::raiseError(_("The uploaded data was lost since the previous step.")); - } - /* Build the result data set as an associative array. */ - $data = array(); - foreach ($_SESSION['import_data']['data'] as $row) { - $data_row = array(); - foreach ($row as $key => $val) { - if (isset($_SESSION['import_data']['map'][$key])) { - $mapped_key = $_SESSION['import_data']['map'][$key]; - if ($action == self::IMPORT_DATETIME && - !empty($val) && - isset($param['time_fields']) && - isset($param['time_fields'][$mapped_key])) { - $val = $this->mapDate($val, $param['time_fields'][$mapped_key], $params, $key); - } - $data_row[$_SESSION['import_data']['map'][$key]] = $val; - } - } - $data[] = $data_row; - } - return $data; - } - } - - /** - * Cleans the session data up and removes any uploaded and moved - * files. If a function called "_cleanup()" exists, this gets - * called too. - * - * @return mixed If _cleanup() was called, the return value of this call. - * This should be the value of the first import step. - */ - function cleanup() - { - if (isset($_SESSION['import_data']['file_name'])) { - @unlink($_SESSION['import_data']['file_name']); - } - $_SESSION['import_data'] = array(); - if (function_exists('_cleanup')) { - return _cleanup(); - } - } - -} diff --git a/framework/Data/Data/csv.php b/framework/Data/Data/csv.php deleted file mode 100644 index a1724f747..000000000 --- a/framework/Data/Data/csv.php +++ /dev/null @@ -1,261 +0,0 @@ - - * @author Chuck Hagenbuch - * @package Horde_Data - */ -class Horde_Data_csv extends Horde_Data { - - var $_extension = 'csv'; - var $_contentType = 'text/comma-separated-values'; - - /** - * Tries to discover the CSV file's parameters. - * - * @param string $filename The name of the file to investigate. - * - * @return array An associative array with the following possible keys: - *
-     * 'sep':    The field separator
-     * 'quote':  The quoting character
-     * 'fields': The number of fields (columns)
-     * 
- */ - function discoverFormat($filename) - { - return Horde_File_Csv::discoverFormat($filename); - } - - /** - * Imports and parses a CSV file. - * - * @param string $filename The name of the file to parse. - * @param boolean $header Does the first line contain the field/column - * names? - * @param string $sep The field/column separator. - * @param string $quote The quoting character. - * @param integer $fields The number or fields/columns. - * @param string $charset The file's charset. @since Horde 3.1. - * @param string $crlf The file's linefeed characters. @since Horde 3.1. - * - * @return array A two-dimensional array of all imported data rows. If - * $header was true the rows are associative arrays with the - * field/column names as the keys. - *@throws Horde_File_Csv_Exception - */ - function importFile($filename, $header = false, $sep = '', $quote = '', - $fields = null, $import_mapping = array(), - $charset = null, $crlf = null) - { - /* Horde_File_Csv is a bit picky at what parameters it expects. */ - $conf = array(); - if ($fields) { - $conf['fields'] = $fields; - } else { - return array(); - } - if (!empty($quote)) { - $conf['quote'] = $quote; - } - if (empty($sep)) { - $conf['sep'] = ','; - } else { - $conf['sep'] = $sep; - } - if (!empty($crlf)) { - $conf['crlf'] = $crlf; - } - - /* Strip and keep the first line if it contains the field - * names. */ - if ($header) { - $head = Horde_File_Csv::read($filename, $conf); - if (!empty($charset)) { - $head = Horde_String::convertCharset($head, $charset, Horde_Nls::getCharset()); - } - } - - $data = array(); - while ($line = Horde_File_Csv::read($filename, $conf)) { - if (!empty($charset)) { - $line = Horde_String::convertCharset($line, $charset, Horde_Nls::getCharset()); - } - if (!isset($head)) { - $data[] = $line; - } else { - $newline = array(); - for ($i = 0; $i < count($head); $i++) { - if (isset($import_mapping[$head[$i]])) { - $head[$i] = $import_mapping[$head[$i]]; - } - $cell = $line[$i]; - $cell = preg_replace("/\"\"/", "\"", $cell); - $newline[$head[$i]] = empty($cell) ? '' : $cell; - } - $data[] = $newline; - } - } - - $fp = Horde_File_Csv::getPointer($filename, $conf); - if ($fp) { - rewind($fp); - } - - $this->_warnings = Horde_File_Csv::warning(); - return $data; - } - - /** - * Builds a CSV file from a given data structure and returns it as a - * string. - * - * @param array $data A two-dimensional array containing the data set. - * @param boolean $header If true, the rows of $data are associative - * arrays with field names as their keys. - * - * @return string The CSV data. - */ - function exportData($data, $header = false, $export_mapping = array()) - { - if (!is_array($data) || count($data) == 0) { - return ''; - } - - $export = ''; - $eol = "\n"; - $head = array_keys(current($data)); - if ($header) { - foreach ($head as $key) { - if (!empty($key)) { - if (isset($export_mapping[$key])) { - $key = $export_mapping[$key]; - } - $export .= '"' . $key . '"'; - } - $export .= ','; - } - $export = substr($export, 0, -1) . $eol; - } - - foreach ($data as $row) { - foreach ($head as $key) { - $cell = $row[$key]; - if (!empty($cell) || $cell === 0) { - $export .= '"' . $cell . '"'; - } - $export .= ','; - } - $export = substr($export, 0, -1) . $eol; - } - - return $export; - } - - /** - * Builds a CSV file from a given data structure and triggers its - * download. It DOES NOT exit the current script but only outputs the - * correct headers and data. - * - * @param string $filename The name of the file to be downloaded. - * @param array $data A two-dimensional array containing the data - * set. - * @param boolean $header If true, the rows of $data are associative - * arrays with field names as their keys. - */ - function exportFile($filename, $data, $header = false, - $export_mapping = array()) - { - $export = $this->exportData($data, $header, $export_mapping); - $GLOBALS['browser']->downloadHeaders($filename, 'application/csv', false, strlen($export)); - echo $export; - } - - /** - * Takes all necessary actions for the given import step, parameters and - * form values and returns the next necessary step. - * - * @param integer $action The current step. One of the IMPORT_* constants. - * @param array $param An associative array containing needed - * parameters for the current step. - * - * @return mixed Either the next step as an integer constant or imported - * data set after the final step. - */ - function nextStep($action, $param = array()) - { - switch ($action) { - case self::IMPORT_FILE: - $next_step = parent::nextStep($action, $param); - if (is_a($next_step, 'PEAR_Error')) { - return $next_step; - } - - /* Move uploaded file so that we can read it again in the next - step after the user gave some format details. */ - $file_name = Horde::getTempFile('import', false); - if (!move_uploaded_file($_FILES['import_file']['tmp_name'], $file_name)) { - return PEAR::raiseError(_("The uploaded file could not be saved.")); - } - $_SESSION['import_data']['file_name'] = $file_name; - - /* Try to discover the file format ourselves. */ - $conf = $this->discoverFormat($file_name); - if (!$conf) { - $conf = array('sep' => ','); - } - $_SESSION['import_data'] = array_merge($_SESSION['import_data'], $conf); - - /* Check if charset was specified. */ - $_SESSION['import_data']['charset'] = Horde_Util::getFormData('charset'); - - /* Read the file's first two lines to show them to the user. */ - $_SESSION['import_data']['first_lines'] = ''; - $fp = @fopen($file_name, 'r'); - if ($fp) { - $line_no = 1; - while ($line_no < 3 && $line = fgets($fp)) { - if (!empty($_SESSION['import_data']['charset'])) { - $line = Horde_String::convertCharset($line, $_SESSION['import_data']['charset'], Horde_Nls::getCharset()); - } - $newline = Horde_String::length($line) > 100 ? "\n" : ''; - $_SESSION['import_data']['first_lines'] .= substr($line, 0, 100) . $newline; - $line_no++; - } - } - return self::IMPORT_CSV; - - case self::IMPORT_CSV: - $_SESSION['import_data']['header'] = Horde_Util::getFormData('header'); - $import_mapping = array(); - if (isset($param['import_mapping'])) { - $import_mapping = $param['import_mapping']; - } - $import_data = $this->importFile($_SESSION['import_data']['file_name'], - $_SESSION['import_data']['header'], - Horde_Util::getFormData('sep'), - Horde_Util::getFormData('quote'), - Horde_Util::getFormData('fields'), - $import_mapping, - $_SESSION['import_data']['charset'], - $_SESSION['import_data']['crlf']); - $_SESSION['import_data']['data'] = $import_data; - unset($_SESSION['import_data']['map']); - return self::IMPORT_MAPPED; - - default: - return parent::nextStep($action, $param); - } - } - -} diff --git a/framework/Data/Data/icalendar.php b/framework/Data/Data/icalendar.php deleted file mode 100644 index c13022883..000000000 --- a/framework/Data/Data/icalendar.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @author Karsten Fourmont - * @package Horde_Data - */ -class Horde_Data_icalendar extends Horde_Data_imc { - -} diff --git a/framework/Data/Data/imc.php b/framework/Data/Data/imc.php deleted file mode 100644 index f25dcea6d..000000000 --- a/framework/Data/Data/imc.php +++ /dev/null @@ -1,100 +0,0 @@ - - * @package Horde_Data - */ -class Horde_Data_imc extends Horde_Data { - - var $_iCal = false; - - function importData($text) - { - $this->_iCal = new Horde_iCalendar(); - if (!$this->_iCal->parsevCalendar($text)) { - return PEAR::raiseError(_("There was an error importing the iCalendar data.")); - } - - return $this->_iCal->getComponents(); - } - - /** - * Builds an iCalendar file from a given data structure and - * returns it as a string. - * - * @param array $data An array containing Horde_iCalendar_vevent - * objects - * @param string $method The iTip method to use. - * - * @return string The iCalendar data. - */ - function exportData($data, $method = 'REQUEST') - { - $this->_iCal = new Horde_iCalendar(); - $this->_iCal->setAttribute('METHOD', $method); - - foreach ($data as $event) { - $this->_iCal->addComponent($event); - } - - return $this->_iCal->exportvCalendar(); - } - - /** - * Builds an iCalendar file from a given data structure and - * triggers its download. It DOES NOT exit the current script but - * only outputs the correct headers and data. - * - * @param string $filename The name of the file to be downloaded. - * @param array $data An array containing Horde_iCalendar_vevents - */ - function exportFile($filename, $data) - { - $export = $this->exportData($data); - $GLOBALS['browser']->downloadHeaders($filename, 'text/calendar', false, strlen($export)); - echo $export; - } - - /** - * Takes all necessary actions for the given import step, - * parameters and form values and returns the next necessary step. - * - * @param integer $action The current step. One of the IMPORT_* constants. - * @param array $param An associative array containing needed - * parameters for the current step. - * @return mixed Either the next step as an integer constant or imported - * data set after the final step. - */ - function nextStep($action, $param = array()) - { - switch ($action) { - case self::IMPORT_FILE: - $next_step = parent::nextStep($action, $param); - if (is_a($next_step, 'PEAR_Error')) { - return $next_step; - } - - $import_data = $this->importFile($_FILES['import_file']['tmp_name']); - if (is_a($import_data, 'PEAR_Error')) { - return $import_data; - } - - return $this->_iCal->getComponents(); - break; - - default: - return parent::nextStep($action, $param); - break; - } - } - -} diff --git a/framework/Data/Data/outlookcsv.php b/framework/Data/Data/outlookcsv.php deleted file mode 100644 index 627bda232..000000000 --- a/framework/Data/Data/outlookcsv.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @author Chuck Hagenbuch - * @package Horde_Data - */ -class Horde_Data_tsv extends Horde_Data { - - var $_extension = 'tsv'; - var $_contentType = 'text/tab-separated-values'; - - /** - * Convert data file contents to list of data records. - * - * @param string $contents Data file contents. - * @param boolean $header True if a header row is present. - * @param string $delimiter Field delimiter. - * - * @return array List of data records. - */ - function importData($contents, $header = false, $delimiter = "\t") - { - if ($_SESSION['import_data']['format'] == 'pine') { - $contents = preg_replace('/\n +/', '', $contents); - } - $contents = explode("\n", $contents); - $data = array(); - if ($header) { - $head = explode($delimiter, array_shift($contents)); - } - foreach ($contents as $line) { - if (trim($line) == '') { - continue; - } - $line = explode($delimiter, $line); - if (!isset($head)) { - $data[] = $line; - } else { - $newline = array(); - for ($i = 0; $i < count($head); $i++) { - $newline[$head[$i]] = empty($line[$i]) ? '' : $line[$i]; - } - $data[] = $newline; - } - } - return $data; - } - - /** - * Builds a TSV file from a given data structure and returns it as a - * string. - * - * @param array $data A two-dimensional array containing the data set. - * @param boolean $header If true, the rows of $data are associative - * arrays with field names as their keys. - * - * @return string The TSV data. - */ - function exportData($data, $header = false) - { - if (!is_array($data) || count($data) == 0) { - return ''; - } - $export = ''; - $head = array_keys(current($data)); - if ($header) { - $export = implode("\t", $head) . "\n"; - } - foreach ($data as $row) { - foreach ($head as $key) { - $cell = $row[$key]; - if (!empty($cell) || $cell === 0) { - $export .= $cell; - } - $export .= "\t"; - } - $export = substr($export, 0, -1) . "\n"; - } - return $export; - } - - /** - * Builds a TSV file from a given data structure and triggers its download. - * It DOES NOT exit the current script but only outputs the correct headers - * and data. - * - * @param string $filename The name of the file to be downloaded. - * @param array $data A two-dimensional array containing the data - * set. - * @param boolean $header If true, the rows of $data are associative - * arrays with field names as their keys. - */ - function exportFile($filename, $data, $header = false) - { - $export = $this->exportData($data, $header); - $GLOBALS['browser']->downloadHeaders($filename, 'text/tab-separated-values', false, strlen($export)); - echo $export; - } - - /** - * Takes all necessary actions for the given import step, parameters and - * form values and returns the next necessary step. - * - * @param integer $action The current step. One of the IMPORT_* constants. - * @param array $param An associative array containing needed - * parameters for the current step. - * - * @return mixed Either the next step as an integer constant or imported - * data set after the final step. - */ - function nextStep($action, $param = array()) - { - switch ($action) { - case self::IMPORT_FILE: - $next_step = parent::nextStep($action, $param); - if (is_a($next_step, 'PEAR_Error')) { - return $next_step; - } - - if ($_SESSION['import_data']['format'] == 'mulberry' || - $_SESSION['import_data']['format'] == 'pine') { - $_SESSION['import_data']['data'] = $this->importFile($_FILES['import_file']['tmp_name']); - $format = $_SESSION['import_data']['format']; - if ($format == 'mulberry') { - $appKeys = array('alias', 'name', 'email', 'company', 'workAddress', 'workPhone', 'homePhone', 'fax', 'notes'); - $dataKeys = array(0, 1, 2, 3, 4, 5, 6, 7, 9); - } elseif ($format == 'pine') { - $appKeys = array('alias', 'name', 'email', 'notes'); - $dataKeys = array(0, 1, 2, 4); - } - foreach ($appKeys as $key => $app) { - $map[$dataKeys[$key]] = $app; - } - $data = array(); - foreach ($_SESSION['import_data']['data'] as $row) { - $hash = array(); - if ($format == 'mulberry') { - if (preg_match("/^Grp:/", $row[0]) || empty($row[1])) { - continue; - } - $row[1] = preg_replace('/^([^,"]+),\s*(.*)$/', '$2 $1', $row[1]); - foreach ($dataKeys as $key) { - if (array_key_exists($key, $row)) { - $hash[$key] = stripslashes(preg_replace('/\\\\r/', "\n", $row[$key])); - } - } - } elseif ($format == 'pine') { - if (count($row) < 3 || preg_match("/^#DELETED/", $row[0]) || preg_match("/[()]/", $row[2])) { - continue; - } - $row[1] = preg_replace('/^([^,"]+),\s*(.*)$/', '$2 $1', $row[1]); - /* Address can be a full RFC822 address */ - try { - $addr_arr = Horde_Mime_Address::parseAddressList($row[2]); - } catch (Horde_Mime_Exception $e) { - continue; - } - if (empty($addr_arr[0]->mailbox)) { - continue; - } - $row[2] = $addr_arr[0]->mailbox . '@' . $addr_arr[0]->host; - if (empty($row[1]) && !empty($addr_arr[0]->personal)) { - $row[1] = $addr_arr[0]->personal; - } - foreach ($dataKeys as $key) { - if (array_key_exists($key, $row)) { - $hash[$key] = $row[$key]; - } - } - } - $data[] = $hash; - } - $_SESSION['import_data']['data'] = $data; - $_SESSION['import_data']['map'] = $map; - $ret = $this->nextStep(self::IMPORT_DATA, $param); - return $ret; - } - - /* Move uploaded file so that we can read it again in the next step - after the user gave some format details. */ - try { - $GLOBALS['browser']->wasFileUploaded('import_file', _("TSV file")); - } catch (Horde_Browser_Exception $e) { - return PEAR::raiseError($e->getMessage()); - } - $file_name = Horde::getTempFile('import', false); - if (!move_uploaded_file($_FILES['import_file']['tmp_name'], $file_name)) { - return PEAR::raiseError(_("The uploaded file could not be saved.")); - } - $_SESSION['import_data']['file_name'] = $file_name; - - /* Read the file's first two lines to show them to the user. */ - $_SESSION['import_data']['first_lines'] = ''; - $fp = @fopen($file_name, 'r'); - if ($fp) { - $line_no = 1; - while ($line_no < 3 && $line = fgets($fp)) { - $newline = Horde_String::length($line) > 100 ? "\n" : ''; - $_SESSION['import_data']['first_lines'] .= substr($line, 0, 100) . $newline; - $line_no++; - } - } - return self::IMPORT_TSV; - break; - - case self::IMPORT_TSV: - $_SESSION['import_data']['header'] = Horde_Util::getFormData('header'); - $import_data = $this->importFile($_SESSION['import_data']['file_name'], - $_SESSION['import_data']['header']); - $_SESSION['import_data']['data'] = $import_data; - unset($_SESSION['import_data']['map']); - return self::IMPORT_MAPPED; - break; - - default: - return parent::nextStep($action, $param); - break; - } - } - -} diff --git a/framework/Data/Data/vcard.php b/framework/Data/Data/vcard.php deleted file mode 100644 index 9807a1ebd..000000000 --- a/framework/Data/Data/vcard.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @package Horde_Data - */ -class Horde_Data_vcard extends Horde_Data_imc { - - /** - * Exports vcalendar data as a string. Unlike vEvent, vCard data - * is not enclosed in BEGIN|END:vCalendar. - * - * @param array $data An array containing Horde_iCalendar_vcard - * objects. - * @param string $method The iTip method to use. - * - * @return string The iCalendar data. - */ - function exportData($data, $method = 'REQUEST') - { - $s = ''; - foreach ($data as $vcard) { - $s.= $vcard->exportvCalendar(); - } - return $s; - } - -} diff --git a/framework/Data/Data/vnote.php b/framework/Data/Data/vnote.php deleted file mode 100644 index 1e03c9b19..000000000 --- a/framework/Data/Data/vnote.php +++ /dev/null @@ -1,40 +0,0 @@ - - * @author Chuck Hagenbuch - * @package Horde_Data - */ -class Horde_Data_vnote extends Horde_Data_imc { - - /** - * Exports vcalendar data as a string. Unlike vEvent, vNote data - * is not enclosed in BEGIN|END:vCalendar. - * - * @param array $data An array containing Horde_iCalendar_vnote - * objects. - * @param string $method The iTip method to use. - * - * @return string The iCalendar data. - */ - function exportData($data, $method = 'REQUEST') - { - global $prefs; - - $this->_iCal = new Horde_iCalendar(); - - $this->_iCal->setAttribute('METHOD', $method); - $s = ''; - foreach ($data as $event) { - $s.= $event->exportvCalendar(); - } - return $s; - } - -} diff --git a/framework/Data/Data/vtodo.php b/framework/Data/Data/vtodo.php deleted file mode 100644 index 683ff172e..000000000 --- a/framework/Data/Data/vtodo.php +++ /dev/null @@ -1,16 +0,0 @@ - - * @author Chuck Hagenbuch - * @package Horde_Data - */ -class Horde_Data_vtodo extends Horde_Data_imc { - -} diff --git a/framework/Data/lib/Horde/Data.php b/framework/Data/lib/Horde/Data.php new file mode 100644 index 000000000..313b14d05 --- /dev/null +++ b/framework/Data/lib/Horde/Data.php @@ -0,0 +1,73 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +class Horde_Data +{ + /* Import already mapped csv data. */ + const IMPORT_MAPPED = 1; + /* Map date and time entries of csv data. */ + const IMPORT_DATETIME = 2; + /* Import generic CSV data. */ + const IMPORT_CSV = 3; + /* Import MS Outlook data. */ + const IMPORT_OUTLOOK = 4; + /* Import vCalendar/iCalendar data. */ + const IMPORT_ICALENDAR = 5; + /* Import vCards. */ + const IMPORT_VCARD = 6; + /* Import generic tsv data. */ + const IMPORT_TSV = 7; + /* Import Mulberry address book data. */ + const IMPORT_MULBERRY = 8; + /* Import Pine address book data. */ + const IMPORT_PINE = 9; + /* Import file. */ + const IMPORT_FILE = 11; + /* Import data. */ + const IMPORT_DATA = 12; + + /* Export generic CSV data. */ + const EXPORT_CSV = 100; + /* Export iCalendar data. */ + const EXPORT_ICALENDAR = 101; + /* Export vCards. */ + const EXPORT_VCARD = 102; + /* Export TSV data. */ + const EXPORT_TSV = 103; + /* Export Outlook CSV data. */ + const EXPORT_OUTLOOKCSV = 104; + + /** + * Attempts to return a concrete instance based on $format. + * + * @param string $format The type of concrete subclass to return. + * @param array $params Parameters to pass to the format driver. + * + * @return Horde_Data_Driver The newly created concrete instance. + * @throws Horde_Data_Exception + */ + public function factory($format, array $params = array()) + { + $format = ucfirst(strtolower(basename($format))); + $class = __CLASS__ . '_' . $format; + + if (class_exists($class)) { + return new $class($params); + } + + throw new Horde_Data_Exception('Driver not found: ' . $driver); + } + +} diff --git a/framework/Data/lib/Horde/Data/Csv.php b/framework/Data/lib/Horde/Data/Csv.php new file mode 100644 index 000000000..cec3c5cf4 --- /dev/null +++ b/framework/Data/lib/Horde/Data/Csv.php @@ -0,0 +1,269 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +class Horde_Data_Csv extends Horde_Data_Driver +{ + /** + * File extension. + * + * @var string + */ + protected $_extension = 'csv'; + + /** + * MIME content type. + * + * @var string + */ + protected $_contentType = 'text/comma-separated-values'; + + /** + * Tries to discover the CSV file's parameters. + * + * @param string $filename The name of the file to investigate. + * + * @return array An associative array with the following possible keys: + *
+     * 'sep':    The field separator
+     * 'quote':  The quoting character
+     * 'fields': The number of fields (columns)
+     * 
+ */ + public function discoverFormat($filename) + { + return Horde_File_Csv::discoverFormat($filename); + } + + /** + * Imports and parses a CSV file. + * + * @param string $filename The name of the file to parse. + * @param boolean $header Does the first line contain the field/column + * names? + * @param string $sep The field/column separator. + * @param string $quote The quoting character. + * @param integer $fields The number or fields/columns. + * @param string $charset The file's charset. + * @param string $crlf The file's linefeed characters. + * + * @return array A two-dimensional array of all imported data rows. If + * $header was true the rows are associative arrays with the + * field/column names as the keys. + *@throws Horde_File_Csv_Exception + */ + public function importFile($filename, $header = false, $sep = ',', + $quote = '', $fields = null, + $import_mapping = array(), $charset = null, + $crlf = null) + { + if (empty($fields)) { + return array(); + } + + $conf = array( + 'crlf' => $crlf, + 'fields' => $fields, + 'quote' => $quote, + 'sep' => $sep + ); + + /* Strip and keep the first line if it contains the field + * names. */ + if ($header) { + $head = Horde_File_Csv::read($filename, $conf); + if (!empty($charset)) { + $head = Horde_String::convertCharset($head, $charset, Horde_Nls::getCharset()); + } + } + + $data = array(); + while ($line = Horde_File_Csv::read($filename, $conf)) { + if (!empty($charset)) { + $line = Horde_String::convertCharset($line, $charset, Horde_Nls::getCharset()); + } + if (!isset($head)) { + $data[] = $line; + } else { + $newline = array(); + for ($i = 0; $i < count($head); $i++) { + if (isset($import_mapping[$head[$i]])) { + $head[$i] = $import_mapping[$head[$i]]; + } + $cell = $line[$i]; + $cell = preg_replace("/\"\"/", "\"", $cell); + $newline[$head[$i]] = empty($cell) ? '' : $cell; + } + $data[] = $newline; + } + } + + $fp = Horde_File_Csv::getPointer($filename, $conf); + if ($fp) { + rewind($fp); + } + + $this->_warnings = Horde_File_Csv::warning(); + + return $data; + } + + /** + * Builds a CSV file from a given data structure and returns it as a + * string. + * + * @param array $data A two-dimensional array containing the data set. + * @param boolean $header If true, the rows of $data are associative + * arrays with field names as their keys. + * + * @return string The CSV data. + */ + public function exportData($data, $header = false, + $export_mapping = array()) + { + if (!is_array($data) || count($data) == 0) { + return ''; + } + + $export = ''; + $eol = "\n"; + $head = array_keys(current($data)); + if ($header) { + foreach ($head as $key) { + if (!empty($key)) { + if (isset($export_mapping[$key])) { + $key = $export_mapping[$key]; + } + $export .= '"' . $key . '"'; + } + $export .= ','; + } + $export = substr($export, 0, -1) . $eol; + } + + foreach ($data as $row) { + foreach ($head as $key) { + $cell = $row[$key]; + if (!empty($cell) || $cell === 0) { + $export .= '"' . $cell . '"'; + } + $export .= ','; + } + $export = substr($export, 0, -1) . $eol; + } + + return $export; + } + + /** + * Builds a CSV file from a given data structure and triggers its + * download. It DOES NOT exit the current script but only outputs the + * correct headers and data. + * + * @param string $filename The name of the file to be downloaded. + * @param array $data A two-dimensional array containing the data + * set. + * @param boolean $header If true, the rows of $data are associative + * arrays with field names as their keys. + */ + public function exportFile($filename, $data, $header = false, + $export_mapping = array()) + { + $export = $this->exportData($data, $header, $export_mapping); + $this->_browser->downloadHeaders($filename, 'application/csv', false, strlen($export)); + echo $export; + } + + /** + * Takes all necessary actions for the given import step, parameters and + * form values and returns the next necessary step. + * + * @param integer $action The current step. One of the IMPORT_* constants. + * @param array $param An associative array containing needed + * parameters for the current step. + * + * @return mixed Either the next step as an integer constant or imported + * data set after the final step. + * @throws Horde_Data_Exception + */ + public function nextStep($action, $param = array()) + { + switch ($action) { + case Horde_Data::IMPORT_FILE: + parent::nextStep($action, $param); + + /* Move uploaded file so that we can read it again in the next + step after the user gave some format details. */ + $file_name = Horde_Util::getTempFile('import', false); + if (!move_uploaded_file($_FILES['import_file']['tmp_name'], $file_name)) { + throw new Horde_Data_Exception('The uploaded file could not be saved.'); + } + $_SESSION['import_data']['file_name'] = $file_name; + + /* Try to discover the file format ourselves. */ + $conf = $this->discoverFormat($file_name); + if (!$conf) { + $conf = array('sep' => ','); + } + $_SESSION['import_data'] = array_merge($_SESSION['import_data'], $conf); + + /* Check if charset was specified. */ + $_SESSION['import_data']['charset'] = $this->_vars->charset; + + /* Read the file's first two lines to show them to the user. */ + $_SESSION['import_data']['first_lines'] = ''; + $fp = @fopen($file_name, 'r'); + if ($fp) { + $line_no = 1; + while ($line_no < 3 && $line = fgets($fp)) { + if (!empty($_SESSION['import_data']['charset'])) { + $line = Horde_String::convertCharset($line, $_SESSION['import_data']['charset'], Horde_Nls::getCharset()); + } + $newline = Horde_String::length($line) > 100 ? "\n" : ''; + $_SESSION['import_data']['first_lines'] .= substr($line, 0, 100) . $newline; + $line_no++; + } + } + return Horde_Data::IMPORT_CSV; + + case Horde_Data::IMPORT_CSV: + $_SESSION['import_data']['header'] = $this->_vars->header; + $import_mapping = array(); + if (isset($param['import_mapping'])) { + $import_mapping = $param['import_mapping']; + } + $import_data = $this->importFile( + $_SESSION['import_data']['file_name'], + $_SESSION['import_data']['header'], + $this->_vars->sep, + $this->_vars->quote, + $this->_vars->fields, + $import_mapping, + $_SESSION['import_data']['charset'], + $_SESSION['import_data']['crlf'] + ); + $_SESSION['import_data']['data'] = $import_data; + unset($_SESSION['import_data']['map']); + return Horde_Data::IMPORT_MAPPED; + + default: + return parent::nextStep($action, $param); + } + } + +} diff --git a/framework/Data/lib/Horde/Data/Driver.php b/framework/Data/lib/Horde/Data/Driver.php new file mode 100644 index 000000000..b4da049d3 --- /dev/null +++ b/framework/Data/lib/Horde/Data/Driver.php @@ -0,0 +1,368 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +abstract class Horde_Data +{ + /** + * Browser object. + * + * @var Horde_Browser + */ + protected $_browser; + + /** + * File extension. + * + * @var string + */ + protected $_extension = ''; + + /** + * MIME content type. + * + * @var string + */ + protected $_contentType = 'text/plain'; + + /** + * Cleanup callback function. + * + * @var callback + */ + protected $_cleanupCallback; + + /** + * Variables object. + * + * @var Horde_Variables + */ + protected $_vars; + + /** + * A list of warnings raised during the last operation. + * + * @var array + */ + protected $_warnings = array(); + + /** + * Constructor. + * + * @param array $params Optional parameters: + *
+     * 'browser' - (Horde_Browser) A Horde_Browser object.
+     * 'cleanup' - (callback) A callback to call at cleanup time.
+     * 
+ * + * @throws Horde_Data_Exception + */ + public function __construct(array $params = array()) + { + if (!isset($params['browser'])) { + throw new Horde_Data_Exception('Missing browser parameter.'); + } + $this->_browser = $params['browser']; + + if (isset($params['cleanup']) && + is_callable($params['cleanup'])) { + $this->_cleanupCallback = $params['cleanup']; + } + + $this->_vars = isset($params['vars']) + ? $params['vars'] + : Horde_Variables::getDefaultVariables(); + } + + /** + * Stub to import passed data. + */ + abstract public function importData(); + + /** + * Stub to return exported data. + */ + abstract public function exportData(); + + /** + * Stub to import a file. + */ + public function importFile($filename, $header = false) + { + $data = file_get_contents($filename); + return $this->importData($data, $header); + } + + /** + * Stub to export data to a file. + */ + abstract public function exportFile(); + + /** + * Tries to determine the expected newline character based on the + * platform information passed by the browser's agent header. + * + * @return string The guessed expected newline characters, either \n, \r + * or \r\n. + */ + public function getNewline() + { + switch ($this->_browser->getPlatform()) { + case 'win': + return "\r\n"; + + case 'mac': + return "\r"; + + case 'unix': + default: + return "\n"; + } + } + + /** + * Returns the full filename including the basename and extension. + * + * @param string $basename Basename for the file. + * + * @return string The file name. + */ + public function getFilename($basename) + { + return $basename . '.' . $this->_extension; + } + + /** + * Returns the content type. + * + * @return string The content type. + */ + public function getContentType() + { + return $this->_contentType; + } + + /** + * Returns a list of warnings that have been raised during the last + * operation. + * + * @return array A (possibly empty) list of warnings. + */ + public function warnings() + { + return $this->_warnings; + } + + /** + * Maps a date/time string to an associative array. + * + * @param string $date The date. + * @param string $type One of 'date', 'time' or 'datetime'. + * @param array $params Two-dimensional array with additional information + * about the formatting. Possible keys are: + * - delimiter - The character that seperates the + * different date/time parts. + * - format - If 'ampm' and $date contains a time we + * assume that it is in AM/PM format. + * - order - If $type is 'datetime' the order of the + * day and time parts: -1 (timestamp), 0 + * (day/time), 1 (time/day). + * @param integer $key The key to use for $params. + * + * @return string The date or time in ISO format. + */ + protected function _mapDate($date, $type, $params, $key) + { + switch ($type) { + case 'date': + case 'monthday': + case 'monthdayyear': + $dates = explode($params['delimiter'][$key], $date); + if (count($dates) != 3) { + return $date; + } + $index = array_flip(explode('/', $params['format'][$key])); + return $dates[$index['year']] . '-' . $dates[$index['month']] . '-' . $dates[$index['mday']]; + + case 'time': + $dates = explode($params['delimiter'][$key], $date); + if (count($dates) < 2 || count($dates) > 3) { + return $date; + } + if ($params['format'][$key] == 'ampm') { + if (strpos(strtolower($dates[count($dates)-1]), 'pm') !== false) { + if ($dates[0] !== '12') { + $dates[0] += 12; + } + } elseif ($dates[0] == '12') { + $dates[0] = '0'; + } + $dates[count($dates) - 1] = sprintf('%02d', $dates[count($dates)-1]); + } + return $dates[0] . ':' . $dates[1] . (count($dates) == 3 ? (':' . $dates[2]) : ':00'); + + case 'datetime': + switch ($params['order'][$key]) { + case -1: + return (string)(int)$date == $date + ? date('Y-m-d H:i:s', $date) + : $date; + case 0: + list($day, $time) = explode(' ', $date, 2); + break; + case 1: + list($time, $day) = explode(' ', $date, 2); + break; + } + $date = $this->mapDate($day, 'date', + array('delimiter' => $params['day_delimiter'], + 'format' => $params['day_format']), + $key); + $time = $this->mapDate($time, 'time', + array('delimiter' => $params['time_delimiter'], + 'format' => $params['time_format']), + $key); + return $date . ' ' . $time; + + } + } + + /** + * Takes all necessary actions for the given import step, parameters and + * form values and returns the next necessary step. + * + * @param integer $action The current step. One of the IMPORT_* constants. + * @param array $param An associative array containing needed + * parameters for the current step. + * + * @return mixed Either the next step as an integer constant or imported + * data set after the final step. + * @throws Horde_Data_Exception + */ + public function nextStep($action, $param = array()) + { + /* First step. */ + if (is_null($action)) { + $_SESSION['import_data'] = array(); + return Horde_Data::IMPORT_FILE; + } + + switch ($action) { + case Horde_Data::IMPORT_FILE: + /* Sanitize uploaded file. */ + try { + $this->_browser->wasFileUploaded('import_file', $param['file_types'][$this->_vars->import_format]); + } catch (Horde_Exception $e) { + throw new Horde_Data_Exception($e); + } + if ($_FILES['import_file']['size'] <= 0) { + return PEAR::raiseError(_("The file contained no data.")); + } + $_SESSION['import_data']['format'] = $this->_vars->import_format; + break; + + case Horde_Data::IMPORT_MAPPED: + if (!$this->_vars->dataKeys || !$this->_vars->appKeys) { + throw new Horde_Data_Exception('You didn\'t map any fields from the imported file to the corresponding fields.'); + } + $dataKeys = explode("\t", $this->_vars->dataKeys); + $appKeys = explode("\t", $this->_vars->appKeys); + $map = array(); + $dates = array(); + foreach ($appKeys as $key => $app) { + $map[$dataKeys[$key]] = $app; + if (isset($param['time_fields']) && + isset($param['time_fields'][$app])) { + $dates[$dataKeys[$key]]['type'] = $param['time_fields'][$app]; + $dates[$dataKeys[$key]]['values'] = array(); + $i = 0; + /* Build an example array of up to 10 date/time fields. */ + while ($i < count($_SESSION['import_data']['data']) && count($dates[$dataKeys[$key]]['values']) < 10) { + if (!empty($_SESSION['import_data']['data'][$i][$dataKeys[$key]])) { + $dates[$dataKeys[$key]]['values'][] = $_SESSION['import_data']['data'][$i][$dataKeys[$key]]; + } + $i++; + } + } + } + $_SESSION['import_data']['map'] = $map; + if (count($dates) > 0) { + foreach ($dates as $key => $data) { + if (count($data['values'])) { + $_SESSION['import_data']['dates'] = $dates; + return Horde_Data::IMPORT_DATETIME; + } + } + } + return $this->nextStep(Horde_Data::IMPORT_DATA, $param); + + case Horde_Data::IMPORT_DATETIME: + case Horde_Data::IMPORT_DATA: + if ($action == Horde_Data::IMPORT_DATETIME) { + $params = array( + 'delimiter' => $this->_vars->delimiter, + 'format' => $this->_vars->format, + 'order' => $this->_vars->order, + 'day_delimiter' => $this->_vars->day_delimiter, + 'day_format' => $this->_vars->day_format, + 'time_delimiter' => $this->_vars->time_delimiter, + 'time_format' => $this->_vars->time_format + ); + } + + if (!isset($_SESSION['import_data']['data'])) { + throw new Horde_Data_Exception('The uploaded data was lost since the previous step.'); + } + + /* Build the result data set as an associative array. */ + $data = array(); + foreach ($_SESSION['import_data']['data'] as $row) { + $data_row = array(); + foreach ($row as $key => $val) { + if (isset($_SESSION['import_data']['map'][$key])) { + $mapped_key = $_SESSION['import_data']['map'][$key]; + if ($action == Horde_Data::IMPORT_DATETIME && + !empty($val) && + isset($param['time_fields']) && + isset($param['time_fields'][$mapped_key])) { + $val = $this->mapDate($val, $param['time_fields'][$mapped_key], $params, $key); + } + $data_row[$_SESSION['import_data']['map'][$key]] = $val; + } + } + $data[] = $data_row; + } + return $data; + } + } + + /** + * Cleans the session data up and removes any uploaded and moved + * files. + * + * @return mixed If callback called, the return value of this call. + * This should be the value of the first import step. + */ + public function cleanup() + { + if (isset($_SESSION['import_data']['file_name'])) { + @unlink($_SESSION['import_data']['file_name']); + } + $_SESSION['import_data'] = array(); + + if ($this->_cleanupCallback) { + return call_user_func($this->_cleanupCallback); + } + } + +} diff --git a/framework/Data/lib/Horde/Data/Exception.php b/framework/Data/lib/Horde/Data/Exception.php new file mode 100644 index 000000000..12a23281b --- /dev/null +++ b/framework/Data/lib/Horde/Data/Exception.php @@ -0,0 +1,16 @@ + + * @category Horde + * @package Data + */ +class Horde_Data_Exception extends Horde_Exception_Prior +{ +} diff --git a/framework/Data/lib/Horde/Data/Icalendar.php b/framework/Data/lib/Horde/Data/Icalendar.php new file mode 100644 index 000000000..dd39f2483 --- /dev/null +++ b/framework/Data/lib/Horde/Data/Icalendar.php @@ -0,0 +1,15 @@ + + * @author Karsten Fourmont + * @category Horde + * @package Data + */ +class Horde_Data_Icalendar extends Horde_Data_Imc {} diff --git a/framework/Data/lib/Horde/Data/Imc.php b/framework/Data/lib/Horde/Data/Imc.php new file mode 100644 index 000000000..8e7150e1e --- /dev/null +++ b/framework/Data/lib/Horde/Data/Imc.php @@ -0,0 +1,99 @@ + + * @category Horde + * @package Data + */ +class Horde_Data_Imc extends Horde_Data_Driver +{ + /** + * @var + */ + protected $_iCal = false; + + /** + * + * @throws Horde_Data_Exception + */ + public function importData($text) + { + $this->_iCal = new Horde_iCalendar(); + if (!$this->_iCal->parsevCalendar($text)) { + throw new Horde_Data_Exception('There was an error importing the iCalendar data.'); + } + + return $this->_iCal->getComponents(); + } + + /** + * Builds an iCalendar file from a given data structure and + * returns it as a string. + * + * @param array $data An array containing Horde_iCalendar_vevent + * objects + * @param string $method The iTip method to use. + * + * @return string The iCalendar data. + */ + public function exportData($data, $method = 'REQUEST') + { + $this->_iCal = new Horde_iCalendar(); + $this->_iCal->setAttribute('METHOD', $method); + + foreach ($data as $event) { + $this->_iCal->addComponent($event); + } + + return $this->_iCal->exportvCalendar(); + } + + /** + * Builds an iCalendar file from a given data structure and + * triggers its download. It DOES NOT exit the current script but + * only outputs the correct headers and data. + * + * @param string $filename The name of the file to be downloaded. + * @param array $data An array containing Horde_iCalendar_vevents + */ + public function exportFile($filename, $data) + { + $export = $this->exportData($data); + $this->_browser->downloadHeaders($filename, 'text/calendar', false, strlen($export)); + echo $export; + } + + /** + * Takes all necessary actions for the given import step, + * parameters and form values and returns the next necessary step. + * + * @param integer $action The current step. One of the IMPORT_* constants. + * @param array $param An associative array containing needed + * parameters for the current step. + * + * @return mixed Either the next step as an integer constant or imported + * data set after the final step. + * @throws Horde_Data_Exception + */ + public function nextStep($action, $param = array()) + { + switch ($action) { + case Horde_Data::IMPORT_FILE: + parent::nextStep($action, $param); + $this->importFile($_FILES['import_file']['tmp_name']); + return $this->_iCal->getComponents(); + } + + return parent::nextStep($action, $param); + } + +} diff --git a/framework/Data/lib/Horde/Data/Outlookcsv.php b/framework/Data/lib/Horde/Data/Outlookcsv.php new file mode 100644 index 000000000..978ea66e7 --- /dev/null +++ b/framework/Data/lib/Horde/Data/Outlookcsv.php @@ -0,0 +1,67 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +class Horde_Data_Outlookcsv extends Horde_Data_Csv +{ + /** + * Builds a CSV file from a given data structure and returns it as a + * string. + * + * @param array $data A two-dimensional array containing the data + * set. + * @param boolean $header If true, the rows of $data are associative + * arrays with field names as their keys. + * + * @return string The CSV data. + */ + public function exportData($data, $header = false, + $export_mapping = array()) + { + if (!is_array($data) || (count($data) == 0)) { + return ''; + } + + $export = ''; + $eol = "\r\n"; + $head = array_keys(current($data)); + + if ($header) { + foreach ($head as $key) { + if (!empty($key)) { + if (isset($export_mapping[$key])) { + $key = $export_mapping[$key]; + } + $export .= '"' . $key . '"'; + } + $export .= ','; + } + $export = substr($export, 0, -1) . $eol; + } + + foreach ($data as $row) { + foreach ($head as $key) { + $cell = $row[$key]; + if (!empty($cell) || $cell === 0) { + $cell = preg_replace("/\"/", "\"\"", $cell); + $export .= '"' . $cell . '"'; + } + $export .= ','; + } + $export = substr($export, 0, -1) . $eol; + } + + return $export; + } + +} diff --git a/framework/Data/lib/Horde/Data/Tsv.php b/framework/Data/lib/Horde/Data/Tsv.php new file mode 100644 index 000000000..a0f76c9d3 --- /dev/null +++ b/framework/Data/lib/Horde/Data/Tsv.php @@ -0,0 +1,238 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +class Horde_Data_Tsv extends Horde_Data_Driver +{ + /** + * File extension. + * + * @var string + */ + protected $_extension = 'tsv'; + + /** + * MIME content type. + * + * @var string + */ + protected $_contentType = 'text/tab-separated-values'; + + /** + * Convert data file contents to list of data records. + * + * @param string $contents Data file contents. + * @param boolean $header True if a header row is present. + * @param string $delimiter Field delimiter. + * + * @return array List of data records. + */ + public function importData($contents, $header = false, $delimiter = "\t") + { + if ($_SESSION['import_data']['format'] == 'pine') { + $contents = preg_replace('/\n +/', '', $contents); + } + + $contents = explode("\n", $contents); + $data = array(); + if ($header) { + $head = explode($delimiter, array_shift($contents)); + } + + foreach ($contents as $line) { + if (trim($line) == '') { + continue; + } + $line = explode($delimiter, $line); + if (!isset($head)) { + $data[] = $line; + } else { + $newline = array(); + for ($i = 0; $i < count($head); $i++) { + $newline[$head[$i]] = empty($line[$i]) ? '' : $line[$i]; + } + $data[] = $newline; + } + } + + return $data; + } + + /** + * Builds a TSV file from a given data structure and returns it as a + * string. + * + * @param array $data A two-dimensional array containing the data set. + * @param boolean $header If true, the rows of $data are associative + * arrays with field names as their keys. + * + * @return string The TSV data. + */ + public function exportData($data, $header = false) + { + if (!is_array($data) || count($data) == 0) { + return ''; + } + $export = ''; + $head = array_keys(current($data)); + if ($header) { + $export = implode("\t", $head) . "\n"; + } + foreach ($data as $row) { + foreach ($head as $key) { + $cell = $row[$key]; + if (!empty($cell) || $cell === 0) { + $export .= $cell; + } + $export .= "\t"; + } + $export = substr($export, 0, -1) . "\n"; + } + + return $export; + } + + /** + * Builds a TSV file from a given data structure and triggers its download. + * It DOES NOT exit the current script but only outputs the correct headers + * and data. + * + * @param string $filename The name of the file to be downloaded. + * @param array $data A two-dimensional array containing the data + * set. + * @param boolean $header If true, the rows of $data are associative + * arrays with field names as their keys. + */ + public function exportFile($filename, $data, $header = false) + { + $export = $this->exportData($data, $header); + $this->_browser->downloadHeaders($filename, 'text/tab-separated-values', false, strlen($export)); + echo $export; + } + + /** + * Takes all necessary actions for the given import step, parameters and + * form values and returns the next necessary step. + * + * @param integer $action The current step. One of the IMPORT_* constants. + * @param array $param An associative array containing needed + * parameters for the current step. + * + * @return mixed Either the next step as an integer constant or imported + * data set after the final step. + * @throws Horde_Data_Exception + */ + public function nextStep($action, $param = array()) + { + switch ($action) { + case Horde_Data::IMPORT_FILE: + parent::nextStep($action, $param); + + if ($_SESSION['import_data']['format'] == 'mulberry' || + $_SESSION['import_data']['format'] == 'pine') { + $_SESSION['import_data']['data'] = $this->importFile($_FILES['import_file']['tmp_name']); + $format = $_SESSION['import_data']['format']; + if ($format == 'mulberry') { + $appKeys = array('alias', 'name', 'email', 'company', 'workAddress', 'workPhone', 'homePhone', 'fax', 'notes'); + $dataKeys = array(0, 1, 2, 3, 4, 5, 6, 7, 9); + } elseif ($format == 'pine') { + $appKeys = array('alias', 'name', 'email', 'notes'); + $dataKeys = array(0, 1, 2, 4); + } + foreach ($appKeys as $key => $app) { + $map[$dataKeys[$key]] = $app; + } + $data = array(); + foreach ($_SESSION['import_data']['data'] as $row) { + $hash = array(); + if ($format == 'mulberry') { + if (preg_match("/^Grp:/", $row[0]) || empty($row[1])) { + continue; + } + $row[1] = preg_replace('/^([^,"]+),\s*(.*)$/', '$2 $1', $row[1]); + foreach ($dataKeys as $key) { + if (array_key_exists($key, $row)) { + $hash[$key] = stripslashes(preg_replace('/\\\\r/', "\n", $row[$key])); + } + } + } elseif ($format == 'pine') { + if (count($row) < 3 || preg_match("/^#DELETED/", $row[0]) || preg_match("/[()]/", $row[2])) { + continue; + } + $row[1] = preg_replace('/^([^,"]+),\s*(.*)$/', '$2 $1', $row[1]); + /* Address can be a full RFC822 address */ + try { + $addr_arr = Horde_Mime_Address::parseAddressList($row[2]); + } catch (Horde_Mime_Exception $e) { + continue; + } + if (empty($addr_arr[0]->mailbox)) { + continue; + } + $row[2] = $addr_arr[0]->mailbox . '@' . $addr_arr[0]->host; + if (empty($row[1]) && !empty($addr_arr[0]->personal)) { + $row[1] = $addr_arr[0]->personal; + } + foreach ($dataKeys as $key) { + if (array_key_exists($key, $row)) { + $hash[$key] = $row[$key]; + } + } + } + $data[] = $hash; + } + $_SESSION['import_data']['data'] = $data; + $_SESSION['import_data']['map'] = $map; + $ret = $this->nextStep(Horde_Data::IMPORT_DATA, $param); + return $ret; + } + + /* Move uploaded file so that we can read it again in the next step + after the user gave some format details. */ + try { + $this->_browser->wasFileUploaded('import_file', _("TSV file")); + } catch (Horde_Browser_Exception $e) { + throw new Horde_Data_Exception($e); + } + $file_name = Horde_Util::getTempFile('import', false); + if (!move_uploaded_file($_FILES['import_file']['tmp_name'], $file_name)) { + throw new Horde_Data_Exception('The uploaded file could not be saved.'); + } + $_SESSION['import_data']['file_name'] = $file_name; + + /* Read the file's first two lines to show them to the user. */ + $_SESSION['import_data']['first_lines'] = ''; + $fp = @fopen($file_name, 'r'); + if ($fp) { + $line_no = 1; + while ($line_no < 3 && $line = fgets($fp)) { + $newline = Horde_String::length($line) > 100 ? "\n" : ''; + $_SESSION['import_data']['first_lines'] .= substr($line, 0, 100) . $newline; + $line_no++; + } + } + return Horde_Data::IMPORT_TSV; + + case Horde_Data::IMPORT_TSV: + $_SESSION['import_data']['header'] = $this->_vars->header; + $import_data = $this->importFile($_SESSION['import_data']['file_name'], + $_SESSION['import_data']['header']); + $_SESSION['import_data']['data'] = $import_data; + unset($_SESSION['import_data']['map']); + return Horde_Data::IMPORT_MAPPED; + } + + return parent::nextStep($action, $param); + } + +} diff --git a/framework/Data/lib/Horde/Data/Vcard.php b/framework/Data/lib/Horde/Data/Vcard.php new file mode 100644 index 000000000..89f6b8d5f --- /dev/null +++ b/framework/Data/lib/Horde/Data/Vcard.php @@ -0,0 +1,37 @@ + + * @category Horde + * @package Data + */ +class Horde_Data_Vcard extends Horde_Data_Imc { + + /** + * Exports vcalendar data as a string. Unlike vEvent, vCard data + * is not enclosed in BEGIN|END:vCalendar. + * + * @param array $data An array containing Horde_iCalendar_Vcard + * objects. + * @param string $method The iTip method to use. + * + * @return string The iCalendar data. + */ + public function exportData($data, $method = 'REQUEST') + { + $s = ''; + + foreach ($data as $vcard) { + $s.= $vcard->exportvCalendar(); + } + + return $s; + } + +} diff --git a/framework/Data/lib/Horde/Data/Vnote.php b/framework/Data/lib/Horde/Data/Vnote.php new file mode 100644 index 000000000..068ce153f --- /dev/null +++ b/framework/Data/lib/Horde/Data/Vnote.php @@ -0,0 +1,40 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +class Horde_Data_Vnote extends Horde_Data_Imc +{ + /** + * Exports vcalendar data as a string. Unlike vEvent, vNote data + * is not enclosed in BEGIN|END:vCalendar. + * + * @param array $data An array containing Horde_iCalendar_Vnote + * objects. + * @param string $method The iTip method to use. + * + * @return string The iCalendar data. + */ + public function exportData($data, $method = 'REQUEST') + { + $this->_iCal = new Horde_iCalendar(); + $this->_iCal->setAttribute('METHOD', $method); + + $s = ''; + foreach ($data as $event) { + $s. = $event->exportvCalendar(); + } + + return $s; + } + +} diff --git a/framework/Data/lib/Horde/Data/Vtodo.php b/framework/Data/lib/Horde/Data/Vtodo.php new file mode 100644 index 000000000..123fcf945 --- /dev/null +++ b/framework/Data/lib/Horde/Data/Vtodo.php @@ -0,0 +1,15 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +class Horde_Data_Vtodo extends Horde_Data_Imc {} diff --git a/framework/Data/package.xml b/framework/Data/package.xml index fe19682ea..9d53aca29 100644 --- a/framework/Data/package.xml +++ b/framework/Data/package.xml @@ -3,7 +3,7 @@ http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> - Horde_Data + Data pear.horde.org Horde Data API This package provides a data import and export API, with backends for: @@ -26,37 +26,46 @@ http://pear.php.net/dtd/package-2.0.xsd"> chuck@horde.org yes - 2006-05-08 - + 2010-05-12 - 0.0.3 - 0.0.3 + 0.1.0 + 0.1.0 beta beta LGPL - Converted to package.xml 2.0 for pear.horde.org + * Initial Horde 4 package. - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -68,7 +77,15 @@ http://pear.php.net/dtd/package-2.0.xsd"> 1.7.0 - iCalendar + Browser + pear.horde.org + + + Exception + pear.horde.org + + + File_Csv pear.horde.org @@ -79,16 +96,44 @@ http://pear.php.net/dtd/package-2.0.xsd"> Util pear.horde.org + + iCalendar + pear.horde.org + - - - gettext - - - + + + + + + + + + + + + + + + + 2006-05-08 + + + 0.0.3 + 0.0.3 + + + beta + beta + + LGPL + Converted to package.xml 2.0 for pear.horde.org + + + 0.0.2 0.0.2 diff --git a/framework/Data/test/Horde/Data/csv_importFile_01.phpt b/framework/Data/test/Horde/Data/csv_importFile_01.phpt new file mode 100644 index 000000000..7999b3df0 --- /dev/null +++ b/framework/Data/test/Horde/Data/csv_importFile_01.phpt @@ -0,0 +1,90 @@ +--TEST-- +Simple CSV files +--FILE-- +importFile(dirname(__FILE__) . '/simple_dos.csv', false, '', '', 4)); +var_dump($data->importFile(dirname(__FILE__) . '/simple_unix.csv', false, '', '', 4)); +var_dump($data->importFile(dirname(__FILE__) . '/simple_dos.csv', true, '', '', 4)); +var_dump($data->importFile(dirname(__FILE__) . '/simple_unix.csv', true, '', '', 4)); + +?> +--EXPECT-- +array(2) { + [0]=> + array(4) { + [0]=> + string(3) "one" + [1]=> + string(3) "two" + [2]=> + string(10) "three four" + [3]=> + string(4) "five" + } + [1]=> + array(4) { + [0]=> + string(3) "six" + [1]=> + string(5) "seven" + [2]=> + string(10) "eight nine" + [3]=> + string(4) " ten" + } +} +array(2) { + [0]=> + array(4) { + [0]=> + string(3) "one" + [1]=> + string(3) "two" + [2]=> + string(10) "three four" + [3]=> + string(4) "five" + } + [1]=> + array(4) { + [0]=> + string(3) "six" + [1]=> + string(5) "seven" + [2]=> + string(10) "eight nine" + [3]=> + string(4) " ten" + } +} +array(1) { + [0]=> + array(4) { + ["one"]=> + string(3) "six" + ["two"]=> + string(5) "seven" + ["three four"]=> + string(10) "eight nine" + ["five"]=> + string(4) " ten" + } +} +array(1) { + [0]=> + array(4) { + ["one"]=> + string(3) "six" + ["two"]=> + string(5) "seven" + ["three four"]=> + string(10) "eight nine" + ["five"]=> + string(4) " ten" + } +} \ No newline at end of file diff --git a/framework/Data/test/Horde/Data/simple_dos.csv b/framework/Data/test/Horde/Data/simple_dos.csv new file mode 100644 index 000000000..7cc025df3 --- /dev/null +++ b/framework/Data/test/Horde/Data/simple_dos.csv @@ -0,0 +1,2 @@ +one,two,three four,five +six,seven,eight nine, ten diff --git a/framework/Data/test/Horde/Data/simple_unix.csv b/framework/Data/test/Horde/Data/simple_unix.csv new file mode 100644 index 000000000..7cc025df3 --- /dev/null +++ b/framework/Data/test/Horde/Data/simple_unix.csv @@ -0,0 +1,2 @@ +one,two,three four,five +six,seven,eight nine, ten diff --git a/framework/Data/tests/csv_importFile_01.phpt b/framework/Data/tests/csv_importFile_01.phpt deleted file mode 100644 index 7999b3df0..000000000 --- a/framework/Data/tests/csv_importFile_01.phpt +++ /dev/null @@ -1,90 +0,0 @@ ---TEST-- -Simple CSV files ---FILE-- -importFile(dirname(__FILE__) . '/simple_dos.csv', false, '', '', 4)); -var_dump($data->importFile(dirname(__FILE__) . '/simple_unix.csv', false, '', '', 4)); -var_dump($data->importFile(dirname(__FILE__) . '/simple_dos.csv', true, '', '', 4)); -var_dump($data->importFile(dirname(__FILE__) . '/simple_unix.csv', true, '', '', 4)); - -?> ---EXPECT-- -array(2) { - [0]=> - array(4) { - [0]=> - string(3) "one" - [1]=> - string(3) "two" - [2]=> - string(10) "three four" - [3]=> - string(4) "five" - } - [1]=> - array(4) { - [0]=> - string(3) "six" - [1]=> - string(5) "seven" - [2]=> - string(10) "eight nine" - [3]=> - string(4) " ten" - } -} -array(2) { - [0]=> - array(4) { - [0]=> - string(3) "one" - [1]=> - string(3) "two" - [2]=> - string(10) "three four" - [3]=> - string(4) "five" - } - [1]=> - array(4) { - [0]=> - string(3) "six" - [1]=> - string(5) "seven" - [2]=> - string(10) "eight nine" - [3]=> - string(4) " ten" - } -} -array(1) { - [0]=> - array(4) { - ["one"]=> - string(3) "six" - ["two"]=> - string(5) "seven" - ["three four"]=> - string(10) "eight nine" - ["five"]=> - string(4) " ten" - } -} -array(1) { - [0]=> - array(4) { - ["one"]=> - string(3) "six" - ["two"]=> - string(5) "seven" - ["three four"]=> - string(10) "eight nine" - ["five"]=> - string(4) " ten" - } -} \ No newline at end of file diff --git a/framework/Data/tests/simple_dos.csv b/framework/Data/tests/simple_dos.csv deleted file mode 100644 index 7cc025df3..000000000 --- a/framework/Data/tests/simple_dos.csv +++ /dev/null @@ -1,2 +0,0 @@ -one,two,three four,five -six,seven,eight nine, ten diff --git a/framework/Data/tests/simple_unix.csv b/framework/Data/tests/simple_unix.csv deleted file mode 100644 index 7cc025df3..000000000 --- a/framework/Data/tests/simple_unix.csv +++ /dev/null @@ -1,2 +0,0 @@ -one,two,three four,five -six,seven,eight nine, ten diff --git a/kronolith/data.php b/kronolith/data.php index d57392da9..aa9bb0ec4 100644 --- a/kronolith/data.php +++ b/kronolith/data.php @@ -9,10 +9,9 @@ * @package Kronolith */ -function _cleanup() +function _cleanupData() { - global $import_step; - $import_step = 1; + $GLOBALS['import_step'] = 1; return Horde_Data::IMPORT_FILE; } @@ -147,8 +146,7 @@ case 'export': } } - $csv = &Horde_Data::singleton('csv'); - $csv->exportFile(_("events.csv"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Csv', array('cleanup' => '_cleanupData'))->exportFile(_("events.csv"), $data, true); exit; case Horde_Data::EXPORT_ICALENDAR: @@ -185,34 +183,36 @@ case Horde_Data::IMPORT_FILE: } if (!$error) { - $data = &Horde_Data::singleton($import_format); - if ($data instanceof PEAR_Error) { - $notification->push(_("This file format is not supported."), 'horde.error'); - $next_step = Horde_Data::IMPORT_FILE; - } else { + try { + $data = $injector->getInstance('Horde_Data')->getOb($import_format, array('cleanup' => '_cleanupData')); + if ($actionID == Horde_Data::IMPORT_FILE) { + $cleanup = true; try { $share = $kronolith_shares->getShare($_SESSION['import_data']['import_cal']); if (!$share->hasPermission(Horde_Auth::getAuth(), Horde_Perms::EDIT)) { $notification->push(_("You do not have permission to add events to the selected calendar."), 'horde.error'); - $next_step = $data->cleanup(); } else { $next_step = $data->nextStep($actionID, $param); - if ($next_step instanceof PEAR_Error) { - $notification->push($next_step->getMessage(), 'horde.error'); - $next_step = $data->cleanup(); - } + $cleanup = false; } } catch (Exception $e) { $notification->push(_("You have specified an invalid calendar."), 'horde.error'); + } + + if ($cleanup) { $next_step = $data->cleanup(); } } else { $next_step = $data->nextStep($actionID, $param); - if ($next_step instanceof PEAR_Error) { - $notification->push($next_step->getMessage(), 'horde.error'); - $next_step = $data->cleanup(); - } + } + } catch (Horde_Data_Exception $e) { + if ($data) { + $notification->push($e, 'horde.error'); + $next_step = $data->cleanup(); + } else { + $notification->push(_("This file format is not supported."), 'horde.error'); + $next_step = Horde_Data::IMPORT_FILE; } } } diff --git a/mnemo/data.php b/mnemo/data.php index 76a7f0088..b47d917b7 100644 --- a/mnemo/data.php +++ b/mnemo/data.php @@ -1,26 +1,21 @@ - * @since Mnemo 1.0 * @package Mnemo */ -function _cleanup() +function _cleanupData() { - global $import_step; - $import_step = 1; + $GLOBALS['import_step'] = 1; return Horde_Data::IMPORT_FILE; } -@define('MNEMO_BASE', dirname(__FILE__)); -require_once MNEMO_BASE . '/lib/Application.php'; +require_once dirname(__FILE__) . '/lib/Application.php'; Horde_Registry::appInit('mnemo'); if (!$conf['menu']['import_export']) { @@ -86,8 +81,7 @@ case 'export': unset($note['uid']); $data[] = $note; } - $csv = &Horde_Data::singleton('csv'); - $csv->exportFile(_("notes.csv"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Csv', array('cleanup' => '_cleanupData'))->exportFile(_("notes.csv"), $data, true); exit; } } @@ -100,15 +94,16 @@ case Horde_Data::IMPORT_FILE: $next_step = null; if (!$error) { - $data = &Horde_Data::singleton($import_format); - if (is_a($data, 'PEAR_Error')) { - $notification->push(_("This file format is not supported."), 'horde.error'); - $next_step = Horde_Data::IMPORT_FILE; - } else { + try { + $data = $injector->getInstance('Horde_Data')->getOb($import_format, array('cleanup' => '_cleanupData')); $next_step = $data->nextStep($actionID, $param); - if (is_a($next_step, 'PEAR_Error')) { - $notification->push($next_step->getMessage(), 'horde.error'); + } catch (Horde_Data_Exception $e) { + if ($data) { + $notification->push($e, 'horde.error'); $next_step = $data->cleanup(); + } else { + $notification->push(_("This file format is not supported."), 'horde.error'); + $next_step = Horde_Data::IMPORT_FILE; } } } diff --git a/nag/data.php b/nag/data.php index 69c2ff547..93400662e 100644 --- a/nag/data.php +++ b/nag/data.php @@ -10,10 +10,9 @@ * @author Jan Schneider */ -function _cleanup() +function _cleanupData() { - global $import_step; - $import_step = 1; + $GLOBALS['import_step'] = 1; return Horde_Data::IMPORT_FILE; } @@ -114,8 +113,7 @@ case 'export': unset($task['delete_link']); $data[] = $task; } - $csv = Horde_Data::singleton('csv'); - $csv->exportFile(_("tasks.csv"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Csv', array('cleanup' => '_cleanupData'))->exportFile(_("tasks.csv"), $data, true); exit; case Horde_Data::EXPORT_ICALENDAR: @@ -140,15 +138,16 @@ case Horde_Data::IMPORT_FILE: } if (!$error) { - $data = Horde_Data::singleton($import_format); - if (is_a($data, 'PEAR_Error')) { - $notification->push(_("This file format is not supported."), 'horde.error'); - $next_step = Horde_Data::IMPORT_FILE; - } else { + try { + $data = $injector->getInstance('Horde_Data')->getOb($import_format, array('cleanup' => '_cleanupData')); $next_step = $data->nextStep($actionID, $param); - if (is_a($next_step, 'PEAR_Error')) { - $notification->push($next_step->getMessage(), 'horde.error'); + } catch (Horde_Data_Exception $e) { + if ($data) { + $notification->push($e, 'horde.error'); $next_step = $data->cleanup(); + } else { + $notification->push(_("This file format is not supported."), 'horde.error'); + $next_step = Horde_Data::IMPORT_FILE; } } } diff --git a/operator/lib/Form/SearchCDR.php b/operator/lib/Form/SearchCDR.php index f886f0d9b..ecbc60b2a 100644 --- a/operator/lib/Form/SearchCDR.php +++ b/operator/lib/Form/SearchCDR.php @@ -125,12 +125,12 @@ class ExportCDRForm extends SearchCDRForm switch($this->_vars->get('format')) { case Horde_Data::EXPORT_CSV: $ext = 'csv'; - $fmt = Horde_Data::singleton('csv'); + $fmt = $GLOBALS['injector']->getInstance('Horde_Data')->getOb('Csv'); break; case Horde_Data::EXPORT_TSV: $ext = 'tsv'; - $fmt = Horde_Data::singleton('tsv'); + $fmt = $GLOBALS['injector']->getInstance('Horde_Data')->getOb('Tsv'); break; default: diff --git a/skoli/data.php b/skoli/data.php index c5470ae20..6d05f4dd2 100644 --- a/skoli/data.php +++ b/skoli/data.php @@ -9,7 +9,6 @@ */ require_once dirname(__FILE__) . '/lib/base.php'; -require_once 'Horde/Data.php'; if (!$conf['menu']['export']) { header('Location: ' . Horde::applicationUrl('list.php', true)); @@ -159,13 +158,11 @@ case 'export': switch (Horde_Util::getFormData('exportID')) { case EXPORT_CSV: - $csv = &Horde_Data::singleton('csv'); - $csv->exportFile(_("class.csv"), $data, (Horde_Util::getFormData('student') == 'all')); + $injector->getInstance('Horde_Data')->getOb('Csv')->exportFile(_("class.csv"), $data, (Horde_Util::getFormData('student') == 'all')); exit; case EXPORT_TSV: - $tsv = &Horde_Data::singleton('tsv'); - $tsv->exportFile(_("class.tsv"), $data, (Horde_Util::getFormData('student') == 'all')); + $injector->getInstance('Horde_Data')->getOb('Tsv')->exportFile(_("class.tsv"), $data, (Horde_Util::getFormData('student') == 'all')); exit; } diff --git a/turba/data.php b/turba/data.php index 42e493087..7680be58d 100644 --- a/turba/data.php +++ b/turba/data.php @@ -10,10 +10,9 @@ * @author Jan Schneider */ -function _cleanup() +function _cleanupData() { - global $import_step; - $import_step = 1; + $GLOBALS['import_step'] = 1; return Horde_Data::IMPORT_FILE; } @@ -310,27 +309,25 @@ case 'export': switch ($exportType) { case Horde_Data::EXPORT_CSV: - $csv = Horde_Data::singleton('csv'); - $csv->exportFile(_("contacts.csv"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Csv', array('cleanup' => '_cleanupData'))->exportFile(_("contacts.csv"), $data, true); exit; case Horde_Data::EXPORT_OUTLOOKCSV: - $csv = Horde_Data::singleton('outlookcsv'); - $csv->exportFile(_("contacts.csv"), $data, true, array_flip($outlook_mapping)); + $injector->getInstance('Horde_Data')->getOb('Outlookcsv', array('cleanup' => '_cleanupData'))->exportFile(_("contacts.csv"), $data, true, array_flip($outlook_mapping)); exit; case Horde_Data::EXPORT_TSV: - $tsv = Horde_Data::singleton('tsv'); - $tsv->exportFile(_("contacts.tsv"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Tsv', array('cleanup' => '_cleanupData'))->exportFile(_("contacts.tsv"), $data, true); exit; case Horde_Data::EXPORT_VCARD: case 'vcard30': - $vcard = Horde_Data::singleton('vcard'); - $vcard->exportFile(_("contacts.vcf"), $data, true); + $injector->getInstance('Horde_Data')->getOb('Vcard', array('cleanup' => '_cleanupData'))->exportFile(_("contacts.vcf"), $data, true); exit; case 'ldif': + // TODO + //$injector->getInstance('Horde_Data')->getOb('Csv', array('cleanup' => '_cleanupData')) $ldif = Horde_Data::singleton(array('turba', 'ldif')); $ldif->exportFile(_("contacts.ldif"), $data, true); exit; @@ -381,6 +378,7 @@ case Horde_Data::IMPORT_DATETIME: } if (!$error && !empty($import_format)) { + // TODO if ($import_format == 'ldif') { $data = Horde_Data::singleton(array('turba', $import_format)); } else { diff --git a/turba/vcard.php b/turba/vcard.php index dd4b04b69..eb048951b 100644 --- a/turba/vcard.php +++ b/turba/vcard.php @@ -38,9 +38,9 @@ if (!$object->hasPermission(Horde_Perms::READ)) { exit; } -$vcard = Horde_Data::singleton('vcard'); $filename = str_replace(' ', '_', $object->getValue('name')); if (!$filename) { $filename = _("contact"); } -$vcard->exportFile($filename . '.vcf', array($driver->tovCard($object, '2.1', null, true)), Horde_Nls::getCharset()); + +$injector->getInstance('Horde_Data')->getOb('Vcard')->exportFile($filename . '.vcf', array($driver->tovCard($object, '2.1', null, true)), Horde_Nls::getCharset());