From: Jan Schneider Date: Tue, 15 Jun 2010 09:56:32 +0000 (+0200) Subject: Make Horde_Data working again, import not yet tested (Bug #9086). X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=af3026787e3c0fa81dd7ee1307e111a56d555a83;p=horde.git Make Horde_Data working again, import not yet tested (Bug #9086). --- diff --git a/framework/Data/lib/Horde/Data.php b/framework/Data/lib/Horde/Data.php index 313b14d05..840ebb798 100644 --- a/framework/Data/lib/Horde/Data.php +++ b/framework/Data/lib/Horde/Data.php @@ -67,7 +67,7 @@ class Horde_Data return new $class($params); } - throw new Horde_Data_Exception('Driver not found: ' . $driver); + throw new Horde_Data_Exception('Driver not found: ' . $class); } } diff --git a/framework/Data/lib/Horde/Data/Base.php b/framework/Data/lib/Horde/Data/Base.php new file mode 100644 index 000000000..7a596fcd4 --- /dev/null +++ b/framework/Data/lib/Horde/Data/Base.php @@ -0,0 +1,370 @@ + + * @author Chuck Hagenbuch + * @category Horde + * @package Data + */ +abstract class Horde_Data_Base +{ + /** + * 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. + */ + public function importData($text) + { + } + + /** + * Stub to return exported data. + */ + abstract public function exportData($data, $method = 'REQUEST'); + + /** + * 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($filename, $data); + + /** + * 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/Csv.php b/framework/Data/lib/Horde/Data/Csv.php index cec3c5cf4..674383fdb 100644 --- a/framework/Data/lib/Horde/Data/Csv.php +++ b/framework/Data/lib/Horde/Data/Csv.php @@ -17,7 +17,7 @@ * @category Horde * @package Data */ -class Horde_Data_Csv extends Horde_Data_Driver +class Horde_Data_Csv extends Horde_Data_Base { /** * File extension. @@ -65,7 +65,7 @@ class Horde_Data_Csv extends Horde_Data_Driver * @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 + * @throws Horde_File_Csv_Exception */ public function importFile($filename, $header = false, $sep = ',', $quote = '', $fields = null, diff --git a/framework/Data/lib/Horde/Data/Driver.php b/framework/Data/lib/Horde/Data/Driver.php deleted file mode 100644 index b4da049d3..000000000 --- a/framework/Data/lib/Horde/Data/Driver.php +++ /dev/null @@ -1,368 +0,0 @@ - - * @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/Imc.php b/framework/Data/lib/Horde/Data/Imc.php index 8e7150e1e..ab2f6b93c 100644 --- a/framework/Data/lib/Horde/Data/Imc.php +++ b/framework/Data/lib/Horde/Data/Imc.php @@ -14,7 +14,7 @@ * @category Horde * @package Data */ -class Horde_Data_Imc extends Horde_Data_Driver +class Horde_Data_Imc extends Horde_Data_Base { /** * @var diff --git a/framework/Data/lib/Horde/Data/Tsv.php b/framework/Data/lib/Horde/Data/Tsv.php index a0f76c9d3..094221e69 100644 --- a/framework/Data/lib/Horde/Data/Tsv.php +++ b/framework/Data/lib/Horde/Data/Tsv.php @@ -12,7 +12,7 @@ * @category Horde * @package Data */ -class Horde_Data_Tsv extends Horde_Data_Driver +class Horde_Data_Tsv extends Horde_Data_Base { /** * File extension. diff --git a/framework/Data/package.xml b/framework/Data/package.xml index 9d53aca29..65f616147 100644 --- a/framework/Data/package.xml +++ b/framework/Data/package.xml @@ -43,8 +43,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + - @@ -104,8 +104,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + - diff --git a/turba/data.php b/turba/data.php index fbcda3ce1..12f9b865f 100644 --- a/turba/data.php +++ b/turba/data.php @@ -326,9 +326,10 @@ case 'export': exit; case 'ldif': - // TODO - //$injector->getInstance('Horde_Data')->getData('Csv', array('cleanup' => '_cleanupData')) - $ldif = Horde_Data::singleton(array('turba', 'ldif')); + $ldif = new Turba_Data_Ldif( + array('browser' => $this->_injector->getInstance('Horde_Browser'), + 'vars' => Horde_Variables::getDefaultVariables(), + 'cleanup' => '_cleanupData')); $ldif->exportFile(_("contacts.ldif"), $data, true); exit; } @@ -380,9 +381,12 @@ case Horde_Data::IMPORT_DATETIME: if (!$error && !empty($import_format)) { // TODO if ($import_format == 'ldif') { - $data = Horde_Data::singleton(array('turba', $import_format)); + $data = new Turba_Data_Ldif( + array('browser' => $this->_injector->getInstance('Horde_Browser'), + 'vars' => Horde_Variables::getDefaultVariables(), + 'cleanup' => '_cleanupData')); } else { - $data = Horde_Data::singleton($import_format); + $data = $injector->getInstance('Horde_Data')->getData($import_format, array('cleanup' => '_cleanupData')); } if ($data instanceof PEAR_Error) { $notification->push(_("This file format is not supported."), 'horde.error'); diff --git a/turba/lib/Data/Ldif.php b/turba/lib/Data/Ldif.php new file mode 100644 index 000000000..39be80e6e --- /dev/null +++ b/turba/lib/Data/Ldif.php @@ -0,0 +1,297 @@ + + * @package Horde_Data + */ +class Turba_Data_Ldif extends Horde_Data +{ + var $_extension = 'ldif'; + + var $_contentType = 'text/ldif'; + + /** + * Useful Mozilla address book attribute names. + * + * @private + * @var array + */ + var $_mozillaAttr = array('cn', 'givenName', 'sn', 'mail', 'mozillaNickname', + 'homeStreet', 'mozillaHomeStreet2', 'mozillaHomeLocalityName', + 'mozillaHomeState', 'mozillaHomePostalCode', + 'mozillaHomeCountryName', 'street', + 'mozillaWorkStreet2', 'l', 'st', 'postalCode', + 'c', 'homePhone', 'telephoneNumber', 'mobile', + 'fax', 'title', 'company', 'description', 'mozillaWorkUrl', + 'department', 'mozillaNickname'); + + /** + * Useful Turba address book attribute names. + * + * @private + * @var array + */ + var $_turbaAttr = array('name', 'firstname', 'lastname', 'email', 'alias', + 'homeAddress', 'homeStreet', 'homeCity', + 'homeProvince', 'homePostalCode', 'homeCountry', + 'workAddress', 'workStreet', 'workCity', 'workProvince', + 'workPostalCode', 'workCountry', + 'homePhone', 'workPhone', 'cellPhone', + 'fax', 'title', 'company', 'notes', 'website', + 'department', 'nickname'); + /** + * Turba address book attribute names and the corresponding Mozilla name. + * + * @private + * @var array + */ + var $_turbaMozillaMap = array('name' => 'cn', + 'firstname' => 'givenName', + 'lastname' => 'sn', + 'email' => 'mail', + 'alias' => 'mozillaNickname', + 'homePhone' => 'homePhone', + 'workPhone' => 'telephoneNumber', + 'cellPhone' => 'mobile', + 'fax' => 'fax', + 'title' => 'title', + 'company' => 'company', + 'notes' => 'description', + 'homeAddress' => 'homeStreet', + 'homeStreet' => 'mozillaHomeStreet2', + 'homeCity' => 'mozillaHomeLocalityName', + 'homeProvince' => 'mozillaHomeState', + 'homePostalCode' => 'mozillaHomePostalCode', + 'homeCountry' => 'mozillaHomeCountryName', + 'workAddress' => 'street', + 'workStreet' => 'mozillaWorkStreet2', + 'workCity' => 'l', + 'workProvince' => 'st', + 'workPostalCode' => 'postalCode', + 'workCountry' => 'c', + 'website' => 'mozillaWorkUrl', + 'department' => 'department', + 'nickname' => 'mozillaNickname'); + + /** + * Check if a string is safe according to RFC 2849, or if it needs to be + * base64 encoded. + * + * @private + * + * @param string $str The string to check. + * + * @return boolean True if the string is safe, false otherwise. + */ + function _is_safe_string($str) + { + /* SAFE-CHAR = %x01-09 / %x0B-0C / %x0E-7F + * ; any value <= 127 decimal except NUL, LF, + * ; and CR + * + * SAFE-INIT-CHAR = %x01-09 / %x0B-0C / %x0E-1F / + * %x21-39 / %x3B / %x3D-7F + * ; any value <= 127 except NUL, LF, CR, + * ; SPACE, colon (":", ASCII 58 decimal) + * ; and less-than ("<" , ASCII 60 decimal) */ + if (!strlen($str)) { + return true; + } + if ($str[0] == ' ' || $str[0] == ':' || $str[0] == '<') { + return false; + } + for ($i = 0; $i < strlen($str); ++$i) { + if (ord($str[$i]) > 127 || $str[$i] == NULL || $str[$i] == "\n" || + $str[$i] == "\r") { + return false; + } + } + + return true; + } + + function importData($contents, $header = false) + { + $data = array(); + $records = preg_split('/(\r?\n){2}/', $contents); + foreach ($records as $record) { + if (trim($record) == '') { + /* Ignore empty records */ + continue; + } + /* one key:value pair per line */ + $lines = preg_split('/\r?\n/', $record); + $hash = array(); + foreach ($lines as $line) { + list($key, $delimiter, $value) = preg_split('/(:[:<]?) */', $line, 2, PREG_SPLIT_DELIM_CAPTURE); + if (in_array($key, $this->_mozillaAttr)) { + $hash[$key] = ($delimiter == '::' ? base64_decode($value) : $value); + } + } + $data[] = $hash; + } + + return $data; + } + + /** + * Builds a LDIF 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/ldif', false, strlen($export)); + echo $export; + } + + /** + * Builds a LDIF 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 LDIF data. + */ + function exportData($data, $header = false) + { + if (!is_array($data) || !count($data)) { + return ''; + } + $export = ''; + $mozillaTurbaMap = array_flip($this->_turbaMozillaMap) ; + foreach ($data as $row) { + $recordData = ''; + foreach ($this->_mozillaAttr as $value) { + if (isset($row[$mozillaTurbaMap[$value]])) { + // Base64 encode each value as necessary and store it. + // Store cn and mail separately for use in record dn + if (!$this->_is_safe_string($row[$mozillaTurbaMap[$value]])) { + $recordData .= $value . ':: ' . base64_encode($row[$mozillaTurbaMap[$value]]) . "\n"; + } else { + $recordData .= $value . ': ' . $row[$mozillaTurbaMap[$value]] . "\n"; + } + } + } + + $dn = 'cn=' . $row[$mozillaTurbaMap['cn']] . ',mail=' . $row[$mozillaTurbaMap['mail']]; + if (!$this->_is_safe_string($dn)) { + $export .= 'dn:: ' . base64_encode($dn) . "\n"; + } else { + $export .= 'dn: ' . $dn . "\n"; + } + + $export .= "objectclass: top\n" + . "objectclass: person\n" + . "objectclass: organizationalPerson\n" + . "objectclass: inetOrgPerson\n" + . "objectclass: mozillaAbPersonAlpha\n" + . $recordData . "modifytimestamp: 0Z\n\n"; + } + + return $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 Horde_Data::IMPORT_FILE: + $next_step = parent::nextStep($action, $param); + if (is_a($next_step, 'PEAR_Error')) { + return $next_step; + } + + $_SESSION['import_data']['data'] = $this->importFile($_FILES['import_file']['tmp_name']); + $data = array(); + foreach ($_SESSION['import_data']['data'] as $record) { + $turbaHash = array(); + foreach ($this->_turbaAttr as $value) { + switch ($value) { + case 'homeAddress': + // These are the keys we're interested in. + $keys = array('homeStreet', 'mozillaHomeStreet2', + 'mozillaHomeLocalityName', 'mozillaHomeState', + 'mozillaHomePostalCode', 'mozillaHomeCountryName'); + + // Grab all of them that exist in $record. + $values = array_intersect_key($record, array_flip($keys)); + + // Special handling for State if both State + // and Locality Name are set. + if (isset($values['mozillaHomeLocalityName']) + && isset($values['mozillaHomeState'])) { + $values['mozillaHomeLocalityName'] .= ', ' . $values['mozillaHomeState']; + unset($values['mozillaHomeState']); + } + + if ($values) { + $turbaHash[$value] = implode("\n", $values); + } + break; + + case 'workAddress': + // These are the keys we're interested in. + $keys = array('street', 'mozillaWorkStreet2', 'l', + 'st', 'postalCode', 'c'); + + // Grab all of them that exist in $record. + $values = array_intersect_key($record, array_flip($keys)); + + // Special handling for "st" if both "st" and + // "l" are set. + if (isset($values['l']) && isset($values['st'])) { + $values['l'] .= ', ' . $values['st']; + unset($values['st']); + } + + if ($values) { + $turbaHash[$value] = implode("\n", $values); + } + break; + + default: + if (isset($record[$this->_turbaMozillaMap[$value]])) { + $turbaHash[$value] = $record[$this->_turbaMozillaMap[$value]]; + } + break; + } + } + + $data[] = $turbaHash; + } + + unset($_SESSION['import_data']['data']); + return $data; + + default: + return parent::nextStep($action, $param); + } + } + +} diff --git a/turba/lib/Data/ldif.php b/turba/lib/Data/ldif.php deleted file mode 100644 index 34539cb69..000000000 --- a/turba/lib/Data/ldif.php +++ /dev/null @@ -1,297 +0,0 @@ - - * @package Horde_Data - */ -class Horde_Data_Ldif extends Horde_Data -{ - var $_extension = 'ldif'; - - var $_contentType = 'text/ldif'; - - /** - * Useful Mozilla address book attribute names. - * - * @private - * @var array - */ - var $_mozillaAttr = array('cn', 'givenName', 'sn', 'mail', 'mozillaNickname', - 'homeStreet', 'mozillaHomeStreet2', 'mozillaHomeLocalityName', - 'mozillaHomeState', 'mozillaHomePostalCode', - 'mozillaHomeCountryName', 'street', - 'mozillaWorkStreet2', 'l', 'st', 'postalCode', - 'c', 'homePhone', 'telephoneNumber', 'mobile', - 'fax', 'title', 'company', 'description', 'mozillaWorkUrl', - 'department', 'mozillaNickname'); - - /** - * Useful Turba address book attribute names. - * - * @private - * @var array - */ - var $_turbaAttr = array('name', 'firstname', 'lastname', 'email', 'alias', - 'homeAddress', 'homeStreet', 'homeCity', - 'homeProvince', 'homePostalCode', 'homeCountry', - 'workAddress', 'workStreet', 'workCity', 'workProvince', - 'workPostalCode', 'workCountry', - 'homePhone', 'workPhone', 'cellPhone', - 'fax', 'title', 'company', 'notes', 'website', - 'department', 'nickname'); - /** - * Turba address book attribute names and the corresponding Mozilla name. - * - * @private - * @var array - */ - var $_turbaMozillaMap = array('name' => 'cn', - 'firstname' => 'givenName', - 'lastname' => 'sn', - 'email' => 'mail', - 'alias' => 'mozillaNickname', - 'homePhone' => 'homePhone', - 'workPhone' => 'telephoneNumber', - 'cellPhone' => 'mobile', - 'fax' => 'fax', - 'title' => 'title', - 'company' => 'company', - 'notes' => 'description', - 'homeAddress' => 'homeStreet', - 'homeStreet' => 'mozillaHomeStreet2', - 'homeCity' => 'mozillaHomeLocalityName', - 'homeProvince' => 'mozillaHomeState', - 'homePostalCode' => 'mozillaHomePostalCode', - 'homeCountry' => 'mozillaHomeCountryName', - 'workAddress' => 'street', - 'workStreet' => 'mozillaWorkStreet2', - 'workCity' => 'l', - 'workProvince' => 'st', - 'workPostalCode' => 'postalCode', - 'workCountry' => 'c', - 'website' => 'mozillaWorkUrl', - 'department' => 'department', - 'nickname' => 'mozillaNickname'); - - /** - * Check if a string is safe according to RFC 2849, or if it needs to be - * base64 encoded. - * - * @private - * - * @param string $str The string to check. - * - * @return boolean True if the string is safe, false otherwise. - */ - function _is_safe_string($str) - { - /* SAFE-CHAR = %x01-09 / %x0B-0C / %x0E-7F - * ; any value <= 127 decimal except NUL, LF, - * ; and CR - * - * SAFE-INIT-CHAR = %x01-09 / %x0B-0C / %x0E-1F / - * %x21-39 / %x3B / %x3D-7F - * ; any value <= 127 except NUL, LF, CR, - * ; SPACE, colon (":", ASCII 58 decimal) - * ; and less-than ("<" , ASCII 60 decimal) */ - if (!strlen($str)) { - return true; - } - if ($str[0] == ' ' || $str[0] == ':' || $str[0] == '<') { - return false; - } - for ($i = 0; $i < strlen($str); ++$i) { - if (ord($str[$i]) > 127 || $str[$i] == NULL || $str[$i] == "\n" || - $str[$i] == "\r") { - return false; - } - } - - return true; - } - - function importData($contents, $header = false) - { - $data = array(); - $records = preg_split('/(\r?\n){2}/', $contents); - foreach ($records as $record) { - if (trim($record) == '') { - /* Ignore empty records */ - continue; - } - /* one key:value pair per line */ - $lines = preg_split('/\r?\n/', $record); - $hash = array(); - foreach ($lines as $line) { - list($key, $delimiter, $value) = preg_split('/(:[:<]?) */', $line, 2, PREG_SPLIT_DELIM_CAPTURE); - if (in_array($key, $this->_mozillaAttr)) { - $hash[$key] = ($delimiter == '::' ? base64_decode($value) : $value); - } - } - $data[] = $hash; - } - - return $data; - } - - /** - * Builds a LDIF 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/ldif', false, strlen($export)); - echo $export; - } - - /** - * Builds a LDIF 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 LDIF data. - */ - function exportData($data, $header = false) - { - if (!is_array($data) || !count($data)) { - return ''; - } - $export = ''; - $mozillaTurbaMap = array_flip($this->_turbaMozillaMap) ; - foreach ($data as $row) { - $recordData = ''; - foreach ($this->_mozillaAttr as $value) { - if (isset($row[$mozillaTurbaMap[$value]])) { - // Base64 encode each value as necessary and store it. - // Store cn and mail separately for use in record dn - if (!$this->_is_safe_string($row[$mozillaTurbaMap[$value]])) { - $recordData .= $value . ':: ' . base64_encode($row[$mozillaTurbaMap[$value]]) . "\n"; - } else { - $recordData .= $value . ': ' . $row[$mozillaTurbaMap[$value]] . "\n"; - } - } - } - - $dn = 'cn=' . $row[$mozillaTurbaMap['cn']] . ',mail=' . $row[$mozillaTurbaMap['mail']]; - if (!$this->_is_safe_string($dn)) { - $export .= 'dn:: ' . base64_encode($dn) . "\n"; - } else { - $export .= 'dn: ' . $dn . "\n"; - } - - $export .= "objectclass: top\n" - . "objectclass: person\n" - . "objectclass: organizationalPerson\n" - . "objectclass: inetOrgPerson\n" - . "objectclass: mozillaAbPersonAlpha\n" - . $recordData . "modifytimestamp: 0Z\n\n"; - } - - return $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 Horde_Data::IMPORT_FILE: - $next_step = parent::nextStep($action, $param); - if (is_a($next_step, 'PEAR_Error')) { - return $next_step; - } - - $_SESSION['import_data']['data'] = $this->importFile($_FILES['import_file']['tmp_name']); - $data = array(); - foreach ($_SESSION['import_data']['data'] as $record) { - $turbaHash = array(); - foreach ($this->_turbaAttr as $value) { - switch ($value) { - case 'homeAddress': - // These are the keys we're interested in. - $keys = array('homeStreet', 'mozillaHomeStreet2', - 'mozillaHomeLocalityName', 'mozillaHomeState', - 'mozillaHomePostalCode', 'mozillaHomeCountryName'); - - // Grab all of them that exist in $record. - $values = array_intersect_key($record, array_flip($keys)); - - // Special handling for State if both State - // and Locality Name are set. - if (isset($values['mozillaHomeLocalityName']) - && isset($values['mozillaHomeState'])) { - $values['mozillaHomeLocalityName'] .= ', ' . $values['mozillaHomeState']; - unset($values['mozillaHomeState']); - } - - if ($values) { - $turbaHash[$value] = implode("\n", $values); - } - break; - - case 'workAddress': - // These are the keys we're interested in. - $keys = array('street', 'mozillaWorkStreet2', 'l', - 'st', 'postalCode', 'c'); - - // Grab all of them that exist in $record. - $values = array_intersect_key($record, array_flip($keys)); - - // Special handling for "st" if both "st" and - // "l" are set. - if (isset($values['l']) && isset($values['st'])) { - $values['l'] .= ', ' . $values['st']; - unset($values['st']); - } - - if ($values) { - $turbaHash[$value] = implode("\n", $values); - } - break; - - default: - if (isset($record[$this->_turbaMozillaMap[$value]])) { - $turbaHash[$value] = $record[$this->_turbaMozillaMap[$value]]; - } - break; - } - } - - $data[] = $turbaHash; - } - - unset($_SESSION['import_data']['data']); - return $data; - - default: - return parent::nextStep($action, $param); - } - } - -}