adds LXOffice import definition
authorPhilipp Schüle <p.schuele@metaways.de>
Mon, 12 May 2014 14:58:29 +0000 (16:58 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Wed, 4 Jun 2014 19:59:47 +0000 (21:59 +0200)
* adds test for import of lxoffice contacts (merge my duplicates)
* improves merging of existing records during update
* allows to pass available diff to record->merge

Change-Id: If6072c6c4bcf82cd3c2dafa1990f4b12795f2a9e
Reviewed-on: http://gerrit.tine20.com/customers/623
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Philipp Schüle <p.schuele@metaways.de>
tests/tine20/Addressbook/Import/CsvTest.php
tests/tine20/Addressbook/Import/files/importtest_lxoffice1.csv [new file with mode: 0644]
tests/tine20/Addressbook/Import/files/importtest_lxoffice2.csv [new file with mode: 0644]
tine20/Addressbook/Import/definitions/adb_lxoffice_import_csv.xml [new file with mode: 0644]
tine20/Tinebase/Import/Abstract.php
tine20/Tinebase/Record/Abstract.php

index c23d072..87d1b41 100644 (file)
@@ -77,6 +77,8 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
                 'field' => 'container_id', 'operator' => 'equals', 'value' => Addressbook_Controller_Contact::getInstance()->getDefaultAddressbook()->getId()
             ))));
         }
+        
+        Addressbook_Controller_Contact::getInstance()->duplicateCheckFields(Addressbook_Config::getInstance()->get(Addressbook_Config::CONTACT_DUP_FIELDS));
     }
     
     /**
@@ -251,9 +253,9 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
      * @param string $filename
      * @return Tinebase_Model_ImportExportDefinition
      */
-    protected function _getDefinitionFromFile($filename)
+    protected function _getDefinitionFromFile($filename, $path = null)
     {
-        $filename = dirname(__FILE__) . '/files/' . $filename;
+        $filename = ($path ? $path : dirname(__FILE__) . '/files/') . $filename;
         $applicationId = Tinebase_Application::getInstance()->getApplicationByName('Addressbook')->getId();
         $definition = Tinebase_ImportExportDefinition::getInstance()->getFromFile($filename, $applicationId);
         
@@ -287,14 +289,15 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
     
     /**
     * get custom field record
-    *
+    * 
+    * @param string $name
     * @return Tinebase_Model_CustomField_Config
     */
-    protected function _createCustomField()
+    protected function _createCustomField($name = 'Yomi Name')
     {
         $cfData = new Tinebase_Model_CustomField_Config(array(
             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Addressbook')->getId(),
-            'name'              => 'Yomi Name',
+            'name'              => $name,
             'model'             => 'Addressbook_Model_Contact',
             'definition'        => array(
                 'label' => Tinebase_Record_Abstract::generateUID(),
@@ -313,7 +316,7 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
         } catch (Zend_Db_Statement_Exception $zdse) {
             // customfield already exists
             $cfs = Tinebase_CustomField::getInstance()->getCustomFieldsForApplication('Addressbook');
-            $result = $cfs->filter('name', 'Yomi Name')->getFirstRecord();
+            $result = $cfs->filter('name', $name)->getFirstRecord();
         }
         
         return $result;
@@ -335,7 +338,6 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
         $this->_deletePersonalContacts = TRUE;
 
         $this->_filename = dirname(__FILE__) . '/files/import_duplicate_2.csv';
-        $this->_deleteImportFile = FALSE;
         
         $result = $this->_doImport(array(), $definition);
         
@@ -343,4 +345,51 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
         $this->assertEquals('joerg@home.com', $result['results'][0]->email_home, 'duplicates resolving did not work: ' . print_r($result['results']->toArray(), true));
         $this->assertEquals('Jörg', $result['results'][0]->n_given, 'wrong encoding: ' . print_r($result['results']->toArray(), true));
     }
+
+    /**
+     * testImportLxOffice
+     */
+    public function testImportLxOffice()
+    {
+        $options = array(
+            'container_id'  => Addressbook_Controller_Contact::getInstance()->getDefaultAddressbook()->getId(),
+        );
+        
+        // add duplicate field "customernumber"
+        Addressbook_Controller_Contact::getInstance()->duplicateCheckFields(array(
+            array('n_given', 'n_family', 'org_name'),
+            array('email'),
+            array('customernumber')
+        ));
+        
+        $this->_createCustomField('customernumber');
+        
+        $definition = $this->_getDefinitionFromFile('adb_lxoffice_import_csv.xml',
+            dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/tine20/Addressbook/Import/definitions/');
+        
+        $this->_filename = dirname(__FILE__) . '/files/importtest_lxoffice1.csv';
+        $this->_deleteImportFile = FALSE;
+        
+        $result = $this->_doImport($options, $definition);
+        $this->_deletePersonalContacts = TRUE;
+        
+        $this->assertEquals(3, $result['totalcount'], print_r($result['results']->toArray(), true));
+        
+        $contacts = $result['results'];
+        $berger = $contacts->getFirstRecord();
+        $this->assertEquals(array('customernumber' => '73029'), $berger->customfields, print_r($berger->toArray(), true));
+        
+        $this->_filename = dirname(__FILE__) . '/files/importtest_lxoffice2.csv';
+        
+        $result = $this->_doImport($options, $definition);
+        
+        $this->assertEquals(4, count($result['results']));
+        $this->assertEquals(3, $result['updatecount'], 'should have updated 3 contacts');
+        $this->assertEquals(1, $result['totalcount'], 'should have added 1 contact');
+        $this->assertEquals('Straßbough', $result['results'][1]['adr_one_locality'],
+                'should have changed the locality of contact #2: ' . print_r($result['results'][1]->toArray(), true));
+        $this->assertEquals('Dr. Schutheiss', $result['results'][3]['n_family']);
+        $this->assertEquals(1, $result['results'][2]['seq'], 'Wolfer should not be updated - nothing changed');
+        $this->assertEquals('Weixdorf DD', $result['results'][0]['adr_one_locality'], 'locality should persist');
+    }
 }
diff --git a/tests/tine20/Addressbook/Import/files/importtest_lxoffice1.csv b/tests/tine20/Addressbook/Import/files/importtest_lxoffice1.csv
new file mode 100644 (file)
index 0000000..48d18c4
--- /dev/null
@@ -0,0 +1,4 @@
+customernumber,greeting,name,department_1,street,country,zipcode,city,phone,fax,email,cp_greeting,cp_title,cp_givenname,cp_name,cp_phone1,cp_phone2,cp_fax,cp_mobile1,cp_mobile2,cp_email
+"73029",,"Wolfgang Berger","","Heuerstraße 2","D","11108","Weixdorf DD","12345","","",,,,,,,,,,
+"72810",,"Nitsch","","Heuerstraße 16","D","11418","Staßfurt","1234","","",,,,,,,,,,
+"72911",,"Wolfer","","Heuerstraße 44","D","11558","Großenhain","","","",,,,,,,,,,
diff --git a/tests/tine20/Addressbook/Import/files/importtest_lxoffice2.csv b/tests/tine20/Addressbook/Import/files/importtest_lxoffice2.csv
new file mode 100644 (file)
index 0000000..9e568ba
--- /dev/null
@@ -0,0 +1,5 @@
+customernumber,greeting,name,department_1,street,country,zipcode,city,phone,fax,email,cp_greeting,cp_title,cp_givenname,cp_name,cp_phone1,cp_phone2,cp_fax,cp_mobile1,cp_mobile2,cp_email
+"73029","Wolfgang","Berger","","Heuerstraße 2","D","11108","","12345","","",,,,,,,,,,
+"72810",,"Nitsch","","Heuerstraße 16","D","22222","Straßbough","12345","","",,,,,,,,,,
+"72911",,"Wolfer","","Heuerstraße 44","D","11558","Großenhain","","","",,,,,,,,,,
+"73222",,"Dr. Schutheiss","","Heuerstraße 4","D","11120","Magdeburg","1233453","","",,,,,,,,,,
\ No newline at end of file
diff --git a/tine20/Addressbook/Import/definitions/adb_lxoffice_import_csv.xml b/tine20/Addressbook/Import/definitions/adb_lxoffice_import_csv.xml
new file mode 100644 (file)
index 0000000..e6c72c3
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<config>
+    <name>adb_lxoffice_import_csv_merge</name>
+    <model>Addressbook_Model_Contact</model>
+    <plugin>Addressbook_Import_Csv</plugin>
+    <type>import</type>
+    <headline>1</headline>
+    <dryrun>0</dryrun>
+    <delimiter>,</delimiter>
+    <label>Contact CSV import</label>
+    <!-- _("Contact CSV import") -->
+    <description>tine adb import</description>
+    <example>Addressbook/Import/examples/adb_tine_import.csv</example>
+    <extension>csv</extension>
+    <duplicateResolveStrategy>mergeMine</duplicateResolveStrategy>
+    <!--container_id>12</container_id-->
+    <mapping>
+        <field>
+            <source>customernumber</source>
+            <destination>customernumber</destination>
+        </field>
+        <field>
+            <source>greeting</source>
+            <destination>salutation</destination>
+        </field>
+        <field>
+            <source>name</source>
+            <destination>n_fn</destination>
+        </field>
+        <field>
+            <source>department_1</source>
+            <destination>adr_one_region</destination>
+        </field>
+        <field>
+            <source>street</source>
+            <destination>adr_one_street</destination>
+        </field>
+        <field>
+            <source>country</source>
+            <destination>adr_one_countryname</destination>
+        </field>
+        <field>
+            <source>zipcode</source>
+            <destination>adr_one_postalcode</destination>
+        </field>
+        <field>
+            <source>city</source>
+            <destination>adr_one_locality</destination>
+        </field>
+        <field>
+            <source>phone</source>
+            <destination>tel_work</destination>
+        </field>
+        <field>
+            <source>fax</source>
+            <destination>tel_fax</destination>
+        </field>
+        <field>
+            <source>email</source>
+            <destination>email</destination>
+        </field>
+     </mapping>
+</config>
index ef36597..8cd769f 100644 (file)
@@ -809,42 +809,86 @@ abstract class Tinebase_Import_Abstract implements Tinebase_Import_Interface
      * 
      * @param Tinebase_Record_Abstract $record
      * @param string $resolveStrategy
-     * @param Tinebase_Record_Abstract $existingRecord
+     * @param Tinebase_Record_Abstract $clientRecord
      * @return Tinebase_Record_Abstract
      */
-    protected function _importAndResolveConflict(Tinebase_Record_Abstract $record, $resolveStrategy = null, $existingRecord = null)
+    protected function _importAndResolveConflict(Tinebase_Record_Abstract $record, $resolveStrategy = null, $clientRecord = null)
     {
         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
-            . ' resolveStrategy: ' . $resolveStrategy);
-        if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
-            . ' record to import: ' . print_r($record->toArray(), TRUE));
-        if (Tinebase_Core::isLogLevel(Zend_Log::TRACE) && $existingRecord) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
-            . ' existing record: ' . print_r($existingRecord->toArray(), TRUE));
+            . ' ResolveStrategy: ' . $resolveStrategy);
+        if ($clientRecord && Tinebase_Core::isLogLevel(Zend_Log::TRACE) && $clientRecord) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
+            . ' Client record: ' . print_r($clientRecord->toArray(), TRUE));
         
         switch ($resolveStrategy) {
             case 'mergeTheirs':
             case 'mergeMine':
                 if ($resolveStrategy === 'mergeTheirs') {
-                    $record = $record->merge($existingRecord);
-                } else if ($existingRecord) {
-                    $record = $existingRecord->merge($record);
+                    $recordToUpdate = $this->_mergeRecord($record, $clientRecord);
+                } else if ($clientRecord) {
+                    $recordToUpdate = $this->_mergeRecord($clientRecord, $record);
+                } else {
+                    $recordToUpdate = null;
+                }
+                
+                if ($recordToUpdate !== null) {
+                    if (Tinebase_Core::isLogLevel(Zend_Log::TRACE) && $clientRecord) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
+                        . ' Merged record: ' . print_r($record->toArray(), TRUE));
+                    
+                    $record = call_user_func(array($this->_controller, $this->_options['updateMethod']), $recordToUpdate, FALSE);
                 }
-                $record = call_user_func(array($this->_controller, $this->_options['updateMethod']), $record, FALSE);
+                
                 break;
             case 'keep':
-                // do not check for duplicates (keep both)
+                if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
+                    . ' Record to import (keep both / no duplicate check): ' . print_r($record->toArray(), TRUE));
+                
                 $record = call_user_func(array($this->_controller, $this->_options['createMethod']), $record, FALSE);
                 break;
             default:
+                if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
+                    . ' Record to import: ' . print_r($record->toArray(), TRUE));
+                
                 $record = call_user_func(array($this->_controller, $this->_options['createMethod']), $record);
         }
         
-        if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($record->toArray(), TRUE));
+        if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ 
+            . ' record: ' . print_r($record->toArray(), TRUE));
         
         return $record;
     }
     
     /**
+     * merge record / skip if no diff
+     * 
+     * @param Tinebase_Record_Abstract $updateRecord
+     * @param Tinebase_Record_Abstract $mergeRecord
+     * @return Tinebase_Record_Abstract
+     */
+    protected function _mergeRecord($updateRecord, $mergeRecord)
+    {
+        $omitFields = array(
+            'creation_time',
+            'created_by',
+            'last_modified_time',
+            'last_modified_by',
+            'seq',
+            'id'
+        );
+        
+        $diff = $updateRecord->diff($mergeRecord, $omitFields);
+        if (! $diff || $diff->isEmpty()) {
+            if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+                . ' Records are identical, no need to update');
+            return null;
+        } else {
+            if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
+                . ' Got diff: ' . print_r($diff->diff, TRUE));
+        }
+        
+        return $updateRecord->merge($mergeRecord, $diff);
+    }
+    
+    /**
      * handle import exceptions
      * 
      * @param Exception $e
index 8f55130..f954d48 100644 (file)
@@ -1029,14 +1029,19 @@ abstract class Tinebase_Record_Abstract implements Tinebase_Record_Interface
      * merge given record into $this
      * 
      * @param Tinebase_Record_Interface $record
+     * @param Tinebase_Record_Diff $diff
      * @return Tinebase_Record_Interface
      */
-    public function merge($record)
+    public function merge($record, $diff = null)
     {
         if (! $this->getId()) {
             $this->setId($record->getId());
         }
-        $diff = $this->diff($record);
+        
+        if ($diff === null) {
+            $diff = $this->diff($record);
+        }
+        
         if ($diff === null || empty($diff->diff)) {
             return $this;
         }