Files
server/usr/share/psa-pear/pear/php/Horde/Data/Base.php
2026-01-07 20:52:11 +01:00

432 lines
13 KiB
PHP

<?php
/**
* Copyright 1999-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Jan Schneider <jan@horde.org>
* @author Chuck Hagenbuch <chuck@horde.org>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Data
*/
/**
* Abstract class that Data drivers extend.
*
* @author Jan Schneider <jan@horde.org>
* @author Chuck Hagenbuch <chuck@horde.org>
* @category Horde
* @copyright 1999-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Data
*/
abstract class Horde_Data_Base
{
/**
* Storage object.
*
* @var Horde_Data_Storage
*/
public $storage;
/**
* Browser object.
*
* @var Horde_Browser
*/
protected $_browser;
/**
* HTTP client object.
*
* @var Horde_Http_Client
*/
protected $_http;
/**
* Cleanup callback function.
*
* @var callback
*/
protected $_cleanupCallback;
/**
* MIME content type.
*
* @var string
*/
protected $_contentType = 'text/plain';
/**
* File extension.
*
* @var string
*/
protected $_extension = '';
/**
* Variables object.
*
* @var Horde_Variables
*/
protected $_vars;
/**
* A list of warnings raised during the last operation.
*
* @var array
*/
protected $_warnings = array();
/**
* Constructor.
*
* @param Horde_Data_Storage A storage object.
* @param array $params Optional parameters:
* - browser: (Horde_Browser) A browser object.
* - http: (Horde_Http_Client) A HTTP client object.
* - cleanup: (callback) A callback to call at cleanup time.
* - vars: (Horde_Variables) Form data.
*/
public function __construct(Horde_Data_Storage $storage,
array $params = array())
{
$this->storage = $storage;
if (isset($params['browser'])) {
$this->_browser = $params['browser'];
}
if (isset($params['http'])) {
$this->_http = $params['http'];
}
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');
/**
* Imports a file.
*/
public function importFile($filename, $header = false)
{
$data = file_get_contents($filename);
return $this->importData($data, $header);
}
/**
* Imports a URL.
*
* @since 2.1.0
*/
public function importUrl($url, $header = false)
{
if (!isset($this->_http)) {
throw new LogicException('Missing http parameter.');
}
try {
$response = $this->_http->get($url);
if ($response->code != 200) {
throw new Horde_Data_Exception(sprintf(
Horde_Data_Translation::t("URL %s not found"),
$url
));
}
$data = $response->getBody();
} catch (Horde_Http_Exception $e) {
throw new Horde_Data_Exception($e);
}
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()
{
if (!isset($this->_browser)) {
throw new LogicException('Missing browser parameter.');
}
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(Horde_String::lower($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, array $param = array())
{
/* First step. */
if (is_null($action)) {
return Horde_Data::IMPORT_FILE;
}
switch ($action) {
case Horde_Data::IMPORT_FILE:
if (!isset($this->_browser)) {
throw new LogicException('Missing browser parameter.');
}
/* 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) {
throw new Horde_Data_Exception(Horde_Data_Translation::t("The file contained no data."));
}
$this->storage->set('format', $this->_vars->import_format);
break;
case Horde_Data::IMPORT_MAPPED:
if (!$this->_vars->dataKeys || !$this->_vars->appKeys) {
throw new Horde_Data_Exception(Horde_Data_Translation::t("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);
$dates = $map = array();
if (!$import_data = $this->storage->get('data')) {
$import_data = 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($import_data) &&
count($dates[$dataKeys[$key]]['values']) < 10) {
if (!empty($import_data[$i][$dataKeys[$key]])) {
$dates[$dataKeys[$key]]['values'][] = $import_data[$i][$dataKeys[$key]];
}
++$i;
}
}
}
$this->storage->set('map', $map);
if (count($dates) > 0) {
foreach ($dates as $key => $data) {
if (count($data['values'])) {
$this->storage->set('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 (!$this->storage->exists('data')) {
throw new Horde_Data_Exception(Horde_Data_Translation::t("The uploaded data was lost since the previous step."));
}
/* Build the result data set as an associative array. */
$data = array();
$data_map = $this->storage->get('map');
foreach ($this->storage->get('data') as $row) {
$data_row = array();
foreach ($row as $key => $val) {
if (isset($data_map[$key])) {
$mapped_key = $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[$mapped_key] = $val;
}
}
$data[] = $data_row;
}
return $data;
}
}
/**
* 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()
{
$this->storage->clear();
if ($this->_cleanupCallback) {
return call_user_func($this->_cleanupCallback);
}
}
}