adds LXOffice import definition
[tine20] / tests / tine20 / Addressbook / Import / CsvTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Addressbook
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2008-2013 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  */
10
11 /**
12  * Test helper
13  */
14 require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
15
16 /**
17  * Test class for Addressbook_Import_Csv
18  */
19 class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
20 {
21     /**
22      * @var Addressbook_Import_Csv instance
23      */
24     protected $_instance = NULL;
25     
26     /**
27      * @var string $_filename
28      */
29     protected $_filename = NULL;
30     
31     /**
32      * @var boolean
33      */
34     protected $_deleteImportFile = TRUE;
35     
36     protected $_deletePersonalContacts = FALSE;
37     
38     /**
39      * Runs the test methods of this class.
40      *
41      * @access public
42      * @static
43      */
44     public static function main()
45     {
46         $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Addressbook Csv Import Tests');
47         PHPUnit_TextUI_TestRunner::run($suite);
48     }
49
50     /**
51      * Sets up the fixture.
52      * This method is called before a test is executed.
53      *
54      * @access protected
55      */
56     protected function setUp()
57     {
58         // always resolve customfields
59         Addressbook_Controller_Contact::getInstance()->resolveCustomfields(TRUE);
60     }
61
62     /**
63      * Tears down the fixture
64      * This method is called after a test is executed.
65      *
66      * @access protected
67      */
68     protected function tearDown()
69     {
70         // cleanup
71         if (file_exists($this->_filename) && $this->_deleteImportFile) {
72             unlink($this->_filename);
73         }
74         
75         if ($this->_deletePersonalContacts) {
76             Addressbook_Controller_Contact::getInstance()->deleteByFilter(new Addressbook_Model_ContactFilter(array(array(
77                 'field' => 'container_id', 'operator' => 'equals', 'value' => Addressbook_Controller_Contact::getInstance()->getDefaultAddressbook()->getId()
78             ))));
79         }
80         
81         Addressbook_Controller_Contact::getInstance()->duplicateCheckFields(Addressbook_Config::getInstance()->get(Addressbook_Config::CONTACT_DUP_FIELDS));
82     }
83     
84     /**
85      * test import duplicate data
86      * 
87      * @return array
88      */
89     public function testImportDuplicates()
90     {
91         $internalContainer = Tinebase_Container::getInstance()->getContainerByName('Addressbook', 'Internal Contacts', Tinebase_Model_Container::TYPE_SHARED);
92         $options = array(
93             'container_id'  => $internalContainer->getId(),
94         );
95         $result = $this->_doImport($options, 'adb_tine_import_csv', new Addressbook_Model_ContactFilter(array(
96             array('field' => 'container_id', 'operator' => 'equals', 'value' => $internalContainer->getId()),
97         )));
98         
99         $this->assertGreaterThan(0, $result['duplicatecount'], 'no duplicates.');
100         $this->assertTrue($result['exceptions'] instanceof Tinebase_Record_RecordSet);
101         
102         return $result;
103     }
104     
105     /**
106      * test import data
107      */
108     public function testImportSalutation()
109     {
110         $myContact = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId());
111         $salutation = Addressbook_Config::getInstance()->get(Addressbook_Config::CONTACT_SALUTATION)->records->getFirstRecord()->value;
112         $myContact->salutation = $salutation;
113         Addressbook_Controller_Contact::getInstance()->update($myContact);
114         
115         $result = $this->testImportDuplicates();
116         
117         $found = FALSE;
118         foreach ($result['exceptions'] as $exception) {
119             if ($exception['exception']['clientRecord']['n_fn'] === Tinebase_Core::getUser()->accountFullName) {
120                 $found = TRUE;
121                 $this->assertTrue(isset($exception['exception']['clientRecord']['salutation']), 'no salutation found: ' . print_r($exception['exception']['clientRecord'], TRUE));
122                 $this->assertEquals($salutation, $exception['exception']['clientRecord']['salutation']);
123                 break;
124             }
125         }
126         
127         $this->assertTrue($found);
128     }
129
130     /**
131      * test import umlaut
132      * 
133      * @see 0006936: detect import file encoding
134      */
135     public function testImportUmlaut()
136     {
137         $myContact = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId());
138         $myContact->org_name = 'Übel leckerer Äppler';
139         Addressbook_Controller_Contact::getInstance()->update($myContact);
140         
141         $result = $this->testImportDuplicates();
142         
143         $found = FALSE;
144         foreach ($result['exceptions'] as $exception) {
145             $record = $exception['exception']['clientRecord'];
146             if ($record['n_fn'] === Tinebase_Core::getUser()->accountFullName) {
147                 $found = TRUE;
148                 $this->assertEquals($myContact->org_name, $record['org_name']);
149             }
150         }
151         
152         $this->assertTrue($found);
153     }
154     
155     /**
156      * import google contacts
157      */
158     public function testImportGoogleContacts()
159     {
160         $this->_filename = dirname(__FILE__) . '/files/google_contacts.csv';
161         $this->_deleteImportFile = FALSE;
162         
163         $result = $this->_doImport(array('dryrun' => TRUE), 'adb_google_import_csv');
164         
165         $this->assertEquals(5, $result['totalcount']);
166         $this->assertEquals('Niedersachsen Ring 22', $result['results'][4]->adr_one_street);
167         $this->assertEquals('abc@here.de', $result['results'][3]->email);
168         $this->assertEquals('+49227913452', $result['results'][0]->tel_work);
169     }
170     
171     /**
172      * test import of a customfield
173      */
174     public function testImportCustomField()
175     {
176         $customField = $this->_createCustomField();
177         
178         // create/get new import/export definition with customfield
179         $filename = dirname(__FILE__) . '/files/adb_google_import_csv_test.xml';
180         $applicationId = Tinebase_Application::getInstance()->getApplicationByName('Addressbook')->getId();
181         $definition = Tinebase_ImportExportDefinition::getInstance()->getFromFile($filename, $applicationId);
182         
183         $this->_filename = dirname(__FILE__) . '/files/google_contacts.csv';
184         $this->_deleteImportFile = FALSE;
185         
186         $result = $this->_doImport(array(), $definition);
187         $this->_deletePersonalContacts = TRUE;
188         $this->assertEquals(5, $result['totalcount']);
189         
190         $contacts = Addressbook_Controller_Contact::getInstance()->search(new Addressbook_Model_ContactFilter(array(
191             array('field' => 'container_id', 'operator' => 'equals', 'value' => Addressbook_Controller_Contact::getInstance()->getDefaultAddressbook()->getId()),
192             array('field' => 'n_given', 'operator' => 'equals', 'value' => 'Ando'),
193         )));
194         $this->assertEquals(1, count($contacts));
195         $ando = $contacts->getFirstRecord();
196         $this->assertEquals(array('Yomi Name' => 'yomi'), $ando->customfields);
197     }
198     
199     /**
200      * testExportAndImportWithCustomField
201      * 
202      * @see 0006230: add customfields to csv export
203      */
204     public function testExportAndImportWithCustomField()
205     {
206         $customField = $this->_createCustomField();
207         $ownContact = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId());
208         $cfValue = array($customField->name => 'testing');
209         $ownContact->customfields = $cfValue;
210         Addressbook_Controller_Contact::getInstance()->update($ownContact);
211         
212         $definition = Tinebase_ImportExportDefinition::getInstance()->getByName('adb_tine_import_csv');
213         $definition->plugin_options = preg_replace('/<\/mapping>/',
214             '<field>
215                 <source>Yomi Name</source>
216                 <destination>Yomi Name</destination>
217             </field></mapping>', $definition->plugin_options);
218         $result = $this->_doImport(array(), $definition, new Addressbook_Model_ContactFilter(array(
219             array('field' => 'id', 'operator' => 'equals', 'value' => $ownContact->getId()),
220         )));
221         $this->assertGreaterThan(0, $result['duplicatecount'], 'no duplicates.');
222         $this->assertTrue($result['exceptions'] instanceof Tinebase_Record_RecordSet);
223
224         $exceptionArray = $result['exceptions']->toArray();
225         $this->assertTrue(isset($exceptionArray[0]['exception']['clientRecord']['customfields']),
226             'could not find customfields in client record: ' . print_r($exceptionArray[0]['exception']['clientRecord'], TRUE));
227         $this->assertEquals('testing', $exceptionArray[0]['exception']['clientRecord']['customfields'][$customField->name],
228             'could not find cf value in client record: ' . print_r($exceptionArray[0]['exception']['clientRecord'], TRUE));
229     }
230     
231     /**
232      * testImportWithUmlautsWin1252
233      * 
234      * @see 0006534: import of contacts with umlaut as first char fails
235      */
236     public function testImportWithUmlautsWin1252()
237     {
238         $definition = $this->_getDefinitionFromFile('adb_import_csv_win1252.xml');
239         
240         $this->_filename = dirname(__FILE__) . '/files/importtest_win1252.csv';
241         $this->_deleteImportFile = FALSE;
242         
243         $result = $this->_doImport(array(), $definition);
244         $this->_deletePersonalContacts = TRUE;
245         
246         $this->assertEquals(4, $result['totalcount']);
247         $this->assertEquals('Üglü, ÖzdemirÖ', $result['results'][2]->n_fileas, 'Umlauts were not imported correctly: ' . print_r($result['results'][2]->toArray(), TRUE));
248     }
249     
250     /**
251      * returns import defintion from file
252      * 
253      * @param string $filename
254      * @return Tinebase_Model_ImportExportDefinition
255      */
256     protected function _getDefinitionFromFile($filename, $path = null)
257     {
258         $filename = ($path ? $path : dirname(__FILE__) . '/files/') . $filename;
259         $applicationId = Tinebase_Application::getInstance()->getApplicationByName('Addressbook')->getId();
260         $definition = Tinebase_ImportExportDefinition::getInstance()->getFromFile($filename, $applicationId);
261         
262         return $definition;
263     }
264     
265     /**
266      * import helper
267      * 
268      * @param array $_options
269      * @param string|Tinebase_Model_ImportExportDefinition $_definition
270      * @param Addressbook_Model_ContactFilter $_exportFilter
271      * @return array
272      */
273     protected function _doImport(array $_options, $_definition, Addressbook_Model_ContactFilter $_exportFilter = NULL)
274     {
275         $definition = ($_definition instanceof Tinebase_Model_ImportExportDefinition) ? $_definition : Tinebase_ImportExportDefinition::getInstance()->getByName($_definition);
276         $this->_instance = Addressbook_Import_Csv::createFromDefinition($definition, $_options);
277         
278         // export first
279         if ($_exportFilter !== NULL) {
280             $exporter = new Addressbook_Export_Csv($_exportFilter, Addressbook_Controller_Contact::getInstance());
281             $this->_filename = $exporter->generate();
282         }
283         
284         // then import
285         $result = $this->_instance->importFile($this->_filename);
286         
287         return $result;
288     }
289     
290     /**
291     * get custom field record
292     * 
293     * @param string $name
294     * @return Tinebase_Model_CustomField_Config
295     */
296     protected function _createCustomField($name = 'Yomi Name')
297     {
298         $cfData = new Tinebase_Model_CustomField_Config(array(
299             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Addressbook')->getId(),
300             'name'              => $name,
301             'model'             => 'Addressbook_Model_Contact',
302             'definition'        => array(
303                 'label' => Tinebase_Record_Abstract::generateUID(),
304                 'type'  => 'string',
305                 'uiconfig' => array(
306                     'xtype'  => Tinebase_Record_Abstract::generateUID(),
307                     'length' => 10,
308                     'group'  => 'unittest',
309                     'order'  => 100,
310                 )
311             )
312         ));
313         
314         try {
315             $result = Tinebase_CustomField::getInstance()->addCustomField($cfData);
316         } catch (Zend_Db_Statement_Exception $zdse) {
317             // customfield already exists
318             $cfs = Tinebase_CustomField::getInstance()->getCustomFieldsForApplication('Addressbook');
319             $result = $cfs->filter('name', $name)->getFirstRecord();
320         }
321         
322         return $result;
323     }
324
325     /**
326      * testImportDuplicateResolve
327      * 
328      * @see 0009316: add duplicate resolving to cli import
329      */
330     public function testImportDuplicateResolve()
331     {
332         $definition = $this->_getDefinitionFromFile('adb_import_csv_duplicate.xml');
333         
334         $this->_filename = dirname(__FILE__) . '/files/import_duplicate_1.csv';
335         $this->_deleteImportFile = FALSE;
336         
337         $result = $this->_doImport(array(), $definition);
338         $this->_deletePersonalContacts = TRUE;
339
340         $this->_filename = dirname(__FILE__) . '/files/import_duplicate_2.csv';
341         
342         $result = $this->_doImport(array(), $definition);
343         
344         $this->assertEquals(1, $result['updatecount'], 'should have updated 1 contact');
345         $this->assertEquals('joerg@home.com', $result['results'][0]->email_home, 'duplicates resolving did not work: ' . print_r($result['results']->toArray(), true));
346         $this->assertEquals('Jörg', $result['results'][0]->n_given, 'wrong encoding: ' . print_r($result['results']->toArray(), true));
347     }
348
349     /**
350      * testImportLxOffice
351      */
352     public function testImportLxOffice()
353     {
354         $options = array(
355             'container_id'  => Addressbook_Controller_Contact::getInstance()->getDefaultAddressbook()->getId(),
356         );
357         
358         // add duplicate field "customernumber"
359         Addressbook_Controller_Contact::getInstance()->duplicateCheckFields(array(
360             array('n_given', 'n_family', 'org_name'),
361             array('email'),
362             array('customernumber')
363         ));
364         
365         $this->_createCustomField('customernumber');
366         
367         $definition = $this->_getDefinitionFromFile('adb_lxoffice_import_csv.xml',
368             dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/tine20/Addressbook/Import/definitions/');
369         
370         $this->_filename = dirname(__FILE__) . '/files/importtest_lxoffice1.csv';
371         $this->_deleteImportFile = FALSE;
372         
373         $result = $this->_doImport($options, $definition);
374         $this->_deletePersonalContacts = TRUE;
375         
376         $this->assertEquals(3, $result['totalcount'], print_r($result['results']->toArray(), true));
377         
378         $contacts = $result['results'];
379         $berger = $contacts->getFirstRecord();
380         $this->assertEquals(array('customernumber' => '73029'), $berger->customfields, print_r($berger->toArray(), true));
381         
382         $this->_filename = dirname(__FILE__) . '/files/importtest_lxoffice2.csv';
383         
384         $result = $this->_doImport($options, $definition);
385         
386         $this->assertEquals(4, count($result['results']));
387         $this->assertEquals(3, $result['updatecount'], 'should have updated 3 contacts');
388         $this->assertEquals(1, $result['totalcount'], 'should have added 1 contact');
389         $this->assertEquals('Straßbough', $result['results'][1]['adr_one_locality'],
390                 'should have changed the locality of contact #2: ' . print_r($result['results'][1]->toArray(), true));
391         $this->assertEquals('Dr. Schutheiss', $result['results'][3]['n_family']);
392         $this->assertEquals(1, $result['results'][2]['seq'], 'Wolfer should not be updated - nothing changed');
393         $this->assertEquals('Weixdorf DD', $result['results'][0]['adr_one_locality'], 'locality should persist');
394     }
395 }