"Fossies" - the Fresh Open Source Software Archive

Member "tine20-2019.12.4/tine20/Tinebase/Import/Csv/Abstract.php" (16 Jan 2020, 15382 Bytes) of package /linux/www/tine20-2019.12.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Abstract.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 /**
    3  * Tine 2.0
    4  * 
    5  * @package     Tinebase
    6  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
    7  * @author      Philipp Schüle <p.schuele@metaways.de>
    8  * @copyright   Copyright (c) 2007-2018 Metaways Infosystems GmbH (http://www.metaways.de)
    9  */
   10 
   11 /**
   12  * abstract csv import class
   13  * 
   14  * some documentation for the xml import definition:
   15  * 
   16  * <delimiter>TAB</delimiter>:           use tab as delimiter
   17  * <config> main tags
   18  * <container_id>34</container_id>:     container id for imported records (required)
   19  * <encoding>UTF-8</encoding>:          encoding of input file
   20  * <duplicates>1<duplicates>:           check for duplicates
   21  * <use_headline>0</use_headline>:      just remove the headline/first line but do not use it for mapping
   22  *
   23  * <mapping><field> special tags:
   24  * <append>glue</append>:               value is appended to destination field with 'glue' as glue
   25  * <replace>\n</replace>:               replace \r\n with \n
   26  * <fixed>fixed</fixed>:                the field has a fixed value ('fixed' in this example)
   27  * 
   28  *
   29  * @todo        add tests for notes
   30  * @todo        add more documentation
   31  * @package     Tinebase
   32  * @subpackage  Import
   33  */
   34 abstract class Tinebase_Import_Csv_Abstract extends Tinebase_Import_Abstract
   35 {
   36     /**
   37      * csv headline
   38      * 
   39      * @var array
   40      */
   41     protected $_headline = array();
   42 
   43     // this week
   44     protected $_monday       = NULL;
   45     protected $_tuesday      = NULL;
   46     protected $_wednesday    = NULL;
   47     protected $_thursday     = NULL;
   48     protected $_friday       = NULL;
   49     protected $_saturday     = NULL;
   50     protected $_sunday       = NULL;
   51 
   52     // last week
   53     protected $_lastMonday   = NULL;
   54     protected $_lastFriday   = NULL;
   55     protected $_lastSaturday = NULL;
   56     protected $_lastSunday   = NULL;
   57 
   58     // next week
   59     protected $_nextMonday     = NULL;
   60     protected $_nextWednesday  = NULL;
   61     protected $_nextFriday     = NULL;
   62 
   63     protected $_wednesday2week = NULL;
   64     protected $_friday2week    = NULL;
   65 
   66     // next year
   67     protected $_nextyear       = NULL;
   68     protected $_next2year       = NULL;
   69     
   70     /**
   71      * special delimiters
   72      * 
   73      * @var array
   74      */
   75     protected $_specialDelimiter = array(
   76         'TAB'   => "\t"
   77     );
   78     
   79     /**
   80      * constructs a new importer from given config
   81      * 
   82      * @param array $_options
   83      * @throws Tinebase_Exception_InvalidArgument
   84      */
   85     public function __construct(array $_options = array())
   86     {
   87         $this->_options = array_merge($this->_options, array(
   88             'maxLineLength'               => 8000,
   89             'delimiter'                   => ',',
   90             'enclosure'                   => '"',
   91             'escape'                      => '\\',
   92             'encodingTo'                  => 'UTF-8',
   93             'mapping'                     => '',
   94             'headline'                    => 0,
   95             'use_headline'                => 1,
   96             'mapUndefinedFieldsEnable'    => 0,
   97             'mapUndefinedFieldsTo'        => 'description',
   98             'demoData'                    => false
   99         ));
  100 
  101         $this->_days();
  102         
  103         parent::__construct($_options);
  104 
  105         if (empty($this->_options['model'])) {
  106             throw new Tinebase_Exception_InvalidArgument(get_class($this) . ' needs model in config.');
  107         }
  108         
  109         $this->_setController();
  110     }
  111 
  112     /**
  113      *
  114      * @param Tinebase_DateTime $now
  115      */
  116     protected function _days(Tinebase_DateTime $now = NULL)
  117     {
  118         // find out where we are
  119         if (! $now) {
  120             $now = new Tinebase_DateTime();
  121         }
  122         $weekday = $now->format('w');
  123 
  124         $subdaysLastMonday = 6 + $weekday;    // Monday last Week
  125         $subdaysLastFriday = 2 + $weekday;    // Friday last Week
  126 
  127         // this week
  128         $this->_monday = new Tinebase_DateTime();
  129         $this->_monday->sub(date_interval_create_from_date_string(($weekday - 1) . ' days'));
  130         $this->_tuesday = new Tinebase_DateTime();
  131         $this->_tuesday->sub(date_interval_create_from_date_string(($weekday - 2) . ' days'));
  132         $this->_wednesday = new Tinebase_DateTime();
  133         $this->_wednesday->sub(date_interval_create_from_date_string(($weekday - 3) . ' days'));
  134         $this->_thursday = new Tinebase_DateTime();
  135         $this->_thursday->sub(date_interval_create_from_date_string(($weekday - 4) . ' days'));
  136         $this->_friday = new Tinebase_DateTime();
  137         $this->_friday->sub(date_interval_create_from_date_string(($weekday - 5) . ' days'));
  138         $this->_saturday = clone $this->_friday;
  139         $this->_saturday->add(date_interval_create_from_date_string('1 day'));
  140         $this->_sunday = clone $this->_friday;
  141         $this->_sunday->add(date_interval_create_from_date_string('2 days'));
  142 
  143         // last week
  144         $this->_lastMonday = clone $this->_monday;
  145         $this->_lastMonday->subWeek(1);
  146         $this->_lastWednesday = clone $this->_wednesday;
  147         $this->_lastWednesday->subWeek(1);
  148         $this->_lastFriday = clone $this->_friday;
  149         $this->_lastFriday->subWeek(1);
  150         $this->_lastThursday = clone $this->_thursday;
  151         $this->_lastThursday->subWeek(1);
  152         $this->_lastSaturday = clone $this->_saturday;
  153         $this->_lastSaturday->subWeek(1);
  154         $this->_lastSunday = clone $this->_sunday;
  155         $this->_lastSunday->subWeek(1);
  156 
  157         $this->_nextMonday = clone $this->_monday;
  158         $this->_nextMonday->addWeek(1);
  159         $this->_nextTuesday = clone $this->_tuesday;
  160         $this->_nextTuesday->addWeek(1);
  161         $this->_nextWednesday = clone $this->_wednesday;
  162         $this->_nextWednesday->addWeek(1);
  163         $this->_nextThursday = clone $this->_thursday;
  164         $this->_nextThursday->addWeek(1);
  165         $this->_nextFriday = clone $this->_friday;
  166         $this->_nextFriday->addWeek(1);
  167 
  168         $this->_wednesday2week = clone $this->_nextWednesday;
  169         $this->_wednesday2week->addWeek(1);
  170         $this->_friday2week = clone $this->_nextFriday;
  171         $this->_friday2week->addWeek(1);
  172 
  173         $this->_nextyear = new Tinebase_DateTime();
  174         $this->_nextyear->addYear(1);
  175         $this->_next2year = new Tinebase_DateTime();
  176         $this->_next2year->addYear(2);
  177 
  178 
  179     }
  180 
  181     protected function _getDay($data,$dates)
  182     {
  183         foreach ($dates as $date) {
  184             if(!empty($data[$date]) && $data[$date] != 'today') {
  185                 $data[$date] = $this->{'_' . $data[$date]};
  186             }else
  187             {
  188                 $data[$date] = new Tinebase_DateTime();
  189             }
  190         }
  191         return $data;
  192     }
  193 
  194     /**
  195      * do conversions
  196      *
  197      * @param array $_data
  198      * @return array
  199      */
  200     protected function _doConversions($_data)
  201     {
  202         if ($this->_options['demoData'] && isset($this->_additionalOptions['dates'])) $_data = $this->_getDay($_data,
  203             $this->_additionalOptions['dates']);
  204         $result = parent::_doConversions($_data);
  205         return $result;
  206     }
  207 
  208     /**
  209      * get raw data of a single record
  210      * 
  211      * @param  resource $_resource
  212      * @return array|null
  213      */
  214     protected function _getRawData(&$_resource)
  215     {
  216         $delimiter = ((isset($this->_specialDelimiter[$this->_options['delimiter']])
  217             || array_key_exists($this->_options['delimiter'], $this->_specialDelimiter))
  218         )
  219             ? $this->_specialDelimiter[$this->_options['delimiter']]
  220             : $this->_options['delimiter'];
  221         $lineData = fgetcsv(
  222             $_resource,
  223             $this->_options['maxLineLength'],
  224             $delimiter,
  225             $this->_options['enclosure'],
  226             $this->_options['escape']
  227         );
  228         
  229         if (is_array($lineData) && count($lineData) == 1) {
  230             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(
  231                 __METHOD__ . '::' . __LINE__ . ' Only got 1 field in line. Wrong delimiter?');
  232             return null;
  233         }
  234         
  235         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
  236             . ' Raw data: ' . print_r($lineData, true));
  237         
  238         return $lineData;
  239     }
  240     
  241     /**
  242      * do something before the import
  243      * 
  244      * @param resource $_resource
  245      */
  246     protected function _beforeImport($_resource = NULL)
  247     {
  248         // get headline
  249         if (isset($this->_options['headline']) && $this->_options['headline']) {
  250             $firstLine = $this->_getRawData($_resource);
  251             $this->_headline = $firstLine ? $firstLine : array();
  252             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
  253                 . ' Got headline: ' . implode(', ', $this->_headline));
  254             if (! $this->_options['use_headline']) {
  255                 // just read headline but do not use it
  256                 $this->_headline = array();
  257             } else {
  258                 array_walk($this->_headline, function(&$value) {
  259                     $value = trim($value);
  260                 });
  261             }
  262         }
  263     }
  264     
  265     /**
  266      * do the mapping
  267      *
  268      * @param array $_data
  269      * @return array
  270      * @throws Tinebase_Exception_UnexpectedValue
  271      */
  272     protected function _doMapping($_data)
  273     {
  274         $data = array();
  275         $_data_indexed = array();
  276 
  277         if (! $_data) {
  278             if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(
  279                 __METHOD__ . '::' . __LINE__ . ' Got empty raw data - skipping.');
  280             return $data;
  281         }
  282 
  283         if (!empty($this->_headline)) {
  284             $headlineSize = sizeof($this->_headline);
  285             $dataSize = sizeof($_data);
  286             if ($headlineSize > $dataSize) {
  287                 $arrayWithEmptyValues = array_fill($dataSize, $headlineSize - $dataSize, '');
  288                 if (is_array($arrayWithEmptyValues)) {
  289                     $_data = array_merge($_data, $arrayWithEmptyValues);
  290                 }
  291             } elseif ($dataSize > $headlineSize) {
  292                 // TODO throw an exception if this happens?
  293                 $arrayWithUnknownValues = array_fill($headlineSize, $dataSize - $headlineSize, 'unknown');
  294                 if (is_array($arrayWithUnknownValues)) {
  295                     $this->_headline = array_merge($this->_headline, $arrayWithUnknownValues);
  296                 }
  297             }
  298             $_data_indexed = array_combine($this->_headline, $_data);
  299         }
  300 
  301         if (! isset($this->_options['mapping']['field']) || ! is_array($this->_options['mapping']['field'])) {
  302             throw new Tinebase_Exception_UnexpectedValue('No field mapping defined');
  303         }
  304 
  305         $this->_mapValuesToDestination($_data_indexed, $_data, $data);
  306 
  307         if ($this->_options['mapUndefinedFieldsEnable'] == 1) {
  308             $undefinedFieldsText = $this->_createInfoTextForUnmappedFields($_data_indexed);
  309             if (! $undefinedFieldsText === false) {
  310                 if ((isset($data[$this->_options['mapUndefinedFieldsTo']]) || array_key_exists($this->_options['mapUndefinedFieldsTo'], $data))) {
  311                     $data[$this->_options['mapUndefinedFieldsTo']] .= $this->_createInfoTextForUnmappedFields($_data_indexed);
  312                 } else {
  313                     $data[$this->_options['mapUndefinedFieldsTo']] = $this->_createInfoTextForUnmappedFields($_data_indexed);
  314                 }
  315             }
  316         }
  317         
  318         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
  319             . ' Mapped data: ' . print_r($data, true));
  320         
  321         return $data;
  322     }
  323 
  324     /**
  325      * map values to destination fields
  326      *
  327      * @param $_data_indexed
  328      * @param $_data
  329      * @param $data
  330      */
  331     protected function _mapValuesToDestination($_data_indexed, $_data, &$data)
  332     {
  333         foreach ($this->_options['mapping']['field'] as $index => $field) {
  334             if (empty($_data_indexed) && isset($_data[$index])) {
  335                 $value = $_data[$index];
  336             } else if (isset($field['source']) && isset($_data_indexed[$field['source']])) {
  337                 $value = $_data_indexed[$field['source']];
  338             } else {
  339                 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
  340                     . ' No value found for field ' . (isset($field['source']) ? $field['source'] : print_r($field, true)));
  341                 continue;
  342             }
  343 
  344             if ((! isset($field['destination']) || empty($field['destination'])) && ! isset($field['destinations'])) {
  345                 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
  346                     . ' No destination in definition for field ' . $field['source']);
  347                 continue;
  348             }
  349 
  350             if (isset($field['destinations']) && isset($field['destinations']['destination'])) {
  351                 $destinations = $field['destinations']['destination'];
  352                 $delimiter = isset($field['$separator']) && ! empty($field['$separator']) ? $field['$separator'] : ' ';
  353                 $values = array_map('trim', explode($delimiter, $value, count($destinations)));
  354                 if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
  355                     . ' values: ' . print_r($values, true));
  356                 $i = 0;
  357                 foreach ($destinations as $destination) {
  358                     if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
  359                         . ' destination ' . $destination);
  360                     if (isset($values[$i])) {
  361                         $data[$destination] = trim($values[$i]);
  362                     }
  363                     $i++;
  364                 }
  365             } else {
  366                 $data[$field['destination']] = $value;
  367             }
  368         }
  369     }
  370     
  371     /**
  372      * Generates a text with every undefined data from import 
  373      * 
  374      * @param array $_data_indexed
  375      * @return string
  376      */
  377     protected function _createInfoTextForUnmappedFields ($_data_indexed)
  378     {
  379         $return = null;
  380         
  381         $translation = Tinebase_Translation::getTranslation('Tinebase');
  382         
  383         $validKeys = array();
  384         foreach ($this->_options['mapping']['field'] as $keys) {
  385             $validKeys[$keys['source']] = null;
  386         }
  387         // This is an array containing every not mapped field as key with his value.
  388         $notImportedFields = array_diff_key($_data_indexed, $validKeys);
  389         
  390         if (count($notImportedFields) >= 1) {
  391             $description = sprintf($translation->_("The following fields weren't imported: %s"), "\n");
  392             $valueIfEmpty = $translation->_("N/A");
  393             
  394             foreach ($notImportedFields as $nKey => $nVal) {
  395                 if (trim($nKey) == "") $nKey = $valueIfEmpty;
  396                 if (trim($nVal) == "") $nVal = $valueIfEmpty;
  397                 
  398                 $description .= $nKey . " : " . $nVal . " \n";
  399             }
  400             $return = $description;
  401         } else {
  402             $return = false;
  403         }
  404         return $return;
  405     }
  406 }