Merge branch '2014.11' into 2014.11-develop
authorPhilipp Schüle <p.schuele@metaways.de>
Wed, 9 Mar 2016 14:39:00 +0000 (15:39 +0100)
committerPhilipp Schüle <p.schuele@metaways.de>
Wed, 9 Mar 2016 14:39:00 +0000 (15:39 +0100)
417 files changed:
tests/tine20/ActiveSync/TestCase.php
tests/tine20/Addressbook/Convert/Contact/VCard/AllTests.php
tests/tine20/Addressbook/Convert/Contact/VCard/TelefonbuchTest.php [new file with mode: 0644]
tests/tine20/Addressbook/Import/CsvTest.php
tests/tine20/Addressbook/Import/files/adb_import_csv_split.xml [new file with mode: 0644]
tests/tine20/Addressbook/Import/files/import_split.csv [new file with mode: 0644]
tests/tine20/Addressbook/Import/files/import_split_duplicate.csv [new file with mode: 0644]
tests/tine20/Addressbook/Import/files/telefonbuch.vcf [new file with mode: 0644]
tests/tine20/Addressbook/JsonTest.php
tests/tine20/Admin/JsonTest.php
tests/tine20/Calendar/Controller/EventNotificationsTests.php
tests/tine20/Calendar/Convert/Event/VCalendar/GenericTest.php
tests/tine20/Calendar/Export/ICalTest.php
tests/tine20/Calendar/Frontend/ActiveSyncTest.php
tests/tine20/Calendar/Frontend/WebDAV/EventTest.php
tests/tine20/Calendar/Frontend/files/event_with_attendee.xml [new file with mode: 0644]
tests/tine20/Calendar/Frontend/files/repeating_with_first_instance_exception.xml [new file with mode: 0644]
tests/tine20/Calendar/Import/files/lightning_repeating_exdate_mozlastack.ics
tests/tine20/Calendar/Import/files/lightning_repeating_group_first_instance_exception.ics [new file with mode: 0644]
tests/tine20/Crm/Acl/RolesTest.php
tests/tine20/Crm/AllTests.php
tests/tine20/Crm/Backend/LeadTest.php
tests/tine20/Crm/Export/CsvTest.php
tests/tine20/Crm/Export/PdfTest.php
tests/tine20/Crm/Export/XlsTest.php
tests/tine20/Crm/Import/CsvTest.php [new file with mode: 0644]
tests/tine20/Crm/Import/files/leads.csv [new file with mode: 0644]
tests/tine20/Crm/JsonTest.php
tests/tine20/Crm/NotificationsTests.php
tests/tine20/ExampleApplication/JsonTest.php
tests/tine20/ExampleApplication/TestCase.php
tests/tine20/Filemanager/Controller/DownloadLinkTests.php
tests/tine20/ImportTestCase.php [new file with mode: 0644]
tests/tine20/Sales/AllTests.php
tests/tine20/Sales/ControllerTest.php
tests/tine20/Sales/InvoiceControllerTests.php
tests/tine20/Sales/ProductControllerTest.php [new file with mode: 0644]
tests/tine20/Sales/PurchaseInvoiceTest.php [new file with mode: 0644]
tests/tine20/Sales/SuppliersTest.php [new file with mode: 0644]
tests/tine20/Tasks/JsonTest.php
tests/tine20/TestCase.php
tests/tine20/Timetracker/JsonTest.php
tests/tine20/Tinebase/AllTests.php
tests/tine20/Tinebase/ConfigTest.php
tests/tine20/Tinebase/ContainerTest.php
tests/tine20/Tinebase/ControllerServerTest.php
tests/tine20/Tinebase/Frontend/CliTest.php
tests/tine20/Tinebase/Frontend/HttpTest.php
tests/tine20/Tinebase/Frontend/JsonTest.php
tests/tine20/Tinebase/LockTest.php [new file with mode: 0644]
tests/tine20/Tinebase/PreferenceTest.php
tests/tine20/Tinebase/ScheduledImportTest.php
tests/tine20/Tinebase/User/ActiveDirectoryTest.php
tests/tine20/Tinebase/User/SqlTest.php
tests/tine20/Tinebase/WebDav/Plugin/SyncTokenTest.php
tine20/ActiveSync/Config.php
tine20/Addressbook/Acl/Rights.php
tine20/Addressbook/Config.php
tine20/Addressbook/Controller.php
tine20/Addressbook/Convert/Contact/VCard/Abstract.php
tine20/Addressbook/Convert/Contact/VCard/Factory.php
tine20/Addressbook/Convert/Contact/VCard/Telefonbuch.php [new file with mode: 0644]
tine20/Addressbook/Convert/Contact/config/convert_from_string_improved.xml [new file with mode: 0644]
tine20/Addressbook/Frontend/Http.php
tine20/Addressbook/Frontend/Json.php
tine20/Addressbook/Import/Csv.php
tine20/Addressbook/Import/VCard.php
tine20/Addressbook/Model/Contact.php
tine20/Addressbook/Model/ContactFilter.php
tine20/Addressbook/Setup/Initialize.php
tine20/Addressbook/css/Addressbook.css
tine20/Addressbook/js/ContactEditDialog.js
tine20/Addressbook/js/ContactGrid.js
tine20/Addressbook/js/SearchCombo.js
tine20/Addressbook/translations/de.po
tine20/Addressbook/translations/en.po
tine20/Addressbook/translations/template.pot
tine20/Admin/Admin.jsb2
tine20/Admin/Controller/Config.php [new file with mode: 0644]
tine20/Admin/Controller/Keyfield.php [new file with mode: 0644]
tine20/Admin/Controller/User.php
tine20/Admin/Event/DeleteAccount.php [deleted file]
tine20/Admin/Frontend/Json.php
tine20/Admin/js/AdminPanel.js
tine20/Admin/js/Applications.js
tine20/Admin/js/Groups.js
tine20/Admin/js/Models.js
tine20/Admin/js/Roles.js
tine20/Admin/js/Tags.js
tine20/Admin/js/config/FieldManager.js [new file with mode: 0644]
tine20/Admin/js/config/GridPanel.js [new file with mode: 0644]
tine20/Admin/js/customfield/EditDialog.js
tine20/Admin/js/customfield/GridPanel.js
tine20/Calendar/Acl/Rights.php
tine20/Calendar/Backend/Sql.php
tine20/Calendar/Backend/Sql/Attendee.php
tine20/Calendar/Calendar.jsb2
tine20/Calendar/Config.php
tine20/Calendar/Controller.php
tine20/Calendar/Controller/Event.php
tine20/Calendar/Controller/EventNotifications.php
tine20/Calendar/Controller/MSEventFacade.php
tine20/Calendar/Controller/Resource.php
tine20/Calendar/Convert/Event/Json.php
tine20/Calendar/Convert/Event/VCalendar/Abstract.php
tine20/Calendar/Frontend/Json.php
tine20/Calendar/Frontend/WebDAV.php
tine20/Calendar/Frontend/WebDAV/Container.php
tine20/Calendar/Model/Attender.php
tine20/Calendar/Model/AttenderFilter.php
tine20/Calendar/Model/EventFilter.php
tine20/Calendar/Model/Resource.php
tine20/Calendar/Preference.php
tine20/Calendar/Setup/Initialize.php
tine20/Calendar/Setup/Update/Release8.php
tine20/Calendar/Setup/setup.xml
tine20/Calendar/js/AdminPanel.js
tine20/Calendar/js/AttendeeGridPanel.js
tine20/Calendar/js/CalendarPanelSplitPlugin.js [new file with mode: 0644]
tine20/Calendar/js/DaysView.js
tine20/Calendar/js/EventContextAttendeesItem.js [new file with mode: 0644]
tine20/Calendar/js/EventContextTagsItem.js [new file with mode: 0644]
tine20/Calendar/js/EventEditDialog.js
tine20/Calendar/js/EventUI.js
tine20/Calendar/js/GridView.js
tine20/Calendar/js/MainScreenCenterPanel.js
tine20/Calendar/js/Model.js
tine20/Calendar/js/MonthView.js
tine20/Calendar/js/PerspectiveCombo.js
tine20/Calendar/js/ResourceEditDialog.js
tine20/Calendar/translations/de.po
tine20/Courses/Acl/Rights.php
tine20/Courses/Config.php
tine20/Courses/Setup/Initialize.php
tine20/Crm/Acl/Rights.php
tine20/Crm/Config.php
tine20/Crm/Controller.php
tine20/Crm/Controller/Lead.php
tine20/Crm/Crm.jsb2
tine20/Crm/Export/Helper.php
tine20/Crm/Frontend/Json.php
tine20/Crm/Import/Csv.php [new file with mode: 0644]
tine20/Crm/Import/definitions/crm_tine_import_csv.xml [new file with mode: 0644]
tine20/Crm/Model/Lead.php
tine20/Crm/Model/LeadFilter.php
tine20/Crm/Model/LeadQueryFilter.php
tine20/Crm/Setup/Update/Release8.php
tine20/Crm/Setup/setup.xml
tine20/Crm/js/AdminPanel.js
tine20/Crm/js/Crm.js
tine20/Crm/js/LeadEditDialog.js
tine20/Crm/js/LeadGridPanel.js
tine20/Crm/js/LeadSource.js
tine20/Crm/js/Model.js
tine20/Crm/js/Product.js
tine20/Crm/js/ProductPickerCombo.js [new file with mode: 0644]
tine20/Crm/translations/de.po
tine20/Crm/translations/template.pot
tine20/Crm/views/importNotificationPlain.php [new file with mode: 0644]
tine20/Crm/views/newLeadHtml.php
tine20/ExampleApplication/Config.php
tine20/ExampleApplication/Controller.php
tine20/ExampleApplication/ExampleApplication.jsb2
tine20/ExampleApplication/Model/ExampleRecord.php
tine20/ExampleApplication/Setup/Initialize.php
tine20/ExampleApplication/Setup/setup.xml
tine20/ExampleApplication/js/AdminPanel.js [new file with mode: 0644]
tine20/ExampleApplication/js/ExampleRecordEditDialog.js
tine20/Felamimail/Controller/Cache/Folder.php
tine20/Felamimail/Controller/Cache/Message.php
tine20/Felamimail/Controller/Message/Send.php
tine20/Felamimail/Preference.php
tine20/Felamimail/Setup/Update/Release8.php
tine20/Felamimail/Setup/setup.xml
tine20/Felamimail/js/AccountEditDialog.js
tine20/Felamimail/js/Felamimail.js
tine20/Felamimail/js/MessageEditDialog.js
tine20/Filemanager/Acl/Rights.php
tine20/Filemanager/Controller.php
tine20/Filemanager/Controller/DownloadLink.php
tine20/Filemanager/Frontend/Download.php
tine20/Filemanager/js/DownloadLinkDialog.js
tine20/Filemanager/js/DownloadLinkGridPanel.js
tine20/Filemanager/js/Filemanager.js
tine20/HumanResources/Config.php
tine20/HumanResources/Setup/Initialize.php
tine20/HumanResources/js/AdminPanel.js
tine20/HumanResources/js/DatePicker.js
tine20/Inventory/Config.php
tine20/Inventory/Controller.php
tine20/Inventory/Setup/Initialize.php
tine20/Phone/Frontend/Snom.php
tine20/Projects/Acl/Rights.php
tine20/Projects/Config.php
tine20/Projects/Controller.php
tine20/Projects/Setup/Initialize.php
tine20/Sales/Acl/Rights.php
tine20/Sales/Backend/Invoice.php
tine20/Sales/Backend/PurchaseInvoice.php [new file with mode: 0644]
tine20/Sales/Backend/Supplier.php [new file with mode: 0644]
tine20/Sales/Config.php
tine20/Sales/Controller.php
tine20/Sales/Controller/Contract.php
tine20/Sales/Controller/Invoice.php
tine20/Sales/Controller/Product.php
tine20/Sales/Controller/ProductAggregate.php
tine20/Sales/Controller/PurchaseInvoice.php [new file with mode: 0644]
tine20/Sales/Controller/Supplier.php [new file with mode: 0644]
tine20/Sales/Export/Ods/PurchaseInvoice.php [new file with mode: 0644]
tine20/Sales/Export/Ods/Supplier.php [new file with mode: 0644]
tine20/Sales/Export/definitions/purchaseinvoice_default_ods.xml [new file with mode: 0644]
tine20/Sales/Export/definitions/supplier_default_ods.xml [new file with mode: 0644]
tine20/Sales/Frontend/Cli.php
tine20/Sales/Frontend/Http.php
tine20/Sales/Frontend/Json.php
tine20/Sales/Frontend/WebDAV.php [new file with mode: 0644]
tine20/Sales/Frontend/WebDAV/Import.php [new file with mode: 0644]
tine20/Sales/Frontend/WebDAV/Module.php [new file with mode: 0644]
tine20/Sales/Model/Accountable/Abstract.php
tine20/Sales/Model/Accountable/Interface.php
tine20/Sales/Model/Invoice.php
tine20/Sales/Model/Offer.php
tine20/Sales/Model/OrderConfirmation.php
tine20/Sales/Model/PaymentMethod.php [new file with mode: 0644]
tine20/Sales/Model/Product.php
tine20/Sales/Model/ProductAggregate.php
tine20/Sales/Model/ProductCategory.php [new file with mode: 0644]
tine20/Sales/Model/PurchaseInvoice.php [new file with mode: 0644]
tine20/Sales/Model/PurchaseInvoiceFilter.php [new file with mode: 0644]
tine20/Sales/Model/Supplier.php [new file with mode: 0644]
tine20/Sales/Model/SupplierFilter.php [new file with mode: 0644]
tine20/Sales/Sales.jsb2
tine20/Sales/Scheduler/Task.php [new file with mode: 0644]
tine20/Sales/Setup/Initialize.php
tine20/Sales/Setup/Update/Release8.php
tine20/Sales/Setup/setup.xml
tine20/Sales/js/AddressGridPanel.js
tine20/Sales/js/AdminPanel.js
tine20/Sales/js/ContractEditDialog.js
tine20/Sales/js/CustomerEditDialog.js
tine20/Sales/js/InvoiceDetailsPanel.js
tine20/Sales/js/InvoiceEditDialog.js
tine20/Sales/js/InvoiceSearchCombo.js [new file with mode: 0644]
tine20/Sales/js/OfferSearchCombo.js [new file with mode: 0644]
tine20/Sales/js/OrderConfirmationSearchCombo.js [new file with mode: 0644]
tine20/Sales/js/ProductEditDialog.js
tine20/Sales/js/PurchaseInvoiceApproverFilterModel.js [new file with mode: 0644]
tine20/Sales/js/PurchaseInvoiceDetailsPanel.js [new file with mode: 0644]
tine20/Sales/js/PurchaseInvoiceEditDialog.js [new file with mode: 0644]
tine20/Sales/js/PurchaseInvoiceGridPanel.js [new file with mode: 0644]
tine20/Sales/js/Sales.js
tine20/Sales/js/SupplierDetailsPanel.js [new file with mode: 0644]
tine20/Sales/js/SupplierEditDialog.js [new file with mode: 0644]
tine20/Sales/js/SupplierFilterModel.js [new file with mode: 0644]
tine20/Sales/js/SupplierGridPanel.js [new file with mode: 0644]
tine20/Sales/js/SupplierSearchCombo.js [new file with mode: 0644]
tine20/Sales/translations/de.po
tine20/Sales/translations/en.po
tine20/Sales/translations/template.pot
tine20/Setup/Backend/Mysql.php
tine20/Setup/Backend/Oracle.php
tine20/Setup/Backend/Pgsql.php
tine20/Setup/Controller.php
tine20/Setup/Initialize.php
tine20/Setup/js/Setup.js
tine20/Setup/js/init.js
tine20/SimpleFAQ/Controller.php
tine20/SimpleFAQ/js/AdminPanel.js
tine20/Sipgate/Config.php
tine20/Sipgate/Setup/Initialize.php
tine20/Tasks/Acl/Rights.php
tine20/Tasks/Config.php
tine20/Tasks/Controller.php
tine20/Tasks/Model/TaskFilter.php
tine20/Tasks/Setup/Initialize.php
tine20/Timetracker/Acl/Rights.php
tine20/Timetracker/Controller/Timesheet.php
tine20/Timetracker/Model/Timeaccount.php
tine20/Timetracker/Model/TimeaccountFilter.php
tine20/Timetracker/js/TimeaccountEditDialog.js
tine20/Tinebase/AccessLog.php
tine20/Tinebase/Acl/Rights/Abstract.php
tine20/Tinebase/Application.php
tine20/Tinebase/Auth.php
tine20/Tinebase/Auth/CredentialCache.php
tine20/Tinebase/Backend/Sql/Abstract.php
tine20/Tinebase/Backend/Sql/Command/Interface.php
tine20/Tinebase/Backend/Sql/Command/Mysql.php
tine20/Tinebase/Backend/Sql/Command/Oracle.php
tine20/Tinebase/Backend/Sql/Command/Pgsql.php
tine20/Tinebase/Backend/Sql/Filter/GroupSelect.php
tine20/Tinebase/Config.php
tine20/Tinebase/Config/Abstract.php
tine20/Tinebase/Config/KeyFieldRecord.php
tine20/Tinebase/Container.php
tine20/Tinebase/Controller.php
tine20/Tinebase/Controller/Abstract.php
tine20/Tinebase/Controller/Record/Abstract.php
tine20/Tinebase/Controller/ScheduledImport.php
tine20/Tinebase/Convert/ImportExportDefinition/Json.php
tine20/Tinebase/Convert/Json.php
tine20/Tinebase/Core.php
tine20/Tinebase/CustomField.php
tine20/Tinebase/DateTime.php
tine20/Tinebase/EmailUser/Imap/Dovecot.php
tine20/Tinebase/Event/User/DeleteAccount.php [new file with mode: 0644]
tine20/Tinebase/Export.php
tine20/Tinebase/Export/Abstract.php
tine20/Tinebase/Export/Spreadsheet/Abstract.php
tine20/Tinebase/Export/Spreadsheet/Ods.php
tine20/Tinebase/Export/Spreadsheet/Xls.php
tine20/Tinebase/FileSystem.php
tine20/Tinebase/Frontend/Cli.php
tine20/Tinebase/Frontend/Cli/Abstract.php
tine20/Tinebase/Frontend/Http.php
tine20/Tinebase/Frontend/Json.php
tine20/Tinebase/Frontend/Json/Abstract.php
tine20/Tinebase/Import/Abstract.php
tine20/Tinebase/Import/Csv/Abstract.php
tine20/Tinebase/ImportExportDefinition.php
tine20/Tinebase/Lock.php [new file with mode: 0644]
tine20/Tinebase/Model/Config.php
tine20/Tinebase/Model/Converter/Interface.php [new file with mode: 0644]
tine20/Tinebase/Model/Converter/Json.php [new file with mode: 0644]
tine20/Tinebase/Model/CustomField/Config.php
tine20/Tinebase/Model/Filter/Abstract.php
tine20/Tinebase/Model/Filter/Container.php
tine20/Tinebase/Model/Filter/Date.php
tine20/Tinebase/Model/Filter/FilterGroup.php
tine20/Tinebase/Model/Filter/Float.php [new file with mode: 0644]
tine20/Tinebase/Model/Filter/Int.php
tine20/Tinebase/Model/Filter/Query.php
tine20/Tinebase/Model/Preference.php
tine20/Tinebase/Model/TagRight.php
tine20/Tinebase/ModelConfiguration.php
tine20/Tinebase/Notification.php
tine20/Tinebase/Preference.php
tine20/Tinebase/Preference/Abstract.php
tine20/Tinebase/Record/Abstract.php
tine20/Tinebase/Record/Iterator.php
tine20/Tinebase/Relation/Backend/Sql.php
tine20/Tinebase/Relations.php
tine20/Tinebase/Scheduler/Task.php
tine20/Tinebase/Server/Cli.php
tine20/Tinebase/Session/Abstract.php
tine20/Tinebase/Setup/Initialize.php
tine20/Tinebase/Setup/Update/Release8.php
tine20/Tinebase/Setup/setup.xml
tine20/Tinebase/Tags.php
tine20/Tinebase/Timemachine/ModificationLog.php
tine20/Tinebase/Tinebase.jsb2
tine20/Tinebase/User.php
tine20/Tinebase/User/ActiveDirectory.php
tine20/Tinebase/User/Interface.php
tine20/Tinebase/User/Sql.php
tine20/Tinebase/WebDav/Collection/AbstractContainerTree.php
tine20/Tinebase/css/ux/display/DisplayPanel.css
tine20/Tinebase/js/AppManager.js
tine20/Tinebase/js/Application.js
tine20/Tinebase/js/ApplicationStarter.js
tine20/Tinebase/js/ExceptionDialog.js
tine20/Tinebase/js/ExceptionHandler.js
tine20/Tinebase/js/LoginPanel.js
tine20/Tinebase/js/MainMenu.js
tine20/Tinebase/js/MainScreen.js
tine20/Tinebase/js/Models.js
tine20/Tinebase/js/common.js
tine20/Tinebase/js/data/Record.js
tine20/Tinebase/js/data/RecordProxy.js
tine20/Tinebase/js/extInit.js
tine20/Tinebase/js/tineInit.js
tine20/Tinebase/js/ux/PopupWindow.js
tine20/Tinebase/js/ux/WindowFactory.js
tine20/Tinebase/js/ux/display/DisplayField.js
tine20/Tinebase/js/ux/file/Upload.js
tine20/Tinebase/js/ux/form/ColorField.js
tine20/Tinebase/js/ux/form/LayerCombo.js
tine20/Tinebase/js/ux/form/NumberField.js
tine20/Tinebase/js/ux/util/Cookie.js [new file with mode: 0644]
tine20/Tinebase/js/ux/util/MixedLocalStorageCollection.js [new file with mode: 0644]
tine20/Tinebase/js/widgets/ActivitiesPanel.js
tine20/Tinebase/js/widgets/TimezoneChooser.js
tine20/Tinebase/js/widgets/account/ChangeAccountAction.js
tine20/Tinebase/js/widgets/container/FilterModel.js
tine20/Tinebase/js/widgets/container/GrantsGrid.js
tine20/Tinebase/js/widgets/customfields/ConfigManager.js
tine20/Tinebase/js/widgets/dialog/DuplicateResolveGridPanel.js
tine20/Tinebase/js/widgets/dialog/EditDialog.js
tine20/Tinebase/js/widgets/dialog/ImportDialog.js
tine20/Tinebase/js/widgets/dialog/PreferencesPanel.js
tine20/Tinebase/js/widgets/dialog/SimpleImportDialog.js
tine20/Tinebase/js/widgets/dialog/WizardPanel.js
tine20/Tinebase/js/widgets/form/ConfigPanel.js
tine20/Tinebase/js/widgets/form/RecordPickerComboBox.js
tine20/Tinebase/js/widgets/grid/BbarGridPanel.js [new file with mode: 0644]
tine20/Tinebase/js/widgets/grid/DetailsPanel.js
tine20/Tinebase/js/widgets/grid/ExportButton.js
tine20/Tinebase/js/widgets/grid/FileUploadGrid.js
tine20/Tinebase/js/widgets/grid/FilterPanel.js
tine20/Tinebase/js/widgets/grid/GridPanel.js
tine20/Tinebase/js/widgets/grid/PickerGridPanel.js
tine20/Tinebase/js/widgets/grid/QuickaddGridPanel.js
tine20/Tinebase/js/widgets/grid/RendererManager.js
tine20/Tinebase/js/widgets/keyfield/ComboBox.js
tine20/Tinebase/js/widgets/keyfield/ConfigField.js [new file with mode: 0644]
tine20/Tinebase/js/widgets/keyfield/ConfigGrid.js [new file with mode: 0644]
tine20/Tinebase/js/widgets/keyfield/Store.js
tine20/Tinebase/js/widgets/tags/TagsMassAttachAction.js
tine20/Tinebase/js/widgets/tags/TagsPanel.js
tine20/Tinebase/translations/de.po
tine20/Tinebase/translations/template.pot
tine20/Tinebase/views/includeJsAndCss.php
tine20/composer.json
tine20/composer.lock
tine20/library/Store/store.bind.js [new file with mode: 0644]
tine20/library/Store/store.compat.js [new file with mode: 0644]
tine20/library/Store/store2.js [new file with mode: 0644]

index f6acf86..6117917 100644 (file)
@@ -181,7 +181,22 @@ abstract class ActiveSync_TestCase extends TestCase
 
         return $this->objects['devices'][$_deviceType];
     }
-    
+
+    protected function _asXML($syncrotonModel)
+    {
+        $imp                   = new DOMImplementation();
+
+        $dtd                   = $imp->createDocumentType('AirSync', "-//AIRSYNC//DTD AirSync//EN", "http://www.microsoft.com/");
+        $testDoc               = $imp->createDocument('uri:AirSync', 'Sync', $dtd);
+        $testDoc->formatOutput = true;
+        $testDoc->encoding     = 'utf-8';
+
+        $appData    = $testDoc->documentElement->appendChild($testDoc->createElementNS('uri:AirSync', 'ApplicationData'));
+
+        $syncrotonModel->appendXML($appData, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE));
+
+        return $testDoc->saveXML();
+    }
     /**
      * returns a test event
      * 
index 74c9372..d0ce696 100644 (file)
@@ -25,6 +25,7 @@ class Addressbook_Convert_Contact_VCard_AllTests
         $suite = new PHPUnit_Framework_TestSuite('Tine 2.0 Addressbook All Import Vcard Tests');
         $suite->addTestSuite('Addressbook_Convert_Contact_VCard_FactoryTest');
         $suite->addTestSuite('Addressbook_Convert_Contact_VCard_GenericTest');
+        $suite->addTestSuite('Addressbook_Convert_Contact_VCard_TelefonbuchTest');
         $suite->addTestSuite('Addressbook_Convert_Contact_VCard_IOSTest');
         $suite->addTestSuite('Addressbook_Convert_Contact_VCard_MacOSXTest');
         $suite->addTestSuite('Addressbook_Convert_Contact_VCard_SogoTest');
diff --git a/tests/tine20/Addressbook/Convert/Contact/VCard/TelefonbuchTest.php b/tests/tine20/Addressbook/Convert/Contact/VCard/TelefonbuchTest.php
new file mode 100644 (file)
index 0000000..4fa2b18
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Addressbook
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Michael Spahn <kontakt@michaelspahn.de>
+ */
+
+/**
+ * Test helper
+ */
+require_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Addressbook_Convert_Contact_VCard_TelefonbuchTest::main');
+}
+
+/**
+ * Test class for Addressbook_Convert_Contact_VCard_TelefonbuchTest
+ */
+class Addressbook_Convert_Contact_VCard_TelefonbuchTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * @var array test objects
+     */
+    protected $objects = array();
+    
+    /**
+     * Runs the test methods of this class.
+     *
+     * @access public
+     * @static
+     */
+    public static function main()
+    {
+        $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Addressbook WebDAV Telefonbuch Contact Tests');
+        PHPUnit_TextUI_TestRunner::run($suite);
+    }
+
+    /**
+     * Sets up the fixture.
+     * This method is called before a test is executed.
+     *
+     * @access protected
+     */
+    protected function setUp()
+    {
+    }
+
+    /**
+     * Tears down the fixture
+     * This method is called after a test is executed.
+     *
+     * @access protected
+     */
+    protected function tearDown()
+    {
+    }
+
+    /**
+     * test converting vcard from sogo connector to Addressbook_Model_Contact
+     * 
+     * @return Addressbook_Model_Contact
+     */
+    public function testConvertToTine20Model()
+    {
+        $vcardStream = fopen(dirname(__FILE__) . '/../../../Import/files/telefonbuch.vcf', 'r');
+
+        $converter = Addressbook_Convert_Contact_VCard_Factory::factory(Addressbook_Convert_Contact_VCard_Factory::CLIENT_TELEFONBUCH);
+
+        $contact = $converter->toTine20Model($vcardStream);
+
+        $this->assertEquals('Hamburg',                 $contact->adr_one_locality);
+        $this->assertEquals('12345',                   $contact->adr_one_postalcode);
+        $this->assertEquals('Teststraße 1',            $contact->adr_one_street);
+        $this->assertEquals('Spahn',                   $contact->n_family);
+        $this->assertEquals('Spahn, Michael',          $contact->n_fileas);
+        $this->assertEquals('Michael',                 $contact->n_given);
+        $this->assertEquals('040 12345',               $contact->tel_work);
+        $this->assertEquals('http://michaelspahn.de', $contact->url);
+
+        return $contact;
+    }
+}
index cbf247e..280d6a5 100644 (file)
@@ -4,48 +4,20 @@
  * 
  * @package     Addressbook
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2008-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2008-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
  */
 
 /**
- * Test helper
- */
-require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-/**
  * Test class for Addressbook_Import_Csv
  */
-class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
+class Addressbook_Import_CsvTest extends ImportTestCase
 {
-    /**
-     * @var Addressbook_Import_Csv instance
-     */
-    protected $_instance = NULL;
-    
-    /**
-     * @var string $_filename
-     */
-    protected $_filename = NULL;
-    
-    /**
-     * @var boolean
-     */
-    protected $_deleteImportFile = TRUE;
-    
-    protected $_deletePersonalContacts = FALSE;
-    
-    /**
-     * Runs the test methods of this class.
-     *
-     * @access public
-     * @static
-     */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Addressbook Csv Import Tests');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
+    protected $_deletePersonalContacts = false;
+
+    protected $_importerClassName = 'Addressbook_Import_Csv';
+    protected $_exporterClassName = 'Addressbook_Export_Csv';
+    protected $_modelName = 'Addressbook_Model_Contact';
 
     /**
      * Sets up the fixture.
@@ -83,7 +55,7 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
     
     /**
      * test import duplicate data
-     * 
+     *
      * @return array
      */
     public function testImportDuplicates()
@@ -95,10 +67,10 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
         $result = $this->_doImport($options, 'adb_tine_import_csv', new Addressbook_Model_ContactFilter(array(
             array('field' => 'container_id', 'operator' => 'equals', 'value' => $internalContainer->getId()),
         )));
-        
+
         $this->assertGreaterThan(0, $result['duplicatecount'], 'no duplicates.');
         $this->assertTrue($result['exceptions'] instanceof Tinebase_Record_RecordSet);
-        
+
         return $result;
     }
     
@@ -124,7 +96,7 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
             }
         }
         
-        $this->assertTrue($found);
+        $this->assertTrue($found, 'did not find user record in import exceptions: ' . print_r($result['exceptions']->toArray(), true));
     }
 
     /**
@@ -173,7 +145,7 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
      */
     public function testImportCustomField()
     {
-        $customField = $this->_createCustomField();
+        $this->_createCustomField();
         
         // create/get new import/export definition with customfield
         $filename = dirname(__FILE__) . '/files/adb_google_import_csv_test.xml';
@@ -248,7 +220,7 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
     }
     
     /**
-     * returns import defintion from file
+     * returns import definition from file
      * 
      * @param string $filename
      * @return Tinebase_Model_ImportExportDefinition
@@ -263,31 +235,6 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
     }
     
     /**
-     * import helper
-     * 
-     * @param array $_options
-     * @param string|Tinebase_Model_ImportExportDefinition $_definition
-     * @param Addressbook_Model_ContactFilter $_exportFilter
-     * @return array
-     */
-    protected function _doImport(array $_options, $_definition, Addressbook_Model_ContactFilter $_exportFilter = NULL)
-    {
-        $definition = ($_definition instanceof Tinebase_Model_ImportExportDefinition) ? $_definition : Tinebase_ImportExportDefinition::getInstance()->getByName($_definition);
-        $this->_instance = Addressbook_Import_Csv::createFromDefinition($definition, $_options);
-        
-        // export first
-        if ($_exportFilter !== NULL) {
-            $exporter = new Addressbook_Export_Csv($_exportFilter, Addressbook_Controller_Contact::getInstance());
-            $this->_filename = $exporter->generate();
-        }
-        
-        // then import
-        $result = $this->_instance->importFile($this->_filename);
-        
-        return $result;
-    }
-    
-    /**
     * get custom field record
     * 
     * @param string $name
@@ -334,7 +281,7 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
         $this->_filename = dirname(__FILE__) . '/files/import_duplicate_1.csv';
         $this->_deleteImportFile = FALSE;
         
-        $result = $this->_doImport(array(), $definition);
+        $this->_doImport(array(), $definition);
         $this->_deletePersonalContacts = TRUE;
 
         $this->_filename = dirname(__FILE__) . '/files/import_duplicate_2.csv';
@@ -388,9 +335,43 @@ class Addressbook_Import_CsvTest extends PHPUnit_Framework_TestCase
         $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');
+        // TODO this should be researched, imho the relation should not trigger an update of the record
+        $this->assertEquals(1, $result['results'][3]['seq'], 'Wolfer has been updated - relations changed');
         $this->assertEquals('Weixdorf DD', $result['results'][0]['adr_one_locality'], 'locality should persist');
         $this->assertEquals('Gartencenter Röhr & Vater', $result['results'][4]['n_fileas']);
         $this->assertEquals('Straßback', $result['results'][5]['adr_one_locality']);
     }
+
+    public function testSplitField()
+    {
+        $definition = $this->_getDefinitionFromFile('adb_import_csv_split.xml');
+
+        $this->_filename = dirname(__FILE__) . '/files/import_split.csv';
+        $this->_deleteImportFile = FALSE;
+
+        $result = $this->_doImport(array('dryrun' => true), $definition);
+
+        $this->assertEquals(1, $result['totalcount'], print_r($result, true));
+        $importedRecord = $result['results']->getFirstRecord();
+
+        $this->assertEquals('21222', $importedRecord->adr_one_postalcode, print_r($importedRecord->toArray(), true));
+        $this->assertEquals('Käln', $importedRecord->adr_one_locality, print_r($importedRecord->toArray(), true));
+    }
+
+    /**
+     * @see 0011354: keep both records if duplicates are within current import file
+     */
+    public function testImportDuplicateInImport()
+    {
+        $definition = $this->_getDefinitionFromFile('adb_import_csv_split.xml');
+
+        $this->_filename = dirname(__FILE__) . '/files/import_split_duplicate.csv';
+        $this->_deletePersonalContacts = TRUE;
+        $this->_deleteImportFile = FALSE;
+
+        $result = $this->_doImport(array('dryrun' => false), $definition);
+
+        $this->assertEquals(2, $result['totalcount'], print_r($result, true));
+        $this->assertEquals(2, count(array_unique($result['results']->getArrayOfIds())));
+    }
 }
diff --git a/tests/tine20/Addressbook/Import/files/adb_import_csv_split.xml b/tests/tine20/Addressbook/Import/files/adb_import_csv_split.xml
new file mode 100644 (file)
index 0000000..ec4109b
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<config>
+    <name>adb_csv_split</name>
+    <model>Addressbook_Model_Contact</model>
+    <plugin>Addressbook_Import_Csv</plugin>
+    <type>import</type>
+    <headline>1</headline>
+    <dryrun>0</dryrun>
+    <delimiter>,</delimiter>
+    <label>Simple CSV import</label>
+    <description>simple import</description>
+    <extension>csv</extension>
+    <duplicateResolveStrategy>mergeTheirs</duplicateResolveStrategy>
+    <mapping>
+        <field>
+            <source>n_family</source>
+            <destination>n_family</destination>
+        </field>
+        <field>
+            <source>n_given</source>
+            <destination>n_given</destination>
+        </field>
+        <field>
+            <source>PLZ/Ort Kombination</source>
+            <destinations>
+                <destination>adr_one_postalcode</destination>
+                <destination>adr_one_locality</destination>
+            </destinations>
+        </field>
+    </mapping>
+</config>
\ No newline at end of file
diff --git a/tests/tine20/Addressbook/Import/files/import_split.csv b/tests/tine20/Addressbook/Import/files/import_split.csv
new file mode 100644 (file)
index 0000000..46c2f36
--- /dev/null
@@ -0,0 +1,2 @@
+"n_given","n_family","PLZ/Ort Kombination"\r
+"Jörg","Heinz","21222 Käln"\r
diff --git a/tests/tine20/Addressbook/Import/files/import_split_duplicate.csv b/tests/tine20/Addressbook/Import/files/import_split_duplicate.csv
new file mode 100644 (file)
index 0000000..204f1ab
--- /dev/null
@@ -0,0 +1,3 @@
+"n_given","n_family","PLZ/Ort Kombination"\r
+"Jörg","Heinz","21222 Käln"\r
+"Jörg","Heinz","21225 Kön"\r
diff --git a/tests/tine20/Addressbook/Import/files/telefonbuch.vcf b/tests/tine20/Addressbook/Import/files/telefonbuch.vcf
new file mode 100644 (file)
index 0000000..37a9fc4
--- /dev/null
@@ -0,0 +1,9 @@
+BEGIN:VCARD
+VERSION:2.1
+N:Spahn
+FN:Michael
+ADR:;;Teststraße 1;Hamburg;;12345;
+TEL;WORK:040 12345
+EMAIL;INTERNET:m.spahn@metaways.de
+URL;WORK:http://michaelspahn.de
+END:VCARD
index 383350b..6ab5621 100644 (file)
@@ -4,18 +4,13 @@
  *
  * @package     Addressbook
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2008-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2008-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  *
  * @todo        add testSetImage (NOTE: we can't test the upload yet, so we needd to simulate the upload)
  */
 
 /**
- * Test helper
- */
-require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-/**
  * Test class for Addressbook_Frontend_Json
  */
 class Addressbook_JsonTest extends TestCase
@@ -74,6 +69,8 @@ class Addressbook_JsonTest extends TestCase
      */
     protected $_groupIdsToDelete = NULL;
     
+    protected $_originalRoleRights = null;
+    
     /**
      * Runs the test methods of this class.
      *
@@ -157,8 +154,21 @@ class Addressbook_JsonTest extends TestCase
                 $this->objects['createdTagIds'] = array();
             }
         }
+        
+        $this->_resetOriginalRoleRights();
     }
-
+    
+    protected function _resetOriginalRoleRights()
+    {
+        if (! empty($this->_originalRoleRights)) {
+            foreach ($this->_originalRoleRights as $roleId => $rights) {
+                Tinebase_Acl_Roles::getInstance()->setRoleRights($roleId, $rights);
+            }
+            
+            $this->_originalRoleRights = null;
+        }
+    }
+    
     /**
      * try to get all contacts
      */
@@ -385,7 +395,6 @@ class Addressbook_JsonTest extends TestCase
         $this->_checkChangedNote($record['id'], 'adr_one_region ( -> PHPUNIT_multipleUpdate) url ( -> http://www.phpunit.de) customfields ( -> {');
         
         // check invalid data
-        
         $changes = array(
             array('name' => 'type', 'value' => 'Z'),
         );
@@ -489,8 +498,10 @@ class Addressbook_JsonTest extends TestCase
 
     /**
     * test attach multiple tags modlog
+    * 
+    * @param string $type tag type
     */
-    public function testAttachMultipleTagsModlog()
+    public function testAttachMultipleTagsModlog($type = Tinebase_Model_Tag::TYPE_SHARED)
     {
         $contact = $this->_addContact();
         $filter = new Addressbook_Model_ContactFilter(array(array(
@@ -498,7 +509,7 @@ class Addressbook_JsonTest extends TestCase
             'operator' => 'equals',
             'value'    =>  $contact['id']
         )));
-        $sharedTagName = $this->_createAndAttachTag($filter);
+        $sharedTagName = $this->_createAndAttachTag($filter, $type);
         $this->_checkChangedNote($contact['id'], array(',"name":"' . $sharedTagName . '","description":"testTagDescription"', 'tags ([] -> [{'));
     }
     
@@ -518,6 +529,38 @@ class Addressbook_JsonTest extends TestCase
     }
     
     /**
+     * testCreatePersonalTagWithoutRight
+     * 
+     * @see 0010732: add "use personal tags" right to all applications
+     */
+    public function testCreatePersonalTagWithoutRight()
+    {
+        $this->_originalRoleRights = $this->_removeRoleRight('Addressbook', Tinebase_Acl_Rights::USE_PERSONAL_TAGS);
+        
+        try {
+            $this->testAttachMultipleTagsModlog(Tinebase_Model_Tag::TYPE_PERSONAL);
+            $this->fail('personal tags right is disabled');
+        } catch (Tinebase_Exception $e) {
+            $this->assertTrue($e instanceof Tinebase_Exception_AccessDenied, 'did not get expected exception: ' . $e);
+        }
+    }
+    
+    /**
+     * testFetchPersonalTagWithoutRight
+     * 
+     * @see 0010732: add "use personal tags" right to all applications
+     */
+    public function testFetchPersonalTagWithoutRight()
+    {
+        $contact = $this->testAttachMultipleTagsModlog(Tinebase_Model_Tag::TYPE_PERSONAL);
+        $this->_originalRoleRights = $this->_removeRoleRight('Addressbook', Tinebase_Acl_Rights::USE_PERSONAL_TAGS);
+        
+        $contact = $this->_instance->getContact($contact['id']);
+        
+        $this->assertTrue(! isset($contact['tags']) || count($contact['tags'] === 0), 'record should not have any tags');
+    }
+    
+    /**
      * try to get contacts by owner
      *
      */
@@ -1679,4 +1722,27 @@ Steuernummer 33/111/32212";
         $result = $this->_instance->searchLists($filter, array());
         $this->assertEquals(0, $result['totalcount'], 'should not find hidden list: ' . print_r($result, TRUE));
     }
+
+    public function testAttachMultipleTagsToMultipleRecords()
+    {
+        $contact1 = $this->_addContact('contact1');
+        $contact2 = $this->_addContact('contact2');
+        $tag1 = Tinebase_Tags::getInstance()->create($this->_getTag(Tinebase_Model_Tag::TYPE_PERSONAL, 'tag1'));
+        $tag2 = Tinebase_Tags::getInstance()->create($this->_getTag(Tinebase_Model_Tag::TYPE_PERSONAL, 'tag2'));
+
+        $filter = array(array('field' => 'id','operator' => 'in', 'value' => array($contact1['id'], $contact2['id'])));
+        $json = new Tinebase_Frontend_Json();
+
+        $json->attachMultipleTagsToMultipleRecords($filter,'Addressbook_Model_ContactFilter',array(
+            $tag1->toArray(),
+            $tag2->toArray(),
+        ));
+
+        $result = $this->_instance->searchContacts($filter, array());
+        $this->assertCount(2, $result['results'], 'search count failed');
+
+        foreach($result['results'] as $contactData) {
+            $this->assertCount(2, $contactData['tags'], $contactData['n_fn'] . ' tags failed');
+        }
+    }
 }
index 58b4cb1..b103515 100644 (file)
@@ -1143,4 +1143,88 @@ class Admin_JsonTest extends TestCase
         $this->assertEquals($registryData['primarydomain'], 'localhost');
         $this->assertEquals($registryData['secondarydomains'], 'example.com');
     }
+
+//    public function testGetConfig()
+//    {
+//        $afj = new Admin_Frontend_Json();
+//        $config = $afj->getConfig('Calendar');
+//
+//        $this->assertGreaterThanOrEqual(2, count($config));
+//        $this->assertArrayHasKey('attendeeRoles', $config);
+//        $this->assertArrayHasKey('records', $config['attendeeRoles']);
+//        $this->assertGreaterThanOrEqual(1, $config['attendeeRoles']['records']);
+//    }
+//
+//    public function testSetConfig()
+//    {
+//        $afj = new Admin_Frontend_Json();
+//        $config = $afj->getConfig('Calendar');
+//
+//        $attendeeRoles = $config['attendeeRoles'];
+//        $attendeeRoles['records'][] = array(
+//            'id'    => 'CHAIR',
+//            'value' => 'Chair'
+//        );
+//
+//        $afj->setConfig('Calendar', 'attendeeRoles', $attendeeRoles);
+//
+//        $updatedConfig = $afj->getConfig('Calendar');
+//        $this->assertEquals(count($attendeeRoles['records']), count($updatedConfig['attendeeRoles']['records']));
+//    }
+
+    public function testSearchConfigs()
+    {
+        $afj = new Admin_Frontend_Json();
+
+        $result = $afj->searchConfigs(array(
+            'application_id' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId()
+        ), array());
+
+        $this->assertGreaterThanOrEqual(2, $result['totalcount']);
+
+        $attendeeRoles = NULL;
+        foreach($result['results'] as $configData) {
+            if ($configData['name'] == 'attendeeRoles') {
+                $attendeeRoles = $configData;
+                break;
+            }
+        }
+
+        $this->assertNotNull($attendeeRoles);
+        $this->assertContains('{', $attendeeRoles);
+
+        return $attendeeRoles;
+    }
+
+    public function testGetConfig()
+    {
+        $attendeeRoles = $this->testUpdateConfig();
+
+        $afj = new Admin_Frontend_Json();
+        $fetchedAttendeeRoles = $afj->getConfig($attendeeRoles['id']);
+
+        $this->assertEquals($attendeeRoles['value'], $fetchedAttendeeRoles['value']);
+    }
+
+    public function testUpdateConfig()
+    {
+        $attendeeRoles = $this->testSearchConfigs();
+
+        $keyFieldConfig = json_decode($attendeeRoles['value'], true);
+        $keyFieldConfig['records'][] = array(
+            'id'    => 'CHAIR',
+            'value' => 'Chair'
+        );
+        $attendeeRoles['value'] = json_encode($keyFieldConfig);
+        $attendeeRoles['id'] = '';
+
+
+        $afj = new Admin_Frontend_Json();
+        $afj->saveConfig($attendeeRoles);
+
+        $updatedAttendeeRoles = $this->testSearchConfigs();
+
+        $this->assertEquals($attendeeRoles['value'], $updatedAttendeeRoles['value']);
+        return $updatedAttendeeRoles;
+    }
 }
index 8b1c92b..fe8a90e 100644 (file)
@@ -509,7 +509,36 @@ class Calendar_Controller_EventNotificationsTests extends Calendar_TestCase
         $this->_assertMail('sclever', 'Alarm');
         $this->assertEquals(1, count(self::getMessages()));
     }
-    
+
+    public function testAlarmSkipPreference()
+    {
+        Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely");
+        
+        $calPreferences = Tinebase_Core::getPreference('Calendar');
+        $calPreferences->setValueForUser(
+            Calendar_Preference::SEND_ALARM_NOTIFICATIONS,
+            0,
+            $this->_getPersona('sclever')->getId(), TRUE
+        );
+
+        $event = $this->_getEvent();
+        $event->dtstart = Tinebase_DateTime::now()->addMinute(15);
+        $event->dtend = clone $event->dtstart;
+        $event->dtend->addMinute(30);
+        $event->attendee = $this->_getAttendee();
+        $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
+            new Tinebase_Model_Alarm(array(
+                'minutes_before' => 30
+            ), TRUE)
+        ));
+
+        $persistentEvent = $this->_eventController->create($event);
+
+        self::flushMailer();
+        Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely");
+        $this->_assertMail('sclever', NULL);
+    }
+
     /**
      * CalDAV/Custom can have alarms with odd times
      */
@@ -547,7 +576,7 @@ class Calendar_Controller_EventNotificationsTests extends Calendar_TestCase
         Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely");
         $this->_assertMail('sclever');
     }
-    
+
     /**
      * testParallelAlarmTrigger
      * 
@@ -1302,12 +1331,15 @@ class Calendar_Controller_EventNotificationsTests extends Calendar_TestCase
      * checks if notification mail is sent to configured mail address of a resource
      * 
      * @see 0009954: resource manager and email handling
+     *
+     * @param boolean $suppress_notification
      */
-    public function testResourceNotification()
+    public function testResourceNotification($suppress_notification = false)
     {
         // create resource with email address of unittest user
         $resource = $this->_getResource();
-        $resource->email = Tinebase_Core::getUser()->accountEmailAddress;
+        $resource->email = $this->_GetPersonasContacts('pwulf')->email;
+        $resource->suppress_notification = $suppress_notification;
         $persistentResource = Calendar_Controller_Resource::getInstance()->create($resource);
         
         // create event with this resource as attender
@@ -1320,24 +1352,39 @@ class Calendar_Controller_EventNotificationsTests extends Calendar_TestCase
         $this->assertEquals(3, count($persistentEvent->attendee));
 
         $messages = self::getMessages();
-        
-        $this->assertEquals(2, count($messages), 'two mails should be send to current user (resource + attender)');
+
+        if ($suppress_notification) {
+            $this->assertEquals(2, count($messages), 'two mails should be send to attender (invite) + resource');
+        } else {
+            $this->assertEquals(2, count($messages), 'two mails should be send to current user (resource + attender)');
+        }
     }
 
     /**
      * Enable by a preference which sends mails to every user who got permissions to edit the resource
      */
-    public function testResourceNotificationForGrantedUsers()
+    public function testResourceNotificationForGrantedUsers($userIsAttendee = true, $suppress_notification = false)
     {
         // Enable feature, disabled by default!
         Calendar_Config::getInstance()->set(Calendar_Config::RESOURCE_MAIL_FOR_EDITORS, true);
 
         $resource = $this->_getResource();
         $resource->email = Tinebase_Core::getUser()->accountEmailAddress;
+        $resource->suppress_notification = $suppress_notification;
         $persistentResource = Calendar_Controller_Resource::getInstance()->create($resource);
 
         $event = $this->_getEvent(/*now = */ true);
         $event->attendee->addRecord($this->_createAttender($persistentResource->getId(), Calendar_Model_Attender::USERTYPE_RESOURCE));
+
+        if (! $userIsAttendee) {
+            // remove organizer attendee
+            foreach ($event->attendee as $idx => $attender) {
+                if ($attender->user_id === $event->organizer) {
+                    $event->attendee->removeRecord($attender);
+                }
+            }
+        }
+
         $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($resource->container_id);
 
         $newGrants = array(
@@ -1357,9 +1404,34 @@ class Calendar_Controller_EventNotificationsTests extends Calendar_TestCase
 
         Tinebase_Container::getInstance()->setGrants($resource->container_id, $grants);
 
+        $this->assertContains('Resource "' . $persistentResource->name . '" was booked', print_r($messages, true));
         $this->assertContains('Meeting Room (Required, No response)', print_r($messages, true));
-        $this->assertEquals(4, count($messages), 'four mails should be send to current user (resource + attender + everybody whos allowed to edit this resource)');
-        $this->assertEquals(3, count($persistentEvent->attendee));
+
+        if ($suppress_notification) {
+            $this->assertEquals(2, count($messages), 'two mails should be send to current user (resource + attender)');
+        } else {
+            $this->assertEquals(4, count($messages), 'four mails should be send to current user (resource + attender + everybody who is allowed to edit this resource)');
+            $this->assertEquals(count($event->attendee), count($persistentEvent->attendee));
+        }
+    }
+
+    /**
+     * @see 0011272: ressource invitation: organizer receives no mail if he is no attendee
+     */
+    public function testResourceNotificationForNonAttendeeOrganizer()
+    {
+        $this->testResourceNotificationForGrantedUsers(/* $userIsAttendee = */ false);
+    }
+
+    /**
+     * testResourceNotificationMuteForEditors
+     *
+     * @see 0011312: Make resource notification handling and default status configurable
+     */
+    public function testResourceNotificationMuteForEditors()
+    {
+        $this->testResourceNotification(/* $suppress_notification = */ true);
+        $this->testResourceNotificationForGrantedUsers(/* $userIsAttendee = */ false, /* $suppress_notification = */ true);
     }
     
     /**
index f8bd0fc..489b579 100644 (file)
@@ -183,7 +183,7 @@ class Calendar_Convert_Event_VCalendar_GenericTest extends PHPUnit_Framework_Tes
      */
     public function testConvertToTine20ModelWithGroupInvitation()
     {
-        $smtpConfig = Tinebase_Config::getInstance()->get(Tinebase_Model_Config::SMTP, new Tinebase_Config_Struct())->toArray();
+        $smtpConfig = Tinebase_Config::getInstance()->get(Tinebase_Config::SMTP, new Tinebase_Config_Struct())->toArray();
         if (!isset($smtpConfig['primarydomain'])) {
             $this->markTestSkipped('no primary smtp domain configured');
         }
index 8f49c0e..30e53c8 100644 (file)
@@ -191,7 +191,7 @@ class Calendar_Export_ICalTest extends Calendar_TestCase
     {
         $eventData = $this->_getEvent(TRUE)->toArray();
         $this->_uit = new Calendar_Frontend_Json();
-         $this->_uit->saveEvent($eventData);
+        $this->_uit->saveEvent($eventData);
         
         $this->_testNeedsTransaction();
         $cmd = realpath(__DIR__ . "/../../../../tine20/tine20.php") . ' --method Calendar.exportICS ' .
index 8ea740a..e2e0643 100644 (file)
@@ -31,55 +31,7 @@ class Calendar_Frontend_ActiveSyncTest extends ActiveSync_Controller_ControllerT
      */
     protected $objects = array();
     
-    protected $_testXMLInput = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
-    <Sync xmlns="uri:AirSync" xmlns:Calendar="uri:Calendar">
-        <Collections>
-            <Collection>
-                <Class>Calendar</Class>
-                <SyncKey>9</SyncKey>
-                <CollectionId>41</CollectionId>
-                <DeletesAsMoves/>
-                <GetChanges/>
-                <WindowSize>50</WindowSize>
-                <Options><FilterType>5</FilterType></Options>
-                <Commands>
-                    <Change>
-                        <ServerId>6de7cb687964dc6eea109cd81750177979362217</ServerId>
-                        <ApplicationData>
-                            <Calendar:Timezone>xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Calendar:Timezone>
-                            <Calendar:AllDayEvent>0</Calendar:AllDayEvent>
-                            <Calendar:BusyStatus>2</Calendar:BusyStatus>
-                            <Calendar:DtStamp>20121125T150537Z</Calendar:DtStamp>
-                            <Calendar:EndTime>20121123T160000Z</Calendar:EndTime>
-                            <Calendar:Sensitivity>2</Calendar:Sensitivity>
-                            <Calendar:Subject>Repeat</Calendar:Subject>
-                            <Calendar:StartTime>20121123T130000Z</Calendar:StartTime>
-                            <Calendar:UID>6de7cb687964dc6eea109cd81750177979362217</Calendar:UID>
-                            <Calendar:MeetingStatus>1</Calendar:MeetingStatus>
-                            <Calendar:Attendees>
-                                <Calendar:Attendee>
-                                    <Calendar:Name>Lars Kneschke</Calendar:Name>
-                                    <Calendar:Email>lars@kneschke.de</Calendar:Email>
-                                </Calendar:Attendee>
-                            </Calendar:Attendees>
-                            <Calendar:Recurrence>
-                                <Calendar:Type>0</Calendar:Type><Calendar:Interval>1</Calendar:Interval><Calendar:Until>20121128T225959Z</Calendar:Until>
-                            </Calendar:Recurrence>
-                            <Calendar:Exceptions>
-                                <Calendar:Exception>
-                                    <Calendar:Deleted>0</Calendar:Deleted><Calendar:ExceptionStartTime>20121125T130000Z</Calendar:ExceptionStartTime><Calendar:StartTime>20121125T140000Z</Calendar:StartTime><Calendar:EndTime>20121125T170000Z</Calendar:EndTime><Calendar:Subject>Repeat mal anders</Calendar:Subject><Calendar:BusyStatus>2</Calendar:BusyStatus><Calendar:AllDayEvent>0</Calendar:AllDayEvent>
-                                </Calendar:Exception>
-                                <Calendar:Exception>
-                                    <Calendar:Deleted>1</Calendar:Deleted><Calendar:ExceptionStartTime>20121124T130000Z</Calendar:ExceptionStartTime></Calendar:Exception>
-                                </Calendar:Exceptions>
-                            <Calendar:Reminder>15</Calendar:Reminder>
-                            <Body xmlns="uri:AirSyncBase"><Type>1</Type><Data>Hello</Data></Body>
-                        </ApplicationData>
-                    </Change>
-                </Commands>
-            </Collection>
-        </Collections>
-    </Sync>';
+    protected $_testXMLInput;
     
     protected $_testXMLInput_palmPreV12 = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
     <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
@@ -504,17 +456,26 @@ Zeile 3</AirSyncBase:Data>
         parent::setUp();
         
         Calendar_Controller_Event::getInstance()->doContainerACLChecks(true);
-        
+
+        $this->_testXMLInput = $this->_loadFile('event_with_attendee.xml');
+    }
+
+    protected function _loadFile($filename)
+    {
+        $xml = file_get_contents(__DIR__ . '/files/' . $filename);
+
         // replace email to make current user organizer and attendee
         $testConfig = Zend_Registry::get('testConfig');
         $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
-        
-        $this->_testXMLInput = str_replace(array(
+
+        $xml = str_replace(array(
             'lars@kneschke.de',
             'unittest@tine20.org',
-        ), $email, $this->_testXMLInput);
+        ), $email, $xml);
+
+        return $xml;
     }
-    
+
     /**
      * (non-PHPdoc)
      * @see ActiveSync_TestCase::testCreateEntry()
@@ -809,7 +770,29 @@ Zeile 3</AirSyncBase:Data>
         $this->assertTrue((bool) $syncrotonEvent->exceptions[0]->deleted);
         $this->assertTrue((bool) $syncrotonEvent->exceptions[1]->deleted);
     }
-    
+
+    public function testRecurEventFirstInstanceException($syncrotonFolder = null)
+    {
+        if ($syncrotonFolder === null) {
+            $syncrotonFolder = $this->testCreateFolder();
+        }
+
+        $xmlString = $this->_loadFile('repeating_with_first_instance_exception.xml');
+        $xml = new SimpleXMLElement($xmlString);
+        $syncrotonEvent = new Syncroton_Model_Event($xml->Collections->Collection->Commands->Add[0]->ApplicationData);
+
+        $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
+
+        $serverId = $controller->createEntry($syncrotonFolder->serverId, $syncrotonEvent);
+
+        $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
+
+        $this->assertEquals('First Instance Exception', $syncrotonEvent->exceptions[0]->subject);
+        $this->assertCount(1, $syncrotonEvent->exceptions);
+
+        return array($serverId, $syncrotonEvent);
+    }
+
     public function testStatusUpdate($syncrotonFolder = null)
     {
         if ($syncrotonFolder === null) {
index b19777e..2e21e40 100644 (file)
@@ -538,7 +538,99 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
 
         $this->assertEquals('New Event', $record->summary);
     }
-    
+
+    /**
+     * test updating existing event when attendee or organizer email changed in the meantime
+     */
+    public function testPutEventWhenEmailChanged()
+    {
+        $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
+
+        $event = $this->testCreateEventWithExternalOrganizer();
+
+        // change email address of organizer / attendee
+        $contact = $event->getRecord()->organizer;
+        $contact->email = 'changed@mail.domain';
+        sleep(1);
+        Addressbook_Controller_Contact::getInstance()->update($contact);
+        Calendar_Model_Attender::clearCache();
+
+        $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
+        $vcalendar = str_replace("lars@kneschke.de", "l.kneschke@metaways.de", $vcalendar);
+        $event->put($vcalendar);
+
+        $record = $event->getRecord();
+
+        $this->assertEquals($contact->getId(), $record->organizer->getId(), 'organizer must not change');
+        $this->assertCount(1, $record->attendee->filter('user_id', $contact->getId()), 'attendee must not change');
+    }
+
+    public function testPutEventWithRecurExceptions()
+    {
+        $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
+
+        $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_exdate_mozlastack.ics');
+        $id = Tinebase_Record_Abstract::generateUID();
+        $targetContainer = $this->objects['initialContainer'];
+
+        $event = Calendar_Frontend_WebDAV_Event::create($targetContainer, "$id.ics", $vcalendar);
+        $record = $event->getRecord();
+
+        $eventToUpdate = new Calendar_Frontend_WebDAV_Event($targetContainer, $id);
+        $vcalendarToUpdate = stream_get_contents($eventToUpdate->get());
+        $vcalendarToUpdate = str_replace("abendessen später am heiligabend", "vesper später am heiligabend", $vcalendarToUpdate);
+//        echo $vcalendarToUpdate;
+        $eventToUpdate->put($vcalendarToUpdate);
+
+        $updatedRecord = Calendar_Controller_MSEventFacade::getInstance()->get($id);
+        $xmas = $updatedRecord->exdate->filter('summary', '/^vesper.*/', true)->getFirstRecord();
+        $this->assertNotNull($xmas, print_r($updatedRecord->toArray(), true));
+    }
+
+    public function testPutEventWithRecurFirstInstanceException()
+    {
+        $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
+
+        $vcalendar = file_get_contents(dirname(__FILE__) . '/../../Import/files/lightning_repeating_group_first_instance_exception.ics');
+        $vcalendar = str_replace("d981a72be8f21808b588daa0e8644046c634250f", Tinebase_Group::getInstance()->getDefaultGroup()->list_id, $vcalendar);
+
+        $id = Tinebase_Record_Abstract::generateUID();
+        $targetContainer = $this->objects['initialContainer'];
+
+        $event = Calendar_Frontend_WebDAV_Event::create($targetContainer, "$id.ics", $vcalendar);
+
+        // sometimes base_event_id is set for base_events;
+        $record = $event->getRecord();
+        $record->base_event_id = $record->getId();
+        Calendar_Controller_Event::getInstance()->update($record);
+
+        $eventToUpdate = new Calendar_Frontend_WebDAV_Event($targetContainer, $id);
+        $vcalendarToUpdate = stream_get_contents($eventToUpdate->get());
+        $eventToUpdate->put($vcalendarToUpdate);
+    }
+
+    public function testPutEventWithRecurExceptionsExternalOrganizer()
+    {
+        $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
+
+        $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_exdate_mozlastack.ics');
+        $vcalendar = str_replace("sclever", "external", $vcalendar);
+//        echo $vcalendar;
+
+        $id = Tinebase_Record_Abstract::generateUID();
+        $targetContainer = $this->objects['initialContainer'];
+
+        $event = Calendar_Frontend_WebDAV_Event::create($targetContainer, "$id.ics", $vcalendar);
+        $record = $event->getRecord();
+
+        $loadedEvent = new Calendar_Frontend_WebDAV_Event($targetContainer, $id);
+        $loadedRecord = $loadedEvent->getRecord();
+//        echo stream_get_contents($loadedEvent->get());
+
+        $xmas = $loadedRecord->exdate->filter('summary', '/.*heiligabend$/', true)->getFirstRecord();
+        $this->assertNotNull($xmas, print_r($loadedRecord->toArray(), true));
+    }
+
     /**
      * test deleting attachment from existing event
      */
diff --git a/tests/tine20/Calendar/Frontend/files/event_with_attendee.xml b/tests/tine20/Calendar/Frontend/files/event_with_attendee.xml
new file mode 100644 (file)
index 0000000..abf2ea0
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+<Sync xmlns="uri:AirSync" xmlns:Calendar="uri:Calendar">
+    <Collections>
+        <Collection>
+            <Class>Calendar</Class>
+            <SyncKey>9</SyncKey>
+            <CollectionId>41</CollectionId>
+            <DeletesAsMoves/>
+            <GetChanges/>
+            <WindowSize>50</WindowSize>
+            <Options><FilterType>5</FilterType></Options>
+            <Commands>
+                <Change>
+                    <ServerId>6de7cb687964dc6eea109cd81750177979362217</ServerId>
+                    <ApplicationData>
+                        <Calendar:Timezone>xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Calendar:Timezone>
+                        <Calendar:AllDayEvent>0</Calendar:AllDayEvent>
+                        <Calendar:BusyStatus>2</Calendar:BusyStatus>
+                        <Calendar:DtStamp>20121125T150537Z</Calendar:DtStamp>
+                        <Calendar:EndTime>20121123T160000Z</Calendar:EndTime>
+                        <Calendar:Sensitivity>2</Calendar:Sensitivity>
+                        <Calendar:Subject>Repeat</Calendar:Subject>
+                        <Calendar:StartTime>20121123T130000Z</Calendar:StartTime>
+                        <Calendar:UID>6de7cb687964dc6eea109cd81750177979362217</Calendar:UID>
+                        <Calendar:MeetingStatus>1</Calendar:MeetingStatus>
+                        <Calendar:Attendees>
+                            <Calendar:Attendee>
+                                <Calendar:Name>Lars Kneschke</Calendar:Name>
+                                <Calendar:Email>lars@kneschke.de</Calendar:Email>
+                            </Calendar:Attendee>
+                        </Calendar:Attendees>
+                        <Calendar:Recurrence>
+                            <Calendar:Type>0</Calendar:Type><Calendar:Interval>1</Calendar:Interval><Calendar:Until>20121128T225959Z</Calendar:Until>
+                        </Calendar:Recurrence>
+                        <Calendar:Exceptions>
+                            <Calendar:Exception>
+                                <Calendar:Deleted>0</Calendar:Deleted><Calendar:ExceptionStartTime>20121125T130000Z</Calendar:ExceptionStartTime><Calendar:StartTime>20121125T140000Z</Calendar:StartTime><Calendar:EndTime>20121125T170000Z</Calendar:EndTime><Calendar:Subject>Repeat mal anders</Calendar:Subject><Calendar:BusyStatus>2</Calendar:BusyStatus><Calendar:AllDayEvent>0</Calendar:AllDayEvent>
+                            </Calendar:Exception>
+                            <Calendar:Exception>
+                                <Calendar:Deleted>1</Calendar:Deleted><Calendar:ExceptionStartTime>20121124T130000Z</Calendar:ExceptionStartTime></Calendar:Exception>
+                        </Calendar:Exceptions>
+                        <Calendar:Reminder>15</Calendar:Reminder>
+                        <Body xmlns="uri:AirSyncBase"><Type>1</Type><Data>Hello</Data></Body>
+                    </ApplicationData>
+                </Change>
+            </Commands>
+        </Collection>
+    </Collections>
+</Sync>
\ No newline at end of file
diff --git a/tests/tine20/Calendar/Frontend/files/repeating_with_first_instance_exception.xml b/tests/tine20/Calendar/Frontend/files/repeating_with_first_instance_exception.xml
new file mode 100644 (file)
index 0000000..67e83e4
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+<Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase" xmlns:Calendar="uri:Calendar">
+    <Collections>
+        <Collection>
+            <Class>Calendar</Class>
+            <SyncKey>8</SyncKey>
+            <CollectionId>calendar-root</CollectionId>
+            <DeletesAsMoves/><GetChanges/>
+            <WindowSize>100</WindowSize>
+            <Options><FilterType>4</FilterType><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><AirSyncBase:BodyPreference><AirSyncBase:Type>3</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize><AirSyncBase:AllOrNone>1</AirSyncBase:AllOrNone></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options>
+            <Commands>
+                <Add>
+                    <ClientId>1073741902</ClientId>
+                    <ApplicationData><Calendar:Timezone>xP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==</Calendar:Timezone>
+                        <Calendar:AllDayEvent>0</Calendar:AllDayEvent>
+                        <Calendar:BusyStatus>2</Calendar:BusyStatus>
+                        <Calendar:DtStamp>20101224T082738Z</Calendar:DtStamp>
+                        <Calendar:EndTime>20101220T100000Z</Calendar:EndTime>
+                        <Calendar:MeetingStatus>0</Calendar:MeetingStatus>
+                        <Calendar:Reminder>15</Calendar:Reminder>
+                        <Calendar:Sensitivity>0</Calendar:Sensitivity>
+                        <Calendar:Subject>Tdfffdd</Calendar:Subject>
+                        <Calendar:StartTime>20101220T090000Z</Calendar:StartTime>
+                        <Calendar:UID>040000008200E00074C5B7101A82E00800000000DCA959CF1C69F280D15448CF43450B301000000019B5FB15984956377D4EBEFE125A8EF6</Calendar:UID>
+                        <Calendar:Recurrence>
+                            <Calendar:Type>1</Calendar:Type>
+                            <Calendar:DayOfWeek>2</Calendar:DayOfWeek>
+                            <Calendar:Interval>1</Calendar:Interval>
+                        </Calendar:Recurrence>
+                        <Calendar:Exceptions>
+                            <Calendar:Exception>
+                                <Calendar:ExceptionStartTime>20101220T090000Z</Calendar:ExceptionStartTime>
+                                <Calendar:EndTime>20101221T100000Z</Calendar:EndTime>
+                                <Calendar:Subject>First Instance Exception</Calendar:Subject>
+                                <Calendar:StartTime>20101221T090000Z</Calendar:StartTime>
+                            </Calendar:Exception>
+                        </Calendar:Exceptions>
+                    </ApplicationData>
+                </Add>
+            </Commands>
+        </Collection>
+    </Collections>
+</Sync>
\ No newline at end of file
index 2237d0b..af163b8 100644 (file)
@@ -46,7 +46,6 @@ SEQUENCE:13
 CLASS:PUBLIC
 TRANSP:OPAQUE
 X-MOZ-SNOOZE-TIME;VALUE=DATE-TIME:20140108T123128Z
-X-TINE20-CONTAINER:4
 X-MOZ-GENERATION:1
 BEGIN:VALARM
 ACTION:DISPLAY
@@ -78,7 +77,6 @@ DTEND;TZID=Europe/Berlin:20131224T200000
 SEQUENCE:3
 CLASS:PUBLIC
 TRANSP:OPAQUE
-X-TINE20-CONTAINER:4
 END:VEVENT
 BEGIN:VEVENT
 CREATED:20140108T135658Z
@@ -107,7 +105,6 @@ SEQUENCE:0
 CLASS:PUBLIC
 TRANSP:OPAQUE
 X-MOZ-SNOOZE-TIME;VALUE=DATE-TIME:20140108T123128Z
-X-TINE20-CONTAINER:4
 BEGIN:VALARM
 ACTION:DISPLAY
 TRIGGER;VALUE=DURATION:-P1D
@@ -142,7 +139,6 @@ DTEND;TZID=Europe/Berlin:20131227T200000
 SEQUENCE:1
 CLASS:PUBLIC
 TRANSP:OPAQUE
-X-TINE20-CONTAINER:4
 BEGIN:VALARM
 ACTION:DISPLAY
 TRIGGER;VALUE=DURATION:-PT15M
@@ -176,7 +172,6 @@ SEQUENCE:1
 CLASS:PUBLIC
 TRANSP:OPAQUE
 X-MOZ-SNOOZE-TIME;VALUE=DATE-TIME:20140108T121614Z
-X-TINE20-CONTAINER:4
 BEGIN:VALARM
 ACTION:DISPLAY
 TRIGGER;VALUE=DURATION:-P1D
diff --git a/tests/tine20/Calendar/Import/files/lightning_repeating_group_first_instance_exception.ics b/tests/tine20/Calendar/Import/files/lightning_repeating_group_first_instance_exception.ics
new file mode 100644 (file)
index 0000000..dfc8af5
--- /dev/null
@@ -0,0 +1,334 @@
+BEGIN:VCALENDAR
+PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20151123T164053Z
+LAST-MODIFIED:20151223T085247Z
+DTSTAMP:20151223T085247Z
+UID:ac714fd2aaee06a7d1ebe69710a8317bbc38ffd7
+SUMMARY:MA Besprechung
+STATUS:CONFIRMED
+ORGANIZER;CN="Admin Account, Tine 2.0";EMAIL=unittest@tine20.org:mailto:uni
+ ttest@tine20.org
+ATTENDEE;CN="McBlack, James";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=R
+ EQ-PARTICIPANT;RSVP=FALSE;EMAIL=jmcblack@tine20.org:mailto:jmcblack@tine20
+ .org
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTI
+ ON;ROLE=REQ-PARTICIPANT;RSVP=FALSE;EMAIL=unittest@tine20.org:mailto:unitte
+ st@tine20.org
+ATTENDEE;CN="Wright, Roberta";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=
+ REQ-PARTICIPANT;RSVP=FALSE;EMAIL=rwright@tine20.org:mailto:rwright@tine20.
+ org
+ATTENDEE;CN="Wulf, Paul";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-P
+ ARTICIPANT;RSVP=FALSE;EMAIL=pwulf@tine20.org:mailto:pwulf@tine20.org
+ATTENDEE;CN="Smith, John";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-
+ PARTICIPANT;RSVP=FALSE;EMAIL=jsmith@tine20.org:mailto:jsmith@tine20.org
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=RE
+ Q-PARTICIPANT;RSVP=FALSE;EMAIL=sclever@tine20.org:mailto:sclever@tine20.or
+ g
+ATTENDEE;CN=Users;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;R
+ SVP=FALSE:urn:uuid:d981a72be8f21808b588daa0e8644046c634250f
+RRULE:FREQ=WEEKLY;UNTIL=20151125T235959Z;BYDAY=WE
+EXDATE:20151230T093000Z
+EXDATE:20151216T093000Z
+X-MOZ-LASTACK:20151223T085247Z
+DTSTART;TZID=Europe/Berlin:20151125T103000
+DTEND;TZID=Europe/Berlin:20151125T113000
+CLASS:PUBLIC
+SEQUENCE:40
+DESCRIPTION:https://team.t20.org/sites/tine/TeamSeite/TINE%20Mitarbeiter/A
+ genda-Mitarbeiter.aspx
+LOCATION:-134
+TRANSP:OPAQUE
+X-CALENDARSERVER-ACCESS:PUBLIC
+X-MOZ-SNOOZE-TIME:20151125T084120Z
+X-TINE20-CONTAINER:90
+X-MOZ-GENERATION:1
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;VALUE=DURATION:-PT1H
+DESCRIPTION:MA Besprechung
+X-LIC-ERROR;X-LIC-ERRORTYPE=PROPERTY-PARSE-ERROR:Parse error in property n
+ ame: ACKNOWLEDGED
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20151125T084946Z
+LAST-MODIFIED:20151222T133420Z
+DTSTAMP:20151222T135738Z
+UID:ac714fd2aaee06a7d1ebe69710a8317bbc38ffd7
+SUMMARY:MA Besprechung
+STATUS:CONFIRMED
+RECURRENCE-ID;TZID=Europe/Berlin:20151125T103000
+ORGANIZER;CN="Admin Account, Tine 2.0";EMAIL=unittest@tine20.org:mailto:uni
+ ttest@tine20.org
+ATTENDEE;CN="McBlack, James";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=R
+ EQ-PARTICIPANT;RSVP=FALSE;EMAIL=jmcblack@tine20.org:mailto:jmcblack@tine20
+ .org
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTI
+ ON;ROLE=REQ-PARTICIPANT;RSVP=FALSE;EMAIL=unittest@tine20.org:mailto:unitte
+ st@tine20.org
+ATTENDEE;CN="Wright, Roberta";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=
+ REQ-PARTICIPANT;RSVP=FALSE;EMAIL=rwright@tine20.org:mailto:rwright@tine20.
+ org
+ATTENDEE;CN="Wulf, Paul";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-P
+ ARTICIPANT;RSVP=FALSE;EMAIL=pwulf@tine20.org:mailto:pwulf@tine20.org
+ATTENDEE;CN="Smith, John";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-
+ PARTICIPANT;RSVP=FALSE;EMAIL=jsmith@tine20.org:mailto:jsmith@tine20.org
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=RE
+ Q-PARTICIPANT;RSVP=FALSE;EMAIL=sclever@tine20.org:mailto:sclever@tine20.or
+ g
+ATTENDEE;CN=Users;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;R
+ SVP=FALSE:urn:uuid:d981a72be8f21808b588daa0e8644046c634250f
+DTSTART;TZID=Europe/Berlin:20151125T103000
+DTEND;TZID=Europe/Berlin:20151125T113000
+CLASS:PUBLIC
+SEQUENCE:4
+DESCRIPTION:https://team.t20.org/sites/tine/TeamSeite/TINE%20Mitarbeiter/A
+ genda-Mitarbeiter.aspx
+LOCATION:-134
+TRANSP:OPAQUE
+X-CALENDARSERVER-ACCESS:PUBLIC
+X-TINE20-CONTAINER:90
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20151222T132525Z
+LAST-MODIFIED:20151222T133450Z
+DTSTAMP:20151222T135738Z
+UID:ac714fd2aaee06a7d1ebe69710a8317bbc38ffd7
+SUMMARY:MA Besprechung
+STATUS:CONFIRMED
+RECURRENCE-ID;TZID=Europe/Berlin:20151202T103000
+ORGANIZER;CN="Admin Account, Tine 2.0";EMAIL=unittest@tine20.org:mailto:uni
+ ttest@tine20.org
+ATTENDEE;CN="McBlack, James";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=R
+ EQ-PARTICIPANT;RSVP=FALSE;EMAIL=jmcblack@tine20.org:mailto:jmcblack@tine20
+ .org
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTI
+ ON;ROLE=REQ-PARTICIPANT;RSVP=FALSE;EMAIL=unittest@tine20.org:mailto:unitte
+ st@tine20.org
+ATTENDEE;CN="Wright, Roberta";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=
+ REQ-PARTICIPANT;RSVP=FALSE;EMAIL=rwright@tine20.org:mailto:rwright@tine20.
+ org
+ATTENDEE;CN="Wulf, Paul";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-P
+ ARTICIPANT;RSVP=FALSE;EMAIL=pwulf@tine20.org:mailto:pwulf@tine20.org
+ATTENDEE;CN="Smith, John";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-
+ PARTICIPANT;RSVP=FALSE;EMAIL=jsmith@tine20.org:mailto:jsmith@tine20.org
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=RE
+ Q-PARTICIPANT;RSVP=FALSE;EMAIL=sclever@tine20.org:mailto:sclever@tine20.or
+ g
+ATTENDEE;CN=Users;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;R
+ SVP=FALSE:urn:uuid:d981a72be8f21808b588daa0e8644046c634250f
+DTSTART;TZID=Europe/Berlin:20151202T103000
+DTEND;TZID=Europe/Berlin:20151202T113000
+CLASS:PUBLIC
+SEQUENCE:2
+DESCRIPTION:https://team.t20.org/sites/tine/TeamSeite/TINE%20Mitarbeiter/A
+ genda-Mitarbeiter.aspx
+LOCATION:-134
+TRANSP:OPAQUE
+X-CALENDARSERVER-ACCESS:PUBLIC
+X-TINE20-CONTAINER:90
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20151222T132545Z
+LAST-MODIFIED:20151222T133545Z
+DTSTAMP:20151222T135738Z
+UID:ac714fd2aaee06a7d1ebe69710a8317bbc38ffd7
+SUMMARY:MA Besprechung
+STATUS:CONFIRMED
+RECURRENCE-ID;TZID=Europe/Berlin:20151209T103000
+ORGANIZER;CN="Admin Account, Tine 2.0";EMAIL=unittest@tine20.org:mailto:uni
+ ttest@tine20.org
+ATTENDEE;CN="McBlack, James";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=R
+ EQ-PARTICIPANT;RSVP=FALSE;EMAIL=jmcblack@tine20.org:mailto:jmcblack@tine20
+ .org
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTI
+ ON;ROLE=REQ-PARTICIPANT;RSVP=FALSE;EMAIL=unittest@tine20.org:mailto:unitte
+ st@tine20.org
+ATTENDEE;CN="Wright, Roberta";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=
+ REQ-PARTICIPANT;RSVP=FALSE;EMAIL=rwright@tine20.org:mailto:rwright@tine20.
+ org
+ATTENDEE;CN="Wulf, Paul";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-P
+ ARTICIPANT;RSVP=FALSE;EMAIL=pwulf@tine20.org:mailto:pwulf@tine20.org
+ATTENDEE;CN="Smith, John";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-
+ PARTICIPANT;RSVP=FALSE;EMAIL=jsmith@tine20.org:mailto:jsmith@tine20.org
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=RE
+ Q-PARTICIPANT;RSVP=FALSE;EMAIL=sclever@tine20.org:mailto:sclever@tine20.or
+ g
+ATTENDEE;CN=Users;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;R
+ SVP=FALSE:urn:uuid:d981a72be8f21808b588daa0e8644046c634250f
+DTSTART;TZID=Europe/Berlin:20151209T103000
+DTEND;TZID=Europe/Berlin:20151209T113000
+CLASS:PUBLIC
+SEQUENCE:2
+DESCRIPTION:https://team.t20.org/sites/tine/TeamSeite/TINE%20Mitarbeiter/A
+ genda-Mitarbeiter.aspx
+LOCATION:-134
+TRANSP:OPAQUE
+X-CALENDARSERVER-ACCESS:PUBLIC
+X-TINE20-CONTAINER:90
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20151215T170512Z
+LAST-MODIFIED:20151222T133129Z
+DTSTAMP:20151222T135738Z
+UID:ac714fd2aaee06a7d1ebe69710a8317bbc38ffd7
+SUMMARY:MA Besprechung
+STATUS:CONFIRMED
+RECURRENCE-ID;TZID=Europe/Berlin:20151223T103000
+ORGANIZER;CN="Admin Account, Tine 2.0";EMAIL=unittest@tine20.org:mailto:uni
+ ttest@tine20.org
+ATTENDEE;CN="McBlack, James";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=R
+ EQ-PARTICIPANT;RSVP=FALSE;EMAIL=jmcblack@tine20.org:mailto:jmcblack@tine20
+ .org
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTI
+ ON;ROLE=REQ-PARTICIPANT;RSVP=FALSE;EMAIL=unittest@tine20.org:mailto:unitte
+ st@tine20.org
+ATTENDEE;CN="Wright, Roberta";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=
+ REQ-PARTICIPANT;RSVP=FALSE;EMAIL=rwright@tine20.org:mailto:rwright@tine20.
+ org
+ATTENDEE;CN="Wulf, Paul";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-P
+ ARTICIPANT;RSVP=FALSE;EMAIL=pwulf@tine20.org:mailto:pwulf@tine20.org
+ATTENDEE;CN="Smith, John";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-
+ PARTICIPANT;RSVP=FALSE;EMAIL=jsmith@tine20.org:mailto:jsmith@tine20.org
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=RE
+ Q-PARTICIPANT;RSVP=FALSE;EMAIL=sclever@tine20.org:mailto:sclever@tine20.or
+ g
+ATTENDEE;CN=Users;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;R
+ SVP=FALSE:urn:uuid:d981a72be8f21808b588daa0e8644046c634250f
+
+X-MOZ-LASTACK:20151209T083009Z
+DTSTART;TZID=Europe/Berlin:20151223T103000
+DTEND;TZID=Europe/Berlin:20151223T113000
+CLASS:PUBLIC
+SEQUENCE:3
+DESCRIPTION:https://team.t20.org/sites/tine/TeamSeite/TINE%20Mitarbeiter/A
+ genda-Mitarbeiter.aspx
+LOCATION:-134
+TRANSP:OPAQUE
+X-CALENDARSERVER-ACCESS:PUBLIC
+X-MOZ-SNOOZE-TIME:20151125T084120Z
+X-TINE20-CONTAINER:90
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;VALUE=DURATION:-PT1H
+DESCRIPTION:MA Besprechung
+X-LIC-ERROR;X-LIC-ERRORTYPE=PROPERTY-PARSE-ERROR:Parse error in property n
+ ame: ACKNOWLEDGED
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20151215T170623Z
+LAST-MODIFIED:20151215T170623Z
+DTSTAMP:20151222T135738Z
+UID:ac714fd2aaee06a7d1ebe69710a8317bbc38ffd7
+SUMMARY:MA Besprechung
+STATUS:CONFIRMED
+RECURRENCE-ID;TZID=Europe/Berlin:20160106T103000
+ORGANIZER;CN="Admin Account, Tine 2.0";EMAIL=unittest@tine20.org:mailto:uni
+ ttest@tine20.org
+ATTENDEE;CN="McBlack, James";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=R
+ EQ-PARTICIPANT;RSVP=FALSE;EMAIL=jmcblack@tine20.org:mailto:jmcblack@tine20
+ .org
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTI
+ ON;ROLE=REQ-PARTICIPANT;RSVP=FALSE;EMAIL=unittest@tine20.org:mailto:unitte
+ st@tine20.org
+ATTENDEE;CN="Wright, Roberta";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=
+ REQ-PARTICIPANT;RSVP=FALSE;EMAIL=rwright@tine20.org:mailto:rwright@tine20.
+ org
+ATTENDEE;CN="Wulf, Paul";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-P
+ ARTICIPANT;RSVP=FALSE;EMAIL=pwulf@tine20.org:mailto:pwulf@tine20.org
+ATTENDEE;CN="Smith, John";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-
+ PARTICIPANT;RSVP=FALSE;EMAIL=jsmith@tine20.org:mailto:jsmith@tine20.org
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=RE
+ Q-PARTICIPANT;RSVP=FALSE;EMAIL=sclever@tine20.org:mailto:sclever@tine20.or
+ g
+ATTENDEE;CN=Users;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;R
+ SVP=FALSE:urn:uuid:d981a72be8f21808b588daa0e8644046c634250f
+X-MOZ-LASTACK:20151209T083009Z
+DTSTART;TZID=Europe/Berlin:20160106T103000
+DTEND;TZID=Europe/Berlin:20160106T113000
+CLASS:PUBLIC
+SEQUENCE:1
+DESCRIPTION:https://team.t20.org/sites/tine/TeamSeite/TINE%20Mitarbeiter/A
+ genda-Mitarbeiter.aspx
+LOCATION:-134
+TRANSP:OPAQUE
+X-CALENDARSERVER-ACCESS:PUBLIC
+X-MOZ-SNOOZE-TIME:20151125T084120Z
+X-TINE20-CONTAINER:90
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;VALUE=DURATION:-PT1H
+DESCRIPTION:MA Besprechung
+X-LIC-ERROR;X-LIC-ERRORTYPE=PROPERTY-PARSE-ERROR:Parse error in property n
+ ame: ACKNOWLEDGED
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+CREATED:20151215T170737Z
+LAST-MODIFIED:20151215T170737Z
+DTSTAMP:20151222T135738Z
+UID:ac714fd2aaee06a7d1ebe69710a8317bbc38ffd7
+SUMMARY:MA Besprechung
+STATUS:CONFIRMED
+RECURRENCE-ID;TZID=Europe/Berlin:20160113T103000
+ORGANIZER;CN="Admin Account, Tine 2.0";EMAIL=unittest@tine20.org:mailto:uni
+ ttest@tine20.org
+ATTENDEE;CN="McBlack, James";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=R
+ EQ-PARTICIPANT;RSVP=FALSE;EMAIL=jmcblack@tine20.org:mailto:jmcblack@tine20
+ .org
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTI
+ ON;ROLE=REQ-PARTICIPANT;RSVP=FALSE;EMAIL=unittest@tine20.org:mailto:unitte
+ st@tine20.org
+ATTENDEE;CN="Wright, Roberta";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=
+ REQ-PARTICIPANT;RSVP=FALSE;EMAIL=rwright@tine20.org:mailto:rwright@tine20.
+ org
+ATTENDEE;CN="Wulf, Paul";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-P
+ ARTICIPANT;RSVP=FALSE;EMAIL=pwulf@tine20.org:mailto:pwulf@tine20.org
+ATTENDEE;CN="Smith, John";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=REQ-
+ PARTICIPANT;RSVP=FALSE;EMAIL=jsmith@tine20.org:mailto:jsmith@tine20.org
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION;ROLE=RE
+ Q-PARTICIPANT;RSVP=FALSE;EMAIL=sclever@tine20.org:mailto:sclever@tine20.or
+ g
+ATTENDEE;CN=Users;CUTYPE=GROUP;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;R
+ SVP=FALSE:urn:uuid:d981a72be8f21808b588daa0e8644046c634250f
+X-MOZ-LASTACK:20151209T083009Z
+DTSTART;TZID=Europe/Berlin:20160113T103000
+DTEND;TZID=Europe/Berlin:20160113T113000
+CLASS:PUBLIC
+SEQUENCE:1
+DESCRIPTION:https://team.t20.org/sites/tine/TeamSeite/TINE%20Mitarbeiter/A
+ genda-Mitarbeiter.aspx
+LOCATION:-134
+TRANSP:OPAQUE
+X-CALENDARSERVER-ACCESS:PUBLIC
+X-MOZ-SNOOZE-TIME:20151125T084120Z
+X-TINE20-CONTAINER:90
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;VALUE=DURATION:-PT1H
+DESCRIPTION:MA Besprechung
+X-LIC-ERROR;X-LIC-ERRORTYPE=PROPERTY-PARSE-ERROR:Parse error in property n
+ ame: ACKNOWLEDGED
+END:VALARM
+END:VEVENT
+END:VCALENDAR
index 2567fa3..735e11b 100644 (file)
@@ -173,7 +173,6 @@ class Crm_Acl_RolesTest extends TestCase
     
     /**
      * try to add a role right
-     *
      */
     public function testSetRoleRight()
     {
index e1c5e51..827c838 100644 (file)
@@ -4,26 +4,12 @@
  *
  * @package     Crm
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2008 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2008-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
-/**
- * Test helper
- */
-require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-if (! defined('PHPUnit_MAIN_METHOD')) {
-    define('PHPUnit_MAIN_METHOD', 'Crm_AllTests::main');
-}
-
 class Crm_AllTests
 {
-    public static function main ()
-    {
-        PHPUnit_TextUI_TestRunner::run(self::suite());
-    }
-    
     public static function suite ()
     {
         $suite = new PHPUnit_Framework_TestSuite('Tine 2.0 Crm All Tests');
@@ -33,10 +19,7 @@ class Crm_AllTests
         $suite->addTestSuite('Crm_JsonTest');
         $suite->addTestSuite('Crm_NotificationsTests');
         $suite->addTestSuite('Crm_Acl_RolesTest');
+        $suite->addTestSuite('Crm_Import_CsvTest');
         return $suite;
     }
 }
-
-if (PHPUnit_MAIN_METHOD == 'Crm_AllTests::main') {
-    Crm_AllTests::main();
-}
index 0d284a0..184bd33 100644 (file)
@@ -16,7 +16,7 @@ require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHe
 /**
  * Test class for Crm_Backend_Lead
  */
-class Crm_Backend_LeadTest extends PHPUnit_Framework_TestCase
+class Crm_Backend_LeadTest extends TestCase
 {
     /**
      * Testcontainer
@@ -48,33 +48,12 @@ class Crm_Backend_LeadTest extends PHPUnit_Framework_TestCase
      */
     protected function setUp()
     {
-        Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+        parent::setUp();
         $this->_backend = new Crm_Backend_Lead();
-        
-        $personalContainer = Tinebase_Container::getInstance()->getPersonalContainer(
-            Zend_Registry::get('currentAccount'), 
-            'Crm', 
-            Zend_Registry::get('currentAccount'), 
-            Tinebase_Model_Grants::GRANT_EDIT
-        );
-        
-        if ($personalContainer->count() === 0) {
-            $this->_testContainer = Tinebase_Container::getInstance()->addPersonalContainer(Zend_Registry::get('currentAccount')->accountId, 'Crm', 'PHPUNIT');
-        } else {
-            $this->_testContainer = $personalContainer[0];
-        }
-    }
-    
-    /**
-     * Tears down the fixture
-     * 
-     * This method is called after a test is executed.
-     */
-    protected function tearDown()
-    {
-        Tinebase_TransactionManager::getInstance()->rollBack();
+
+        $this->_testContainer = $this->_getPersonalContainer('Crm');
     }
-    
+
     /**
      * try to add a lead
      * 
index f0a2cde..21720a5 100644 (file)
@@ -5,17 +5,12 @@
  * @package     Crm
  * @subpackage  Export
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2009-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
  * 
  */
 
 /**
- * Test helper
- */
-require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-/**
  * Test class for Crm_Export_Csv
  */
 class Crm_Export_CsvTest extends Crm_Export_AbstractTest
@@ -35,18 +30,6 @@ class Crm_Export_CsvTest extends Crm_Export_AbstractTest
     protected $_filename;
     
     /**
-     * Runs the test methods of this class.
-     *
-     * @access public
-     * @static
-     */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Crm_Export_CsvTest');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
-
-    /**
      * Sets up the fixture.
      * This method is called before a test is executed.
      *
@@ -69,7 +52,7 @@ class Crm_Export_CsvTest extends Crm_Export_AbstractTest
         unlink($this->_filename);
         parent::tearDown();
     }
-
+    
     /**
      * test csv export
      * 
@@ -99,4 +82,24 @@ class Crm_Export_CsvTest extends Crm_Export_AbstractTest
         $dateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat(NULL, NULL, NULL, 'date');
         $this->assertContains($dateString, $export, 'note date wrong');
     }
+    
+    /**
+     * test sorted csv export
+     * 
+     * @return void
+     * 
+     * @see 0010790: use current grid sort in exports
+     */
+    public function testSortedExportCsv()
+    {
+        $options = array(
+            'sortInfo' => array('field' => 'leadstate_id')
+        );
+        $this->_instance = new Crm_Export_Csv(new Crm_Model_LeadFilter($this->_getLeadFilter()), Crm_Controller_Lead::getInstance(), $options);
+        
+        $this->_filename = $this->_instance->generate();
+        
+        $export = file_get_contents($this->_filename);
+        $this->assertContains('"Metaways Infosystems GmbH"', $export);
+    }
 }
index 4422488..57625e6 100644 (file)
@@ -18,7 +18,7 @@ require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHe
 /**
  * Test class for Tinebase_Group
  */
-class Crm_Export_PdfTest extends PHPUnit_Framework_TestCase
+class Crm_Export_PdfTest extends TestCase
 {
     /**
      * @var array test objects
@@ -26,16 +26,9 @@ class Crm_Export_PdfTest extends PHPUnit_Framework_TestCase
     protected $objects = array();
 
     /**
-     * Runs the test methods of this class.
-     *
-     * @access public
-     * @static
+     * @var Tinebase_Model_Container
      */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Crm_Export_PdfTest');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
+    protected $_testContainer = null;
 
     /**
      * Sets up the fixture.
@@ -45,27 +38,15 @@ class Crm_Export_PdfTest extends PHPUnit_Framework_TestCase
      */
     protected function setUp()
     {
-        Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
-        
-        $personalContainer = Tinebase_Container::getInstance()->getPersonalContainer(
-            Zend_Registry::get('currentAccount'), 
-            'Crm', 
-            Zend_Registry::get('currentAccount'), 
-            Tinebase_Model_Grants::GRANT_EDIT
-        );
-        
-        if($personalContainer->count() === 0) {
-            $this->testContainer = Tinebase_Container::getInstance()->addPersonalContainer(Zend_Registry::get('currentAccount')->accountId, 'Crm', 'PHPUNIT');
-        } else {
-            $this->testContainer = $personalContainer[0];
-        }
-        
+        parent::setUp();
+        $this->_testContainer = $this->_getPersonalContainer('Crm');
+
         $this->objects['lead'] = new Crm_Model_Lead(array(
             'lead_name'     => 'PHPUnit',
             'leadstate_id'  => 1,
             'leadtype_id'   => 1,
             'leadsource_id' => 1,
-            'container_id'     => $this->testContainer->id,
+            'container_id'  => $this->_testContainer->id,
             'start'         => new Tinebase_DateTime( "2007-12-12" ),
             'description'   => 'Lead Description',
             'end'           => Tinebase_DateTime::now(),
@@ -79,7 +60,7 @@ class Crm_Export_PdfTest extends PHPUnit_Framework_TestCase
             'leadstate_id'  => 1,
             'leadtype_id'   => 1,
             'leadsource_id' => 1,
-            'container_id'     => $this->testContainer->id,
+            'container_id'  => $this->_testContainer->id,
             'start'         => new Tinebase_DateTime( "2007-12-24" ),
             'description'   => 'Lead Description',
             'end'           => Tinebase_DateTime::now(),
@@ -133,22 +114,11 @@ class Crm_Export_PdfTest extends PHPUnit_Framework_TestCase
             'summary'               => 'task test',
         ));
         
-        $lead = Crm_Controller_Lead::getInstance()->create($this->objects['leadWithLink']);
+        Crm_Controller_Lead::getInstance()->create($this->objects['leadWithLink']);
         $this->objects['linkedContact'] = Addressbook_Controller_Contact::getInstance()->create($this->objects['linkedContact'], FALSE);
     }
 
     /**
-     * Tears down the fixture
-     * This method is called after a test is executed.
-     *
-     * @access protected
-     */
-    protected function tearDown()
-    {
-        Tinebase_TransactionManager::getInstance()->rollBack();
-    }
-    
-    /**
      * try to create a pdf
      *
      */
index 0671c05..4ff2244 100644 (file)
@@ -63,6 +63,7 @@ class Crm_Export_XlsTest extends Crm_Export_AbstractTest
      */
     public function testExportXls()
     {
+        $translate = Tinebase_Translation::getTranslation('Crm');
         $excelObj = $this->_instance->generate();
         
         // output as csv
@@ -79,7 +80,7 @@ class Crm_Export_XlsTest extends Crm_Export_AbstractTest
         $this->assertEquals(1, preg_match("/PHPUnit/",                          $export), 'no name');
         $this->assertEquals(1, preg_match("/Description/",                      $export), 'no description');
         $this->assertEquals(1, preg_match('/' . preg_quote(Tinebase_Core::getUser()->accountDisplayName) . '/',          $export), 'no creator');
-        $this->assertEquals(1, preg_match('/open/',                             $export), 'no leadstate');
+        $this->assertEquals(1, preg_match('/' . $translate->_('open') . '/',    $export), 'no leadstate');
         
         unlink($csvFilename);
     }
diff --git a/tests/tine20/Crm/Import/CsvTest.php b/tests/tine20/Crm/Import/CsvTest.php
new file mode 100644 (file)
index 0000000..564254d
--- /dev/null
@@ -0,0 +1,167 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ *
+ * @package     Crm
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
+ */
+
+/**
+ * Test class for Crm_Import_Csv
+ */
+class Crm_Import_CsvTest extends ImportTestCase
+{
+    protected $_importerClassName = 'Crm_Import_Csv';
+    protected $_exporterClassName = 'Crm_Export_Csv';
+    protected $_modelName         = 'Crm_Model_Lead';
+
+    protected $_tasksToDelete = array();
+
+    /**
+     * tear down tests
+     */
+    protected function tearDown()
+    {
+        parent::tearDown();
+
+        // delete tasks
+        Tasks_Controller_Task::getInstance()->delete($this->_tasksToDelete);
+
+        Crm_Config::getInstance()->set(Crm_Config::LEAD_IMPORT_AUTOTASK, false);
+        Crm_Config::getInstance()->set(Crm_Config::LEAD_IMPORT_NOTIFICATION, false);
+    }
+    /**
+     * test import
+     *
+     * @param boolean $dryrun
+     * @return array
+     */
+    public function testImport($dryrun = true)
+    {
+        $result = $this->_importHelper('leads.csv', 'crm_tine_import_csv', $dryrun);
+        $this->assertEquals(2, $result['totalcount'], 'should import 2 records: ' . print_r($result, true));
+
+        $firstLead = $result['results']->getFirstRecord();
+        $this->assertContains('neuer lead', $firstLead->lead_name);
+        $this->assertEquals(1, count($firstLead->tags));
+        $this->assertEquals(5, count($firstLead->relations),
+            'relations not imported for first lead ' . print_r($firstLead->toArray(), true));
+        $this->assertEquals(6, count($result['results'][1]->relations),
+            'relations not imported for second lead ' . print_r($result['results'][1]->toArray(), true));
+
+        return $result;
+    }
+
+    /**
+     * import helper
+     *
+     * @param        $importFilename
+     * @param string $definitionName
+     * @param bool   $dryrun
+     * @return array
+     * @throws Tinebase_Exception_NotFound
+     */
+    protected function _importHelper($importFilename, $definitionName = 'crm_tine_import_csv', $dryrun = true)
+    {
+        $this->_testNeedsTransaction();
+
+        $this->_testContainer = $this->_getTestContainer('Crm', 'Crm_Model_Lead');
+        $this->_filename = dirname(__FILE__) . '/files/' . $importFilename;
+        $this->_deleteImportFile = false;
+
+        $options = array(
+            'container_id'  => $this->_testContainer->getId(),
+            'dryrun' => $dryrun,
+        );
+
+        $result = $this->_doImport($options, $definitionName);
+
+        return $result;
+    }
+
+    /**
+     * @see 0011234: automatically add task for responsible person on lead import
+     */
+    public function testAutoTaskImport()
+    {
+        Crm_Config::getInstance()->set(Crm_Config::LEAD_IMPORT_AUTOTASK, true);
+        $personalContainerOfSClever = $this->_getPersonalContainer('Tasks', $this->_personas['sclever']);
+        $this->_setPersonaGrantsForTestContainer($personalContainerOfSClever->getId(), 'sclever', true, false);
+
+        $result = $this->testImport(/* dry run = */ false);
+        foreach ($result['results'] as $lead) {
+            foreach ($lead->relations as $relation) {
+                if ($relation->type === 'TASK') {
+                    $this->_tasksToDelete[] = $relation->related_id;
+                }
+            }
+        }
+
+        $tasks = $this->_searchTestTasks($personalContainerOfSClever->getId());
+        $this->assertEquals(1, count($tasks), 'could not find task in sclevers container: '
+            . print_r($personalContainerOfSClever->toArray(), true));
+        $task = $tasks->getFirstRecord();
+        $this->assertEquals($this->_personas['sclever']['accountId'], $task->organizer);
+        $this->assertEquals('IN-PROCESS', $task->status);
+    }
+
+    /**
+     * search tasks
+     *
+     * @param      $containerId
+     * @param null $summary
+     * @return array|Tinebase_Record_RecordSet
+     */
+    protected function _searchTestTasks($containerId, $summary = null)
+    {
+        if (! $summary) {
+            $translate = Tinebase_Translation::getTranslation('Crm');
+            $summary = $translate->_('Edit new lead');
+        }
+        $tasksFilter = new Tasks_Model_TaskFilter(array(
+            array('field' => 'container_id', 'operator' => 'equals', 'value' => $containerId),
+            array('field' => 'summary', 'operator' => 'contains', 'value' => $summary),
+        ));
+        $tasks = Tasks_Controller_Task::getInstance()->search($tasksFilter);
+        $this->_tasksToDelete = array_merge($this->_tasksToDelete, $tasks->getArrayOfIds());
+        return $tasks;
+    }
+
+    /**
+     * @see 0011376: send mail on lead import to responsibles
+     */
+    public function testEmailNotification()
+    {
+        $smtpConfig = Tinebase_Config::getInstance()->get(Tinebase_Config::SMTP, new Tinebase_Config_Struct())->toArray();
+        if (empty($smtpConfig)) {
+            $this->markTestSkipped('No SMTP config found: this is needed to send notifications.');
+        }
+
+        Crm_Config::getInstance()->set(Crm_Config::LEAD_IMPORT_NOTIFICATION, true);
+        $this->testImport(/* dry run = */ false);
+        // mark tasks for deletion
+        $this->_searchTestTasks(Tinebase_Container::getInstance()->getDefaultContainer('Tasks_Model_Task')->getId(), 'task');
+
+        // assert emails for responsibles
+        $messages = self::getMessages();
+        $this->assertGreaterThan(1, count($messages));
+
+        $translate = Tinebase_Translation::getTranslation('Crm');
+        $importNotifications = array();
+        $subjectToMatch = sprintf($translate->_('%s new leads have been imported'), 1);
+        foreach ($messages as $message) {
+            if ($message->getSubject() == $subjectToMatch) {
+                $importNotifications[] = $message;
+            }
+        }
+
+        $this->assertGreaterThan(1, count($importNotifications),
+            'expecting 2 or more mails (at least for unittest + sclever) / messages:'
+            . print_r($messages, true));
+        $firstMessage = $importNotifications[0];
+        $this->assertContains('neuer lead 2', $firstMessage->getBodyText()->getContent(), 'lead name missing');
+        $this->assertContains('PHPUnit', $firstMessage->getBodyText()->getContent(), 'container name missing');
+    }
+}
diff --git a/tests/tine20/Crm/Import/files/leads.csv b/tests/tine20/Crm/Import/files/leads.csv
new file mode 100644 (file)
index 0000000..b3c0386
--- /dev/null
@@ -0,0 +1,3 @@
+"lead_name","leadstate_id","Leadstate","leadtype_id","Leadtype","leadsource_id","Leadsource","container_id","start","description","end","turnover","probableTurnover","probability","end_scheduled","resubmission_date","tags","attachments","notes","seq","CUSTOMER","PARTNER","RESPONSIBLE","TASK","PRODUCT"
+"neuer lead 2","2","kontaktiert","1","Kunde","2","Email","","2015-01-31 00:00:00","","","0","0","10","","","","","31.01.2015 17:39:58 - created by schüle, phil","1","derkunde","schüle","clever","task1","product1"
+"neuer lead 1","1","","1","Kunde","1","","","2015-01-31 00:00:00","","","0","0","0","","","","","31.01.2015 17:39:42 - created by schüle, phil","1","derkunde","schüle","unittest","task2","product1;product2"
index 6bbb3a1..b236c69 100644 (file)
@@ -24,7 +24,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      *
      * @var Crm_Frontend_Json
      */
-    protected $_instance;
+    protected $_instance = null;
     
     /**
      * fs controller
@@ -33,6 +33,14 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     protected $_fsController;
 
+
+    /**
+     * customfield name
+     *
+     * @var string
+     */
+    protected $_cfcName = null;
+
    /**
      * Sets up the fixture.
      * This method is called before a test is executed.
@@ -43,8 +51,20 @@ class Crm_JsonTest extends Crm_AbstractTest
     {
         parent::setUp();
         
-        $this->_instance = new Crm_Frontend_Json();
         $this->_fsController = Tinebase_FileSystem::getInstance();
+        Crm_Controller_Lead::getInstance()->duplicateCheckFields(array());
+    }
+
+    /**
+     * @return Crm_Frontend_Json
+     */
+    protected function _getUit()
+    {
+        if ($this->_instance === null) {
+            $this->_instance = new Crm_Frontend_Json();
+        }
+
+        return new $this->_instance;
     }
 
     /**
@@ -66,6 +86,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         }
         
         parent::tearDown();
+        Crm_Controller_Lead::getInstance()->duplicateCheckFields(array('lead_name'));
     }
      
     /**
@@ -75,7 +96,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testGetRegistryData()
     {
-        $registry = $this->_instance->getRegistryData();
+        $registry = $this->_getUit()->getRegistryData();
         
         $types = array('leadtypes', 'leadstates', 'leadsources');
         
@@ -108,7 +129,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testGetSettings()
     {
-        $result = $this->_instance->getSettings();
+        $result = $this->_getUit()->getSettings();
         
         $this->assertArrayHasKey('leadstates',  $result);
         $this->assertArrayHasKey('leadtypes',   $result);
@@ -126,7 +147,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testSaveSettings()
     {
-        $oldSettings = $this->_instance->getSettings();
+        $oldSettings = $this->_getUit()->getSettings();
         
         // change some settings
         $newSettings = $oldSettings;
@@ -135,11 +156,11 @@ class Crm_JsonTest extends Crm_AbstractTest
             'id' => 5,
             'leadsource' => 'Another Leadsource'
         );
-        $anotherResult = $this->_instance->saveSettings($newSettings);
+        $anotherResult = $this->_getUit()->saveSettings($newSettings);
         $this->assertEquals($newSettings, $anotherResult, 'new settings have not been saved');
         
         // reset original settings
-        $result = $this->_instance->saveSettings($oldSettings);
+        $result = $this->_getUit()->saveSettings($oldSettings);
         $this->assertEquals($result, $oldSettings, 'old settings have not been reset');
         
         // test Crm_Model_Config::getOptionById
@@ -154,9 +175,9 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testAddGetSearchDeleteLead()
     {
-        $savedLead = $this->_saveLead();
-        $getLead = $this->_instance->getLead($savedLead['id']);
-        $searchLeads = $this->_instance->searchLeads($this->_getLeadFilter(), '');
+        $savedLead = $this->saveLead();
+        $getLead = $this->_getUit()->getLead($savedLead['id']);
+        $searchLeads = $this->_getUit()->searchLeads($this->_getLeadFilter(), '');
         
         // test manual resolving of organizer in related_record and set it back for following tests
         for ($i = 0; $i < count($getLead['relations']); $i++) {
@@ -176,13 +197,15 @@ class Crm_JsonTest extends Crm_AbstractTest
         $this->assertEquals($searchLeads['results'][0]['turnover']*$getLead['probability']/100, $searchLeads['results'][0]['probableTurnover']);
         // now we need 2 relations here (frontend search shall return relations with related_model Addressbook_Model_Contact or Sales_Model_Product
         $this->assertEquals(2, count($searchLeads['results'][0]['relations']), 'did not get all relations');
-        
+
+        $relatedTask = null;
         foreach($getLead['relations'] as $rel) {
             if ($rel['type'] == 'TASK') {
                 $relatedTask = $rel['related_record'];
             }
         }
-        
+
+        $this->assertTrue($relatedTask !== null);
         $this->assertEquals($this->_getTask()->summary, $relatedTask['summary'], 'task summary does not match');
         $defaultTaskContainerId = Tinebase_Core::getPreference('Tasks')->getValue(Tasks_Preference::DEFAULTTASKLIST);
         $this->assertEquals($defaultTaskContainerId, $relatedTask['container_id']);
@@ -216,12 +239,12 @@ class Crm_JsonTest extends Crm_AbstractTest
         $this->assertEquals($this->_getProduct()->name, $relatedProduct['name'], 'product name does not match');
         
         // delete all
-        $this->_instance->deleteLeads($savedLead['id']);
+        $this->_getUit()->deleteLeads($savedLead['id']);
         Addressbook_Controller_Contact::getInstance()->delete($relatedContact['id']);
         Sales_Controller_Product::getInstance()->delete($relatedProduct['id']);
         
         // check if delete worked
-        $result = $this->_instance->searchLeads($this->_getLeadFilter(), '');
+        $result = $this->_getUit()->searchLeads($this->_getLeadFilter(), '');
         $this->assertEquals(0, $result['totalcount']);
         
         // check if linked task got removed as well
@@ -234,7 +257,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      * 
      * @return array
      */
-    protected function _saveLead()
+    public function saveLead()
     {
         $contact    = $this->_getContact();
         $task       = $this->_getTask();
@@ -255,7 +278,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         );
         $leadData['notes'] = array($note);
         
-        $savedLead = $this->_instance->saveLead($leadData);
+        $savedLead = $this->_getUit()->saveLead($leadData);
         return $savedLead;
     }
     
@@ -267,7 +290,7 @@ class Crm_JsonTest extends Crm_AbstractTest
     public function testTagFilter()
     {
         $lead       = $this->_getLead();
-        $savedLead = $this->_instance->saveLead($lead->toArray());
+        $savedLead = $this->_getUit()->saveLead($lead->toArray());
         
         $sharedTagName = Tinebase_Record_Abstract::generateUID();
         $tag = new Tinebase_Model_Tag(array(
@@ -287,7 +310,7 @@ class Crm_JsonTest extends Crm_AbstractTest
             array('field' => 'tag',           'operator' => 'equals',       'value' => $tag->getId()),
         );
         
-        $result = $this->_instance->searchLeads($filter, array());
+        $result = $this->_getUit()->searchLeads($filter, array());
         $this->assertEquals(0, $result['totalcount'], 'Should not find the lead!');
     }    
     
@@ -299,7 +322,7 @@ class Crm_JsonTest extends Crm_AbstractTest
     public function testSearchByBrokenFilter()
     {
         $filter = Zend_Json::decode('[{"field":"query","operator":"contains","value":"test"},{"field":"container_id","operator":"equals","value":{"path":"/"}},{"field":"contact","operator":"AND","value":[{"field":":id","operator":"equals","value":{"n_fn":"","n_fileas":"","org_name":"","container_id":"2576"}}]}]');
-        $result = $this->_instance->searchLeads($filter, array());
+        $result = $this->_getUit()->searchLeads($filter, array());
         $this->assertEquals(0, $result['totalcount']);
     }
     
@@ -318,16 +341,16 @@ class Crm_JsonTest extends Crm_AbstractTest
         $leadData['relations'] = array(
             array('type'  => 'PARTNER', 'related_record' => $savedContact->toArray()),
         );
-        $savedLead = $this->_instance->saveLead($leadData);
+        $savedLead = $this->_getUit()->saveLead($leadData);
         
         $savedLead['relations'] = array();
-        $savedLead = $this->_instance->saveLead($savedLead);
+        $savedLead = $this->_getUit()->saveLead($savedLead);
         $this->assertEquals(0, count($savedLead['relations']));
         
         $savedLead['relations'] = array(
             array('type'  => 'PARTNER', 'related_record' => $savedContact->toArray()),
         );
-        $savedLead = $this->_instance->saveLead($savedLead);
+        $savedLead = $this->_getUit()->saveLead($savedLead);
         
         $this->assertEquals(1, count($savedLead['relations']), 'Relation has not been added');
         $this->assertEquals($contact->n_fn, $savedLead['relations'][0]['related_record']['n_fn'], 'Contact name does not match');
@@ -348,7 +371,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         $leadData['relations'] = array(
             array('type'  => '', 'related_record' => $savedContact->toArray()),
         );
-        $savedLead = $this->_instance->saveLead($leadData);
+        $savedLead = $this->_getUit()->saveLead($leadData);
         
         $this->assertEquals(1, count($savedLead['relations']), 'Relation has not been added');
         $this->assertEquals('CUSTOMER', $savedLead['relations'][0]['type'], 'default type should be CUSTOMER');
@@ -362,7 +385,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testConcurrentRelationSetting()
     {
-        $leadData = $this->_instance->saveLead($this->_getLead()->toArray());
+        $leadData = $this->_getUit()->saveLead($this->_getLead()->toArray());
         $task = $this->_getTask();
         
         $taskJson = new Tasks_Frontend_Json();
@@ -382,16 +405,16 @@ class Crm_JsonTest extends Crm_AbstractTest
         
         $taskData = $taskJson->saveTask($taskData);
         $taskData['description'] = 1;
-        $taskData = $taskJson->saveTask($taskData);
+        $taskJson->saveTask($taskData);
         
-        $savedLead = $this->_instance->getLead($leadData['id']);
+        $savedLead = $this->_getUit()->getLead($leadData['id']);
         $savedLead['relations'][0]['related_record']['description'] = '2';
         $savedLead['relations'][0]['related_record']['due'] = '2012-10-18 12:54:33';
         
         // client may send wrong seq -> this should cause a concurrency conflict
         $savedLead['relations'][0]['related_record']['seq'] = 0;
         try {
-            $savedLead = $this->_instance->saveLead($savedLead);
+            $this->_getUit()->saveLead($savedLead);
             $this->fail('expected concurrency exception');
         } catch (Tinebase_Timemachine_Exception_ConcurrencyConflict $ttecc) {
             $this->assertEquals('concurrency conflict!', $ttecc->getMessage());
@@ -406,7 +429,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testConstraintsOtherSide()
     {
-        $leadData1 = $this->_instance->saveLead($this->_getLead(FALSE, FALSE)->toArray());
+        $leadData1 = $this->_getUit()->saveLead($this->_getLead(FALSE, FALSE)->toArray());
         $task = $this->_getTask();
         
         $taskJson = new Tasks_Frontend_Json();
@@ -426,7 +449,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         
         $taskData = $taskJson->saveTask($taskData);
         
-        $leadData2 = $this->_instance->saveLead($this->_getLead(FALSE, FALSE)->toArray());
+        $leadData2 = $this->_getUit()->saveLead($this->_getLead(FALSE, FALSE)->toArray());
         $taskData['relations'][] = array(
             'type'  => 'TASK',
             'own_model' => 'Tasks_Model_Task',
@@ -447,7 +470,7 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testOtherRecordConstraintsConfig()
     {
-        $leadData1 = $this->_instance->saveLead($this->_getLead(FALSE, FALSE)->toArray());
+        $leadData1 = $this->_getUit()->saveLead($this->_getLead(FALSE, FALSE)->toArray());
         $task = $this->_getTask();
         
         $taskJson = new Tasks_Frontend_Json();
@@ -469,7 +492,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         
         $taskData = $taskJson->saveTask($taskData);
         
-        $leadData2 = $this->_instance->saveLead($this->_getLead(FALSE, FALSE)->toArray());
+        $leadData2 = $this->_getUit()->saveLead($this->_getLead(FALSE, FALSE)->toArray());
         
         $leadData2['relations'] = array(
             array(
@@ -490,6 +513,27 @@ class Crm_JsonTest extends Crm_AbstractTest
     }
     
     /**
+     * try to add multiple related tasks with one save
+     *
+     */
+    public function testLeadWithMultipleTasks()
+    {
+        $lead = $this->_getLead();
+        $task1 = $this->_getTask();
+        $task2 = $this->_getTask();
+        
+        
+        $leadData = $lead->toArray();
+        $leadData['relations'] = array(
+                array('type'  => 'TASK', 'related_record' => $task1->toArray()),
+                array('type'  => 'TASK', 'related_record' => $task2->toArray())
+        );
+        
+        $savedLead = $this->_getUit()->saveLead($leadData);
+        $this->assertEquals(2, count($savedLead['relations']), 'Relations missing');
+    }
+    
+    /**
      * get contact
      * 
      * @return Addressbook_Model_Contact
@@ -592,7 +636,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         }
         
         return new Crm_Model_Lead(array(
-            'lead_name'     => 'PHPUnit',
+            'lead_name'     => 'PHPUnit LEAD',
             'leadstate_id'  => 1,
             'leadtype_id'   => 1,
             'leadsource_id' => 1,
@@ -641,7 +685,7 @@ class Crm_JsonTest extends Crm_AbstractTest
     public function testRelatedModlog()
     {
         // create lead with tag, customfield and related contacts
-        $savedLead = $this->_saveLead();
+        $savedLead = $this->saveLead();
         
         // change relations, customfields + tags
         $savedLead['tags'][] = array('name' => 'another tag', 'type' => Tinebase_Model_Tag::TYPE_PERSONAL);
@@ -654,7 +698,7 @@ class Crm_JsonTest extends Crm_AbstractTest
             }
         }
         $savedLead['customfields'][$this->_cfcName] = '5678';
-        $updatedLead = $this->_instance->saveLead($savedLead);
+        $updatedLead = $this->_getUit()->saveLead($savedLead);
         
         // check modlog + history
         $modifications = Tinebase_Timemachine_ModificationLog::getInstance()->getModifications('Crm', $updatedLead['id']);
@@ -699,7 +743,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         $lead = $this->_getLead()->toArray();
         $lead['attachments'] = array(array('tempFile' => $tempFile->toArray()));
         
-        $savedLead = $this->_instance->saveLead($lead);
+        $savedLead = $this->_getUit()->saveLead($lead);
         // add path to files to remove
         $this->_objects['paths'][] = Tinebase_FileSystem_RecordAttachments::getInstance()->getRecordAttachmentPath(new Crm_Model_Lead($savedLead, TRUE)) . '/' . $tempFile->name;
         
@@ -722,7 +766,7 @@ class Crm_JsonTest extends Crm_AbstractTest
     public function testUpdateLeadWithAttachment()
     {
         $lead = $this->testCreateLeadWithAttachment();
-        $savedLead = $this->_instance->saveLead($lead);
+        $savedLead = $this->_getUit()->saveLead($lead);
         $this->assertTrue(isset($savedLead['attachments']), 'no attachments found');
         $this->assertEquals(1, count($savedLead['attachments']));
     }
@@ -737,7 +781,7 @@ class Crm_JsonTest extends Crm_AbstractTest
         $lead = $this->testCreateLeadWithAttachment();
         $lead['attachments'] = array();
     
-        $savedLead = $this->_instance->saveLead($lead);
+        $savedLead = $this->_getUit()->saveLead($lead);
         $this->assertEquals(0, count($savedLead['attachments']));
         $this->assertFalse($this->_fsController->fileExists($this->_objects['paths'][0]));
     }
@@ -750,7 +794,7 @@ class Crm_JsonTest extends Crm_AbstractTest
     public function testDeleteLeadWithAttachment()
     {
         $lead = $this->testCreateLeadWithAttachment();
-        $this->_instance->deleteLeads(array($lead['id']));
+        $this->_getUit()->deleteLeads(array($lead['id']));
         $this->assertFalse($this->_fsController->fileExists($this->_objects['paths'][0]));
     }
 
@@ -763,7 +807,7 @@ class Crm_JsonTest extends Crm_AbstractTest
     {
         $leadArray = $this->_getLead()->toArray();
         $leadArray['start'] = null;
-        $newLead = $this->_instance->saveLead($leadArray);
+        $newLead = $this->_getUit()->saveLead($leadArray);
         
         $this->assertContains(Tinebase_DateTime::now()->format('Y-m-d'), $newLead['start'], 'start should be set to now if missing');
     }
@@ -775,17 +819,34 @@ class Crm_JsonTest extends Crm_AbstractTest
      */
     public function testSortByLeadState()
     {
-        $savedLead1 = $this->_saveLead();
+        $this->saveLead();
         $lead2 = $this->_getLead()->toArray();  // open
         $lead2['leadstate_id'] = 2;             // contacted
-        $savedLead2 = $this->_instance->saveLead($lead2);
+        $this->_getUit()->saveLead($lead2);
         
         $sort = array(
             'sort' => 'leadstate_id',
             'dir' => 'ASC'
         );
-        $searchLeads = $this->_instance->searchLeads($this->_getLeadFilter(), $sort);
+        $searchLeads = $this->_getUit()->searchLeads($this->_getLeadFilter(), $sort);
         
         $this->assertEquals(2, $searchLeads['results'][0]['leadstate_id'], 'leadstate "contacted" should come first');
     }
+    
+    /**
+     * testAdvancedSearch in related products
+     * 
+     * @see 0010814: quicksearch should search in related records
+     */
+    public function testAdvancedSearchInProduct()
+    {
+        Tinebase_Core::getPreference()->setValue(Tinebase_Preference::ADVANCED_SEARCH, true);
+        
+        $this->saveLead();
+        $filter = array(
+            array('field' => 'query',           'operator' => 'contains',       'value' => 'PHPUnit test product'),
+        );
+        $searchLeads = $this->_getUit()->searchLeads($filter, '');
+        $this->assertEquals(1, $searchLeads['totalcount']);
+    }
 }
index 6a6586e..fc810aa 100644 (file)
@@ -21,11 +21,6 @@ class Crm_NotificationsTests extends Crm_AbstractTest
     protected $_leadController;
     
     /**
-     * @var Zend_Mail_Transport_Array
-     */
-    protected static $_mailer = NULL;
-    
-    /**
      * (non-PHPdoc)
      * @see tests/tine20/Crm/AbstractTest::setUp()
      */
index d4c2b94..ece137f 100644 (file)
@@ -26,7 +26,6 @@ class ExampleApplication_JsonTest extends ExampleApplication_TestCase
      */
     public function setUp()
     {
-        // enable courses app
         Tinebase_Application::getInstance()->setApplicationState(array(
             Tinebase_Application::getInstance()->getApplicationByName('ExampleApplication')->getId()
         ), Tinebase_Application::ENABLED);
@@ -79,7 +78,7 @@ class ExampleApplication_JsonTest extends ExampleApplication_TestCase
         $searchDefaultFilter = $this->_getFilter();
         $mergedSearchFilter = array_merge($searchIDFilter, $searchDefaultFilter);
         
-        $returned = $this->_json->searchExampleRecords($searchDefaultFilter, $this->_getPaging());
+        $returned = $this->_json->searchExampleRecords($mergedSearchFilter, $this->_getPaging());
         
         $this->assertEquals($returned['totalcount'], 1);
         
@@ -104,7 +103,8 @@ class ExampleApplication_JsonTest extends ExampleApplication_TestCase
     public function testSearchExampleRecordsTags()
     {
         $exampleRecordWithTag = $this->testCreateExampleRecord();
-        $exampleRecordWithoutTag = $this->testCreateExampleRecord();
+        // create a second record with no tag
+        $this->testCreateExampleRecord();
         
         $exampleRecordWithTag['tags'] = array(array(
             'name'    => 'supi',
@@ -132,7 +132,6 @@ class ExampleApplication_JsonTest extends ExampleApplication_TestCase
         $this->assertEquals($returnValueDeletion['status'], 'success');
         
         $this->setExpectedException('Tinebase_Exception_NotFound');
-        $returnValueGet = $this->_json->getExampleRecord($exampleRecordID);
+        $this->_json->getExampleRecord($exampleRecordID);
     }
-    
 }
index 0c2375e..5e45272 100644 (file)
@@ -16,60 +16,37 @@ require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php'
 /**
  * Test class for Inventory_TestCase
  */
-class ExampleApplication_TestCase extends PHPUnit_Framework_TestCase
+class ExampleApplication_TestCase extends TestCase
 {
     /**
      * @var ExampleApplication_Frontend_Json
      */
     protected $_json = array();
-    
-    /**
-     * Runs the test methods of this class.
-     *
-     * @access public
-     * @static
-     */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 ExampleApplication Json Tests');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
-    
-    /**
-     * Sets up the fixture.
-     * This method is called before a test is executed.
-     *
-     * @access protected
-     */
-    protected function setUp()
-    {
-        Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
-    }
-    
+
     /**
      * get ExampleRecord record
      *
-     * @return Inventory_Model_ExampleRecord
+     * @return ExampleApplication_Model_ExampleRecord
      */
     protected function _getExampleRecord()
     {
         return new ExampleApplication_Model_ExampleRecord(array(
-                'name' => 'minimal example record by PHPUnit::ExampleApplication_JsonTest'
+            'name' => 'minimal example record by PHPUnit::ExampleApplication_JsonTest'
         ));
     }
     
     /**
      * get filter for ExampleApplication search
      *
-     * @return Tasks_Model_Task
+     * @return array
      */
     protected function _getFilter()
     {
         // define filter
         return array(
-                array('field' => 'container_id', 'operator' => 'specialNode', 'value' => 'all'),
-                array('field' => 'name'        , 'operator' => 'contains',    'value' => 'example record by PHPUnit'),
-                array('field' => 'due'         , 'operator' => 'within',      'value' => 'dayThis'),
+            array('field' => 'container_id', 'operator' => 'specialNode', 'value' => 'all'),
+            array('field' => 'name'        , 'operator' => 'contains',    'value' => 'example record by PHPUnit'),
+            array('field' => 'due'         , 'operator' => 'within',      'value' => 'dayThis'),
         );
     }
     
@@ -82,21 +59,10 @@ class ExampleApplication_TestCase extends PHPUnit_Framework_TestCase
     {
         // define paging
         return array(
-                'start' => 0,
-                'limit' => 50,
-                'sort' => 'name',
-                'dir' => 'ASC',
+            'start' => 0,
+            'limit' => 50,
+            'sort' => 'name',
+            'dir' => 'ASC',
         );
     }
-    
-    /**
-     * Tears down the fixture
-     * This method is called after a test is executed.
-     *
-     * @access protected
-     */
-    protected function tearDown()
-    {
-        Tinebase_TransactionManager::getInstance()->rollBack();
-    }
 }
index 5197732..3e220ed 100644 (file)
@@ -141,4 +141,20 @@ class Filemanager_Controller_DownloadLinkTests extends TestCase
             $this->assertEquals('Download link has expired', $tead->getMessage());
         }
     }
+
+    /**
+     * testDownloadLinkAccessCount
+     */
+    public function testDownloadLinkAccessCount()
+    {
+        $initialDownloadLink = $this->testCreateDownloadLink();
+
+        // simulate two concurrent downloads
+        $this->_getUit()->increaseAccessCount($initialDownloadLink);
+        $this->_getUit()->increaseAccessCount($initialDownloadLink);
+
+        $downloadLink = $this->_getUit()->get($initialDownloadLink->getId());
+
+        $this->assertEquals(2, $downloadLink->access_count);
+    }
 }
diff --git a/tests/tine20/ImportTestCase.php b/tests/tine20/ImportTestCase.php
new file mode 100644 (file)
index 0000000..eebc320
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tests
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
+ */
+
+/**
+ * abstract Test class for import tests
+ * 
+ * @package     Tests
+ */
+abstract class ImportTestCase extends TestCase
+{
+    /**
+     * importer instance
+     * 
+     * @var Object
+     */
+    protected $_instance = null;
+
+    /**
+     * @var string $_filename of the export
+     */
+    protected $_filename = null;
+
+    /**
+     * @var boolean
+     *
+     * TODO needed here?
+     */
+    protected $_deleteImportFile = true;
+
+    protected $_importerClassName = null;
+    protected $_exporterClassName = null;
+    protected $_modelName = null;
+    protected $_testContainer = null;
+
+    /**
+     * tear down tests
+     */
+    protected function tearDown()
+    {
+        parent::tearDown();
+
+        if ($this->_testContainer) {
+            Tinebase_Container::getInstance()->deleteContainer($this->_testContainer);
+        }
+    }
+
+    /**
+     * import helper
+     *
+     * @param array $_options
+     * @param string|Tinebase_Model_ImportExportDefinition $_definition
+     * @param Tinebase_Model_Filter_FilterGroup $_exportFilter
+     * @throws Tinebase_Exception_NotFound
+     * @return array
+     */
+    protected function _doImport(array $_options, $_definition, Tinebase_Model_Filter_FilterGroup $_exportFilter = NULL)
+    {
+        if (! $this->_importerClassName || ! $this->_modelName) {
+            throw new Tinebase_Exception_NotFound('No import class or model name given');
+        }
+
+        $definition = ($_definition instanceof Tinebase_Model_ImportExportDefinition) ? $_definition : Tinebase_ImportExportDefinition::getInstance()->getByName($_definition);
+        $this->_instance = call_user_func_array($this->_importerClassName . '::createFromDefinition' , array($definition, $_options));
+
+        // export first
+        if ($_exportFilter !== NULL && $this->_exporterClassName) {
+            $exporter = new $this->_exporterClassName($_exportFilter, Tinebase_Core::getApplicationInstance($this->_modelName));
+            $this->_filename = $exporter->generate();
+        }
+
+        // then import
+        $result = $this->_instance->importFile($this->_filename);
+
+        return $result;
+    }
+}
index 5b32961..fa42eaf 100644 (file)
@@ -32,6 +32,8 @@ class Sales_AllTests
         $suite->addTestSuite('Sales_Backend_CostCenterTest');
         $suite->addTestSuite('Sales_ControllerTest');
         $suite->addTestSuite('Sales_JsonTest');
+        $suite->addTestSuite('Sales_SuppliersTest');
+        $suite->addTestSuite('Sales_PurchaseInvoiceTest');
         $suite->addTestSuite('Sales_CustomFieldTest');
         $suite->addTestSuite('Sales_InvoiceControllerTests');
         $suite->addTestSuite('Sales_InvoiceJsonTests');
index 64869a1..7e3e337 100644 (file)
@@ -186,4 +186,69 @@ class Sales_ControllerTest extends PHPUnit_Framework_TestCase
         $result = Sales_Controller_Address::getInstance()->resolveVirtualFields($address);
         $this->assertEquals($result['fulltext'], "Meister Eder, Brunnengässla 4, 80331 Munich ($i18nTypeString - de-234)");
     }
+
+    /**
+     * tests adding and removing of products to a contract
+     */
+    public function testAddDeleteProducts()
+    {
+        $prodTest = new Sales_ProductControllerTest();
+        $productOne = $prodTest->testCreateProduct();
+        $productTwo = $prodTest->testCreateProduct();
+
+        $contractData = $this->_getContract();
+        $contractData->products = array(
+            array(
+                'product_id' => $productOne->getId(),
+                'quantity' => 1,
+                'interval' => 1,
+                'billing_point' => 1,
+            ),
+            array(
+                'product_id' => $productTwo->getId(),
+                'quantity' => 1,
+                'interval' => 1,
+                'billing_point' => 1,
+            ),
+        );
+        $this->_backend->create($contractData);
+        $contract = $this->_backend->get($contractData->getId());
+
+        // checks
+        $this->assertEquals($contractData->getId(), $contract->getId());
+        $this->assertGreaterThan(0, $contract->number);
+        $this->assertEquals(Tinebase_Core::getUser()->getId(), $contract->created_by);
+
+        // check count of product aggregates
+        $filter = new Sales_Model_ProductAggregateFilter(array());
+        $filter->addFilter(new Tinebase_Model_Filter_Text(
+            array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId())
+        ));
+        $productAggregates = Sales_Controller_ProductAggregate::getInstance()->search($filter);
+        $this->assertEquals(2, count($productAggregates));
+
+        $contractData->products = array(
+            array(
+                'product_id' => $productOne->getId(),
+                'quantity' => 1,
+                'interval' => 1,
+                'billing_point' => 1,
+            ),
+        );
+        $this->_backend->update($contractData);
+        $contract = $this->_backend->get($contractData->getId());
+
+        // check count of product aggregates
+        $filter = new Sales_Model_ProductAggregateFilter(array());
+        $filter->addFilter(new Tinebase_Model_Filter_Text(
+            array('field' => 'contract_id', 'operator' => 'equals', 'value' => $contract->getId())
+        ));
+        $productAggregates = Sales_Controller_ProductAggregate::getInstance()->search($filter);
+        $this->assertEquals(1, count($productAggregates));
+
+        // cleanup
+        $this->_backend->delete($contract->getId());
+        $this->_decreaseNumber();
+        $prodTest->getUit()->delete(array($productOne->getId(), $productTwo->getId()));
+    }
 }
index f3e1132..8be275f 100644 (file)
@@ -9,6 +9,7 @@
  * 
  */
 
+
 /**
  * Test class for Sales Invoice Controller
  */
@@ -319,7 +320,227 @@ class Sales_InvoiceControllerTests extends Sales_InvoiceTestCase
             $this->assertTrue($ts->invoice_id == NULL, print_r($ts->toArray(), 1));
         }
     }
-    
+
+    protected function _createInvoiceUpdateRecreationFixtures($createTimesheet = true)
+    {
+        $this->_createFullFixtures();
+
+        // we dont want this contract 1 to be part of the runs below, move it out of the way
+        $this->_contractRecords->getByIndex(0)->start_date->addMonth(12);
+        Sales_Controller_Contract::getInstance()->update($this->_contractRecords->getByIndex(0));
+
+        $date = clone $this->_referenceDate;
+        $customer4Timeaccount = $this->_timeaccountRecords->filter('title', 'TA-for-Customer4')->getFirstRecord();
+        $customer4Timeaccount->status = 'to bill';
+        $customer4Timeaccount->budget = NULL;
+
+        if (null === $this->_timesheetController)
+            $this->_timesheetController = Timetracker_Controller_Timesheet::getInstance();
+        if (null === $this->_timeaccountController)
+            $this->_timeaccountController = Timetracker_Controller_Timeaccount::getInstance();
+        $this->_timeaccountController->update($customer4Timeaccount);
+
+        // this is a ts on 20xx-03-18
+        $this->sharedTimesheet = new Timetracker_Model_Timesheet(array(
+            'account_id' => Tinebase_Core::getUser()->getId(),
+            'timeaccount_id' => $customer4Timeaccount->getId(),
+            'start_date' => $date->addMonth(2)->addDay(17),
+            'duration' => 120,
+            'description' => 'ts from ' . (string) $date,
+        ));
+        if (true === $createTimesheet)
+            $this->_timesheetController->create($this->sharedTimesheet);
+
+        //run autoinvoicing with 20xx-04-01
+        $date = clone $this->_referenceDate;
+        $date->addMonth(3);
+        $result = $this->_invoiceController->createAutoInvoices($date);
+        $this->assertEquals(2, count($result['created']));
+
+        return $result;
+    }
+
+    public function testInvoiceRecreation()
+    {
+        $result = $this->_createInvoiceUpdateRecreationFixtures();
+
+        $oldInvoiceId0 = $result['created'][0];
+        $ipc = Sales_Controller_InvoicePosition::getInstance();
+        $f = new Sales_Model_InvoicePositionFilter(array(
+            array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
+                array('field' => 'id', 'operator' => 'equals', 'value' => $oldInvoiceId0),
+            )),
+        ));
+        $positions = $ipc->search($f);
+        $this->assertEquals(9, $positions->count());
+
+        $oldInvoiceId1 = $result['created'][1];
+        $ipc = Sales_Controller_InvoicePosition::getInstance();
+        $f = new Sales_Model_InvoicePositionFilter(array(
+            array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
+                array('field' => 'id', 'operator' => 'equals', 'value' => $oldInvoiceId1),
+            )),
+        ));
+        $positions = $ipc->search($f);
+        $this->assertEquals(4, $positions->count());
+
+        $contract4 = $this->_contractRecords->getByIndex(3);
+        $filter = new Sales_Model_ProductAggregateFilter(
+            array(
+                array('field' => 'interval', 'operator' => 'equals', 'value' => 3),
+                //array('field' => 'contract_id', 'operator' => 'equals', 'value' => $this->_contractRecords->getByIndex(3)->getId()),
+            ), 'AND');
+        $filter->addFilter(new Tinebase_Model_Filter_ForeignId(//ExplicitRelatedRecord(
+            array('field' => 'contract_id', 'operator' => 'AND', 'value' =>
+                array(
+                    array(
+                        'field' =>  ':id', 'operator' => 'equals', 'value' => $contract4->getId()
+                    )
+                ),
+                'options' => array(
+                    'controller'        => 'Sales_Controller_Contract',
+                    'filtergroup'       => 'Sales_Model_ContractFilter',
+                    //'own_filtergroup'   => 'Sales_Model_ProductAggregateFilter',
+                    //'own_controller'    => 'Sales_Controller_ProductAggregate',
+                    //'related_model'     => 'Sales_Model_Contract',
+                    'modelName' => 'Sales_Model_Contract',
+                ),
+            )
+        ));
+
+        $pA = Sales_Controller_ProductAggregate::getInstance()->search($filter);
+        $this->assertEquals(1, $pA->count());
+        $pA = $pA->getFirstRecord();
+        $pA->interval = 4;
+        Sales_Controller_ProductAggregate::getInstance()->update($pA);
+        $contract4->title = $contract4->getTitle() . ' changed';
+        sleep(1);
+        $this->_contractController->update($contract4);
+
+        $this->sharedTimesheet->id = NULL;
+        $this->_timesheetController->create($this->sharedTimesheet);
+
+        $result = $this->_invoiceController->checkForContractOrInvoiceUpdates();
+        $this->assertEquals(2, count($result));
+        $this->assertNotEquals($oldInvoiceId0, $result[0]);
+        $this->assertNotEquals($oldInvoiceId1, $result[1]);
+
+        $this->_checkInvoiceUpdateExistingTimeaccount($result[1]);
+
+        $f = new Sales_Model_InvoicePositionFilter(array(
+            array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
+                array('field' => 'id', 'operator' => 'equals', 'value' => $result[0]),
+            )),
+        ));
+        $positions = $ipc->search($f);
+        $this->assertEquals(10, $positions->count());
+
+        $f = new Sales_Model_InvoicePositionFilter(array(
+            array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
+                array('field' => 'id', 'operator' => 'equals', 'value' => $result[1]),
+            )),
+        ));
+        $positions = $ipc->search($f);
+        $this->assertEquals(1, $positions->count());
+    }
+
+    /**
+     *
+     */
+    public function testInvoiceUpdateExistingTimeaccount()
+    {
+        $result = $this->_createInvoiceUpdateRecreationFixtures();
+
+        $this->sharedTimesheet->id = NULL;
+        $this->_timesheetController->create($this->sharedTimesheet);
+
+        $this->_invoiceController->checkForUpdate($result['created'][1]);
+
+        $this->_checkInvoiceUpdateExistingTimeaccount($result['created'][1]);
+
+        //check that the same update run doesnt do anything anymore
+        $this->_invoiceController->checkForUpdate($result['created'][1]);
+
+        $this->_checkInvoiceUpdateExistingTimeaccount($result['created'][1]);
+    }
+
+    public function testCheckForContractOrInvoiceUpdatesExistingTimeaccount()
+    {
+        $result = $this->_createInvoiceUpdateRecreationFixtures();
+
+        $this->sharedTimesheet->id = NULL;
+        $this->_timesheetController->create($this->sharedTimesheet);
+
+        $this->_invoiceController->checkForContractOrInvoiceUpdates();
+
+        $this->_checkInvoiceUpdateExistingTimeaccount($result['created'][1]);
+
+        $this->_invoiceController->checkForContractOrInvoiceUpdates();
+
+        $this->_checkInvoiceUpdateExistingTimeaccount($result['created'][1]);
+    }
+
+    protected function _checkInvoiceUpdateExistingTimeaccount($invoiceId)
+    {
+        $ipc = Sales_Controller_InvoicePosition::getInstance();
+        $f = new Sales_Model_InvoicePositionFilter(array(
+            array('field' => 'model', 'operator' => 'equals', 'value' => 'Timetracker_Model_Timeaccount'),
+            array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
+                array('field' => 'id', 'operator' => 'equals', 'value' => $invoiceId),
+            )),
+        ));
+        $positions = $ipc->search($f);
+        $this->assertEquals(1, $positions->count());
+        $this->assertEquals(4, $positions->getFirstRecord()->quantity);
+    }
+
+    protected function _checkInvoiceUpdateWithNewTimeaccount($invoiceId)
+    {
+        $ipc = Sales_Controller_InvoicePosition::getInstance();
+        $f = new Sales_Model_InvoicePositionFilter(array(
+            array('field' => 'model', 'operator' => 'equals', 'value' => 'Timetracker_Model_Timeaccount'),
+            array('field' => 'invoice_id', 'operator' => 'AND', 'value' => array(
+                array('field' => 'id', 'operator' => 'equals', 'value' => $invoiceId),
+            )),
+        ));
+        $positions = $ipc->search($f);
+        $this->assertEquals(1, $positions->count());
+        $this->assertEquals(2, $positions->getFirstRecord()->quantity);
+    }
+    /**
+     *
+     */
+    public function testInvoiceUpdateWithNewTimeaccount()
+    {
+        $result = $this->_createInvoiceUpdateRecreationFixtures(false);
+
+        $this->_timesheetController->create($this->sharedTimesheet);
+
+        $this->_invoiceController->checkForUpdate($result['created'][1]);
+
+        $this->_checkInvoiceUpdateWithNewTimeaccount($result['created'][1]);
+
+        //check that the same update run doesnt do anything anymore
+        $this->_invoiceController->checkForUpdate($result['created'][1]);
+
+        $this->_checkInvoiceUpdateWithNewTimeaccount($result['created'][1]);
+    }
+
+    public function testCheckForContractOrInvoiceUpdatesWithNewTimeaccount()
+    {
+        $result = $this->_createInvoiceUpdateRecreationFixtures(false);
+
+        $this->_timesheetController->create($this->sharedTimesheet);
+
+        $this->_invoiceController->checkForContractOrInvoiceUpdates();
+
+        $this->_checkInvoiceUpdateWithNewTimeaccount($result['created'][1]);
+
+        $this->_invoiceController->checkForContractOrInvoiceUpdates();
+
+        $this->_checkInvoiceUpdateWithNewTimeaccount($result['created'][1]);
+    }
+
     /**
      * @see: rt127444
      * 
@@ -340,7 +561,7 @@ class Sales_InvoiceControllerTests extends Sales_InvoiceTestCase
         $taController = Timetracker_Controller_Timeaccount::getInstance();
         $taController->update($customer1Timeaccount);
         
-        // this is a ts on 20xx-01-17
+        // this is a ts on 20xx-01-18
         $timesheet = new Timetracker_Model_Timesheet(array(
             'account_id' => Tinebase_Core::getUser()->getId(),
             'timeaccount_id' => $customer1Timeaccount->getId(),
diff --git a/tests/tine20/Sales/ProductControllerTest.php b/tests/tine20/Sales/ProductControllerTest.php
new file mode 100644 (file)
index 0000000..1f977bb
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Sales
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
+ * 
+ */
+
+/**
+ * Test class for Sales_Controller_Product
+ */
+class Sales_ProductControllerTest extends TestCase
+{
+    /**
+     * lazy init of uit
+     *
+     * @return Sales_Controller_Product
+     */
+    public function getUit()
+    {
+        if ($this->_uit === null) {
+            $this->_uit = Sales_Controller_Product::getInstance();
+        }
+        
+        return $this->_uit;
+    }
+    
+    /**
+     * 
+     * @return Sales_Model_Contract
+     */
+    public function testCreateProduct()
+    {
+        $product = $this->getUit()->create(new Sales_Model_Product(array(
+            'name' => 'A new product'
+        )));
+        
+        $this->assertNotEmpty($product->number);
+        
+        return $product;
+    }
+    
+    /**
+     * testUpdateProductLifespan
+     * 
+     * @see 0010766: set product lifespan
+     */
+    public function testUpdateProductLifespan()
+    {
+        $product1 = $this->getUit()->create(new Sales_Model_Product(array(
+            'name' => 'product activates in future',
+            'lifespan_start' => Tinebase_DateTime::now()->addDay(1)
+        )));
+        $product2 = $this->getUit()->create(new Sales_Model_Product(array(
+            'name' => 'product lifespan ended',
+            'lifespan_end' => Tinebase_DateTime::now()->subDay(1)
+        )));
+        $product3 = $this->getUit()->create(new Sales_Model_Product(array(
+            'is_active' => 0,
+            'name' => 'product lifespan started',
+            'lifespan_start' => Tinebase_DateTime::now()->subDay(1)
+        )));
+        $product4 = $this->getUit()->create(new Sales_Model_Product(array(
+            'is_active' => 0,
+            'name' => 'product lifespan not yet ended',
+            'lifespan_end' => Tinebase_DateTime::now()->addDay(1)
+        )));
+        
+        $productsToTest = array(
+            array('expectedIsActive' => 0, 'product' => $product1),
+            array('expectedIsActive' => 0, 'product' => $product2),
+            array('expectedIsActive' => 1, 'product' => $product3),
+            array('expectedIsActive' => 1, 'product' => $product4),
+        );
+        
+        $this->getUit()->updateProductLifespan();
+        
+        foreach ($productsToTest as $product) {
+            $updatedProduct = $this->getUit()->get($product['product']);
+            $this->assertEquals($product['expectedIsActive'], $updatedProduct->is_active, print_r($product['product']->toArray(), true));
+        }
+    }
+}
diff --git a/tests/tine20/Sales/PurchaseInvoiceTest.php b/tests/tine20/Sales/PurchaseInvoiceTest.php
new file mode 100644 (file)
index 0000000..826f5da
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ *
+ * @package     Sales
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2008-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Stefanie Stamer <s.stamer@metaways.de>
+ */
+
+/**
+ * Test class for Sales_PurchaseInvoice
+ */
+class Sales_PurchaseInvoiceTest extends TestCase
+{
+    /**
+     *
+     * @var Sales_Frontend_Json
+     */
+    protected $_json;
+    
+    /**
+     * get paging
+     *
+     * @return array
+     */
+    protected function _getPaging()
+    {
+        return array(
+                'start' => 0,
+                'limit' => 50,
+                'sort' => 'number',
+                'dir' => 'ASC',
+        );
+    }
+    
+    /**
+     * get filter
+     *
+     * @return array
+     */
+    protected function _getFilter()
+    {
+        return array(
+                array('field' => 'query', 'operator' => 'contains', 'value' => '12345'),
+        );
+    }
+    /**
+     * Sets up the fixture.
+     * This method is called before a test is executed.
+     *
+     * @access protected
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+    
+        $this->_contactController  = Addressbook_Controller_Contact::getInstance();
+        $this->_json               = new Sales_Frontend_Json();
+    }
+    
+    /**
+     *
+     * @return array
+     */
+    protected function _createPurchaseInvoice()
+    {
+        $container = Tinebase_Container::getInstance()->getSharedContainer(
+                Tinebase_Core::getUser()->getId(),
+                'Addressbook_Model_Contact',
+                'WRITE'
+        );
+    
+        $containerContracts = Tinebase_Container::getInstance()->getSharedContainer(
+                Tinebase_Core::getUser()->getId(),
+                'Sales_Model_Contract',
+                'WRITE'
+        );
+    
+        $container = $container->getFirstRecord();
+    
+        $contact1 = $this->_contactController->create(new Addressbook_Model_Contact(
+                array('n_given' => 'Yiting', 'n_family' => 'Huang', 'container_id' => $container->getId()))
+        );
+        $contact2 = $this->_contactController->create(new Addressbook_Model_Contact(
+                array('n_given' => 'Hans Friedrich', 'n_family' => 'Ochs', 'container_id' => $container->getId()))
+        );
+    
+        $customerData = array(
+                'name' => 'Worldwide Electronics International',
+                'cpextern_id' => $contact1->getId(),
+                'cpintern_id' => $contact2->getId(),
+                'number'      => 54321,
+    
+                'iban'        => 'CN09234098324098234598',
+                'bic'         => '0239580429570923432444',
+                'url'         => 'http://wwei.cn',
+                'vatid'       => '239rc9mwqe9c2q',
+                'credit_term' => '30',
+                'currency'    => 'EUR',
+                'curreny_trans_rate' => 7.034,
+                'discount'    => 12.5,
+    
+                'adr_prefix1' => 'no prefix 1',
+                'adr_prefix2' => 'no prefix 2',
+                'adr_street' => 'Mao st. 2000',
+                'adr_postalcode' => '1',
+                'adr_locality' => 'Shanghai',
+                'adr_region' => 'Shanghai',
+                'adr_countryname' => 'China',
+                'adr_pobox'   => '7777777'
+        );
+        
+        $purchaseData = array(
+                'number' => 'R-12345',
+                'description' => 'test',
+                'discount' => 0,
+                'due_in' => 10,
+                'date' => '2015-03-17 00:00:00',
+                'due_at' => '2015-03-27 00:00:00',
+                'price_net' => 10,
+                'sales_tax' => 19,
+                'price_tax' => 1.9,
+                'price_gross' => 11.9,
+                'price_gross2' => 1,
+                'price_total' => 12.9,
+                'relations' => array(array(
+                        'own_model' => 'Sales_Model_PurchaseInvoice',
+                        'own_degree' => Tinebase_Model_Relation::DEGREE_SIBLING,
+                        'related_model' => 'Sales_Model_Supplier',
+                        'related_record' => $customerData,
+                        'type' => 'SUPPLIER'
+                )
+            )
+        );
+        
+        return $this->_json->savePurchaseInvoice($purchaseData);
+    }
+    
+    /**
+     * try to save a PurchaseInvoice
+     */
+    public function testSavePurchaseInvoice()
+    {
+        $purchase = $this->_createPurchaseInvoice();
+        $this->assertEquals('R-12345', $purchase['number']);
+        $this->assertEquals('Worldwide Electronics International', $purchase['supplier']['name']);
+        $this->assertEquals('2015-03-17 00:00:00', $purchase['date']);
+        $this->assertEquals('2015-03-27 00:00:00', $purchase['due_at']);
+
+        $this->assertEquals(10, $purchase['price_net']);
+        $this->assertEquals(19, $purchase['sales_tax']);
+        $this->assertEquals(1.9, $purchase['price_tax']);
+        $this->assertEquals(11.9, $purchase['price_gross']);
+        $this->assertEquals(1, $purchase['price_gross2']);
+        $this->assertEquals(12.9, $purchase['price_total']);
+    }
+    
+    /**
+     * try to update a PurchaseInvoice
+     */
+    public function testUpdatePurchaseInvoice()
+    {
+        $purchase = $this->_createPurchaseInvoice();
+        $this->assertEquals('2015-03-27 00:00:00', $purchase['due_at']);
+        $purchase['due_at'] = '2015-04-07 00:00:00';
+        $updatedPurchase = $this->_json->savePurchaseInvoice($purchase);
+        $this->assertEquals('2015-04-07 00:00:00', $updatedPurchase['due_at']);
+    }
+
+    /**
+     * try to get a PurchaseInvoice
+     */
+    public function testSearchPurchaseInvoice()
+    {
+        $purchase = $this->_createPurchaseInvoice();
+        
+        // search & check
+        $search = $this->_json->searchPurchaseInvoices($this->_getFilter(), $this->_getPaging());
+        $this->assertEquals($purchase['number'], $search['results'][0]['number']);
+        $this->assertEquals(1, $search['totalcount']);
+    }
+    
+    /**
+     * try to delete a PurchaseInvoice
+     */
+    public function testDeletePurchaseInvoice()
+    {
+        $purchase = $this->_createPurchaseInvoice();
+        $this->assertEquals('R-12345', $purchase['number']);
+        
+        // delete record
+        $this->_json->deletePurchaseInvoices($purchase['id']);
+        
+        $this->setExpectedException('Tinebase_Exception_NotFound');
+        $customerBackend = new Sales_Backend_PurchaseInvoice();
+        $deletedPurchase = $customerBackend->get($purchase['id'], TRUE);
+        $this->assertEquals(1, $deletedPurchase->is_deleted);
+        
+    }
+
+}
diff --git a/tests/tine20/Sales/SuppliersTest.php b/tests/tine20/Sales/SuppliersTest.php
new file mode 100644 (file)
index 0000000..f84fca0
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Sales
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2015-2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Lars Kneschke <l.kneschke@metaways.de>
+ */
+
+/**
+ * Test class for Sales_SuppliersTest
+ */
+class Sales_SuppliersTest extends TestCase
+{
+    /**
+     * 
+     * @var Addressbook_Controller_Contact
+     */
+    protected $_contactController;
+    
+    /**
+     * 
+     * @var Sales_Frontend_Json
+     */
+    protected $_json;
+    
+    /**
+     * Sets up the fixture.
+     * This method is called before a test is executed.
+     *
+     * @access protected
+     */
+    protected function setUp()
+    {
+        parent::setUp();
+        
+        $this->_contactController  = Addressbook_Controller_Contact::getInstance();
+        $this->_json               = new Sales_Frontend_Json();
+    }
+
+    /**
+     * 
+     * @return array
+     */
+    protected function _createSupplier()
+    {
+        $container = Tinebase_Container::getInstance()->getSharedContainer(
+            Tinebase_Core::getUser()->getId(),
+            'Addressbook_Model_Contact',
+            'WRITE'
+        );
+        
+        $containerContracts = Tinebase_Container::getInstance()->getSharedContainer(
+            Tinebase_Core::getUser()->getId(),
+            'Sales_Model_Contract',
+            'WRITE'
+        );
+        
+        $container = $container->getFirstRecord();
+        
+        $contact1 = $this->_contactController->create(new Addressbook_Model_Contact(
+            array('n_given' => 'Yiting', 'n_family' => 'Huang', 'container_id' => $container->getId()))
+        );
+        $contact2 = $this->_contactController->create(new Addressbook_Model_Contact(
+            array('n_given' => 'Hans Friedrich', 'n_family' => 'Ochs', 'container_id' => $container->getId()))
+        );
+        
+        $customerData = array(
+            'name' => 'Worldwide Electronics International',
+            'cpextern_id' => $contact1->getId(),
+            'cpintern_id' => $contact2->getId(),
+            'number'      => 4294967,
+        
+            'iban'        => 'CN09234098324098234598',
+            'bic'         => '0239580429570923432444',
+            'url'         => 'http://wwei.cn',
+            'vatid'       => '239rc9mwqe9c2q',
+            'credit_term' => '30',
+            'currency'    => 'EUR',
+            'curreny_trans_rate' => 7.034,
+            'discount'    => 12.5,
+        
+            'adr_prefix1' => 'no prefix 1',
+            'adr_prefix2' => 'no prefix 2',
+            'adr_street' => 'Mao st. 2000',
+            'adr_postalcode' => '1',
+            'adr_locality' => 'Shanghai',
+            'adr_region' => 'Shanghai',
+            'adr_countryname' => 'China',
+            'adr_pobox'   => '7777777'
+        );
+        
+        return $this->_json->saveSupplier($customerData);
+    }
+    
+    public function testLifecycleSupplier()
+    {
+        $retVal = $this->_createSupplier();
+        
+        $this->assertEquals(4294967, $retVal["number"]);
+        $this->assertEquals("Worldwide Electronics International", $retVal["name"]);
+        $this->assertEquals("http://wwei.cn", $retVal["url"]);
+        $this->assertEquals(NULL, $retVal['description']);
+        
+        $this->assertEquals('Yiting', $retVal['cpextern_id']['n_given']);
+        $this->assertEquals('Huang',  $retVal['cpextern_id']['n_family']);
+        
+        $this->assertEquals('Hans Friedrich', $retVal['cpintern_id']['n_given']);
+        $this->assertEquals('Ochs', $retVal['cpintern_id']['n_family']);
+
+        // delete record (set deleted=1) of customer and assigned addresses
+        $this->_json->deleteSuppliers(array($retVal['id']));
+        
+        $customerBackend = new Sales_Backend_Supplier();
+        $deletedSupplier = $customerBackend->get($retVal['id'], TRUE);
+        $this->assertEquals(1, $deletedSupplier->is_deleted);
+        
+        $addressBackend = new Sales_Backend_Address();
+        $deletedAddresses = $addressBackend->getMultipleByProperty($retVal['id'], 'customer_id', TRUE);
+
+        $this->assertEquals(1, $deletedAddresses->count());
+        
+        foreach($deletedAddresses as $address) {
+            $this->assertEquals(1, $address->is_deleted);
+        }
+        $this->setExpectedException('Tinebase_Exception_NotFound');
+        
+        return $this->_json->getSupplier($retVal['id']);
+    }
+    
+    /**
+     * checks if the number is always set to the correct value
+     */
+    public function testNumberable()
+    {
+        $controller = Sales_Controller_Supplier::getInstance();
+        
+        $record = $controller->create(new Sales_Model_Supplier(array('name' => 'auto1')));
+        
+        $this->assertGreaterThan(0, $record->number);
+        $initialNumber = $record->number;
+        
+        $record = $controller->create(new Sales_Model_Supplier(array('name' => 'auto2')));
+        
+        $this->assertEquals($initialNumber + 1, $record->number);
+        
+        // set number to $initialNumber + 3, should return the formatted number
+        $record = $controller->create(new Sales_Model_Supplier(array('name' => 'manu1', 'number' => $initialNumber + 3)));
+        $this->assertEquals($initialNumber + 3, $record->number);
+        
+        // the next number should be a number after the manual number
+        $record = $controller->create(new Sales_Model_Supplier(array('name' => 'auto3')));
+        $this->assertEquals($initialNumber + 4, $record->number);
+    }
+}
index e0d378a..4f318c0 100644 (file)
@@ -17,7 +17,7 @@ require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php'
 /**
  * Test class for Tasks_JsonTest
  */
-class Tasks_JsonTest extends PHPUnit_Framework_TestCase
+class Tasks_JsonTest extends TestCase
 {
     /**
      * Backend
@@ -46,16 +46,6 @@ class Tasks_JsonTest extends PHPUnit_Framework_TestCase
      * @var Zend_Mail_Transport_Abstract
      */
     protected $_smtpTransport = NULL;
-    
-    /**
-     * main function
-     *
-     */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tasks_JsonTest');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
 
     /**
      * Sets up the fixture, for example, opens a network connection.
@@ -65,7 +55,7 @@ class Tasks_JsonTest extends PHPUnit_Framework_TestCase
      */
     protected function setUp()
     {
-        Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+        parent::setUp();
         
         $this->_backend = new Tasks_Frontend_Json();
         $this->_smtpConfig = Tinebase_Config::getInstance()->get(Tinebase_Config::SMTP, new Tinebase_Config_Struct())->toArray();
@@ -84,8 +74,10 @@ class Tasks_JsonTest extends PHPUnit_Framework_TestCase
             Tinebase_Config::getInstance()->set(Tinebase_Config::SMTP, $this->_smtpConfig);
             Tinebase_Smtp::setDefaultTransport($this->_smtpTransport);
         }
-        
-        Tinebase_TransactionManager::getInstance()->rollBack();
+
+        Tinebase_Core::getPreference()->setValue(Tinebase_Preference::ADVANCED_SEARCH, false);
+
+        parent::tearDown();
     }
     
     /**
@@ -460,5 +452,25 @@ class Tasks_JsonTest extends PHPUnit_Framework_TestCase
             'dir' => 'ASC',
         );
     }
-}
 
+    /**
+     * test advanced search
+     *
+     * @see 0011492: activate advanced search (search in lead relations)
+     */
+    public function testAdvancedSearch()
+    {
+        // create task with lead relation
+        $crmTests = new Crm_JsonTest();
+        $crmTests->saveLead();
+
+        // activate advanced search
+        Tinebase_Core::getPreference()->setValue(Tinebase_Preference::ADVANCED_SEARCH, true);
+
+        // search in lead
+        $result = $this->_backend->searchTasks(array(array(
+            'field' => 'query', 'operator' => 'contains', 'value' => 'PHPUnit LEAD'
+        )), array());
+        $this->assertEquals(1, $result['totalcount']);
+    }
+}
index 9f7f1f2..c8ab7fa 100644 (file)
@@ -4,7 +4,7 @@
  * 
  * @package     Tests
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2013-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2013-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
  */
 
@@ -14,7 +14,7 @@
 require_once __DIR__ . DIRECTORY_SEPARATOR . 'TestHelper.php';
 
 /**
- * Test class for Calendar_Backend_Sql
+ * Abstract test class
  * 
  * @package     Tests
  */
@@ -30,7 +30,7 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
     /**
      * usernames to be deleted (in sync backend)
      * 
-     * @var string
+     * @var array
      */
     protected $_usernamesToDelete = array();
     
@@ -49,6 +49,13 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
     protected $_removeGroupMembers = true;
     
     /**
+     * invalidate roles cache
+     * 
+     * @var boolean
+     */
+    protected $_invalidateRolesCache = false;
+    
+    /**
      * test personas
      * 
      * @var array
@@ -72,10 +79,17 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
     /**
      * the mailer
      * 
-     * @var Zend_Mail_Transport_Array
+     * @var Zend_Mail_Transport_Abstract
      */
     protected static $_mailer = null;
-    
+
+    /**
+     * db lock ids to be released
+     *
+     * @var array
+     */
+    protected $_releaseDBLockIds = array();
+
     /**
      * set up tests
      */
@@ -111,9 +125,24 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
             Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
         }
         
+        if ($this->_invalidateRolesCache) {
+            Tinebase_Acl_Roles::getInstance()->resetClassCache();
+        }
         Tinebase_Cache_PerRequest::getInstance()->resetCache();
 
+        $this->_releaseDBLocks();
+    }
+
+    /**
+     * release db locks
+     */
+    protected function _releaseDBLocks()
+    {
+        foreach ($this->_releaseDBLockIds as $lockId) {
+            Tinebase_Lock::releaseDBSessionLock($lockId);
+        }
 
+        $this->_releaseDBLockIds = array();
     }
 
     /**
@@ -229,27 +258,35 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
             $user = Tinebase_Core::getUser();
         }
         
-        return Tinebase_Container::getInstance()->getPersonalContainer(
+        $personalContainer = Tinebase_Container::getInstance()->getPersonalContainer(
             $user,
             $applicationName, 
             $user,
             Tinebase_Model_Grants::GRANT_EDIT
         )->getFirstRecord();
+
+        if (! $personalContainer) {
+            throw new Tinebase_Exception_UnexpectedValue('no personal container found!');
+        }
+
+        return $personalContainer;
     }
     
     /**
      * get test container
      * 
      * @param string $applicationName
+     * @param string $model
      */
-    protected function _getTestContainer($applicationName)
+    protected function _getTestContainer($applicationName, $model = null)
     {
         return Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
-            'name'           => 'PHPUnit test container',
+            'name'           => 'PHPUnit ' . $model .' container',
             'type'           => Tinebase_Model_Container::TYPE_PERSONAL,
             'owner_id'       => Tinebase_Core::getUser(),
             'backend'        => 'Sql',
-            'application_id' => Tinebase_Application::getInstance()->getApplicationByName($applicationName)->getId()
+            'application_id' => Tinebase_Application::getInstance()->getApplicationByName($applicationName)->getId(),
+            'model'          => $model,
         ), true));
     }
     
@@ -390,13 +427,43 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
     }
     
     /**
-     * set grants for a persona
+     * remove right in all users roles
+     * 
+     * @param string $applicationName
+     * @param string $right
+     * @param boolean $removeAdminRight
+     * @return array original role rights by role id
+     */
+    protected function _removeRoleRight($applicationName, $rightToRemove, $removeAdminRight = true)
+    {
+        $app = Tinebase_Application::getInstance()->getApplicationByName($applicationName);
+        $rolesOfUser = Tinebase_Acl_Roles::getInstance()->getRoleMemberships(Tinebase_Core::getUser()->getId());
+        $this->_invalidateRolesCache = true;
+
+        $roleRights = array();
+        foreach ($rolesOfUser as $roleId) {
+            $roleRights[$roleId] = $rights = Tinebase_Acl_Roles::getInstance()->getRoleRights($roleId);
+            foreach ($rights as $idx => $right) {
+                if ($right['application_id'] === $app->getId() && ($right['right'] === $rightToRemove || $right['right'] === Tinebase_Acl_Rights_Abstract::ADMIN)) {
+                    if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
+                        . ' Removing right ' . $right['right'] . ' from app ' . $applicationName . ' in role (id) ' . $roleId);
+                    unset($rights[$idx]);
+                }
+            }
+            Tinebase_Acl_Roles::getInstance()->setRoleRights($roleId, $rights);
+        }
+        
+        return $roleRights;
+    }
+    
+    /**
+     * set grants for a persona and the current user
      * 
      * @param integer $containerId
      * @param string $persona
      * @param string $adminGrant
      */
-    protected function _setPersonaGrantsForTestContainer($containerId, $persona, $adminGrant = false)
+    protected function _setPersonaGrantsForTestContainer($containerId, $persona, $personaAdminGrant = false, $userAdminGrant = true)
     {
         $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(array(
             'account_id'    => $this->_personas[$persona]->getId(),
@@ -405,7 +472,7 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
             Tinebase_Model_Grants::GRANT_ADD      => true,
             Tinebase_Model_Grants::GRANT_EDIT     => true,
             Tinebase_Model_Grants::GRANT_DELETE   => true,
-            Tinebase_Model_Grants::GRANT_ADMIN    => $adminGrant,
+            Tinebase_Model_Grants::GRANT_ADMIN    => $personaAdminGrant,
         ), array(
             'account_id'    => Tinebase_Core::getUser()->getId(),
             'account_type'  => 'user',
@@ -413,9 +480,20 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
             Tinebase_Model_Grants::GRANT_ADD      => true,
             Tinebase_Model_Grants::GRANT_EDIT     => true,
             Tinebase_Model_Grants::GRANT_DELETE   => true,
-            Tinebase_Model_Grants::GRANT_ADMIN    => true,
+            Tinebase_Model_Grants::GRANT_ADMIN    => $userAdminGrant,
         )));
         
         Tinebase_Container::getInstance()->setGrants($containerId, $grants, TRUE);
     }
+
+    /**
+     * set current user
+     *
+     * @param $user
+     * @throws Tinebase_Exception_InvalidArgument
+     */
+    protected function _setUser($user)
+    {
+        Tinebase_Core::set(Tinebase_Core::USER, $user);
+    }
 }
index 609ed69..d4cfbfc 100644 (file)
@@ -1255,4 +1255,30 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         $tsData = $this->_json->getTimesheet($tsData['id']);
         $this->assertSame(NULL,  $tsData['invoice_id']);
     }
+    
+    /**
+     * try to update a Timesheet with a closed TimeAccount
+     *
+     */
+    public function testUpdateClosedTimeaccount()
+    {
+        $timeaccountData = $this->_saveTimeaccountWithGrants();
+        $timeaccountData['is_open'] = 0;
+        $timeaccount = $this->_json->saveTimeaccount($timeaccountData);
+        
+        $timesheet = $this->_getTimesheet(array(
+             'timeaccount_id'    => $timeaccount['id'],
+        ));
+        $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        
+        Timetracker_ControllerTest::removeManageAllRight();
+        
+        $this->setExpectedException('Tinebase_Exception_AccessDenied');
+        
+        // update Timesheet
+        $timesheetData['description'] = "blubbblubb";
+        $timesheetData['account_id'] = $timesheetData['account_id']['accountId'];
+        $timesheetData['timeaccount_id'] = $timesheetData['timeaccount_id']['id'];
+        $timesheetUpdated = $this->_json->saveTimesheet($timesheetData);
+    }
 }
index 7231529..2b1f346 100644 (file)
@@ -69,7 +69,9 @@ class Tinebase_AllTests
         $suite->addTestSuite('Tinebase_Redis_QueueTest');
         $suite->addTestSuite('Tinebase_Pluggable_ConcreteTest');
         $suite->addTestSuite('Tinebase_TempFileTest');
-        
+        $suite->addTestSuite('Tinebase_LockTest');
+        $suite->addTestSuite('Tinebase_ScheduledImportTest');
+
         $suite->addTest(Tinebase_User_AllTests::suite());
         $suite->addTest(Tinebase_Group_AllTests::suite());
         $suite->addTest(Tinebase_Timemachine_AllTests::suite());
index ca6784c..925be4b 100644 (file)
@@ -237,4 +237,14 @@ class Tinebase_ConfigTest extends PHPUnit_Framework_TestCase
         $cachedConfigFilename = Tinebase_Core::guessTempDir() . DIRECTORY_SEPARATOR . 'cachedConfig.inc.php';
         $this->assertTrue(file_exists($cachedConfigFilename), 'cached config file does not exist: ' . $cachedConfigFilename);
     }
+
+    /**
+     * @see 0011456: unable to add new activesync-devices in tine20
+     */
+    public function testDefaultNull()
+    {
+        // TODO maybe we need to remove the current config if is set
+        $defaultPolicy = ActiveSync_Config::getInstance()->get(ActiveSync_Config::DEFAULT_POLICY, null);
+        $this->assertTrue(is_null($defaultPolicy), 'config should be null: ' . var_export($defaultPolicy, true));
+    }
 }
index 9507d52..777d951 100644 (file)
@@ -521,6 +521,8 @@ class Tinebase_ContainerTest extends PHPUnit_Framework_TestCase
         Tinebase_User::getInstance()->setStatus($user2, 'enabled');
         
         $container = Tinebase_Container::getInstance()->getPersonalContainer($user1, 'Calendar', $user1, Tinebase_Model_Grants::GRANT_READ);
+
+        $this->assertGreaterThan(0, count($container));
         
         $oldGrants = Tinebase_Container::getInstance()->getGrantsOfContainer($container->getFirstRecord()->id, TRUE);
         
@@ -664,7 +666,7 @@ class Tinebase_ContainerTest extends PHPUnit_Framework_TestCase
         Tinebase_Config::getInstance()->set(Tinebase_Config::ANYONE_ACCOUNT_DISABLED, TRUE);
         Tinebase_Core::getCache()->clean();
         $readGrant = $this->_instance->resetClassCache('hasGrant')->hasGrant(Tinebase_Core::getUser(), $sharedContainer, Tinebase_Model_Grants::GRANT_READ);
-        $this->assertFalse($readGrant);
+        $this->assertFalse($readGrant, 'user should not have read grant');
     }
     
     /**
index f03cd0a..ddb09ac 100644 (file)
@@ -4,7 +4,7 @@
  * 
  * @package     Tinebase
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2014-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
@@ -20,8 +20,6 @@ class Tinebase_ControllerServerTest extends ServerTestCase
      */
     public function testValidLogin()
     {
-        Zend_Session::$_unitTestEnabled = true;
-        
         $request = \Zend\Http\PhpEnvironment\Request::fromString(<<<EOS
 POST /index.php HTTP/1.1\r
 Content-Type: application/json\r
@@ -53,8 +51,6 @@ EOS
      */
     public function testInvalidLogin()
     {
-        Zend_Session::$_unitTestEnabled = true;
-        
         $request = \Zend\Http\PhpEnvironment\Request::fromString(<<<EOS
 POST /index.php HTTP/1.1\r
 Content-Type: application/json\r
@@ -83,11 +79,16 @@ EOS
     
     /**
      * @group ServerTests
+     *
+     * @see 0011440: rework login failure handling
      */
     public function testAccountBlocking()
     {
-        Zend_Session::$_unitTestEnabled = true;
-        
+        // NOTE: end transaction here as NOW() returns the start of the current transaction in pgsql
+        //  and is used in user status statement (think about using statement_timestamp() instead of NOW() with pgsql)
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = null;
+
         $request = \Zend\Http\PhpEnvironment\Request::fromString(<<<EOS
 POST /index.php HTTP/1.1\r
 Content-Type: application/json\r
@@ -109,17 +110,23 @@ EOS
         
         $credentials = $this->getTestCredentials();
         
-        $maxLoginFailures = Tinebase_Config::getInstance()->get(Tinebase_Config::MAX_LOGIN_FAILURES, 5);
-        
-        for ($i=0; $i<=$maxLoginFailures; $i++) {
+        for ($i=0; $i <= 3; $i++) {
             $result = Tinebase_Controller::getInstance()->login($credentials['username'], 'foobar', $request);
-            
             $this->assertFalse($result);
         }
         
-        // account must be blocked now
         $result = Tinebase_Controller::getInstance()->login($credentials['username'], $credentials['password'], $request);
-        
-        $this->assertFalse($result);
+        $this->assertFalse($result, 'account must be blocked now');
+
+        // wait for some time (2^4 = 16 +1 seconds)
+        $timeToWait = 17;
+
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+            . ' Waiting for ' . $timeToWait . ' seconds...');
+
+        sleep($timeToWait);
+
+        $result = Tinebase_Controller::getInstance()->login($credentials['username'], $credentials['password'], $request);
+        $this->assertTrue($result, 'account should be unblocked now');
     }
 }
index b0e18b1..eba02bd 100644 (file)
@@ -35,18 +35,6 @@ class Tinebase_Frontend_CliTest extends TestCase
     protected $_userPlugins = array();
     
     /**
-     * Runs the test methods of this class.
-     *
-     * @access public
-     * @static
-     */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Tinebase Cli Tests');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
-
-    /**
      * Sets up the fixture.
      * This method is called before a test is executed.
      *
@@ -140,20 +128,27 @@ class Tinebase_Frontend_CliTest extends TestCase
 
     /**
      * test purge deleted records
+     *
+     * @see 0010249: Tinebase.purgeDeletedRecords fails
      */
     public function testPurgeDeletedRecordsAllTables()
     {
         $opts = $this->_getOpts();
         $deletedContact = $this->_addAndDeleteContact();
         $deletedLead = $this->_addAndDeleteLead();
+
+        // delete personal adb container and tag, too
+        Tinebase_Container::getInstance()->deleteContainer($this->_getPersonalContainer('Addressbook')->getId());
+        Tinebase_Tags::getInstance()->deleteTags($deletedContact->tags->getFirstRecord()->getId());
         
         ob_start();
         $this->_cli->purgeDeletedRecords($opts);
         $out = ob_get_clean();
-        
+
         $this->assertContains('Removing all deleted entries before', $out);
         $this->assertContains('Cleared table addressbook (deleted ', $out);
         $this->assertContains('Cleared table metacrm_lead (deleted ', $out);
+        $this->assertNotContains('Failed to purge', $out);
 
         $contactBackend = Addressbook_Backend_Factory::factory(Addressbook_Backend_Factory::SQL);
         $contacts = $contactBackend->getMultipleByProperty($deletedContact->getId(), 'id', TRUE);
@@ -173,12 +168,13 @@ class Tinebase_Frontend_CliTest extends TestCase
     {
         $newContact = new Addressbook_Model_Contact(array(
             'n_family'          => 'PHPUNIT',
-            'container_id'      => Addressbook_Controller_Contact::getInstance()->getDefaultAddressbook()->getId(),
+            'container_id'      => $this->_getPersonalContainer('Addressbook')->getId(),
             'tel_cell_private'  => '+49TELCELLPRIVATE',
+            'tags'              => array(array('name' => 'temptag')),
         ));
         $newContact = Addressbook_Controller_Contact::getInstance()->create($newContact);
         Addressbook_Controller_Contact::getInstance()->delete($newContact->getId());
-        
+
         return $newContact;
     }
 
@@ -211,7 +207,8 @@ class Tinebase_Frontend_CliTest extends TestCase
         $opts = new Zend_Console_Getopt('abp:');
         $opts->setArguments(array());
         $this->_usernamesToDelete[] = 'cronuser';
-        
+        $this->_releaseDBLockIds[] = 'Tinebase_Frontend_Cli::triggerAsyncEvents';
+
         ob_start();
         $this->_cli->triggerAsyncEvents($opts);
         $out = ob_get_clean();
@@ -220,6 +217,7 @@ class Tinebase_Frontend_CliTest extends TestCase
         $this->assertEquals(0, count($userPlugins));
         
         $cronuserId = Tinebase_Config::getInstance()->get(Tinebase_Config::CRONUSERID);
+        $this->assertTrue(! empty($cronuserId), 'got empty cronuser id');
         $cronuser = Tinebase_User::getInstance()->getFullUserById($cronuserId);
         $this->assertEquals('cronuser', $cronuser->accountLoginName);
         $adminGroup = Tinebase_Group::getInstance()->getDefaultAdminGroup();
index 02f1743..fb5c32b 100644 (file)
@@ -27,7 +27,7 @@ class Tinebase_Frontend_HttpTest extends PHPUnit_Framework_TestCase
     {
         $this->_uit = new Tinebase_Frontend_Http;
     }
-    
+
     public function testMainScreen()
     {
         if (version_compare(PHPUnit_Runner_Version::id(), '3.3.0', '<')) {
index 773ada9..59e3988 100644 (file)
@@ -6,7 +6,7 @@
  * @subpackage  Json
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schüle <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  */
 
@@ -230,7 +230,18 @@ class Tinebase_Frontend_JsonTest extends TestCase
         $noteTypes = $this->_instance->getNoteTypes();
         $this->assertTrue($noteTypes['totalcount'] >= 5);
     }
-    
+
+    /**
+     * toogle advanced search preference
+     */
+    public function testAdvancedSearchToogle()
+    {
+        $toogle = $this->_instance->toogleAdvancedSearch(1);
+
+        $this->assertEquals($toogle, 1);
+        $this->assertEquals(Tinebase_Core::getPreference()->getValue(Tinebase_Preference::ADVANCED_SEARCH, 0), 1);
+    }
+
     /**
      * search preferences by application
      *
@@ -652,6 +663,27 @@ class Tinebase_Frontend_JsonTest extends TestCase
         Tinebase_Controller::getInstance()->initUser($this->_originalTestUser, /* $fixCookieHeader = */ false);
     }
     
+    /**
+     * testOmitPersonalTagsOnSearch
+     * 
+     * @see 0010732: add "use personal tags" right to all applications
+     */
+    public function testOmitPersonalTagsOnSearch()
+    {
+        $personalTag = $this->_getTag(Tinebase_Model_Tag::TYPE_PERSONAL);
+        Tinebase_Tags::getInstance()->createTag($personalTag);
+        
+        $this->_removeRoleRight('Addressbook', Tinebase_Acl_Rights::USE_PERSONAL_TAGS);
+        $filter = array(
+            'application' => 'Addressbook',
+            'grant' => Tinebase_Model_TagRight::VIEW_RIGHT,
+            'type' => Tinebase_Model_Tag::TYPE_PERSONAL
+        );
+        $result = $this->_instance->searchTags($filter, array());
+        
+        $this->assertEquals(0, $result['totalCount']);
+    }
+    
     /******************** protected helper funcs ************************/
     
     /**
diff --git a/tests/tine20/Tinebase/LockTest.php b/tests/tine20/Tinebase/LockTest.php
new file mode 100644 (file)
index 0000000..f330614
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tinebase
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
+ */
+
+/**
+ * Test class for Tinebase_Lock
+ */
+class Tinebase_LockTest extends TestCase
+{
+    protected $_testLockId = 'testlockId';
+    
+    /**
+     * tear down tests
+     */
+    protected function tearDown()
+    {
+        parent::tearDown();
+
+        Tinebase_Lock::releaseDBSessionLock($this->_testLockId);
+    }
+
+        /**
+     * Test create a lock
+     */
+    public function testLock()
+    {
+        $aquireLock1 = Tinebase_Lock::aquireDBSessionLock($this->_testLockId);
+
+        $this->assertTrue($aquireLock1, 'lock should be available');
+
+        $aquireLock2 = Tinebase_Lock::aquireDBSessionLock($this->_testLockId);
+
+        $this->assertFalse($aquireLock2, 'lock should not be available');
+    }
+
+    /**
+     * test lock release
+     */
+    public function testReleaseLock()
+    {
+        $this->testLock();
+
+        Tinebase_Lock::releaseDBSessionLock($this->_testLockId);
+
+        $aquireLock = Tinebase_Lock::aquireDBSessionLock($this->_testLockId);
+
+        $this->assertTrue($aquireLock, 'lock should be available again');
+    }
+}
index 5ee9785..d32c6eb 100644 (file)
@@ -259,7 +259,37 @@ class Tinebase_PreferenceTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(Tinebase_Model_Preference::TYPE_FORCED, $pref->type);
         $this->assertEquals(0, $pref->value);
     }
-    
+
+    /**
+     * testSetLockedPref
+     *
+     * @see 0011178: allow to lock preferences for individual users
+     */
+    public function testSetLockedPref()
+    {
+        $tzPref = $this->_instance->getApplicationPreferenceDefaults(Tinebase_Preference::TIMEZONE);
+        $tzPref->type = Tinebase_Model_Preference::TYPE_USER;
+        $tzPref->locked = true;
+        $tzPref->id = null;
+        $tzPref->account_id = Tinebase_Core::getUser()->getId();
+        $tzPref->account_type = Tinebase_Acl_Rights::ACCOUNT_TYPE_USER;
+        $tzPref = $this->_instance->create($tzPref);
+
+        $result = $this->_instance->search();
+        $pref = $result->filter('name', Tinebase_Preference::TIMEZONE)->getFirstRecord();
+
+        $this->assertTrue($pref !== null);
+        $this->assertEquals(Tinebase_Model_Preference::TYPE_USER, $pref->type);
+        $this->assertEquals(true, $pref->locked);
+
+        try {
+            $this->_instance->setValue(Tinebase_Preference::TIMEZONE, 'Europe/Berlin');
+            $this->fail('it is not allowed to set locked preference: ' . print_r($tzPref->toArray(), true));
+        } catch (Tinebase_Exception_AccessDenied $tead) {
+            $this->assertEquals('You are not allowed to change the locked preference.', $tead->getMessage());
+        }
+    }
+
     /******************** protected helper funcs ************************/
     
     /**
index d2ef323..97e985a 100644 (file)
@@ -4,7 +4,7 @@
  * 
  * @package     Tinebase
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Michael Spahn <m.spahn@metaways.de>
  */
 
@@ -38,7 +38,7 @@ class Tinebase_ScheduledImportTest extends TestCase
     /**
      * Test create a scheduled import
      */
-    public function createScheduledImport()
+    public function createScheduledImport($source = 'http://localhost/test.ics')
     {
         $id = Tinebase_Record_Abstract::generateUID();
         $import = new Tinebase_Model_Import(
@@ -50,7 +50,7 @@ class Tinebase_ScheduledImportTest extends TestCase
                 'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
                 'container_id'      => $this->_testCalendar->getId(),
                 'sourcetype'        => Tinebase_Model_Import::SOURCETYPE_REMOTE,
-                'source'            => 'http://www.schulferien.org/iCal/Ferien/icals/Ferien_Hamburg_2014.ics',
+                'source'            => $source,
                 'options'           => json_encode(array(
                     'forceUpdateExisting' => TRUE,
                     'import_defintion' => NULL,
@@ -62,15 +62,28 @@ class Tinebase_ScheduledImportTest extends TestCase
         $record = $this->_uit->create($import);
 
         $this->assertEquals(Calendar_Controller::getInstance()->getDefaultModel(), $this->_uit->get($id)->model);
-        
+
         return $record;
     }
     
     /**
      * testNextScheduledImport
+     *
+     * TODO this should run without the need for internet access to http://www.schulferien.org
+     *      maybe we should put the file into the local filesystem
      */
     public function testNextScheduledImport()
     {
+        $this->markTestSkipped('FIXME: use local ics file for this test / see TODO in doc block');
+
+        $icsUri = 'http://www.schulferien.org/iCal/Ferien/icals/Ferien_Hamburg_2014.ics';
+        $client = new Zend_Http_Client($icsUri);
+        try {
+            $client->request()->getBody();
+        } catch (Exception $e) {
+            $this->markTestSkipped('no access to ' . $icsUri);
+        }
+
         $cc = Calendar_Controller_Event::getInstance();
         $filter = new Calendar_Model_EventFilter(array(
             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()),
@@ -82,12 +95,14 @@ class Tinebase_ScheduledImportTest extends TestCase
         
         $now = Tinebase_DateTime::now()->subHour(1);
         
-        $record = $this->createScheduledImport();
+        $record = $this->createScheduledImport($icsUri);
         
         // assert setting timestamp to start value
         $this->assertEquals($now->format('YMDHi'), $record->timestamp->format('YMDHi'));
         
         $record = $this->_uit->runNextScheduledImport();
+
+        $this->assertTrue($record !== null, 'import did not run');
         
         // assert updating timestamp after successful run
         $now->addHour(1);
@@ -99,7 +114,7 @@ class Tinebase_ScheduledImportTest extends TestCase
         $this->assertEquals(7, $all->count());
         
         // this must not be run, the interval is not exceed
-        $ret = $this->_uit->runNextScheduledImport();
+        $this->_uit->runNextScheduledImport();
         $all = $cc->search($filter);
         $this->assertEquals($seq, $all->getFirstRecord()->seq);
         
@@ -108,8 +123,42 @@ class Tinebase_ScheduledImportTest extends TestCase
         
         $this->_uit->update($record);
         
-        $ret = $this->_uit->runNextScheduledImport();
+        $this->_uit->runNextScheduledImport();
         $all = $cc->search($filter);
         $this->assertEquals(7, $all->count());
     }
+
+    /**
+     * @see 0011342: ics-scheduled import only imports 1 remote calendar
+     */
+    public function testMultipleScheduledImports()
+    {
+        // add two imports and check if they are both executed
+        $import1 = $this->createScheduledImport();
+        sleep(1); // make sure first one is found first
+        $import2 = $this->createScheduledImport();
+
+        $importRun1 = $this->_uit->runNextScheduledImport();
+        $this->assertEquals($import1->getId(), $importRun1['id'], print_r($importRun1, true));
+        $this->assertGreaterThanOrEqual($importRun1['timestamp'], Tinebase_DateTime::now()->toString());
+
+        $importRun2 = $this->_uit->runNextScheduledImport();
+        $this->assertEquals($import2->getId(), $importRun2['id'], 'second import not run: ' . print_r($importRun1, true));
+        $this->assertGreaterThanOrEqual($importRun2['timestamp'], Tinebase_DateTime::now()->toString());
+    }
+
+    /**
+     * @see 0011342: ics-scheduled import only imports 1 remote calendar
+     */
+    public function testNextScheduledImportFilter()
+    {
+        $record = $this->createScheduledImport();
+        $record->timestamp = $record->timestamp->addHour(2);
+        $this->_uit->update($record);
+
+        $filter = $this->_uit->getScheduledImportFilter();
+        $result = $this->_uit->search($filter);
+
+        $this->assertEquals(0, count($result), 'no imports should be found: ' . print_r($result->toArray(), true));
+    }
 }
index a5f865c..9a3f770 100644 (file)
@@ -31,29 +31,20 @@ class Tinebase_User_ActiveDirectoryTest extends PHPUnit_Framework_TestCase
     protected $userBaseFilter  = 'objectclass=user';
     
     /**
-     * Sets up the fixture.
-     * This method is called before a test is executed.
+     * try to add a group
      *
-     * @access protected
      */
-    protected function setUp()
+    public function testAddUserToSyncBackend()
     {
         $this->markTestIncomplete('group backend breaks mocking');
-        
+
         $this->_userAD = new Tinebase_User_ActiveDirectory(array(
             'userDn'   => $this->userDN,
             'groupsDn' => $this->groupsDN,
             'ldap'     => $this->_getTinebaseLdapStub(),
             'useRfc2307' => true
-        )); 
-    }
+        ));
 
-    /**
-     * try to add a group
-     *
-     */
-    public function testAddUserToSyncBackend()
-    {
         $addedUser = $this->_userAD->addUserToSyncBackend(new Tinebase_Model_FullUser(array(
             'accountLoginName'    => 'larskneschke',
             'accountPrimaryGroup' => $this->groupSid,
@@ -94,7 +85,7 @@ class Tinebase_User_ActiveDirectoryTest extends PHPUnit_Framework_TestCase
     {
         switch ($dn) {
             default:
-                $this->fail("unkown dn $filter in " . __METHOD__);
+                $this->fail("unkown dn $dn in " . __METHOD__);
                 
                 break;
         }
@@ -143,4 +134,27 @@ class Tinebase_User_ActiveDirectoryTest extends PHPUnit_Framework_TestCase
         
         return $stub;
     }
+
+    /**
+     * testConvertADTimestamp
+     *
+     * @see 0011074: Active Directory as User Backend
+     */
+    public function testConvertADTimestamp()
+    {
+        $timestamps = array(
+            '130764553441237094'  => '2015-05-18 20:42:24',
+            '130791798699200155'  => '2015-06-19 09:31:09',
+            '9223372036854775807' => '30828-09-14 02:48:05',
+        );
+
+        foreach ($timestamps as $timestamp => $expected) {
+            $this->assertEquals($expected, Tinebase_User_ActiveDirectory::convertADTimestamp($timestamp)->toString());
+        }
+
+        // sometimes the ad timestamp is a float. i could not reproduce this case
+        // let's create a value like this and pass it directly to Tinebase_DateTime
+        $date = new Tinebase_DateTime('1391776840.7434058000');
+        $this->assertEquals('2014-02-07 12:40:40', $date->toString());
+    }
 }
index 1672e19..07a312c 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Account
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2008 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2008-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
  */
 require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
 
-if (!defined('PHPUnit_MAIN_METHOD')) {
-    define('PHPUnit_MAIN_METHOD', 'Tinebase_User_SqlTest::main');
-}
-
 /**
  * Test class for Tinebase_User
  */
-class Tinebase_User_SqlTest extends PHPUnit_Framework_TestCase
+class Tinebase_User_SqlTest extends TestCase
 {
     /**
      * sql user backend
@@ -36,18 +32,6 @@ class Tinebase_User_SqlTest extends PHPUnit_Framework_TestCase
     protected $objects = array();
 
     /**
-     * Runs the test methods of this class.
-     *
-     * @access public
-     * @static
-     */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tinebase_User_SqlTest');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
-
-    /**
      * Sets up the fixture.
      * This method is called before a test is executed.
      *
@@ -61,30 +45,17 @@ class Tinebase_User_SqlTest extends PHPUnit_Framework_TestCase
         
         $this->_backend = Tinebase_User::factory(Tinebase_User::SQL);
 
-        // remove user left over by broken tests
-        try {
-            $user = $this->_backend->getUserByLoginName('tine20phpunituser', 'Tinebase_Model_FullUser');
-            $this->_backend->deleteUser($user);
-        } catch (Tinebase_Exception_NotFound $tenf) {
-            // do nothing 
-        }
-        
-        $this->objects['users'] = array();
+        parent::setUp();
     }
 
-    /**
-     * Tears down the fixture
-     * This method is called after a test is executed.
-     *
-     * @access protected
-     */
     protected function tearDown()
     {
-        foreach ($this->objects['users'] as $user) {
-            $this->_backend->deleteUser($user);
-        }
+        parent::tearDown();
+
+        Tinebase_Config::getInstance()->set(Tinebase_Config::ACCOUNT_DELETION_EVENTCONFIGURATION, new Tinebase_Config_Struct(array(
+        )));
     }
-    
+
     /**
      * try to add an account
      *
@@ -331,7 +302,65 @@ class Tinebase_User_SqlTest extends PHPUnit_Framework_TestCase
         
         $this->_backend->getUserById($testUser, 'Tinebase_Model_FullUser');
     }
-    
+
+    /**
+     * test if deleted users data is removed
+     *
+     * @see TODO add mantis issue
+     *
+     * TODO add test cases for keepOrganizerEvents and $_keepAsContact and $_keepAsContact
+     */
+    public function testDeleteUsersData()
+    {
+        // configure removal of data
+        Tinebase_Config::getInstance()->set(Tinebase_Config::ACCOUNT_DELETION_EVENTCONFIGURATION, new Tinebase_Config_Struct(array(
+            '_deletePersonalContainers' => true,
+
+        )));
+
+        // we need a valid group and a contact for this test
+        $userContact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array(
+            'n_given' => 'testuser'
+        )));
+        $testUser = $this->getTestRecord();
+        $testUser->contact_id = $userContact->getId();
+        $this->_backend->addUser($testUser);
+        Tinebase_Group::getInstance()->addGroupMember($testUser->accountPrimaryGroup, $testUser->getId());
+
+        $this->_setUser($testUser);
+
+        // add a contact and an event to personal folders
+        $event = Calendar_Controller_Event::getInstance()->create(new Calendar_Model_Event(array(
+            'summary' => 'testevent',
+            'dtstart' => '2015-12-24 12:00:00',
+            'dtend' => '2015-12-24 13:00:00',
+//            'organizer' => $testUser->conta
+        ), true));
+        $contact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array(
+            'n_given' => 'testcontact'
+        )));
+
+        $this->_setUser($this->_originalTestUser);
+
+        $this->_backend->deleteUser($testUser);
+
+        // check if contact and event are removed
+        $adbBackend = new Addressbook_Backend_Sql();
+        try {
+            $adbBackend->get($contact->getId());
+            $this->fail('contact be deleted');
+        } catch (Exception $e) {
+            $this->assertTrue($e instanceof Tinebase_Exception_NotFound);
+        }
+        $calBackend = new Calendar_Backend_Sql();
+        try {
+            $calBackend->get($event->getId());
+            $this->fail('event should be deleted: ' . print_r($event->toArray(), true));
+        } catch (Exception $e) {
+            $this->assertTrue($e instanceof Tinebase_Exception_NotFound);
+        }
+    }
+
     public function testSanitizeAccountPrimaryGroupId()
     {
         $account = Tinebase_Core::get('currentAccount');
index 44aa786..2eeea42 100644 (file)
@@ -52,24 +52,45 @@ class Tinebase_WebDav_Plugin_SyncTokenTest extends Tinebase_WebDav_Plugin_Abstra
     public function tearDown()
     {
         parent::tearDown();
-
-        Tinebase_Container::getInstance()->getContentBackend()->delete($this->objects['containerContent']);
     }
 
     protected function setupCalendarContent()
     {
-        /**
-         * @var Tinebase_Backend_Sql
-         */
-        $contentBackend = Tinebase_Container::getInstance()->getContentBackend();
-        $this->objects['containerContent'] = new Tinebase_Model_ContainerContent(array(
-            'action'          => Tinebase_Model_ContainerContent::ACTION_CREATE,
-            'record_id'       => 'testRecordId',
-            'time'            => Tinebase_DateTime::now(),
-            'container_id'    => $this->objects['initialContainer']->id,
-            'content_seq'     => 1,
+        $eventController = Calendar_Controller_Event::getInstance();
+        $event = new Calendar_Model_Event(array(
+            'uid'           => Tinebase_Record_Abstract::generateUID(),
+            'container_id'  => $this->objects['initialContainer']->id,
+            'summary'       => 'change socks',
+            'dtstart'       => '1979-06-05 07:55:00',
+            'dtend'         => '1979-06-05 08:00:00',
+            'rrule'         => 'FREQ=DAILY;INTERVAL=2;UNTIL=2009-04-01 08:00:00',
+            'exdate'        => '2009-03-31 07:00:00',
+            'originator_tz' => 'Europe/Berlin',
+            'rrule_until'   => '2009-04-01 08:00:00',
+            Tinebase_Model_Grants::GRANT_EDIT     => true,
+        ));
+        $eventController->create($event);
+
+        $event = new Calendar_Model_Event(array(
+            'uid'           => Tinebase_Record_Abstract::generateUID(),
+            'container_id'  => $this->objects['initialContainer']->id,
+            'summary'       => 'change t-shirt',
+            'dtstart'       => '1979-06-05 08:00:00',
+            'dtend'         => '1979-06-05 08:05:00',
+            'rrule'         => 'FREQ=DAILY;INTERVAL=2;UNTIL=2009-04-01 08:00:00',
+            'exdate'        => '2009-03-31 07:00:00',
+            'originator_tz' => 'Europe/Berlin',
+            'rrule_until'   => '2009-04-01 08:00:00',
+            Tinebase_Model_Grants::GRANT_EDIT     => true,
         ));
-        $contentBackend->create($this->objects['containerContent']);
+
+        $persistentEvent = $eventController->create($event);
+
+        $exception = clone $persistentEvent;
+        $exception->summary = 'use blue t-shirt today';
+
+        $eventController->createRecurException($exception);
+
     }
 
     /**
@@ -104,7 +125,7 @@ class Tinebase_WebDav_Plugin_SyncTokenTest extends Tinebase_WebDav_Plugin_Abstra
         $this->server->httpRequest = $request;
         $this->server->exec();
         $this->assertEquals('HTTP/1.1 207 Multi-Status', $this->response->status);
-        $this->assertContains('<d:sync-token>http://tine20.net/ns/sync/1</d:sync-token></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response></d:multistatus>', $this->response->body);
+        $this->assertContains('<d:sync-token>http://tine20.net/ns/sync/5</d:sync-token></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat></d:response></d:multistatus>', $this->response->body);
     }
 
     /**
@@ -133,6 +154,9 @@ class Tinebase_WebDav_Plugin_SyncTokenTest extends Tinebase_WebDav_Plugin_Abstra
         $this->server->exec();
         
         $this->assertEquals('HTTP/1.1 207 Multi-Status', $this->response->status);
-        $this->assertContains('<d:sync-token>http://tine20.net/ns/sync/1</d:sync-token></d:multistatus>', $this->response->body);
+        $this->assertContains('<d:sync-token>http://tine20.net/ns/sync/5</d:sync-token></d:multistatus>', $this->response->body);
+
+        //check that we only got 2 responses, so no reoccuring events!
+        $this->assertEquals(2, preg_match_all('/<d:response>/', $this->response->body, $m));
     }
 }
\ No newline at end of file
index 0f5fd42..94b2db7 100644 (file)
@@ -57,6 +57,7 @@ class ActiveSync_Config extends Tinebase_Config_Abstract
             'clientRegistryInclude' => FALSE,
             'setByAdminModule'      => TRUE,
             'setBySetupModule'      => FALSE,
+            'default'               => null,
         ),
         self::DISABLE_ACCESS_LOG => array(
         //_('Disable Access Log')
@@ -75,7 +76,7 @@ class ActiveSync_Config extends Tinebase_Config_Abstract
         //_('For how long in the past the emails should be synchronized.')
             'description'           => 'For how long in the past the emails should be synchronized.',
             'type'                  => Tinebase_Config_Abstract::TYPE_INT,
-            // @todo options is not used yet (only for TYPE_KEYFIELD configs),
+            // @todo options is not used yet (only for TYPE_KEYFIELD_CONFIG configs),
             //  but this is helpful to see which values are possible here
             'options'               => array(
                 Syncroton_Command_Sync::FILTER_NOTHING,
@@ -98,7 +99,7 @@ class ActiveSync_Config extends Tinebase_Config_Abstract
         //_('For how long in the past the events should be synchronized.')
             'description'           => 'For how long in the past the events should be synchronized.',
             'type'                  => Tinebase_Config_Abstract::TYPE_INT,
-            // @todo options is not used yet (only for TYPE_KEYFIELD configs),
+            // @todo options is not used yet (only for TYPE_KEYFIELD_CONFIG configs),
             //  but this is helpful to see which values are possible here
             'options'               => array(
                 Syncroton_Command_Sync::FILTER_6_MONTHS_BACK,
index 160df5c..2e1f808 100644 (file)
@@ -5,8 +5,8 @@
  * @package     Addressbook
  * @subpackage  Acl
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
- * @author      Philipp Schuele <p.schuele@metaways.de>
+ * @copyright   Copyright (c) 2009-2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Philipp Schüle <p.schuele@metaways.de>
  * 
  */
 
@@ -84,6 +84,7 @@ class Addressbook_Acl_Rights extends Tinebase_Acl_Rights_Abstract
         
         $addRights = array(
             Tinebase_Acl_Rights::MANAGE_SHARED_FOLDERS,
+            Tinebase_Acl_Rights::USE_PERSONAL_TAGS,
             self::MANAGE_SHARED_CONTACT_FAVORITES,
         );
         $allRights = array_merge($allRights, $addRights);
@@ -114,5 +115,4 @@ class Addressbook_Acl_Rights extends Tinebase_Acl_Rights_Abstract
         $rightDescriptions = array_merge($rightDescriptions, parent::getTranslatedRightDescriptions());
         return $rightDescriptions;
     }
-
 }
index 9353b3f..f45cc2a 100644 (file)
@@ -63,7 +63,14 @@ class Addressbook_Config extends Tinebase_Config_Abstract
             'type'                  => 'keyFieldConfig',
             'options'               => array('recordModel' => 'Addressbook_Model_Salutation'),
             'clientRegistryInclude' => TRUE,
-        //            'default'               => 'MR'
+            'default'               => array(
+                'records' => array(
+                    array('id' => 'MR',      'value' => 'Mr',      'gender' => Addressbook_Model_Salutation::GENDER_MALE,   'image' => 'images/empty_photo_male.png',    'system' => true), //_('Mr')
+                    array('id' => 'MS',      'value' => 'Ms',      'gender' => Addressbook_Model_Salutation::GENDER_FEMALE, 'image' => 'images/empty_photo_female.png',  'system' => true), //_('Ms')
+                    array('id' => 'COMPANY', 'value' => 'Company', 'gender' => Addressbook_Model_Salutation::GENDER_OTHER,  'image' => 'images/empty_photo_company.png', 'system' => true), //_('Company')
+                ),
+//                'default' => 'MR'
+            )
         ),
         self::CONTACT_ADDRESS_PARSE_RULES_FILE => array(
         //_('Parsing rules for addresses')
index c375a92..bfbb4c3 100644 (file)
@@ -79,9 +79,24 @@ class Addressbook_Controller extends Tinebase_Controller_Event implements Tineba
             case 'Admin_Event_AddAccount':
                 $this->createPersonalFolder($_eventObject->account);
                 break;
-            case 'Admin_Event_DeleteAccount':
-                foreach ($_eventObject->accountIds as $accountId) {
-                    $this->deletePersonalFolder($accountId);
+            case 'Tinebase_Event_User_DeleteAccount':
+                /**
+                 * @var Tinebase_Event_User_DeleteAccount $_eventObject
+                 */
+                if ($_eventObject->deletePersonalContainers()) {
+                    $this->deletePersonalFolder($_eventObject->account);
+                }
+
+                //make to be deleted accounts (user) contact a normal contact
+                if ($_eventObject->keepAsContact()) {
+                    $contact = Addressbook_Controller_Contact::getInstance()->get($_eventObject->account->contact_id);
+                    $contact->type = Addressbook_Model_Contact::CONTACTTYPE_CONTACT;
+                    Addressbook_Controller_Contact::getInstance()->update($contact);
+
+                } else {
+                    //or just delete it
+                    $contactsBackend = Addressbook_Backend_Factory::factory(Addressbook_Backend_Factory::SQL);
+                    $contactsBackend->delete($_eventObject->account->contact_id);
                 }
                 break;
         }
@@ -115,16 +130,6 @@ class Addressbook_Controller extends Tinebase_Controller_Event implements Tineba
         
         return $container;
     }
-    
-    /**
-     * delete all personal user folders and the contacts associated with these folders
-     *
-     * @param Tinebase_Model_User $_account the accountd object
-     * @todo implement and write test
-     */
-    public function deletePersonalFolder($_account)
-    {
-    }
 
     /**
      * returns contact image
index fda16f0..7d37e46 100644 (file)
@@ -69,7 +69,7 @@ abstract class Addressbook_Convert_Contact_VCard_Abstract implements Tinebase_Co
     public function toTine20Model($blob, Tinebase_Record_Abstract $_record = null, $options = array())
     {
         $vcard = self::getVObject($blob);
-        
+
         if ($_record instanceof Addressbook_Model_Contact) {
             $contact = $_record;
         } else {
@@ -77,7 +77,7 @@ abstract class Addressbook_Convert_Contact_VCard_Abstract implements Tinebase_Co
         }
         
         $data = $this->_emptyArray;
-        
+
         foreach ($vcard->children() as $property) {
             switch ($property->name) {
                 case 'VERSION':
@@ -99,7 +99,7 @@ abstract class Addressbook_Convert_Contact_VCard_Abstract implements Tinebase_Co
                     }
                     
                     $parts = $property->getParts();
-                    
+
                     if ($type == 'home') {
                         // home address
                         $data['adr_two_street2']     = $parts[1];
@@ -212,14 +212,14 @@ abstract class Addressbook_Convert_Contact_VCard_Abstract implements Tinebase_Co
                 $data['n_family'] = empty($data['n_fn']) ? 'VCARD (imported)' : $data['n_fn'];
             }
         }
-        
+
         $contact->setFromArray($data);
 
         if (isset($jpegphoto)) {
             $contact->setSmallContactImage($jpegphoto);
         }
         
-        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) 
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
             Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' data ' . print_r($contact->toArray(), true));
         
         if (isset($options[self::OPTION_USE_SERVER_MODLOG]) && $options[self::OPTION_USE_SERVER_MODLOG] === true) {
index d8925f7..844949a 100644 (file)
@@ -25,6 +25,7 @@ class Addressbook_Convert_Contact_VCard_Factory
     const CLIENT_EMCLIENT       = 'emclient';
     const CLIENT_COLLABORATOR   = 'WebDAVCollaborator';
     const CLIENT_AKONADI        = 'akonadi';
+    const CLIENT_TELEFONBUCH    = 'telefonbuch';
     
     /**
      * cache parsed user-agent strings
@@ -80,8 +81,10 @@ class Addressbook_Convert_Contact_VCard_Factory
                 
             case Addressbook_Convert_Contact_VCard_Factory::CLIENT_COLLABORATOR:
                 return new Addressbook_Convert_Contact_VCard_WebDAVCollaborator($_version);
-                
                 break;
+
+            case Addressbook_Convert_Contact_VCard_Factory::CLIENT_TELEFONBUCH:
+                return new Addressbook_Convert_Contact_VCard_Telefonbuch($_version);
         }
     }
     
diff --git a/tine20/Addressbook/Convert/Contact/VCard/Telefonbuch.php b/tine20/Addressbook/Convert/Contact/VCard/Telefonbuch.php
new file mode 100644 (file)
index 0000000..162b487
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Addressbook
+ * @subpackage  Convert
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Michael Spahn <kontakt@michaelspahn.de>
+ * @copyright   Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+/**
+ * class to convert a telefonbuch (http://www.dastelefonbuch.de/) vcard to contact model and back again
+ *
+ * @package     Addressbook
+ * @subpackage  Convert
+ */
+class Addressbook_Convert_Contact_VCard_Telefonbuch extends Addressbook_Convert_Contact_VCard_Abstract
+{
+    protected $_emptyArray = array();
+
+    /**
+     * converts vcard to Addressbook_Model_Contact
+     *
+     * @param  \Sabre\VObject\Component|stream|string  $blob       the vcard to parse
+     * @param  Tinebase_Record_Abstract                $_record    update existing contact
+     * @param  array                                   $options    array of options
+     * @return Addressbook_Model_Contact
+     */
+    public function toTine20Model($blob, Tinebase_Record_Abstract $_record = null, $options = array())
+    {
+        $vcard = self::getVObject($blob);
+
+        if ($_record instanceof Addressbook_Model_Contact) {
+            $contact = $_record;
+        } else {
+            $contact = new Addressbook_Model_Contact(null, false);
+        }
+
+        $data = $this->_emptyArray;
+
+        foreach ($vcard->children() as $property) {
+            switch ($property->name) {
+                case 'VERSION':
+                case 'PRODID':
+                case 'UID':
+                    // do nothing
+                    break;
+
+                case 'ADR':
+                    $parts = $property->getParts();
+
+                    // work address
+                    $data['adr_one_street2']     = $parts[1];
+                    $data['adr_one_street']      = $parts[2];
+                    $data['adr_one_locality']    = $parts[3];
+                    $data['adr_one_region']      = $parts[4];
+                    $data['adr_one_postalcode']  = $parts[5];
+                    $data['adr_one_countryname'] = $parts[6];
+                    break;
+
+                case 'CATEGORIES':
+                    $tags = Tinebase_Model_Tag::resolveTagNameToTag($property->getParts(), 'Addressbook');
+                    if (! isset($data['tags'])) {
+                        $data['tags'] = $tags;
+                    } else {
+                        $data['tags']->merge($tags);
+                    }
+                    break;
+
+                case 'EMAIL':
+                    $this->_toTine20ModelParseEmail($data, $property, $vcard);
+                    break;
+
+                case 'FN':
+                    $data['n_fn'] = $property->getValue();
+                    $data['n_given'] = $data['n_fn'];
+                    break;
+
+                case 'N':
+                    $parts = $property->getParts();
+
+                    $data['n_family'] = $parts[0];
+                    $data['n_middle'] = isset($parts[2]) ? $parts[2] : null;
+                    $data['n_prefix'] = isset($parts[3]) ? $parts[3] : null;
+                    $data['n_suffix'] = isset($parts[4]) ? $parts[4] : null;
+                    break;
+
+                case 'NOTE':
+                    $data['note'] = $property->getValue();
+                    break;
+
+                case 'ORG':
+                    $parts = $property->getParts();
+
+                    $data['org_name'] = $parts[0];
+                    $data['org_unit'] = isset($parts[1]) ? $parts[1] : null;
+                    break;
+
+                case 'PHOTO':
+                    $data['jpegphoto'] = $property->getValue();
+                    break;
+
+                case 'TEL':
+                    $this->_toTine20ModelParseTel($data, $property);
+                    break;
+
+                case 'URL':
+                    switch (strtoupper($property['TYPE'])) {
+                        case 'HOME':
+                            $data['url_home'] = $property->getValue();
+                            break;
+
+                        case 'WORK':
+                        default:
+                            $data['url'] = $property->getValue();
+                            break;
+                    }
+                    break;
+
+                case 'TITLE':
+                    $data['title'] = $property->getValue();
+                    break;
+
+                case 'BDAY':
+                    $this->_toTine20ModelParseBday($data, $property);
+                    break;
+
+                default:
+                    if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
+                        Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' cardData ' . $property->name);
+                    break;
+            }
+        }
+
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
+            Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' data ' . print_r($data, true));
+
+        // Some email clients will only set a contact with FN (formatted name) without surname
+        if (empty($data['n_family']) && empty($data['org_name']) && (!empty($data['n_fn']))) {
+            if (strpos($data['n_fn'], ",") > 0) {
+                list($lastname, $firstname) = explode(",", $data['n_fn'], 2);
+                $data['n_family'] = trim($lastname);
+                $data['n_given']  = trim($firstname);
+
+            } elseif (strpos($data['n_fn'], " ") > 0) {
+                list($firstname, $lastname) = explode(" ", $data['n_fn'], 2);
+                $data['n_family'] = trim($lastname);
+                $data['n_given']  = trim($firstname);
+
+            } else {
+                $data['n_family'] = $data['n_fn'];
+            }
+        }
+
+        $contact->setFromArray($data);
+
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
+            Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' data ' . print_r($contact->toArray(), true));
+
+        if (isset($options[self::OPTION_USE_SERVER_MODLOG]) && $options[self::OPTION_USE_SERVER_MODLOG] === true) {
+            $contact->creation_time = $_record->creation_time;
+            $contact->last_modified_time = $_record->last_modified_time;
+            $contact->seq = $_record->seq;
+        }
+
+        return $contact;
+    }
+}
diff --git a/tine20/Addressbook/Convert/Contact/config/convert_from_string_improved.xml b/tine20/Addressbook/Convert/Contact/config/convert_from_string_improved.xml
new file mode 100644 (file)
index 0000000..b241e9e
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<config>
+    <rules>
+        <rule>
+            <field>n_family</field>
+            <regex>/^(\w+)\s\w+/um</regex>
+        </rule>
+        <rule>
+            <field>n_given</field>
+            <regex>/^\w+\s(\w+)/um</regex>
+        </rule>
+        <rule>
+            <field>n_prefix</field>
+            <regex>/([Dr\.|Prof\.][\w+{2-4}\.]+)/um</regex>
+        </rule>
+        <rule>
+            <field>org_name</field>
+            <regex>/^\w+\s\w+\s[\w+{2-4}\.]+\s(\w+)/u</regex>
+        </rule>
+        <rule>
+            <field>adr_one_postalcode</field>
+            <regex>/\s*(\d{5})\s+[\w]+/um</regex>
+        </rule>
+        <rule>
+            <field>adr_one_postalcode</field>
+            <regex>/[A-Z]{1,3}-(\d{5})\s+[\w ]+/um</regex>
+        </rule>
+        <rule>
+            <field>adr_one_locality</field>
+            <regex>/\d{5}\s+([\w ]+)/um</regex>
+        </rule>
+        <rule>
+            <field>adr_one_street</field>
+            <regex>/^([\w ]+\s+\d+)/um</regex>
+        </rule>
+        <rule>
+            <field>org_name</field>
+            <regex>/(^.*(?:GmbH|GbR|OHG|KG).*)/um</regex>
+        </rule>
+        <rule>
+            <field>tel_work</field>
+            <regex>/^(?:Tel[^+^0-9]*)(\+{0,1}[0-9\ \t()-]+)/um</regex>
+        </rule>
+        <rule>
+            <field>tel_fax</field>
+            <regex>/^(?:Fax[^+^0-9]*)(\+{0,1}[0-9\ \t()-]+)/um</regex>
+        </rule>
+        <rule>
+            <field>email</field>
+            <regex>/([a-z0-9_\+-\.]+@[a-z0-9-\.]+\.[a-z]{2,5})/i</regex>
+        </rule>
+        <rule>
+            <field>url</field>
+            <regex>/((mailto\:|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/i</regex>
+        </rule>
+    </rules>
+</config>
index 370dac4..8ad0367 100755 (executable)
@@ -6,7 +6,7 @@
  * @subpackage  Frontend
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Lars Kneschke <l.kneschke@metaways.de>
- * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  */
 
 /**
@@ -35,13 +35,17 @@ class Addressbook_Frontend_Http extends Tinebase_Frontend_Http_Abstract
     public function exportContacts($filter, $options)
     {
         $decodedFilter = Zend_Json::decode($filter);
-        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Export filter: ' . print_r($decodedFilter, TRUE));
+        $decodedOptions = Zend_Json::decode($options);
+        
+        if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
+            . ' Export filter: ' . print_r($decodedFilter, true)
+            . ' Options: ' . print_r($decodedOptions, true));
         
         if (! is_array($decodedFilter)) {
             $decodedFilter = array(array('field' => 'id', 'operator' => 'equals', 'value' => $decodedFilter));
         }
         
         $filter = new Addressbook_Model_ContactFilter($decodedFilter);
-        parent::_export($filter, Zend_Json::decode($options), Addressbook_Controller_Contact::getInstance());
+        parent::_export($filter, $decodedOptions, Addressbook_Controller_Contact::getInstance());
     }
 }
index 8770eea..0323f80 100755 (executable)
@@ -25,7 +25,9 @@ class Addressbook_Frontend_Json extends Tinebase_Frontend_Json_Abstract
      * @var string
      */
     protected $_applicationName = 'Addressbook';
-    
+
+    protected $_defaultImportDefinitionName = 'adb_tine_import_csv';
+
     /**
      * resolve images
      * @param Tinebase_Record_RecordSet $_records
@@ -158,16 +160,45 @@ class Addressbook_Frontend_Json extends Tinebase_Frontend_Json_Abstract
     */
     public function parseAddressData($address)
     {
-        $result = Addressbook_Controller_Contact::getInstance()->parseAddressData($address);
-        $contactData = $this->_recordToJson($result['contact']);
-        
-        unset($contactData['jpegphoto']);
-        unset($contactData['salutation']);
-        
-        return array(
-            'contact'             => $contactData,
-            'unrecognizedTokens'  => $result['unrecognizedTokens'],
-        );
+        if (preg_match('/^http/', $address)) {
+            $vcard = file_get_contents($address);
+
+            // Could not load file from remote
+            if ($vcard === false) {
+                return array('exceptions' => "Cannot get file from remote.");
+            }
+
+            $converter = Addressbook_Convert_Contact_VCard_Factory::factory(
+                strpos($address, 'dastelefonbuch')
+                ? Addressbook_Convert_Contact_VCard_Factory::CLIENT_TELEFONBUCH
+                : Addressbook_Convert_Contact_VCard_Factory::CLIENT_GENERIC
+            );
+
+            $record = $converter->toTine20Model($vcard);
+            $contactData = $this->_recordToJson($record);
+
+            if (array_key_exists('jpegphoto', $contactData)) {
+                unset($contactData['jpegphoto']);
+            }
+
+            return array('contact' => $contactData);
+        } else {
+            $result = Addressbook_Controller_Contact::getInstance()->parseAddressData($address);
+            $contactData = $this->_recordToJson($result['contact']);
+
+            if (array_key_exists('jpegphoto', $contactData)) {
+                unset($contactData['jpegphoto']);
+            }
+
+            if (array_key_exists('salutation', $contactData)) {
+                unset($contactData['salutation']);
+            }
+
+            return array(
+                'contact' => $contactData,
+                'unrecognizedTokens' => $result['unrecognizedTokens'],
+            );
+        }
     }
     
     /**
@@ -242,68 +273,19 @@ class Addressbook_Frontend_Json extends Tinebase_Frontend_Json_Abstract
     }
 
     /**
-     * Returns registry data of addressbook.
+     * Returns registry data of Addressbook.
      * @see Tinebase_Application_Json_Abstract
      * 
      * @return mixed array 'variable name' => 'data'
      */
     public function getRegistryData()
     {
-        $definitionConverter = new Tinebase_Convert_ImportExportDefinition_Json();
-        $importDefinitions = $this->_getImportDefinitions();
-        $defaultDefinition = $this->_getDefaultImportDefinition($importDefinitions);
-        
         $registryData = array(
             'defaultAddressbook'        => $this->getDefaultAddressbook(),
-            'defaultImportDefinition'   => $definitionConverter->fromTine20Model($defaultDefinition),
-            'importDefinitions'         => array(
-                'results'               => $definitionConverter->fromTine20RecordSet($importDefinitions),
-                'totalcount'            => count($importDefinitions),
-            ),
         );
+
+        $registryData = array_merge($registryData, $this->_getImportDefinitionRegistryData());
+
         return $registryData;
     }
-    
-    /**
-     * get addressbook import definitions
-     * 
-     * @return Tinebase_Record_RecordSet
-     * 
-     * @todo generalize this
-     */
-    protected function _getImportDefinitions()
-    {
-        $filter = new Tinebase_Model_ImportExportDefinitionFilter(array(
-            array('field' => 'application_id',  'operator' => 'equals', 'value' => Tinebase_Application::getInstance()->getApplicationByName('Addressbook')->getId()),
-            array('field' => 'type',            'operator' => 'equals', 'value' => 'import'),
-        ));
-        
-        $importDefinitions = Tinebase_ImportExportDefinition::getInstance()->search($filter);
-        
-        return $importDefinitions;
-    }
-    
-    /**
-     * get default definition
-     * 
-     * @param Tinebase_Record_RecordSet $_importDefinitions
-     * @return Tinebase_Model_ImportExportDefinition
-     * 
-     * @todo generalize this
-     */
-    protected function _getDefaultImportDefinition($_importDefinitions)
-    {
-        try {
-            $defaultDefinition = Tinebase_ImportExportDefinition::getInstance()->getByName('adb_tine_import_csv');
-        } catch (Tinebase_Exception_NotFound $tenf) {
-            if (count($_importDefinitions) > 0) {
-                $defaultDefinition = $_importDefinitions->getFirstRecord();
-            } else {
-                Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' No import definitions found for Addressbook');
-                $defaultDefinition = NULL;
-            }
-        }
-        
-        return $defaultDefinition;
-    }
 }
index b468cda..adf8a30 100644 (file)
@@ -6,7 +6,7 @@
  * @subpackage  Import
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schuele <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
  */
 
 /**
@@ -28,11 +28,11 @@ class Addressbook_Import_Csv extends Tinebase_Import_Csv_Abstract
     
     /**
      * creates a new importer from an importexport definition
-     * 
+     *
      * @param  Tinebase_Model_ImportExportDefinition $_definition
      * @param  array                                 $_options
      * @return Calendar_Import_Ical
-     * 
+     *
      * @todo move this to abstract when we no longer need to be php 5.2 compatible
      */
     public static function createFromDefinition(Tinebase_Model_ImportExportDefinition $_definition, array $_options = array())