diff --git a/tests/tine20/ActiveSync/TestCase.php b/tests/tine20/ActiveSync/TestCase.php index 37f24c196c0..0e6bb174e0f 100644 --- a/tests/tine20/ActiveSync/TestCase.php +++ b/tests/tine20/ActiveSync/TestCase.php @@ -48,6 +48,7 @@ abstract class ActiveSync_TestCase extends TestCase const TYPE_ANDROID_5 = 'android5'; const TYPE_ANDROID_6 = 'android6'; const TYPE_ANDROID_7 = 'android7'; + const TYPE_IOS_11 = 'iOS11'; /** * (non-PHPdoc) @@ -359,6 +360,20 @@ public static function getTestDevice($_type = null) 'remotewipe' => 0 )); break; + + case self::TYPE_IOS_11: + $device = new Syncroton_Model_Device(array( + 'deviceid' => 'iphone9c1', + 'devicetype' => Syncroton_Model_Device::TYPE_IPHONE, + 'policykey' => null, + 'policyId' => null, + 'ownerId' => Tinebase_Core::getUser()->getId(), + 'useragent' => 'Apple-iPhone9C1/1501.530400009', + 'acsversion' => '12.1', + 'remotewipe' => 0 + )); + break; + case Syncroton_Model_Device::TYPE_IPHONE: default: diff --git a/tests/tine20/Admin/JsonTest.php b/tests/tine20/Admin/JsonTest.php index 01417c8c741..a3b691566f7 100644 --- a/tests/tine20/Admin/JsonTest.php +++ b/tests/tine20/Admin/JsonTest.php @@ -1446,6 +1446,11 @@ public function testResourceContainerGet() 'description' => 'Our main meeting room', 'email' => 'room@example.com', 'is_location' => TRUE, + 'grants' => [[ + 'account_id' => Tinebase_Core::getUser()->getId(), + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER, + Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true, + ]] ])); $container = $this->_json->getContainer($resource->container_id); diff --git a/tests/tine20/Calendar/Controller/EventNotificationsTests.php b/tests/tine20/Calendar/Controller/EventNotificationsTests.php index f26c97e3a40..fcb71b76820 100644 --- a/tests/tine20/Calendar/Controller/EventNotificationsTests.php +++ b/tests/tine20/Calendar/Controller/EventNotificationsTests.php @@ -1391,7 +1391,7 @@ public function testResourceNotificationForGrantedUsers($userIsAttendee = true, if ($suppress_notification) { $this->assertEquals(1, count($messages), 'one mail should be send to current user (attender)'); } else { - $this->assertEquals(3, count($messages), 'four mails should be send to current user (resource + attender + everybody who is allowed to edit this resource)'); + $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)); $this->assertContains('Resource "' . $persistentResource->name . '" was booked', print_r($messages, true)); $this->assertContains('Meeting Room (Required, No response)', print_r($messages, true)); @@ -1457,6 +1457,17 @@ public function testResourceNotificationMuteForEditors() $this->testResourceNotification(/* $suppress_notification = */ true); $this->testResourceNotificationForGrantedUsers(/* $userIsAttendee = */ false, /* $suppress_notification = */ true); } + + /** + * testResourceNotificationMute without editors config + * + */ + public function testResourceNotificationMute() + { + Calendar_Config::getInstance()->set(Calendar_Config::RESOURCE_MAIL_FOR_EDITORS, false); + $this->testResourceNotification(/* $suppress_notification = */ true); + $this->testResourceNotificationForGrantedUsers(/* $userIsAttendee = */ false, /* $suppress_notification = */ true); + } /** * testGroupInvitation diff --git a/tests/tine20/Calendar/Controller/RecurTest.php b/tests/tine20/Calendar/Controller/RecurTest.php index f77592aef68..baede1c6878 100644 --- a/tests/tine20/Calendar/Controller/RecurTest.php +++ b/tests/tine20/Calendar/Controller/RecurTest.php @@ -560,6 +560,46 @@ public function testCreateRecurExceptionAllFollowingGeneral() )), 'dtstart of new series'); } + public function testCreateRecurExceptionAllFollowingRruleChange() + { + // Wednesday + $from = new Tinebase_DateTime('2015-07-01 00:00:00'); + $until = new Tinebase_DateTime('2015-09-29 23:59:59'); + + $event = new Calendar_Model_Event(array( + 'summary' => 'Mettwoch', + 'dtstart' => '2015-02-10 12:00:00', + 'dtend' => '2015-02-10 13:00:00', + 'description' => '2 Pfund Mett. 15 Brotchen. 1ne Zwiebel', + 'rrule' => 'FREQ=WEEKLY;INTERVAL=1;WKST=MO;BYDAY=TU,FR', + 'container_id' => method_exists($this, '_getTestCalendar') ? + $this->_getTestCalendar()->getId() : + $this->_testCalendar->getId(), + 'attendee' => $this->_getAttendee(), + )); + + $persistentEvent = $this->_controller->create($event); + + $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); + // Friday, 03. 07. 2015 - Tuesday, 07. 07. 2015 + $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); + static::assertEquals('2015-07-03 11:00:00', $recurSet->getFirstRecord()->dtstart); + + $recurSet[1]->dtstart->addDay(1); + $recurSet[1]->dtend->addDay(1); + $newBaseEvent = $this->_controller->createRecurException($recurSet[1], FALSE, TRUE); + static::assertEquals('2015-07-08 11:00:00', $newBaseEvent->dtstart); + static::assertEquals('WE,FR', $newBaseEvent->rrule->byday); + + $oldBaseEvent = $this->_controller->get($persistentEvent->getId()); + static::assertEquals('2015-07-07 10:59:59', $oldBaseEvent->rrule->until); + $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); + $oldRecurSet = Calendar_Model_Rrule::computeRecurrenceSet($oldBaseEvent, $exceptions, $from, $until); + + $this->assertCount(1, $oldRecurSet, 'one event should be left in given period '); + $this->assertEquals('2015-07-03 11:00:00', $oldRecurSet[0]->dtstart); + } + public function testCreateRecurExceptionAllFollowingAllDay() { $from = new Tinebase_DateTime('2015-07-01 00:00:00'); diff --git a/tests/tine20/Calendar/Controller/ResourceTest.php b/tests/tine20/Calendar/Controller/ResourceTest.php index a8915e617f0..d651240e3d5 100644 --- a/tests/tine20/Calendar/Controller/ResourceTest.php +++ b/tests/tine20/Calendar/Controller/ResourceTest.php @@ -15,9 +15,9 @@ */ class Calendar_Controller_ResourceTest extends Calendar_TestCase { - public function testCreateResource() + public function testCreateResource($grants = null) { - $resource = $this->_getResource(); + $resource = $this->_getResource($grants); $persistentResource = Calendar_Controller_Resource::getInstance()->create($resource); @@ -85,23 +85,29 @@ public function testDeleteContainer() Calendar_Controller_Resource::getInstance()->get((string)$createResource->container_id); } - public function testManageResourceRight() + public function testManageResourceRightCreate() { - $role = Tinebase_Acl_Roles::getInstance()->getRoleByName('admin role'); - $roleRights = Tinebase_Acl_Roles::getInstance()->getRoleRights($role->getId()); - $roleRights = array_filter($roleRights, function($right) { - return $right['right'] != Calendar_Acl_Rights::MANAGE_RESOURCES && $right['right'] != Calendar_Acl_Rights::ADMIN; - }); - Tinebase_Acl_Roles::getInstance()->setRoleRights($role->getId(), $roleRights); - Tinebase_Acl_Roles::getInstance()->resetClassCache(); - - $this->setExpectedException('Tinebase_Exception_AccessDenied', 'You don\'t have the right to manage resources'); - $this->testRenameContainer(); + $this->_removeRoleRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); + + $this->setExpectedException('Tinebase_Exception_AccessDenied', 'No Permission.'); + $this->testCreateResource(); } - public function testResourceConflict() + public function testManageResourceRightDelete() { $resource = $this->testCreateResource(); + $this->_removeRoleRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); + + $this->setExpectedException('Tinebase_Exception_AccessDenied', 'No Permission.'); + Calendar_Controller_Resource::getInstance()->delete($resource); + } + + public function testResourceConflict() + { + $resource = $this->testCreateResource([ + Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true, + Calendar_Model_ResourceGrants::EVENTS_READ => true, + ]); $event = $this->_getEvent(); $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array( @@ -160,7 +166,32 @@ public function testDeleteResource($resource = null) $this->setExpectedException('Tinebase_Exception_NotFound'); Tinebase_Container::getInstance()->getContainerById($resource->container_id); } - + + /** + * testDeleteResourceAttendee + * + * @see https://github.com/tine20/tine20/issues/48 + */ + public function testDeleteResourceAttendee() + { + $resource = $this->testCreateResource(); + + // add resource to an event + $event = $this->_getEvent(); + $event->attendee->addRecord(new Calendar_Model_Attender([ + 'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE, + 'user_id' => $resource->id + ])); + $newEvent = Calendar_Controller_Event::getInstance()->create($event); + + Calendar_Controller_Resource::getInstance()->delete($resource->getId()); + + // check if resource attendee is removed + $updatedEvent = Calendar_Controller_Event::getInstance()->get($newEvent->getId()); + self::assertEquals(2, count($updatedEvent->attendee), 'resource attender should be removed! ' + . print_r($updatedEvent->toArray(), true)); + } + /** * testDeleteResourceWithmissingContainer * @@ -182,18 +213,17 @@ public function testDeleteResourceMissingContainer() protected function _prepareTestResourceAcl($_removeRoleRight = true) { // create resource with acl for sclever - $resource = $this->_getResource(); + $resource = $this->_getResource([Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true]); $sclever = $this->_getPersona('sclever'); - $grants = array($this->_getAllCalendarGrants($sclever)); - $grants[0] = array_merge($grants[0], array_fill_keys(Calendar_Model_ResourceGrants::getAllGrants(), true)); + $grants = $resource->grants; + $grants[1] = $this->_getAllCalendarGrants($sclever); + $grants[1] = array_merge($grants[1], array_fill_keys(Calendar_Model_ResourceGrants::getAllGrants(), true)); $resource->grants = $grants; $persistentResource = Calendar_Controller_Resource::getInstance()->create($resource); if (true === $_removeRoleRight) { // remove manage_resource right $this->_removeRoleRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); - // reset class cache - Calendar_Controller_Resource::getInstance()->doContainerACLChecks(true); } return $persistentResource; @@ -206,6 +236,7 @@ public function testResourceAclUpdateName() { $persistentResource = $this->_prepareTestResourceAcl(); + Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['pwulf']); // we will get a AccessDenied when we try to update the containers name $persistentResource->name = 'try to overwrite name'; try { @@ -217,110 +248,6 @@ public function testResourceAclUpdateName() // attention we created a roll back! so _prepareTestResourceAcl data is gone now! that is why the test ends here } - /** - * @see 0013348: improve resource permission handling - */ - public function testResourceAclWithManageResources() - { - $persistentResource = $this->_prepareTestResourceAcl(false); - - // test WebDav - $calendarShared = \Sabre\CalDAV\Plugin::CALENDAR_ROOT . '/shared'; - if (isset($_SERVER['REQUEST_URI'])) { - $oldRequestUri = $_SERVER['REQUEST_URI']; - } else { - $oldRequestUri = null; - } - $_SERVER['REQUEST_URI'] = '/tine20/' . $calendarShared; - $collection = new Calendar_Frontend_WebDAV($calendarShared); - $children = $collection->getChildren(); - - $this->assertTrue(is_array($children)); - $this->assertTrue(count($children) > 0); - $this->assertTrue($children[0] instanceof Calendar_Frontend_WebDAV_Container); - - $found = false; - /** @var Calendar_Frontend_WebDAV $child */ - foreach ($children as $child) { - if ($persistentResource->name === $child->getName()) { - $found = true; - } - } - static::assertTrue($found, 'did not find resource in WebDav'); - - if (null === $oldRequestUri) { - unset($_SERVER['REQUEST_URI']); - } else { - $_SERVER['REQUEST_URI'] = $oldRequestUri; - } - - // test calendar json search - $filter = array(array( - 'field' => 'type', - 'operator' => 'equals', - 'value' => array(Calendar_Model_Attender::USERTYPE_RESOURCE) - ), array( - 'field' => 'query', - 'operator' => 'contains', - 'value' => 'Meeting' - )); - $json = new Calendar_Frontend_Json(); - $result = $json->searchAttenders($filter); - self::assertEquals(1, $result['resource']['totalcount'], 'resource not found'); - - // test tinebase container search - $filter = array(array( - 'field' => 'type', - 'operator' => 'equals', - 'value' => Tinebase_Model_Container::TYPE_SHARED, - ), array( - 'field' => 'name', - 'operator' => 'contains', - 'value' => 'Meeting' - ), array( - 'field' => 'application_id', - 'operator' => 'equals', - 'value' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId() - )); - $json = new Tinebase_Frontend_Json_Container(); - - $containers = $json->searchContainers($filter, null); - - $this->assertTrue(is_array($containers) && isset($containers['results']) && is_array($containers['results'])); - $this->assertTrue(count($containers['results']) > 0, 'did not find resource in shared containers!'); - - $found = false; - foreach ($containers['results'] as $container) { - if ($persistentResource->name === $container['name']) { - $found = true; - } - } - static::assertTrue($found, 'did not find resource in shared containers!'); - - // test tinebase get container - $containers = $json->getContainer('Calendar', Tinebase_Model_Container::TYPE_SHARED, null); - - $this->assertTrue(is_array($containers)); - $this->assertTrue(count($containers) > 0); - - $found = false; - /** @var Tinebase_Model_Container $child */ - foreach ($containers as $container) { - if ($persistentResource->name === $container['name']) { - $found = true; - } - } - static::assertTrue($found, 'did not find resource in shared containers!'); - - // test name change - $persistentResource->name = 'try to overwrite name'; - $persistentResourceUpdated = Calendar_Controller_Resource::getInstance()->update($persistentResource); - - static::assertEquals($persistentResource->name, $persistentResourceUpdated->name, 'update did not work'); - static::assertEquals($persistentResource->name, Tinebase_Container::getInstance()-> - get($persistentResourceUpdated->container_id)->name); - } - /** * @see 0013348: improve resource permission handling */ @@ -328,6 +255,7 @@ public function testResourceAclUpdateDescription() { $persistentResource = $this->_prepareTestResourceAcl(); + Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['pwulf']); // now we will not update the container, but go into abstract update, which will do a get => Tinebase_Exception_AccessDenied $persistentResource->description = 'shalalala'; try { @@ -395,6 +323,7 @@ public function testResourceAclSearchAttenders() { $resource = $this->_prepareTestResourceAcl(); + Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['pwulf']); $filter = array(array( 'field' => 'type', 'operator' => 'equals', @@ -437,6 +366,7 @@ public function testResourceAclSearchTinebase() { $this->_prepareTestResourceAcl(); + Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['pwulf']); $filter = array(array( 'field' => 'type', 'operator' => 'equals', @@ -485,6 +415,7 @@ public function testResourceAclGetTinebase() { $resource = $this->_prepareTestResourceAcl(); + Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['pwulf']); $json = new Tinebase_Frontend_Json_Container(); $containers = $json->getContainer('Calendar', Tinebase_Model_Container::TYPE_SHARED, null); diff --git a/tests/tine20/Calendar/Frontend/ActiveSyncTest.php b/tests/tine20/Calendar/Frontend/ActiveSyncTest.php index c831016e959..cb67bf838f5 100644 --- a/tests/tine20/Calendar/Frontend/ActiveSyncTest.php +++ b/tests/tine20/Calendar/Frontend/ActiveSyncTest.php @@ -1343,6 +1343,66 @@ public function testUpdateEventWithoutDtstartAndEnd() self::assertEquals('2013-10-22 16:00:00', $updatedEvent->dtstart->toString()); } + public function testUpdateEventAddRecureException() + { + $syncrotonFolder = $this->testCreateFolder(); + $event = $this->_createEvent(); + $event->attendee->addRecord(new Calendar_Model_Attender([ + 'user_id' => $this->_personas['sclever']->contact_id, + 'user_type' => Calendar_Model_Attender::USERTYPE_USER, + ])); + $event->rrule = new Calendar_Model_Rrule('FREQ=WEEKLY;INTERVAL=1;WKST=MO;BYDAY=TU,FR'); + $event = Calendar_Controller_Event::getInstance()->update($event); + $event->attendee->find('user_id', $this->_personas['sclever']->contact_id)->status = + Calendar_Model_Attender::STATUS_ACCEPTED; + $event = Calendar_Controller_Event::getInstance()->update($event); + + $controller = Syncroton_Data_Factory::factory($this->_class, + $this->_getDevice(ActiveSync_TestCase::TYPE_IOS_11), $event->creation_time); + + $syncrotonEventtoUpdate = $controller->getEntry(new Syncroton_Model_SyncCollection( + ['collectionId' => $syncrotonFolder->serverId]), $event->getId()); + self::assertNotNull($syncrotonEventtoUpdate->attendees, 'attendee should not be null'); + + // 20180703T160000Z is a Tuesday + $xml = new SimpleXMLElement(' + + 1 + + + 0 + 20180703T160000Z + 20180703T160000Z + 20180703T170000Z + TEST-EXCEPTION-FROM-IOS yeah + 2 + 0 + '); + $exception = new Syncroton_Model_EventException($xml); + $syncrotonEventtoUpdate->exceptions = [$exception]; + $attendees = []; + /** @var Syncroton_Model_EventAttendee $attendee */ + foreach ($syncrotonEventtoUpdate->attendees as $attendee) { + //unset($attendee->attendeeStatus); + $attendees[] = clone $attendee; + } + $exception->attendees = $attendees; + + $serverId = $controller->updateEntry($syncrotonFolder->serverId, $event->getId(), $syncrotonEventtoUpdate); + + $updatedEvent = Calendar_Controller_Event::getInstance()->get($serverId); + static::assertEquals(1, count($updatedEvent->exdate), 'exdates count not right'); + static::assertEquals('2018-07-03 16:00:00', $updatedEvent->exdate[0]); + + $exceptions = Calendar_Controller_Event::getInstance()->getRecurExceptions($updatedEvent); + static::assertEquals(1, $exceptions->count(), 'exdates count not right'); + /** @var Calendar_Model_Event $exception */ + $exception = $exceptions->getFirstRecord(); + static::assertEquals(2, $exception->attendee->count(), 'attendee count is wrong'); + static::assertEquals(2, $exception->attendee->filter('status', Calendar_Model_Attender::STATUS_ACCEPTED) + ->count(), 'expected both attendees have accepted'); + } + public function testPreserveDataOnCreateRecurException() { $cfCfg = $this->_createCustomField(Tinebase_Record_Abstract::generateUID(), Calendar_Model_Event::class); diff --git a/tests/tine20/Calendar/Frontend/Json/PollTest.php b/tests/tine20/Calendar/Frontend/Json/PollTest.php index af5ec3d63af..194be8e0a5a 100644 --- a/tests/tine20/Calendar/Frontend/Json/PollTest.php +++ b/tests/tine20/Calendar/Frontend/Json/PollTest.php @@ -118,7 +118,7 @@ public function testCreatePollForRecurringEvent() $persistentEvent = $this->testCreatePoll(); $persistentEvent['rrule'] = 'FREQ=DAILY;INTERVAL=1'; - $this->setExpectedException(Tasks_Exception_UnexpectedValue::class); + $this->setExpectedException(Tinebase_Exception_SystemGeneric::class); $this->_uit->saveEvent($persistentEvent); } diff --git a/tests/tine20/Calendar/Frontend/Json/ResourceTest.php b/tests/tine20/Calendar/Frontend/Json/ResourceTest.php index 247bd4f529e..66379c33f52 100644 --- a/tests/tine20/Calendar/Frontend/Json/ResourceTest.php +++ b/tests/tine20/Calendar/Frontend/Json/ResourceTest.php @@ -34,8 +34,8 @@ public function setUp() { parent::setUp(); + Tinebase_TransactionManager::getInstance()->unitTestForceSkipRollBack(true); $this->jsonFE = new Calendar_Frontend_Json(); - Calendar_Controller_Resource::destroyInstance(); Tinebase_Container::getInstance()->resetClassCache(); } @@ -74,9 +74,21 @@ protected function _prepareTestResourceAcl($_user = null, $_grants = null) $grants[0]['account_id'] = $user->getId(); $grants[0]['account_type'] = Tinebase_Acl_Rights::ACCOUNT_TYPE_USER; } + $grants[1]['account_id'] = $this->_getPersona('pwulf')->getId(); + $grants[1]['account_type'] = Tinebase_Acl_Rights::ACCOUNT_TYPE_USER; + $grants[1][Calendar_Model_ResourceGrants::RESOURCE_ADMIN] = true; + $grants[1][Calendar_Model_ResourceGrants::EVENTS_ADD] = true; + $grants[1][Calendar_Model_ResourceGrants::EVENTS_EDIT] = true; + $grants[1][Calendar_Model_ResourceGrants::EVENTS_READ] = true; $resource->grants = $grants; - return $this->jsonFE->saveResource($resource->toArray(true)); + $oldUser = Tinebase_Core::getUser(); + try { + Tinebase_Core::set(Tinebase_Core::USER, $this->_getPersona('pwulf')); + return $this->jsonFE->saveResource($resource->toArray(true)); + } finally { + Tinebase_Core::set(Tinebase_Core::USER, $oldUser); + } } protected function _checkResourceGrants($_containerId, $_grant) @@ -144,6 +156,7 @@ protected function _checkResourceGrants($_containerId, $_grant) } $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($_containerId, true); + $grants->removeRecord($grants->find('account_id', $this->_getPersona('pwulf')->getId())); static::assertEquals(1, $grants->count()); /** @var Calendar_Model_ResourceGrants $currentUserGrants */ $currentUserGrants = $grants->getFirstRecord(); @@ -365,7 +378,13 @@ protected function _updateResourceAcl($resource, $shouldSucceed) Calendar_Model_ResourceGrants::EVENTS_FREEBUSY => true, ]; - $this->jsonFE->saveResource($resource); + try { + $this->jsonFE->saveResource($resource); + } catch (Exception $e) { + if ($shouldSucceed) { + static::fail('we should have permission to update the resource ACLs: ' . $e->getMessage()); + } + } $newGrants = Tinebase_Container::getInstance()->getGrantsOfContainer($resource['container_id']['id'], true); $diff = $orgGrants->diff($newGrants); @@ -391,6 +410,8 @@ protected function _preSetTestUserEtc() $this->selfReset['_testUserContact'] = $this->_testUserContact; $this->selfReset['_originalTestUser'] = $this->_originalTestUser; $this->selfReset['_testCalendar'] = $this->_testCalendar; + $this->_originalTestUser = $this->_getPersona('pwulf'); + Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser); $this->_testCalendar = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container([ 'name' => 'PHPUnit shared Calender container', 'type' => Tinebase_Model_Container::TYPE_SHARED, @@ -399,7 +420,14 @@ protected function _preSetTestUserEtc() 'model' => Calendar_Model_Event::class, ])); $this->_testUserContact = null; - $this->_originalTestUser = $this->_getPersona('sclever'); + } + + protected function _preResetTestUser() + { + $this->_testCalendar = $this->selfReset['_testCalendar']; + $this->_testUserContact = $this->selfReset['_testUserContact']; + $this->_originalTestUser = $this->selfReset['_originalTestUser']; + Tinebase_Core::set(Tinebase_Core::USER, $this->selfReset['_originalTestUser']); } protected function _preRemoveACLsFromTestContainer() @@ -408,6 +436,23 @@ protected function _preRemoveACLsFromTestContainer() Tinebase_Model_Grants::class), true, false); } + protected function _preAddReadGrant($resource) + { + $oldUser = Tinebase_Core::getUser(); + foreach ($resource['grants'] as &$grants) { + if ($grants['account_id'] === $oldUser->getId()) { + $grants[Calendar_Model_ResourceGrants::RESOURCE_READ] = true; + break; + } + } + try { + Tinebase_Core::set(Tinebase_Core::USER, $this->_getPersona('pwulf')); + $this->jsonFE->saveResource($resource); + } finally { + Tinebase_Core::set(Tinebase_Core::USER, $oldUser); + } + } + protected function _runGrantsTest($name, $result, $resource, $data = null) { switch ($name) { @@ -460,7 +505,7 @@ protected function _runGrantsTests($data) try { if (isset($test['pre'])) { foreach ($test['pre'] as $pre) { - $this->{$pre}(); + $this->{$pre}($resource); } } @@ -468,7 +513,7 @@ protected function _runGrantsTests($data) if (isset($test['post'])) { foreach ($test['post'] as $pre) { - $this->{$pre}(); + $this->{$pre}($resource); } } @@ -490,228 +535,233 @@ protected function _runGrantsTests($data) public function testResourceInviteGrant() { $this->_runGrantsTests([Calendar_Model_ResourceGrants::RESOURCE_INVITE => [ - // manage resource rights -> _updateResource should work - ['name' => '_updateResource', 'result' => true], + // manage resource rights -> _updateResource must not work + ['name' => '_updateResource', 'result' => false], - // manage resource rights -> _deleteResource should work - ['name' => '_deleteResource', 'result' => true], + // manage resource rights -> _deleteResource still needs read grant -> failure + ['name' => '_deleteResource', 'result' => false], - // no manage resource rights -> _deleteResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_deleteResource', 'result' => false], + // manage resource rights -> _updateResourceAcl must not work + ['name' => '_updateResourceAcl', 'result' => false], - // manage resource rights -> _updateResourceAcl should not work - ['name' => '_updateResourceAcl', 'result' => true], + // resource_invite grant => searchResource must not work + ['name' => '_searchResource', 'result' => 0], - // manage resource rights (+ resource_invite grant) => searchResource should work - ['name' => '_searchResource', 'result' => 1], - - // only resource_invite grant -> searchResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchResource', 'result' => 0], - - // only resource_invite grant -> searchAttender should work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchAttender', 'result' => 1], + // resource_invite grant -> searchAttender must work + ['name' => '_searchAttender', 'result' => 1], - // set user to sclever, create event in resource container (having manage_resources right) - // remove manage_resources right, get previously created event should fail + // set user to pwulf, create event in resource container (having event_add grant) + // set user back, get previously created event should fail ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventInResourceContainer', 'result' => true, - 'post' => ['_preRemoveManageResourcesAndFlush'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], - - // create event in resource container should fail without manage_resources right - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventInResourceContainer', 'result' => false], + 'post' => ['_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], - // invite resource attender should succeed with manage_resources right (+ resource_invite grant) - ['name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => true]], + // create event in resource container should fail with manage_resources right / resource_invite grant + ['name' => '_createEventInResourceContainer', 'result' => false], - // invite resource attender should succeed only with resource_invite grant (without manage_resources right) - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventWithResourceAttendee', - 'result' => ['succeed' => true, 'authKey' => false]], + // invite resource attender should succeed with resource_invite grant, no authkey returned + ['name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => false]], - // no resource_edit grant -> _updateResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_updateResource', 'result' => false], + // set user to pwulf, invite resource attender + // set user back, remove container grants, reading event should not work (event_read required) + ['pre' => ['_preSetTestUserEtc'], + 'name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => true], + 'post' => ['_preRemoveACLsFromTestContainer', '_preResetTestUser'], + 'followUps' => [['name' => '_getLocalEvent', 'result' => false]] + ], ]]); } public function testResourceReadGrant() { $this->_runGrantsTests([Calendar_Model_ResourceGrants::RESOURCE_READ => [ + // manage resource rights -> _updateResource must not work + ['name' => '_updateResource', 'result' => false], + + // manage resource rights and read grant -> success + ['name' => '_deleteResource', 'result' => true], + // no manage resource rights -> _deleteResource should not work ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_deleteResource', 'result' => false], - // manage resource rights => searchAttender should work - ['name' => '_searchAttender', 'result' => 1], + // manage resource rights -> _updateResourceAcl must not work + ['name' => '_updateResourceAcl', 'result' => false], - // resource_read grant -> searchResource should work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchResource', 'result' => 1], + // resource_read grant => searchResource must work + ['name' => '_searchResource', 'result' => 1], - // no resource_invite grant -> searchAttender should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchAttender', 'result' => 0], + // resource_read grant -> searchAttender must not work + ['name' => '_searchAttender', 'result' => 0], - // set user to sclever, create event in resource container (having manage_resources right) - // remove manage_resources right, get previously created event should fail + // set user to pwulf, create event in resource container (having event_add grant) + // set user back, get previously created event should fail ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventInResourceContainer', 'result' => true, - 'post' => ['_preRemoveManageResourcesAndFlush'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], + 'post' => ['_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], - // create event in resource container should fail without manage_resources right - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventInResourceContainer', 'result' => false], + // create event in resource container should fail with manage_resources right / resource_read grant + ['name' => '_createEventInResourceContainer', 'result' => false], - // invite resource attender should succeed with manage_resources right - // then remove manage_resources right and try to reschedule (which should be possible) - ['name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => true], - 'post' => ['_preRemoveManageResourcesAndFlush'], 'followUps' => [ + // invite resource attender should not succeed with resource_read grant + ['name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => false]], + + // invite resource attender with pwulf + // then switch back to vagrant user and try to reschedule (which should be possible) + ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventWithResourceAttendee', + 'result' => ['succeed' => true, 'authKey' => true], 'post' => ['_preResetTestUser'], 'followUps' => [ ['name' => '_rescheduleEvent', 'result' => ['succeed' => true, 'authKey' => false]] ]], - // set user to sclever, invite resource attender should succeed with manage_resources right - // then remove manage_resources right, reading event should fail + // set user to pwulf, invite resource attender + // set user back, remove container grants, reading event should not work (event_read required) ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => true], - 'post' => ['_preRemoveACLsFromTestContainer', '_preRemoveManageResourcesAndFlush'], + 'post' => ['_preRemoveACLsFromTestContainer', '_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]] ], - - // invite resource attender should not succeed - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventWithResourceAttendee', - 'result' => ['succeed' => false]], - - // no resource_edit grant -> _updateResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_updateResource', 'result' => false], ]]); } public function testResourceEditGrant() { $this->_runGrantsTests([Calendar_Model_ResourceGrants::RESOURCE_EDIT => [ - // no manage resource rights -> _deleteResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_deleteResource', 'result' => false], + // edit grant implies read grant -> works + ['name' => '_updateResource', 'result' => true], - // resource_read grant -> searchResource should work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchResource', 'result' => 1], + // manage resource rights and implied read grant -> success + ['name' => '_deleteResource', 'result' => true], - // no resource_invite grant -> searchAttender should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchAttender', 'result' => 0], + // no manage resource rights -> fail + ['pre' => ['_preRemoveManageResourcesAndFlush'],'name' => '_deleteResource', 'result' => false], - // set user to sclever, create event in resource container (having manage_resources right) - // remove manage_resources right, get previously created event should fail + // manage resource rights -> _updateResourceAcl must not work + ['name' => '_updateResourceAcl', 'result' => false], + + // implied read grant => searchResource must work + ['name' => '_searchResource', 'result' => 1], + + // no resource_invite grant -> searchAttender must not work + ['name' => '_searchAttender', 'result' => 0], + + // set user to pwulf, create event in resource container (having event_add grant) + // set user back, get previously created event should fail ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventInResourceContainer', 'result' => true, - 'post' => ['_preRemoveManageResourcesAndFlush'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], + 'post' => ['_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], - // create event in resource container should fail without manage_resources right - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventInResourceContainer', 'result' => false], + // create event in resource container should fail with manage_resources right / resource_edit grant + ['name' => '_createEventInResourceContainer', 'result' => false], - // invite resource attender should not succeed - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventWithResourceAttendee', - 'result' => ['succeed' => false]], + // invite resource attender should not succeed with resource_edit grant + ['name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => false]], - // resource_edit grant -> _updateResource should work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_updateResource', 'result' => true], + // invite resource attender with pwulf + // then switch back to vagrant user and try to reschedule (which should be possible) + ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventWithResourceAttendee', + 'result' => ['succeed' => true, 'authKey' => true], 'post' => ['_preResetTestUser'], 'followUps' => [ + ['name' => '_rescheduleEvent', 'result' => ['succeed' => true, 'authKey' => false]] + ]], - // no resource_admin grant -> _updateResourceAcl should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_updateResourceAcl', 'result' => false], + // set user to pwulf, invite resource attender + // set user back, remove container grants, reading event should not work (event_read required) + ['pre' => ['_preSetTestUserEtc'], + 'name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => true], + 'post' => ['_preRemoveACLsFromTestContainer', '_preResetTestUser'], + 'followUps' => [['name' => '_getLocalEvent', 'result' => false]] + ], ]]); } public function testResourceAdminGrant() { $this->_runGrantsTests([Calendar_Model_ResourceGrants::RESOURCE_ADMIN => [ - // no manage resource rights -> _deleteResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_deleteResource', 'result' => false], + // admin grant -> works + ['name' => '_updateResource', 'result' => true], + + // manage resource rights and admin grant -> works + ['name' => '_deleteResource', 'result' => true], + + // no manage resource rights -> fail + ['pre' => ['_preRemoveManageResourcesAndFlush'],'name' => '_deleteResource', 'result' => false], + + // admin grant -> works + ['name' => '_updateResourceAcl', 'result' => true], - // resource_read grant -> searchResource should work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchResource', 'result' => 1], + // admin grant -> works + ['name' => '_searchResource', 'result' => 1], - // resource_invite grant -> searchAttender should work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchAttender', 'result' => 1], + // admin grant -> works + ['name' => '_searchAttender', 'result' => 1], - // set user to sclever, create event in resource container (having manage_resources right) - // remove manage_resources right, get previously created event should fail + // set user to pwulf, create event in resource container (having event_add grant) + // set user back, get previously created event should fail ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventInResourceContainer', 'result' => true, - 'post' => ['_preRemoveManageResourcesAndFlush'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], + 'post' => ['_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]]], + + // create event in resource container should fail with manage_resources right / resource_admin grant + ['name' => '_createEventInResourceContainer', 'result' => false], + + // admin grant -> works + ['name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => false]], - // set user to sclever, invite resource attender should succeed with manage_resources right - // then remove manage_resources right, reading event should not work + // set user to pwulf, invite resource attender + // set user back, remove container grants, reading event should not work (event_read required) ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => true], - 'post' => ['_preRemoveACLsFromTestContainer', '_preRemoveManageResourcesAndFlush'], + 'post' => ['_preRemoveACLsFromTestContainer', '_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => false]] ], - - // create event in resource container should fail without manage_resources right - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventInResourceContainer', 'result' => false], - - // invite resource attender should succeed - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventWithResourceAttendee', - 'result' => ['succeed' => true, 'authKey' => false]], - - // resource_edit grant -> _updateResource should work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_updateResource', 'result' => true], - - // no resource_admin grant -> _updateResourceAcl should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_updateResourceAcl', 'result' => true], ]]); } public function testEventsReadGrant() { $this->_runGrantsTests([Calendar_Model_ResourceGrants::EVENTS_READ => [ - // no manage resource rights -> _deleteResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_deleteResource', 'result' => false], + ['name' => '_updateResource', 'result' => false], - // no resource_read grant -> searchResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchResource', 'result' => 0], + // manage resource rights -> _deleteResource still needs read grant -> failure + ['name' => '_deleteResource', 'result' => false], - // no resource_invite grant -> searchAttender should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_searchAttender', 'result' => 0], + ['name' => '_updateResourceAcl', 'result' => false], - // set user to sclever, create event in resource container (having manage_resources right) - // remove manage_resources right, get previously created event should work + ['name' => '_searchResource', 'result' => 0], + + ['name' => '_searchAttender', 'result' => 0], + + // set user to pwulf, create event in resource container (having event_add grant) + // set user back, get previously created event should succeed ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventInResourceContainer', 'result' => true, - 'post' => ['_preRemoveManageResourcesAndFlush'], 'followUps' => [['name' => '_getLocalEvent', 'result' => true]]], + 'post' => ['_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => true]]], + + // create event in resource container should fail with manage_resources right + ['name' => '_createEventInResourceContainer', 'result' => false], - // set user to sclever, invite resource attender should succeed with manage_resources right - // then remove manage_resources right, reading event should work + ['name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => false]], + + // set user to pwulf, invite resource attender + // set user back, remove container grants, reading event should work ['pre' => ['_preSetTestUserEtc'], 'name' => '_createEventWithResourceAttendee', 'result' => ['succeed' => true, 'authKey' => true], - 'post' => ['_preRemoveACLsFromTestContainer', '_preRemoveManageResourcesAndFlush'], + 'post' => ['_preRemoveACLsFromTestContainer', '_preResetTestUser'], 'followUps' => [['name' => '_getLocalEvent', 'result' => true]] ], - - // create event in resource container should fail without manage_resources right - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventInResourceContainer', 'result' => false], - - // invite resource attender should not succeed - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_createEventWithResourceAttendee', - 'result' => ['succeed' => false]], - - // no resource_edit grant -> _updateResource should not work - ['pre' => ['_preRemoveManageResourcesAndFlush'], 'name' => '_updateResource', 'result' => false], ]]); } - - public function testDefaultGrants() - { - $resource = $this->_prepareTestResourceAcl(null, []); - - $grants = Tinebase_Container::getInstance()->getGrantsOfContainer($resource['container_id'], true); - static::assertEquals(0, $grants->count()); - } - public function testUpdateGrants() { - $resource = $this->_prepareTestResourceAcl(); + $resource = $this->_prepareTestResourceAcl(Tinebase_Core::getUser()); $orgGrants = Tinebase_Container::getInstance()->getGrantsOfContainer($resource['container_id'], true); - static::assertEquals(1, $orgGrants->count()); + static::assertEquals(2, $orgGrants->count()); $resource['grants'] = null; $resource['name'] = 'shoo'; $resource = $this->jsonFE->saveResource($resource); $updateGrants = Tinebase_Container::getInstance()->getGrantsOfContainer($resource['container_id'], true); - static::assertEquals(1, $updateGrants->count()); + static::assertEquals(2, $updateGrants->count()); static::assertTrue($updateGrants->diff($orgGrants)->isEmpty(), 'grants are not the same'); $resource['grants'] = []; - $resource = $this->jsonFE->saveResource($resource); - $updateGrants = Tinebase_Container::getInstance()->getGrantsOfContainer($resource['container_id'], true); - static::assertEquals(0, $updateGrants->count(), 'grants where not deleted'); + try { + $this->jsonFE->saveResource($resource); + static::fail('empty grants are not allowed'); + } catch (Tinebase_Exception_Backend $teb) {} } } \ No newline at end of file diff --git a/tests/tine20/Calendar/JsonTests.php b/tests/tine20/Calendar/JsonTests.php index dfd139d1dc5..4a56fbdcbd4 100644 --- a/tests/tine20/Calendar/JsonTests.php +++ b/tests/tine20/Calendar/JsonTests.php @@ -858,8 +858,6 @@ public function testUpdateRecurSeriesRruleMonthly1() $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); $this->assertEquals('4TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); } /** @@ -885,8 +883,6 @@ public function testUpdateRecurSeriesRruleMonthly2() $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); $this->assertEquals('-1TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); } /** @@ -912,8 +908,6 @@ public function testUpdateRecurSeriesRruleMonthly3() $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); $this->assertEquals('1TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); } /** @@ -939,8 +933,6 @@ public function testUpdateRecurSeriesRruleMonthly4() $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); $this->assertEquals('4TU', $newBaseEvent['rrule']['byday'], 'Rrule should have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); } /** @@ -960,14 +952,11 @@ public function testUpdateRecurSeriesRruleMonthly5() $updatedEvent['dtstart'] = '2009-03-24 12:00:00'; $updatedEvent['dtend'] = '2009-03-24 13:00:00'; - $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId()); - - $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE); - - $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); - $this->assertEquals('-1TU', $newBaseEvent['rrule']['byday'], 'Rrule should not have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); + try { + // this will trigger a rollback -> don't expect the data to be there afterwards + $this->_uit->updateRecurSeries($updatedEvent, false); + static::fail('Tinebase_Exception_SystemGeneric exception expected'); + } catch (Tinebase_Exception_SystemGeneric $tesg) {} } /** @@ -993,8 +982,6 @@ public function testUpdateRecurSeriesRruleMonthly6() $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); $this->assertEquals('2', $newBaseEvent['rrule']['bymonthday'], 'Rrule should have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); } /** @@ -1014,14 +1001,11 @@ public function testUpdateRecurSeriesRruleMonthly7() $updatedEvent['dtstart'] = '2009-04-02 12:00:00'; $updatedEvent['dtend'] = '2009-04-02 13:00:00'; - $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId()); - - $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE); - - $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); - $this->assertEquals('26', $newBaseEvent['rrule']['bymonthday'], 'Rrule should not have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); + try { + // this will trigger a rollback -> don't expect the data to be there afterwards + $this->_uit->updateRecurSeries($updatedEvent, FALSE); + static::fail('Tinebase_Exception_SystemGeneric exception expected'); + } catch (Tinebase_Exception_SystemGeneric $tesg) {} } /** @@ -1041,15 +1025,11 @@ public function testUpdateRecurSeriesRruleYearly() $updatedEvent['dtstart'] = '2009-04-02 12:00:00'; $updatedEvent['dtend'] = '2009-04-02 13:00:00'; - $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId()); - - $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE); - - $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); - $this->assertEquals('26', $newBaseEvent['rrule']['bymonthday'], 'Rrule should not have changed'); - $this->assertEquals('3', $newBaseEvent['rrule']['bymonth'], 'Rrule should not have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); + try { + // this will trigger a rollback -> don't expect the data to be there afterwards + $this->_uit->updateRecurSeries($updatedEvent, FALSE); + static::fail('Tinebase_Exception_SystemGeneric exception expected'); + } catch (Tinebase_Exception_SystemGeneric $tesg) {} } /** @@ -1076,8 +1056,6 @@ public function testUpdateRecurSeriesRruleYearly1() $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed'); $this->assertEquals('2', $newBaseEvent['rrule']['bymonthday'], 'Rrule should have changed'); $this->assertEquals('4', $newBaseEvent['rrule']['bymonth'], 'Rrule should have changed'); - - $this->_eventController->delete(array($createdEvent->getId())); } @@ -1559,33 +1537,23 @@ public function testSaveResource($grants = [Calendar_Model_ResourceGrants::RESOU } /** - * test new grats for a resource that had no grants before - **/ + * test creating a resource that had no grants + */ public function testSaveResourcesWithoutRights() { - $resourceData = $this->testSaveResource(array()); - $this->assertEmpty($resourceData['grants']); - - $resourceData['grants'] = [[ - Calendar_Model_ResourceGrants::RESOURCE_READ => true, - Calendar_Model_ResourceGrants::RESOURCE_EDIT => true, - Calendar_Model_ResourceGrants::RESOURCE_INVITE => true, - 'account_id' => Tinebase_Core::getUser()->getId(), - 'account_type' => 'user', - ]]; - - $savedResource = $this->_uit->saveResource($resourceData); - $this->assertEquals(1, count($savedResource['grants']), 'grants are not set!'); + static::setExpectedException(Tinebase_Exception_AccessDenied::class, 'No Permission.'); + $this->testSaveResource(array()); } /** - * assert only resources with read grant are returned if the user has no manage right + * assert only resources with read grant are returned */ public function testSearchResources() { + $nonReadableResoureData = $this->testSaveResource(); + Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['pwulf']); $readableResoureData = $this->testSaveResource(); - $nonReadableResoureData = $this->testSaveResource(array()); $filter = array( array('field' => 'name', 'operator' => 'in', 'value' => array( @@ -1593,18 +1561,9 @@ public function testSearchResources() $nonReadableResoureData['name'], )) ); - - $searchResultManager = $this->_uit->searchResources($filter, array()); - $this->assertEquals(2, count($searchResultManager['results']), 'with manage grants all records should be found'); - // remove manage_resource right - $this->_removeRoleRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); - // reset class cache - Calendar_Controller_Resource::destroyInstance(); - Tinebase_Container::getInstance()->resetClassCache(); - $searchResult = $this->_uit->searchResources($filter, array()); - $this->assertEquals(1, count($searchResult['results']), 'without manage grants only one record should be found'); + $this->assertEquals(1, count($searchResult['results']), 'only one record should be found'); } /** @@ -1612,7 +1571,7 @@ public function testSearchResources() */ public function testResourceAttendeeAddFail() { - $nonreadableResourceData = $this->testSaveResource(array()); + $nonreadableResourceData = $this->testSaveResource(); $event = $this->_getEvent(TRUE); $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array( @@ -1623,39 +1582,15 @@ public function testResourceAttendeeAddFail() ) )); - // manage resource right can do everything: $persistentEventData = $this->_uit->saveEvent($event->toArray()); $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $persistentEventData['attendee']); static::assertEquals(1, count($attendee->filter('status', Calendar_Model_Attender::STATUS_ACCEPTED)), 'one accepted'); - // remove manage_resource right - $this->_removeRoleRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); - // reset class cache - Calendar_Controller_Resource::destroyInstance(); - - try { - $this->_uit->saveEvent($event->toArray()); - static::fail('it should not be possible to create an attender for a resource without read rights'); - } catch (Tinebase_Exception_AccessDenied $tead) {} - } - - /** - * test that the Manage Resources right allows to add a "non readable" resource as attendee - * test that it is possible to change the attendee status of that "non readable" resource - * test that a status authkey will be produced for that "non readable" resource attendee - */ - public function testResourceAttendeeNonReadAbleManageResources() - { - $editableResoureData = $this->testSaveResource(); - $nonreadableResourceData = $this->testSaveResource(array()); - + Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['pwulf']); $event = $this->_getEvent(TRUE); + $event->organizer = $this->_personas['pwulf']->contact_id; + $event->container_id = $this->_getPersonalContainer('Calendar', $this->_personas['pwulf'])->getId(); $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array( - array( - 'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE, - 'user_id' => $editableResoureData['id'], - 'status' => Calendar_Model_Attender::STATUS_ACCEPTED - ), array( 'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE, 'user_id' => $nonreadableResourceData['id'], @@ -1663,23 +1598,37 @@ public function testResourceAttendeeNonReadAbleManageResources() ) )); - $persistentEventData = $this->_uit->saveEvent($event->toArray()); + try { + $this->_uit->saveEvent($event->toArray()); + static::fail('it should not be possible to create an attender for a resource without read rights'); + } catch (Tinebase_Exception_AccessDenied $tead) {} + } - $this->assertEquals(2, count($persistentEventData['attendee']), 'resource without read grant must not be missing in attendee: ' - . print_r($persistentEventData['attendee'], true)); - $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $persistentEventData['attendee']); - $this->assertEquals(2, count($attendee->filter('status', Calendar_Model_Attender::STATUS_ACCEPTED)), 'two accepted'); - $this->assertEquals(2, count($attendee->filter('status_authkey', '/[a-z0-9]+/', TRUE)), 'one has authkey'); + protected function _setResourceRights($resource, $grants, $user = null) + { + if (null === $user) { + $user = $this->_personas['pwulf']; + } + if (!empty($grants) && !isset($grants['account_id'])) { + $grants['account_id'] = Tinebase_Core::getUser()->getId(); + $grants['account_type'] = 'user'; + } - // status update should work for non editable resource - $attendee->status = Calendar_Model_Attender::STATUS_TENTATIVE; - $persistentEventData['attendee'] = $attendee->toArray(); - $updatedEventData = $this->_uit->saveEvent($persistentEventData); + $newGrants = [ + 'account_id' => $user->getId(), + 'account_type' => 'user', + Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true, + ]; + $resource['grants'][] = $newGrants; - $this->assertEquals(2, count($updatedEventData['attendee']), 'resource without read grant must not be missing in attendee: ' - . print_r($updatedEventData['attendee'], true)); - $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $updatedEventData['attendee']); - $this->assertEquals(2, count($attendee->filter('status', Calendar_Model_Attender::STATUS_TENTATIVE)), 'two tentative'); + Tinebase_Container::getInstance()->setGrants($resource['container_id'], + new Tinebase_Record_RecordSet(Calendar_Model_ResourceGrants::class, $resource['grants']), true, false); + $oldUser = Tinebase_Core::getUser(); + Tinebase_Core::set(Tinebase_Core::USER, $user); + Tinebase_Container::getInstance()->setGrants($resource['container_id'], + new Tinebase_Record_RecordSet(Calendar_Model_ResourceGrants::class, array_merge([$newGrants], + empty($grants) ? [] : [$grants])), true, false); + Tinebase_Core::set(Tinebase_Core::USER, $oldUser); } /** @@ -1692,7 +1641,8 @@ public function testResourceAttendeeNonReadAbleManageResources() public function testResourceAttendeeNonReadAble() { $editableResoureData = $this->testSaveResource(); - $nonreadableResourceData = $this->testSaveResource(array()); + $nonreadableResourceData = $this->testSaveResource(); + $event = $this->_getEvent(TRUE); $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array( @@ -1710,10 +1660,7 @@ public function testResourceAttendeeNonReadAble() $persistentEventData = $this->_uit->saveEvent($event->toArray()); - // remove manage_resource right -> resource should still be there - $this->_removeRoleRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); - // reset class cache - Calendar_Controller_Resource::destroyInstance(); + $this->_setResourceRights($nonreadableResourceData, []); $persistentEventData = $this->_uit->getEvent($persistentEventData['id']); $this->assertEquals(2, count($persistentEventData['attendee']), 'resource without read grant must not be missing in attendee: ' @@ -1754,12 +1701,9 @@ public function testResourceAttendeeNonReadAble() public function testResourceAttendeeGrants() { $editableResoureData = $this->testSaveResource(); - $nonEditableResoureData = $this->testSaveResource([Calendar_Model_ResourceGrants::RESOURCE_INVITE => true]); + $nonEditableResoureData = $this->testSaveResource(); - // remove manage_resource right - $this->_removeRoleRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); - // reset class cache - Calendar_Controller_Resource::destroyInstance(); + $this->_setResourceRights($nonEditableResoureData, [Calendar_Model_ResourceGrants::RESOURCE_INVITE => true]); $event = $this->_getEvent(TRUE); $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array( @@ -1790,48 +1734,6 @@ public function testResourceAttendeeGrants() $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $updatedEventData['attendee']); $this->assertEquals(1, count($attendee->filter('status', Calendar_Model_Attender::STATUS_TENTATIVE)), 'one tentative'); } - - /** - * testExdateUpdateAllSummary - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - */ - public function testExdateUpdateAllSummary() - { - $events = $this->testCreateRecurException(); - $exception = $this->_getException($events, 1); - $exception['summary'] = 'new summary'; - - $event = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - $this->assertEquals('new summary', $event['summary']); - } - } - - /** - * testExdateUpdateAllDtStart - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - * - * @todo finish - */ - public function testExdateUpdateAllDtStart() - { - $events = $this->testCreateRecurException(); - $exception = $this->_getException($events, 1); - $exception['dtstart'] = '2009-04-01 08:00:00'; - $exception['dtend'] = '2009-04-01 08:15:00'; - - $event = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - $this->assertContains('08:00:00', $event['dtstart'], 'wrong dtstart: ' . print_r($event, TRUE)); - $this->assertContains('08:15:00', $event['dtend']); - } - } /** * testExdateUpdateThis @@ -1858,198 +1760,6 @@ public function testExdateUpdateThis() } } - /** - * testExdateUpdateThisAndFuture - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - */ - public function testExdateUpdateThisAndFuture() - { - $events = $this->testCreateRecurException(); - $exception = $this->_getException($events, 1); - $exception['summary'] = 'new summary'; - - $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE); - $this->assertEquals('new summary', $updatedEvent['summary'], 'summary not changed in exception: ' . print_r($updatedEvent, TRUE)); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - if ($event['dtstart'] >= $updatedEvent['dtstart']) { - $this->assertEquals('new summary', $event['summary'], 'summary not changed in event: ' . print_r($event, TRUE)); - } else { - $this->assertEquals('Wakeup', $event['summary']); - } - } - } - - /** - * testExdateUpdateThisAndFutureWithRruleUntil - * - * @see 0008244: "rrule until must not be before dtstart" when updating recur exception (THISANDFUTURE) - */ - public function testExdateUpdateThisAndFutureWithRruleUntil() - { - $events = $this->testCreateRecurException(); - - $exception = $this->_getException($events, 1); - $exception['dtstart'] = Tinebase_DateTime::now()->toString(); - $exception['dtend'] = Tinebase_DateTime::now()->addHour(1)->toString(); - - // move exception - $updatedEvent = $this->_uit->saveEvent($exception); - // try to update the whole series - $updatedEvent['summary'] = 'new summary'; - $updatedEvent = $this->_uit->saveEvent($updatedEvent, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE); - - $this->assertEquals('new summary', $updatedEvent['summary'], 'summary not changed in event: ' . print_r($updatedEvent, TRUE)); - } - - /** - * testExdateUpdateThisAndFutureRemoveAttendee - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - */ - public function testExdateUpdateThisAndFutureRemoveAttendee() - { - $events = $this->testCreateRecurException(); - $exception = $this->_getException($events, 1); - // remove susan from attendee - unset($exception['attendee'][0]); - - $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE); - $this->assertEquals(1, count($updatedEvent['attendee']), 'attender not removed from exception: ' . print_r($updatedEvent, TRUE)); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - if ($event['dtstart'] >= $updatedEvent['dtstart']) { - $this->assertEquals(1, count($event['attendee']), 'attendee count mismatch: ' . print_r($event, TRUE)); - } else { - $this->assertEquals(2, count($event['attendee']), 'attendee count mismatch: ' . print_r($event, TRUE)); - } - } - } - - /** - * testExdateUpdateAllAddAttendee - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - */ - public function testExdateUpdateAllAddAttendee() - { - $events = $this->testCreateRecurException(); - $exception = $this->_getException($events, 1); - // add new attender - $exception['attendee'][] = $this->_getUserTypeAttender(); - - $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL); - $this->assertEquals(3, count($updatedEvent['attendee']), 'attender not added to exception: ' . print_r($updatedEvent, TRUE)); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - $this->assertEquals(3, count($event['attendee']), 'attendee count mismatch: ' . print_r($event, TRUE)); - } - } - - /** - * testExdateUpdateThisAndFutureChangeDtstart - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - */ - public function testExdateUpdateThisAndFutureChangeDtstart() - { - $events = $this->testCreateRecurException(); - $exception = $this->_getException($events, 1); - $exception['dtstart'] = '2009-04-01 08:00:00'; - $exception['dtend'] = '2009-04-01 08:15:00'; - - $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - if ($event['dtstart'] >= $updatedEvent['dtstart']) { - $this->assertContains('08:00:00', $event['dtstart'], 'wrong dtstart: ' . print_r($event, TRUE)); - $this->assertContains('08:15:00', $event['dtend']); - } else { - $this->assertContains('06:00:00', $event['dtstart'], 'wrong dtstart: ' . print_r($event, TRUE)); - $this->assertContains('06:15:00', $event['dtend']); - } - } - } - - /** - * testExdateUpdateAllWithModlog - * - change base event, then update all - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - * @see 0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlog* - */ - public function testExdateUpdateAllWithModlog() - { - $this->markTestSkipped('this test is broken: see 0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlog*'); - - $events = $this->testCreateRecurException(); - $baseEvent = $events['results'][0]; - $exception = $this->_getException($events, 1); - - $baseEvent['summary'] = 'Get up, lazyboy!'; - $baseEvent = $this->_uit->saveEvent($baseEvent); - sleep(1); - - $exception['summary'] = 'new summary'; - $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - if ($event['dtstart'] == $updatedEvent['dtstart']) { - $this->assertEquals('new summary', $event['summary'], 'Recur exception should have the new summary'); - } else { - $this->assertEquals('Get up, lazyboy!', $event['summary'], 'Wrong summary in base/recur event: ' . print_r($event, TRUE)); - } - } - } - - /** - * testExdateUpdateAllWithModlogAddAttender - * - change base event, then update all - * - * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions - * @see 0007826: add attendee changes to modlog - * @see 0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlog* - */ - public function testExdateUpdateAllWithModlogAddAttender() - { - $this->markTestSkipped('0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlogAddAttender'); - - $events = $this->testCreateRecurException(); - $baseEvent = $events['results'][0]; - $exception = $this->_getException($events, 1); - - // add new attender - $baseEvent['attendee'][] = $this->_getUserTypeAttender(); - $baseEvent = $this->_uit->saveEvent($baseEvent); - $this->assertEquals(3, count($baseEvent['attendee']), 'Attendee count mismatch in baseEvent: ' . print_r($baseEvent, TRUE)); - sleep(1); - - // check recent changes (needs to contain attendee change) - $exdate = Calendar_Controller_Event::getInstance()->get($exception['id']); - $recentChanges = Tinebase_Timemachine_ModificationLog::getInstance()->getModifications('Calendar', $baseEvent['id'], NULL, 'Sql', $exdate->creation_time); - $changedAttributes = Tinebase_Timemachine_ModificationLog::getModifiedAttributes($recentChanges); - $this->assertGreaterThan(2, count($changedAttributes), 'Did not get all recent changes: ' . print_r($recentChanges->toArray(), TRUE)); - $this->assertTrue(in_array('attendee', $changedAttributes), 'Attendee change missing: ' . print_r($recentChanges->toArray(), TRUE)); - - $exception['attendee'][] = $this->_getUserTypeAttender('unittestnotexists@example.com'); - $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL); - - $search = $this->_uit->searchEvents($events['filter'], NULL); - foreach ($search['results'] as $event) { - if ($event['dtstart'] == $updatedEvent['dtstart']) { - $this->assertEquals(3, count($event['attendee']), 'Attendee count mismatch in exdate: ' . print_r($event, TRUE)); - } else { - $this->assertEquals(4, count($event['attendee']), 'Attendee count mismatch: ' . print_r($event, TRUE)); - } - } - } - /** * testConcurrentAttendeeChangeAdd * diff --git a/tests/tine20/Calendar/TestCase.php b/tests/tine20/Calendar/TestCase.php index 53ab6e39af5..81b3b1dfc21 100644 --- a/tests/tine20/Calendar/TestCase.php +++ b/tests/tine20/Calendar/TestCase.php @@ -87,8 +87,6 @@ public function tearDown() Calendar_Controller_Event::getInstance()->sendNotifications(false); - Tinebase_Acl_Roles::getInstance()->resetClassCache(); - if (! $this->_transactionId) { if ($this->_backend != NULL) { $events = $this->_backend->search(new Calendar_Model_EventFilter(array( @@ -320,16 +318,29 @@ protected function _createAttender($userId, $type = Calendar_Model_Attender::USE /** * get resource - * + * + * @param array|null $grants * @return Calendar_Model_Resource */ - protected function _getResource() + protected function _getResource($grants = null) { return new Calendar_Model_Resource(array( 'name' => 'Meeting Room', 'description' => 'Our main meeting room', 'email' => 'room@example.com', 'is_location' => TRUE, + 'grants' => [ + array_merge([ + 'account_id' => Tinebase_Core::getUser()->getId(), + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER, + ], $grants === null ? [ + Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true, + Calendar_Model_ResourceGrants::EVENTS_READ => true, + Calendar_Model_ResourceGrants::EVENTS_SYNC => true, + Calendar_Model_ResourceGrants::EVENTS_FREEBUSY => true, + Calendar_Model_ResourceGrants::EVENTS_EDIT => true, + ] : $grants), + ] )); } diff --git a/tests/tine20/Felamimail/Frontend/JsonTest.php b/tests/tine20/Felamimail/Frontend/JsonTest.php index a93d68529b2..03534603117 100644 --- a/tests/tine20/Felamimail/Frontend/JsonTest.php +++ b/tests/tine20/Felamimail/Frontend/JsonTest.php @@ -2216,6 +2216,25 @@ public function testReplyToSetting() $this->assertEquals('"Tine 2.0 Admin Account" ', $complete['headers']['reply-to']); } + /** + * @see https://github.com/tine20/tine20/issues/2172 + */ + public function testReplyToInMessage() + { + $this->_account->reply_to = 'noreply@tine20.org'; + $this->_json->saveAccount($this->_account->toArray()); + + $this->_foldersToClear[] = 'INBOX'; + $messageToSend = $this->_getMessageData(); + $messageToSend['reply_to'] = 'donotreply@tine20.org'; + $this->_json->saveMessage($messageToSend); + $message = $this->_searchForMessageBySubject($messageToSend['subject']); + + $complete = $this->_json->getMessage($message['id']); + $this->assertTrue(isset($complete['headers']['reply-to']), print_r($complete, true)); + $this->assertEquals('"Tine 2.0 Admin Account" ', $complete['headers']['reply-to']); + } + /** * Its possible to choice the kind of attachment when adding it. * diff --git a/tests/tine20/Inventory/Import/AllTests.php b/tests/tine20/Inventory/Import/AllTests.php index a8c4c222868..4cfb87cc9d1 100644 --- a/tests/tine20/Inventory/Import/AllTests.php +++ b/tests/tine20/Inventory/Import/AllTests.php @@ -24,6 +24,7 @@ public static function suite () { $suite = new PHPUnit_Framework_TestSuite('Tine 2.0 Inventory All Import Tests'); $suite->addTestSuite('Inventory_Import_CsvTest'); + $suite->addTestSuite('Inventory_Import_DemoDataTest'); return $suite; } } diff --git a/tests/tine20/Inventory/Import/DemoDataTest.php b/tests/tine20/Inventory/Import/DemoDataTest.php new file mode 100644 index 00000000000..fa1b592dee0 --- /dev/null +++ b/tests/tine20/Inventory/Import/DemoDataTest.php @@ -0,0 +1,54 @@ + + */ + +/** + * Test class for Inventory + */ +class Inventory_Import_DemoDataTest extends TestCase +{ + /** + * @var Tinebase_Model_Container + */ + protected $_importContainer = null; + + protected function tearDown() + { + parent::tearDown(); + } + + public function testImportDemoData() + { + $this->_importContainer = $this->_getTestContainer('Inventory', 'Inventory_Model_InventoryItem'); + $importer = new Tinebase_Setup_DemoData_Import('Inventory_Model_InventoryItem', [ + 'container_id' => $this->_importContainer->getId(), + 'definition' => 'inv_tine_import_csv', + ]); + $importer->importDemodata(); + + $filter = Tinebase_Model_Filter_FilterGroup::getFilterForModel('Inventory_Model_InventoryItem', [ + ['field' => 'container_id', 'operator' => 'equals', 'value' => $this->_importContainer->getId()] + ]); + $result = Inventory_Controller_InventoryItem::getInstance()->search($filter); + self::assertEquals(18, count($result)); + } + + public function testImportDemoDataViaCli() + { + $this->_cli = new Inventory_Frontend_Cli(); + $out = $this->_appCliHelper('Inventory', 'createDemoData', []); + echo $out; + + $filter = Tinebase_Model_Filter_FilterGroup::getFilterForModel('Inventory_Model_InventoryItem', [ + ['field' => 'creation_time', 'operator' => 'within', 'value' => 'dayThis'] + ]); + $result = Inventory_Controller_InventoryItem::getInstance()->search($filter); + self::assertGreaterThanOrEqual(18, count($result)); + } +} diff --git a/tests/tine20/TestCase.php b/tests/tine20/TestCase.php index 261904feb1d..27c97b5a2e9 100644 --- a/tests/tine20/TestCase.php +++ b/tests/tine20/TestCase.php @@ -569,13 +569,14 @@ protected function _setUser($user) } /** - * call handle cli function with params + * call handle cli function (Setup) with params * example usage: * $result = $this->_cliHelper('getconfig', array('--getconfig','--','configkey=allowedJsonOrigins')); * * @param string $command * @param array $params * @return string + * @todo should be renamed to _setupCliHelper */ protected function _cliHelper($command, $params) { @@ -587,6 +588,24 @@ protected function _cliHelper($command, $params) return $out; } + protected function _appCliHelper($appName, $command, $params) + { + $classname = $appName . '_Frontend_CLi'; + if (! class_exists($classname)) { + throw new Tinebase_Exception_InvalidArgument('CLI class ' . $classname . ' not found'); + } + + $cli = new $classname(); + $opts = new Zend_Console_Getopt('abp:'); + $opts->setArguments($params); + + ob_start(); + call_user_func_array([$cli, $command], [$opts]); + $out = ob_get_clean(); + + return $out; + } + /** * add an attachment to a record * diff --git a/tests/tine20/Timetracker/JsonTest.php b/tests/tine20/Timetracker/JsonTest.php index 0fee3402f4f..6794ad557ff 100644 --- a/tests/tine20/Timetracker/JsonTest.php +++ b/tests/tine20/Timetracker/JsonTest.php @@ -114,7 +114,7 @@ public function testUpdateTimeaccount() } /** - * try to get a Timeaccount + * try to get a Timeaccount - also checks if user and grants are resolved */ public function testSearchTimeaccounts() { @@ -130,14 +130,18 @@ public function testSearchTimeaccounts() // search & check $timeaccountFilter = $this->_getTimeaccountFilter(); $search = $this->_json->searchTimeaccounts($timeaccountFilter, $this->_getPaging()); - $this->assertEquals(0, $search['totalcount'], 'is_open filter not working'); + self::assertEquals(0, $search['totalcount'], 'is_open filter not working'); $search = $this->_json->searchTimeaccounts($this->_getTimeaccountFilter(TRUE), $this->_getPaging()); - $this->assertEquals(1, $search['totalcount']); - $this->assertEquals($timeaccount->description, $search['results'][0]['description']); - - // TODO enable this assertation and clean up the timetracker json converter to use abstract code! - //$this->assertEquals(Tinebase_Core::getUser()->getId(), $search['results'][0]['created_by']['accountId']); + self::assertEquals(1, $search['totalcount']); + $ta = $search['results'][0]; + self::assertEquals($timeaccount->description, $ta['description']); + self::assertTrue(is_array($ta['created_by']), 'user is not resolved in ' . print_r($ta, true)); + self::assertEquals(Tinebase_Core::getUser()->getId(), $ta['created_by']['accountId']); + self::assertGreaterThan(0, count($ta['account_grants']), 'account_grants not resolved in ' . print_r($ta, true)); + + // TODO do we need this? + //self::assertGreaterThan(0, count($ta['grants']), 'grants not resolved in ' . print_r($ta, true)); } /** diff --git a/tests/tine20/Tinebase/AccountTest.php b/tests/tine20/Tinebase/AccountTest.php index db1d0957e22..18b62cade28 100644 --- a/tests/tine20/Tinebase/AccountTest.php +++ b/tests/tine20/Tinebase/AccountTest.php @@ -259,7 +259,7 @@ public function testDeleteAccounts() $this->setExpectedException('Tinebase_Exception_NotFound'); - $account = Tinebase_User::getInstance()->getUserById($account, 'Tinebase_Model_FullUser'); + Tinebase_User::getInstance()->getUserById($account, 'Tinebase_Model_FullUser'); } /** @@ -268,7 +268,7 @@ public function testDeleteAccounts() */ public function testConvertAccountIdToInt() { - $this->setExpectedException('Tinebase_Exception_NotFound'); + $this->setExpectedException('Tinebase_Exception_InvalidArgument'); Tinebase_Model_User::convertUserIdToInt(0); } @@ -291,7 +291,7 @@ public function testConvertAccountIdToIntWithAccount() ) ); - $this->setExpectedException('Tinebase_Exception_NotFound'); + $this->setExpectedException('Tinebase_Exception_InvalidArgument'); Tinebase_Model_User::convertUserIdToInt($noIdAccount); } diff --git a/tests/tine20/Tinebase/AllTests.php b/tests/tine20/Tinebase/AllTests.php index bcc44c0c3b5..ea5ccb62ea4 100644 --- a/tests/tine20/Tinebase/AllTests.php +++ b/tests/tine20/Tinebase/AllTests.php @@ -73,6 +73,8 @@ public static function suite() $suite->addTestSuite(Tinebase_Export_DocTest::class); $suite->addTestSuite(Tinebase_Export_XlsxTest::class); $suite->addTestSuite(Tinebase_AreaLockTest::class); + $suite->addTestSuite(Tinebase_StateTest::class); + $suite->addTest(Tinebase_User_AllTests::suite()); $suite->addTest(Tinebase_Group_AllTests::suite()); diff --git a/tests/tine20/Tinebase/ModelConfigurationTest.php b/tests/tine20/Tinebase/ModelConfigurationTest.php index 166511fa080..dd64d2c593f 100644 --- a/tests/tine20/Tinebase/ModelConfigurationTest.php +++ b/tests/tine20/Tinebase/ModelConfigurationTest.php @@ -18,6 +18,15 @@ */ class Tinebase_ModelConfigurationTest extends TestCase { + protected function tearDown() + { + parent::tearDown(); + + // reset mc config to prevent problems with following tests + $customer = new Timetracker_Model_Timeaccount([], true); + $customer->resetConfiguration(); + } + /** * tests if the modelconfiguration gets created for the traditional models */ @@ -62,6 +71,35 @@ public function testSortableVirtualFields() } } + /** + * testModelConfigWithDisabledRelationApp + */ + public function testModelConfigWithDisabledRelationApp() + { + $sales = Tinebase_Application::getInstance()->getApplicationByName('Sales'); + Tinebase_Application::getInstance()->setApplicationStatus($sales->getId(), + Tinebase_Application::DISABLED); + $customer = new Timetracker_Model_Timeaccount([], true); + $cObj = $customer->getConfiguration(); + $fields = $cObj->getFields(); + $found = false; + foreach ($fields as $name => $field) { + if ($name === 'contract') { + self::assertTrue(! isset($field['label']) || $field['label'] === null, + 'contract field should have no label: ' . print_r($field, true)); + $found = true; + } + } + self::assertTrue($found); + $filterModel = $cObj->getFilterModel(); + self::assertGreaterThan(10, count($filterModel['_filterModel'])); + foreach ($filterModel['_filterModel'] as $name => $filter) { + if ($name === 'contract') { + self::fail('filter model should not contain contract filter: ' . print_r($filter, true)); + } + } + } + /** * testRelationCopyOmit */ diff --git a/tests/tine20/Tinebase/Record/PathTest.php b/tests/tine20/Tinebase/Record/PathTest.php index bd016444c71..6d5b2423b98 100644 --- a/tests/tine20/Tinebase/Record/PathTest.php +++ b/tests/tine20/Tinebase/Record/PathTest.php @@ -84,7 +84,12 @@ public function testBuildPathFromNonPathGeneratingRecord() 'related_backend' => Tinebase_Model_Relation::DEFAULT_RECORD_BACKEND, 'type' => 't' ] - ] + ], + 'grants' => [[ + 'account_id' => Tinebase_Core::getUser()->getId(), + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER, + Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true, + ]] ])); $result = $this->_uit->getPathsForRecord($resource); diff --git a/tests/tine20/Tinebase/StateTest.php b/tests/tine20/Tinebase/StateTest.php new file mode 100644 index 00000000000..d1d811dfdd3 --- /dev/null +++ b/tests/tine20/Tinebase/StateTest.php @@ -0,0 +1,23 @@ + + */ + +class Tinebase_StateTest extends TestCase +{ + public function testDecodeEncodeState() + { + $raw = 'o%3Acolumns%3Da%253Ao%25253Aid%25253Ds%2525253Atype%25255Ewidth%25253Dn%2525253A25%255Eo%25253Aid%25253Ds%2525253Ajpegphoto%25255Ewidth%25253Dn%2525253A25%255Eo%25253Aid%25253Ds%2525253Aattachments%25255Ewidth%25253Dn%2525253A25%255Eo%25253Aid%25253Ds%2525253Atags%25255Ewidth%25253Dn%2525253A61%255Eo%25253Aid%25253Ds%2525253Asalutation%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Acontainer_id%25255Ewidth%25253Dn%2525253A150%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253An_prefix%25255Ewidth%25253Dn%2525253A80%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253An_middle%25255Ewidth%25253Dn%2525253A80%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253An_family%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253An_given%25255Ewidth%25253Dn%2525253A80%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253An_fn%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253An_fileas%25255Ewidth%25253Dn%2525253A133%255Eo%25253Aid%25253Ds%2525253Aorg_name%25255Ewidth%25253Dn%2525253A150%255Eo%25253Aid%25253Ds%2525253Aorg_unit%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Atitle%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_one_street%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_one_locality%25255Ewidth%25253Dn%2525253A189%255Eo%25253Aid%25253Ds%2525253Aadr_one_region%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_one_postalcode%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_one_countryname%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_two_street%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_two_locality%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_two_region%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_two_postalcode%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aadr_two_countryname%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Apreferred_address%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aemail%25255Ewidth%25253Dn%2525253A189%255Eo%25253Aid%25253Ds%2525253Atel_work%25255Ewidth%25253Dn%2525253A125%255Eo%25253Aid%25253Ds%2525253Atel_cell%25255Ewidth%25253Dn%2525253A125%255Eo%25253Aid%25253Ds%2525253Atel_fax%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Atel_car%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Atel_pager%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Atel_home%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Atel_fax_home%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Atel_cell_private%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aemail_home%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aurl%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Aurl_home%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Anote%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Atz%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Ageo%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Abday%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Amemberroles%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Acreation_time%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Acreated_by%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Alast_modified_time%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Alast_modified_by%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253A146271e61c459b5f4847ca4df56a34572b6f990f%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Add4dcff28375ae1babde57bc39582c73a3a61e55%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253A7bd497d35c1f664d343f349a91ffa37b28035573%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Ac190340f0df2f284af7b55368cdbcbd50824b728%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%255Eo%25253Aid%25253Ds%2525253Ac4b0aaf1818fda1aad03b16d65a0ecc2d9ea733d%25255Ewidth%25253Dn%2525253A100%25255Ehidden%25253Db%2525253A1%5Esort%3Do%253Afield%253Ds%25253An_fileas%255Edirection%253Ds%25253AASC'; + + $decoded = Tinebase_State::decode($raw); + $this->assertEquals('c190340f0df2f284af7b55368cdbcbd50824b728', $decoded['columns'][50]['id']); + + $encoded = Tinebase_State::encode($decoded); + $this->assertEquals($raw, $encoded); + } +} \ No newline at end of file diff --git a/tine20/Addressbook/Controller/Contact.php b/tine20/Addressbook/Controller/Contact.php index 452fb74eb23..974aa588f9f 100644 --- a/tine20/Addressbook/Controller/Contact.php +++ b/tine20/Addressbook/Controller/Contact.php @@ -716,10 +716,13 @@ protected function _setGeoDataForAddress($_address, Addressbook_Model_Contact $_ if (! empty($_record->{$_address . 'countryname'})) { try { - $country = Zend_Locale::getTranslation($_record->{$_address . 'countryname'}, 'Country', $_record->{$_address . 'countryname'}); - if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ - . ($_address == 'adr_one_' ? ' Company address' : ' Private address') . ' country ' . $country); - $nominatim->setCountry($country); + $countryname = $_record->{$_address . 'countryname'}; + if (! empty($countryname)) { + $country = Zend_Locale::getTranslation($countryname, 'Country', $countryname); + if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ + . ($_address == 'adr_one_' ? ' Company address' : ' Private address') . ' country ' . $country); + $nominatim->setCountry($country); + } } catch (Zend_Locale_Exception $zle) { Tinebase_Exception::log($zle, true); } @@ -978,7 +981,7 @@ public function inspectAddUser(Tinebase_Model_FullUser $_addedUser, Tinebase_Mod { $contactId = $_addedUser->contact_id; if (!empty($contactId)) { - if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ + if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . " addedUser does have contact_id set: " . $_addedUser->accountLoginName . ' updating existing contact now.'); $this->inspectUpdateUser($_addedUser, $_newUserProperties); diff --git a/tine20/Addressbook/Frontend/Cli.php b/tine20/Addressbook/Frontend/Cli.php index a3c3d729000..5f64ea39f36 100644 --- a/tine20/Addressbook/Frontend/Cli.php +++ b/tine20/Addressbook/Frontend/Cli.php @@ -30,6 +30,14 @@ class Addressbook_Frontend_Cli extends Tinebase_Frontend_Cli_Abstract * @var string */ protected $_configFilename = 'importconfig.inc.php'; + /** + * import demodata default definitions + * + * @var array + */ + protected $_defaultDemoDataDefinition = [ + 'Addressbook_Model_Contact' => 'adb_tine_import_csv' + ]; /** * help array with function names and param descriptions diff --git a/tine20/Addressbook/Setup/DemoData/import/Contact/test.csv b/tine20/Addressbook/Setup/DemoData/import/Contact/test.csv new file mode 100644 index 00000000000..43986bcf4da --- /dev/null +++ b/tine20/Addressbook/Setup/DemoData/import/Contact/test.csv @@ -0,0 +1,3 @@ +"adr_one_countryname","adr_one_locality","adr_one_postalcode","adr_one_region","adr_one_street","adr_one_street2","adr_one_lon","adr_one_lat","adr_two_countryname","adr_two_locality","adr_two_postalcode","adr_two_region","adr_two_street","adr_two_street2","adr_two_lon","adr_two_lat","assistent","bday","calendar_uri","email","email_home","freebusy_uri","geo","note","container_id","pubkey","role","room","salutation","title","tz","url","url_home","n_family","n_fileas","n_fn","n_given","n_middle","n_prefix","n_suffix","org_name","org_unit","tel_assistent","tel_car","tel_cell","tel_cell_private","tel_fax","tel_fax_home","tel_home","tel_other","tel_pager","tel_prefer","tel_work","type","account_id","notes","tags" +"DE","Berlin",,,"Unter den Linden 1",,,,,,,,,,,,,,,"email@example.org",,,,,,,,,,,,,,"Müller",,,"Klaus",,,,"Hotel Adlon",,,,,,,,,,,,"030239923",,,, +"DE","Hamburg",,,"Jungfernstieg 20",,,,,,,,,,,,,,,"fritz@example.org",,,,,,,,,,,,,,"Mayer",,,"Fritz",,,,"HVV",,,,,,,,,,,,"0402343",,,, diff --git a/tine20/Addressbook/Setup/Update/Release11.php b/tine20/Addressbook/Setup/Update/Release11.php index fc995a1d41d..62c0839a512 100644 --- a/tine20/Addressbook/Setup/Update/Release11.php +++ b/tine20/Addressbook/Setup/Update/Release11.php @@ -181,11 +181,47 @@ public function update_10() } /** - * update to 12.0 + * update to 11.12 * * @return void + * @throws \Tinebase_Exception_InvalidArgument + * @throws Tinebase_Exception_NotFound */ public function update_11() + { + $stateRepo = new Tinebase_Backend_Sql(array( + 'modelName' => 'Tinebase_Model_State', + 'tableName' => 'state', + )); + + $states = $stateRepo->search(new Tinebase_Model_StateFilter(array( + array('field' => 'state_id', 'operator' => 'equals', 'value' => 'Addressbook-Contact-GridPanel-Grid'), + ))); + + foreach($states as $state) { + $decodedState = Tinebase_State::decode($state->data); + $spliceAt = 0; + if ($decodedState['columns'][1]['id'] == 'type') { + $spliceAt = 1; + $decodedState['columns'][1]['width'] = 25; + } + + array_splice($decodedState, $spliceAt, 0, ['id' => 'jpegphoto', 'width' => 25]); + array_splice($decodedState, $spliceAt, 0, ['id' => 'attachments', 'width' => 25]); + + $state->data = Tinebase_State::encode($decodedState); + $stateRepo->update($state); + } + + $this->setApplicationVersion('Addressbook', '11.12'); + } + + /** + * update to 12.0 + * + * @return void + */ + public function update_12() { $this->setApplicationVersion('Addressbook', '12.0'); } diff --git a/tine20/Addressbook/Setup/Update/Release12.php b/tine20/Addressbook/Setup/Update/Release12.php index cbf02632114..d4ac246c4ac 100644 --- a/tine20/Addressbook/Setup/Update/Release12.php +++ b/tine20/Addressbook/Setup/Update/Release12.php @@ -21,4 +21,15 @@ public function update_0() $this->setApplicationVersion('Addressbook', '12.1'); } + + /** + * @return void + */ + public function update_1() + { + $release11 = new Addressbook_Setup_Update_Release11($this->_backend); + $release11->update_11(); + + $this->setApplicationVersion('Addressbook', '12.2'); + } } diff --git a/tine20/Addressbook/Setup/setup.xml b/tine20/Addressbook/Setup/setup.xml index febe9b357ef..7eb7ea3c6da 100644 --- a/tine20/Addressbook/Setup/setup.xml +++ b/tine20/Addressbook/Setup/setup.xml @@ -1,7 +1,7 @@ Addressbook - 12.1 + 12.2 10 Admin diff --git a/tine20/Addressbook/js/ContactGrid.js b/tine20/Addressbook/js/ContactGrid.js index 95addee7a0c..e7e4988fe8d 100644 --- a/tine20/Addressbook/js/ContactGrid.js +++ b/tine20/Addressbook/js/ContactGrid.js @@ -197,7 +197,9 @@ Tine.Addressbook.ContactGridPanel.preferredAddressRenderer = function(value) { */ Tine.Addressbook.ContactGridPanel.getBaseColumns = function(i18n) { var columns = [ - { id: 'type', header: i18n._('Type'), dataIndex: 'type', width: 30, renderer: Tine.Addressbook.ContactGridPanel.contactTypeRenderer.createDelegate(this), hidden: false }, + { id: 'type', header: i18n._('Type'), tooltip: i18n._('Type'), dataIndex: 'type', width: 20, renderer: Tine.Addressbook.ContactGridPanel.contactTypeRenderer.createDelegate(this), hidden: false }, + { id: 'jpegphoto', header: i18n._('Contact Image'), tooltip: i18n._('Contact Image'), dataIndex: 'jpegphoto', width: 20, sortable: false, resizable: false, renderer: Tine.widgets.grid.imageRenderer, hidden: false }, + { id: 'attachments', header: window.i18n._('Attachments'), tooltip: window.i18n._('Attachments'), dataIndex: 'attachments', width: 20, sortable: false, resizable: false, renderer: Tine.widgets.grid.attachmentRenderer, hidden: false }, { id: 'tags', header: i18n._('Tags'), dataIndex: 'tags', width: 50, renderer: Tine.Tinebase.common.tagsRenderer, sortable: false, hidden: false }, { id: 'salutation', header: i18n._('Salutation'), dataIndex: 'salutation', renderer: Tine.Tinebase.widgets.keyfield.Renderer.get('Addressbook', 'contactSalutation') }, { diff --git a/tine20/Addressbook/translations/de.po b/tine20/Addressbook/translations/de.po index d743970eacf..3aba5bec8b9 100644 --- a/tine20/Addressbook/translations/de.po +++ b/tine20/Addressbook/translations/de.po @@ -807,6 +807,9 @@ msgstr "Ein neues Kontaktbild wurde hochgeladen." msgid "Deleted contact image." msgstr "Lösche Kontaktbild" +msgid "Contact Image" +msgstr "Kontaktbild" + #: Model/List.php:106 msgid "Percent" msgstr "Prozent" diff --git a/tine20/Admin/js/config/GridPanel.js b/tine20/Admin/js/config/GridPanel.js index df908dfa64d..8ccae771b76 100644 --- a/tine20/Admin/js/config/GridPanel.js +++ b/tine20/Admin/js/config/GridPanel.js @@ -219,12 +219,13 @@ Tine.Admin.config.GridPanel = Ext.extend(Tine.widgets.grid.GridPanel, { value = Ext.encode(record.get('default')); } + value = Ext.decode(value); + switch (record.get('type')) { case 'bool': value = Tine.Tinebase.common.booleanRenderer(value); break; case 'keyField': - value = Ext.decode(value); Ext.each(record.get('options')['records'], function(record) { if (record.id == value) { value = this.configApp.i18n._hidden(record.value); diff --git a/tine20/Calendar/Backend/Sql.php b/tine20/Calendar/Backend/Sql.php index d3ccdb6f430..c725dbc6894 100644 --- a/tine20/Calendar/Backend/Sql.php +++ b/tine20/Calendar/Backend/Sql.php @@ -1007,6 +1007,9 @@ public function increaseSeqsForContainerId($containerId) SELECT cal_event_id FROM tine20_cal_attendee WHERE displaycontainer_id = ' . $this->_db->quote($containerId) . ')) GROUP BY ev.id'); $result = $stmt->fetchAll(); + if (empty($result)) { + return; + } $seq = $this->_db->quoteIdentifier('seq'); $where = $this->_db->quoteInto($this->_db->quoteIdentifier('id') . ' IN (?)', $result); diff --git a/tine20/Calendar/Controller.php b/tine20/Calendar/Controller.php index fdfff83f27e..3e9a53fc552 100644 --- a/tine20/Calendar/Controller.php +++ b/tine20/Calendar/Controller.php @@ -5,7 +5,7 @@ * @package Calendar * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3 * @author Cornelius Weiss - * @copyright Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de) + * @copyright Copyright (c) 2009-2018 Metaways Infosystems GmbH (http://www.metaways.de) */ /** @@ -68,14 +68,23 @@ public static function getInstance() */ protected function _handleEvent(Tinebase_Event_Abstract $_eventObject) { - if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' . __LINE__ . ' handle event of type ' . get_class($_eventObject)); + if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . ' ' + . __LINE__ . ' handle event of type ' . get_class($_eventObject)); switch (get_class($_eventObject)) { case 'Admin_Event_AddAccount': //$this->createPersonalFolder($_eventObject->account); Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, $_eventObject->account->getId()); break; - + + case 'Calendar_Event_DeleteResource': + /** + * @var Calendar_Event_DeleteResource $_eventObject + */ + $this->_deleteEventAttenders($_eventObject); + + break; + case 'Tinebase_Event_User_DeleteAccount': /** * @var Tinebase_Event_User_DeleteAccount $_eventObject @@ -123,39 +132,52 @@ protected function _handleDeleteUserEvent($_eventObject) } } - protected function _deleteEventAttenders($_eventObject) + /** + * @param Tinebase_Event_Abstract $_eventObject + */ + protected function _deleteEventAttenders(Tinebase_Event_Abstract $_eventObject) { - $contactId = $_eventObject->account->contact_id; + if ($_eventObject instanceof Tinebase_Event_User_DeleteAccount) { + $attenderId = $_eventObject->account->contact_id; + $type = Calendar_Model_Attender::USERTYPE_USER; + } else if ($_eventObject instanceof Calendar_Event_DeleteResource) { + $attenderId = $_eventObject->resource->getId(); + $type = Calendar_Model_Attender::USERTYPE_RESOURCE; + } $filter = new Calendar_Model_EventFilter(array(array( 'field' => 'attender', 'operator' => 'in', 'value' => array(array( - 'user_type' => Calendar_Model_Attender::USERTYPE_USER, - 'user_id' => $contactId, + 'user_type' => $type, + 'user_id' => $attenderId, )) ))); + if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . ' ' + . __LINE__ . ' Deleting Event Attender ' . $attenderId . ' (type: ' . $type . ')'); + $eventController = Calendar_Controller_Event::getInstance(); $oldAcl = $eventController->doContainerACLChecks(false); $oldSendNotification = $eventController->sendNotifications(false); $events = $eventController->search($filter); /** @var Calendar_Model_Event $event */ - foreach($events as $event) - { + foreach ($events as $event) { $toRemove = array(); foreach ($event->attendee as $key => $attendee) { $attendeeUserId = $attendee->user_id instanceof Tinebase_Record_Abstract ? $attendee->user_id->getId() : $attendee->user_id; - if ($attendeeUserId === $contactId && ($attendee->user_type === Calendar_Model_Attender::USERTYPE_USER || - $attendee->user_type === Calendar_Model_Attender::USERTYPE_GROUPMEMBER)) - { + if ($attendeeUserId === $attenderId && + ($attendee->user_type === Calendar_Model_Attender::USERTYPE_USER || + $attendee->user_type === Calendar_Model_Attender::USERTYPE_GROUPMEMBER || + $attendee->user_type === Calendar_Model_Attender::USERTYPE_RESOURCE + )) { $toRemove[] = $key; } } if (count($toRemove) > 0) { - foreach($toRemove as $index) { + foreach ($toRemove as $index) { $event->attendee->offsetUnset($index); } diff --git a/tine20/Calendar/Controller/Event.php b/tine20/Calendar/Controller/Event.php index 324d4913261..836d844d7ed 100644 --- a/tine20/Calendar/Controller/Event.php +++ b/tine20/Calendar/Controller/Event.php @@ -914,7 +914,7 @@ protected function _tryForFreeSlot(Calendar_Model_Event $_event, $_startSec, $_e * @throws Tinebase_Exception_AccessDenied * @throws Tinebase_Exception_Record_Validation */ - public function update(Tinebase_Record_Interface $_record, $_checkBusyConflicts = FALSE, $range = Calendar_Model_Event::RANGE_THIS, $skipEvent = false) + public function update(Tinebase_Record_Interface $_record, $_checkBusyConflicts = FALSE, $skipEvent = false) { if (Tinebase_Core::isFilesystemAvailable()) { // fill stat cache to avoid deadlocks. Needs to happen outside a transaction @@ -935,7 +935,7 @@ public function update(Tinebase_Record_Interface $_record, $_checkBusyConflicts //NOTE we check via get(full rights) here whereas _updateACLCheck later checks limited rights from search if ($this->_doContainerACLChecks === FALSE || $event->hasGrant(Tinebase_Model_Grants::GRANT_EDIT)) { - Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " updating event: {$_record->id} (range: {$range})"); + Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " updating event: {$_record->id}"); // we need to resolve groupmembers before free/busy checking Calendar_Model_Attender::resolveGroupMembers($_record->attendee); @@ -974,10 +974,6 @@ public function update(Tinebase_Record_Interface $_record, $_checkBusyConflicts $this->_setSystemNotes($updatedEvent, Tinebase_Model_Note::SYSTEM_NOTE_NAME_CHANGED, $currentMods); } - if ($_record->isRecurException() && in_array($range, array(Calendar_Model_Event::RANGE_ALL, Calendar_Model_Event::RANGE_THISANDFUTURE))) { - $this->_updateExdateRange($_record, $range, $event); - } - Tinebase_TransactionManager::getInstance()->commitTransaction($transactionId); } catch (Exception $e) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Rolling back because: ' . $e); @@ -1017,175 +1013,6 @@ protected function _inspectAfterUpdate($updatedRecord, $record, $currentRecord) $updatedRecord->attendee = clone($record->attendee); } - /** - * update range of events starting with given recur exception - * - * @param Calendar_Model_Event $exdate - * @param string $range - */ - protected function _updateExdateRange($exdate, $range, $oldExdate) - { - if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ - . ' Updating events (range: ' . $range . ') belonging to recur exception event ' . $exdate->getId()); - - $baseEvent = $this->getRecurBaseEvent($exdate); - /** @var Tinebase_Record_Diff $diff */ - $diff = $oldExdate->diff($exdate); - - if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ - . ' Exdate diff: ' . print_r($diff->toArray(), TRUE)); - - if ($range === Calendar_Model_Event::RANGE_ALL) { - $events = $this->getRecurExceptions($baseEvent); - $events->addRecord($baseEvent); - $this->_applyExdateDiffToRecordSet($exdate, $diff, $events); - } else if ($range === Calendar_Model_Event::RANGE_THISANDFUTURE) { - $nextRegularRecurEvent = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $exdate->dtstart); - - if ($nextRegularRecurEvent == $baseEvent) { - // NOTE if a fist instance exception takes place before the - // series would start normally, $nextOccurence is the - // baseEvent of the series. As createRecurException can't - // deal with this situation we update whole series here - $this->_updateExdateRange($exdate, Calendar_Model_Event::RANGE_ALL, $oldExdate); - } else if ($nextRegularRecurEvent !== NULL && ! $nextRegularRecurEvent->dtstart->isEarlier($exdate->dtstart)) { - $this->_applyDiff($nextRegularRecurEvent, $diff, $exdate, FALSE); - - if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ - . ' Next recur exception event at: ' . $nextRegularRecurEvent->dtstart->toString()); - if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ - . ' ' . print_r($nextRegularRecurEvent->toArray(), TRUE)); - - $nextRegularRecurEvent->mute = $exdate->mute; - $newBaseEvent = $this->createRecurException($nextRegularRecurEvent, FALSE, TRUE); - // @todo this should be done by createRecurException - $exdatesOfNewBaseEvent = $this->getRecurExceptions($newBaseEvent); - $this->_applyExdateDiffToRecordSet($exdate, $diff, $exdatesOfNewBaseEvent); - } else { - if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ - . ' No upcoming occurrences found.'); - } - } - } - - protected function _applyDTStartToBaseEventRRULE($_dtstart, $_baseEvent) - { - /** @var Calendar_Model_Rrule $rrule */ - /*$rrule = $_baseEvent->rrule; - if (! $rrule instanceof Calendar_Model_Rrule) { - $rrule = new Calendar_Model_Rrule($rrule); - } - - switch($rrule->freq) - { - case Calendar_Model_Rrule::FREQ_DAILY - } - - if ($rrule->freq == Calendar_Model_Rrule::FREQ_WEEKLY) { - - } - - if ($rrule->freq == Calendar_Model_Rrule::FREQ_MONTHLY) { - if (!empty($rrule->byday)) { - - } elseif(!empty($rrule->bymonthday)) { - - } - } - - if ($rrule->freq == Calendar_Model_Rrule::FREQ_YEARLY) { - // bymonthday + bymonth - } - - switch($rrule->byday) - { - - }*/ - } - - /** - * apply exdate diff to a recordset of events - * - * @param Calendar_Model_Event $exdate - * @param Tinebase_Record_Diff $diff - * @param Tinebase_Record_RecordSet $events - */ - protected function _applyExdateDiffToRecordSet($exdate, $diff, $events) - { - // make sure baseEvent gets updated first to circumvent concurrency conflicts - $events->sort('recurdid', 'ASC'); - - foreach ($events as $event) { - if ($event->getId() === $exdate->getId()) { - // skip the exdate - continue; - } - $this->_applyDiff($event, $diff, $exdate, FALSE); - $this->update($event); - } - } - - /** - * merge updates from exdate into event - * - * @param Calendar_Model_Event $event - * @param Tinebase_Record_Diff $diff - * @param Calendar_Model_Event $exdate - * @param boolean $overwriteMods - * - * @todo is $overwriteMods needed? - */ - protected function _applyDiff($event, $diff, $exdate, $overwriteMods = TRUE) - { - if (! $overwriteMods) { - $recentChanges = Tinebase_Timemachine_ModificationLog::getInstance()->getModifications('Calendar', $event, NULL, 'Sql', $exdate->creation_time)->filter('change_type', Tinebase_Timemachine_ModificationLog::UPDATED); - if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ - . ' Recent changes (since ' . $exdate->creation_time->toString() . '): ' . print_r($recentChanges->toArray(), TRUE)); - } else { - $recentChanges = new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog'); - } - - $changedAttributes = Tinebase_Timemachine_ModificationLog::getModifiedAttributes($recentChanges); - $diffIgnore = array('organizer', 'seq', 'external_seq', 'last_modified_by', 'last_modified_time', 'dtstart', 'dtend'); - foreach ($diff->diff as $key => $newValue) { - if ($key === 'attendee') { - if (in_array($key, $changedAttributes)) { - $attendeeDiff = $diff->diff['attendee']; - if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ - . ' Attendee diff: ' . print_r($attendeeDiff->toArray(), TRUE)); - foreach ($attendeeDiff['added'] as $attenderToAdd) { - $attenderToAdd->setId(NULL); - $event->attendee->addRecord($attenderToAdd); - } - foreach ($attendeeDiff['removed'] as $attenderToRemove) { - $attenderInCurrentSet = Calendar_Model_Attender::getAttendee($event->attendee, $attenderToRemove); - if ($attenderInCurrentSet) { - $event->attendee->removeRecord($attenderInCurrentSet); - } - } - } else { - // remove ids of new attendee - $attendee = clone($exdate->attendee); - foreach ($attendee as $attender) { - if (! $event->attendee->getById($attender->getId())) { - $attender->setId(NULL); - } - } - $event->attendee = $attendee; - } - } else if (! in_array($key, $diffIgnore) && ! in_array($key, $changedAttributes)) { - $event->{$key} = $exdate->{$key}; - } else { - if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ - . ' Ignore / recently changed: ' . $key); - } - } - - if ((isset($diff->diff['dtstart']) || array_key_exists('dtstart', $diff->diff)) || (isset($diff->diff['dtend']) || array_key_exists('dtend', $diff->diff))) { - $this->_applyTimeDiff($event, $exdate); - } - } - /** * update multiple records * @@ -1333,44 +1160,37 @@ public function updateRecurSeries($_recurInstance, $_checkBusyConflicts = FALSE) { $baseEvent = $this->getRecurBaseEvent($_recurInstance); - $originalDtStart = $_recurInstance->getOriginalDtStart(); - $originalDtStart->setTimezone(Tinebase_DateTime::TIMEZONE_UTC); - $dtStart = $_recurInstance->dtstart; - if (! $dtStart instanceof Tinebase_DateTime) { - $dtStart = new Tinebase_DateTime($dtStart); - } - $dtStart->setTimezone(Tinebase_DateTime::TIMEZONE_UTC); - $dtEnd = $_recurInstance->dtend; - if (! $dtEnd instanceof Tinebase_DateTime) { - $dtEnd = new Tinebase_DateTime($dtEnd); - } - $dtEnd->setTimezone(Tinebase_DateTime::TIMEZONE_UTC); - - // prevent unpredictable new rrule - if ($originalDtStart->compare($dtStart) !== 0 || - (($orgDiff = $baseEvent->dtend->diff($baseEvent->dtstart)) && - ($newDiff = $dtEnd->diff($dtStart)) && - ( - $orgDiff->days !== $newDiff->days || - $orgDiff->h !== $newDiff->h || - $orgDiff->m !== $newDiff->m || - $orgDiff->s !== $newDiff->s - ) - )) { - if (strpos($baseEvent->rrule->byday, ',') !== false || - strpos($baseEvent->rrule->bymonthday, ',') !== false ) { - // _('The new recurrence rule is unpredictable. Please choose a valid recurrence rule') - throw new Tinebase_Exception_SystemGeneric('The new recurrence rule is unpredictable. Please choose a valid recurrence rule'); - } - } - // replace baseEvent with adopted instance $newBaseEvent = clone $_recurInstance; $newBaseEvent->setId($baseEvent->getId()); unset($newBaseEvent->recurid); $newBaseEvent->exdate = $baseEvent->exdate; - - $this->_applyTimeDiff($newBaseEvent, $_recurInstance, $baseEvent); + + $rrule = $baseEvent->rrule; + /*if (!$rrule instanceof Calendar_Model_Rrule) { + $rrule = new Calendar_Model_Rrule([], true); + $rrule->setFromString($baseEvent->rrule); + $baseEvent->rrule = $rrule; + }*/ + $newRrule = $newBaseEvent->rrule; + if ((string)$rrule === (string)$newRrule) { + $originalDtStart = $_recurInstance->getOriginalDtStart(); + if ($rrule->freq === Calendar_Model_Rrule::FREQ_WEEKLY && !empty($rrule->byday) && + strpos($rrule->byday, ',') !== false && $_recurInstance->dtstart->format('D') !== $originalDtStart + ->format('D') && $originalDtStart->format('D') !== $baseEvent->dtstart->format('D')) { + // special case for multi byday + $this->_applyDateTimeDiff($newBaseEvent, $_recurInstance, $baseEvent, true); + $this->_updateRruleBasedOnDtstartChange($rrule, $_recurInstance->dtstart, $originalDtStart); + $newBaseEvent->rrule = $rrule; + } else { + // we apply date and time change to base event + $this->_applyDateTimeDiff($newBaseEvent, $_recurInstance, $baseEvent); + } + + } else { + // rrule was changed by the user, so we only apply the time diff to the base event + $this->_applyDateTimeDiff($newBaseEvent, $_recurInstance, $baseEvent, true); + } return $this->update($newBaseEvent, $_checkBusyConflicts); } @@ -1381,8 +1201,9 @@ public function updateRecurSeries($_recurInstance, $_checkBusyConflicts = FALSE) * @param Calendar_Model_Event $newEvent * @param Calendar_Model_Event $fromEvent * @param Calendar_Model_Event $baseEvent + * @param boolean $onlyTime */ - protected function _applyTimeDiff($newEvent, $fromEvent, $baseEvent = NULL) + protected function _applyDateTimeDiff($newEvent, $fromEvent, $baseEvent = NULL, $onlyTime = false) { if (! $baseEvent) { $baseEvent = $newEvent; @@ -1392,16 +1213,23 @@ protected function _applyTimeDiff($newEvent, $fromEvent, $baseEvent = NULL) . ' New event: ' . print_r($newEvent->toArray(), TRUE)); if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' From event: ' . print_r($fromEvent->toArray(), TRUE)); - + // compute time diff (NOTE: if the $fromEvent is the baseEvent, it has no recurid) - $originalDtStart = $fromEvent->recurid ? new Tinebase_DateTime(substr($fromEvent->recurid, -19), 'UTC') : clone $baseEvent->dtstart; - - $dtstartDiff = $originalDtStart->diff($fromEvent->dtstart); + $originalDtStart = $fromEvent->recurid ? new Tinebase_DateTime(substr($fromEvent->recurid, -19), 'UTC') : + clone $baseEvent->dtstart; + + if ($onlyTime) { + $tmp = clone $fromEvent->dtstart; + $tmp->setDate($originalDtStart->format('Y'), $originalDtStart->format('m'), $originalDtStart->format('d')); + $dtstartDiff = $originalDtStart->diff($tmp); + } else { + $dtstartDiff = $originalDtStart->diff($fromEvent->dtstart); + } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ - . " Dtstart diff: " . $dtstartDiff->format('%H:%M:%i')); + . " Dtstart diff: " . $dtstartDiff->format('%Y-%M-%D %H:%I:%S')); $eventDuration = $fromEvent->dtstart->diff($fromEvent->dtend); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ - . " Duration diff: " . $dtstartDiff->format('%H:%M:%i')); + . " Duration diff: " . $dtstartDiff->format('%Y-%M-%D %H:%I:%S')); $newEvent->dtstart = clone $baseEvent->dtstart; $newEvent->dtstart->add($dtstartDiff); @@ -1969,111 +1797,217 @@ protected function _inspectBeforeUpdate($_record, $_oldRecord) $rrule = $_record->rrule; if (! $rrule instanceof Calendar_Model_Rrule) { $rrule = new Calendar_Model_Rrule($rrule); - } else { - $rrule = clone $rrule; } + $this->_updateRruleBasedOnDtstartChange($rrule, $_record->dtstart, $_oldRecord->dtstart); + $_record->rrule = $rrule; + } - switch($rrule->freq) - { - case Calendar_Model_Rrule::FREQ_WEEKLY: - // only do simple bydays - if (empty($rrule->byday) || !isset(Calendar_Model_Rrule::$WEEKDAY_MAP[$rrule->byday])) { - break; - } + Calendar_Controller_Poll::getInstance()->inspectBeforeUpdateEvent($_record, $_oldRecord); + } - // check old dtstart matches byday, if not, we abort - if (strtolower($_oldRecord->dtstart->format('D')) !== Calendar_Model_Rrule::$WEEKDAY_MAP[$rrule->byday]) { - break; - } + /** + * @param Calendar_Model_Rrule $rrule + * @param Tinebase_DateTime $newDtstart + * @param Tinebase_DateTime $oldDtstart + * @throws Tinebase_Exception_SystemGeneric + */ + protected function _updateRruleBasedOnDtstartChange(Calendar_Model_Rrule $rrule, Tinebase_DateTime $newDtstart, + Tinebase_DateTime $oldDtstart) + { + $success = false; - $rrule->byday = Calendar_Model_Rrule::$WEEKDAY_MAP_REVERSE[strtolower($_record->dtstart->format('D'))]; - $_record->rrule = $rrule; + switch($rrule->freq) + { + case Calendar_Model_Rrule::FREQ_DAILY: + // nothing to do, it's a success case + $success = true; + break; + + case Calendar_Model_Rrule::FREQ_WEEKLY: + // we need bydays + if (empty($rrule->byday)) { break; + } + $byDay = explode(',', $rrule->byday); + $oldByDay = Calendar_Model_Rrule::$WEEKDAY_MAP_REVERSE[strtolower($oldDtstart->format('D'))]; + $newByDay = Calendar_Model_Rrule::$WEEKDAY_MAP_REVERSE[strtolower($newDtstart->format('D'))]; - case Calendar_Model_Rrule::FREQ_MONTHLY: - // if there is no day specification, nothing to do - if (empty($rrule->byday) && empty($rrule->bymonthday)) { + // check old dtstart matches a byday, if not, we abort + if (!in_array($oldByDay, $byDay)) { + break; + } + // in case the day of week didn't change, nothing to do (+ it's a success case too) + if ($oldByDay === $newByDay) { + $success = true; + break; + } + + $byDay = array_filter($byDay, function ($val) use ($oldByDay) { + return $val !== $oldByDay; + }); + $byDay[] = Calendar_Model_Rrule::$WEEKDAY_MAP_REVERSE[strtolower($newDtstart->format('D'))]; + usort($byDay, function ($a, $b) { + return $a === $b ? 0 : + (Calendar_Model_Rrule::$WEEKDAY_DIGIT_MAP[$a] > Calendar_Model_Rrule::$WEEKDAY_DIGIT_MAP[$b] ? + 1 : -1); + }); + + $rrule->byday = join(',', $byDay); + $success = true; + break; + + case Calendar_Model_Rrule::FREQ_MONTHLY: + // we need a day specification + if (empty($rrule->byday) && empty($rrule->bymonthday)) { + break; + } + // only do simple rules, byday combined with bymonthday is not supported + // also we only support simple byday rules + if (!empty($rrule->byday) && (!empty($rrule->bymonthday) || strpos($rrule->byday, ',') !== false)) { + break; + } + + if (!empty($rrule->byday)) { + + $bydayPrefix = intval($rrule->byday); + $byday = substr($rrule->byday, -2); + + // if we dont have a quantifier we abort + if ($bydayPrefix === 0) { break; } - // only do simple rules - if (!empty($rrule->byday) && (strpos(',', $rrule->byday) !== false)) { + + // check old dtstart matches byday, if not we abort + if (strtolower($oldDtstart->format('D')) !== Calendar_Model_Rrule::$WEEKDAY_MAP[$byday]) { break; } - if (!empty($rrule->bymonthday) && strpos(',', $rrule->bymonthday) !== false) { + + $dtstartJ = $oldDtstart->format('j'); + // check old dtstart matches bydayPrefix, if not we abort + if ($bydayPrefix === -1) { + if ($oldDtstart->format('t') - $dtstartJ > 6) { + break; + } + } else { + if ($dtstartJ - (($bydayPrefix-1)*7) > 6 || $dtstartJ - (($bydayPrefix-1)*7) < 1) { + break; + } + } + + if ($newDtstart->format('j') > 28 || ($bydayPrefix === -1 && $newDtstart->format('t') - $newDtstart->format('j') < 7)) { + // keep -1 => last X + $prefix = '-1'; + } else { + $prefix = floor(($newDtstart->format('j') - 1) / 7) + 1; + } + + $rrule->byday = $prefix . Calendar_Model_Rrule::$WEEKDAY_MAP_REVERSE[strtolower($newDtstart->format('D'))]; + $success = true; + + } else { + + $byMonthDay = explode(',', $rrule->bymonthday); + $oldMonthDay = $oldDtstart->format('j'); + $newMonthDay = $newDtstart->format('j'); + + // check old dtstart is in bymonthday, if not we abort + if (!in_array($oldMonthDay, $byMonthDay)) { break; } - if (!empty($rrule->byday) && !empty($rrule->bymonthday)) { + // if monthday did not change, nothing to do, it's a success case + if ($oldMonthDay === $newMonthDay) { + $success = true; break; } - if (!empty($rrule->byday)) { - $bydayPrefix = intval($rrule->byday); - $byday = substr($rrule->byday, -2); + $byMonthDay = array_filter($byMonthDay, function ($val) use ($oldMonthDay) { + return $val !== $oldMonthDay; + }); + $byMonthDay[] = $newMonthDay; + asort($byMonthDay); - // if we dont have a quantifier we abort - if ($bydayPrefix === 0) { - break; - } + $rrule->bymonthday = join(',', $byMonthDay); + $success = true; + } - // check old dtstart matches byday, if not we abort - if (strtolower($_oldRecord->dtstart->format('D')) !== Calendar_Model_Rrule::$WEEKDAY_MAP[$byday]) { - break; - } + break; - $dtstartJ = $_oldRecord->dtstart->format('j'); - // check old dtstart matches bydayPrefix, if not we abort - if ($bydayPrefix === -1) { - if ($_oldRecord->dtstart->format('t') - $dtstartJ > 6) { - break; - } - } else { - if ($dtstartJ - (($bydayPrefix-1)*7) > 6 || $dtstartJ - (($bydayPrefix-1)*7) < 1) { - break; - } - } + case Calendar_Model_Rrule::FREQ_YEARLY: + // we need a day specification and a month + if (empty($rrule->bymonth) || (empty($rrule->byday) && empty($rrule->bymonthday))) { + break; + } + // only do simple rules, byday combined with bymonthday is not supported + if (!empty($rrule->byday) && !empty($rrule->bymonthday)) { + break; + } - if ($_record->dtstart->format('j') > 28 || ($bydayPrefix === -1 && $_record->dtstart->format('t') - $_record->dtstart->format('j') < 7)) { - // keep -1 => last X - $prefix = '-1'; - } else { - $prefix = floor(($_record->dtstart->format('j') - 1) / 7) + 1; - } + if (!empty($rrule->byday)) { + // only do simple rules + if (strpos($rrule->byday, ',') !== false) { + break; + } - $rrule->byday = $prefix . Calendar_Model_Rrule::$WEEKDAY_MAP_REVERSE[strtolower($_record->dtstart->format('D'))]; - $_record->rrule = $rrule; + $bydayPrefix = intval($rrule->byday); + $byday = substr($rrule->byday, -2); - } else { + // if we dont have a quantifier we abort + if ($bydayPrefix === 0) { + break; + } + + // check old dtstart matches byday, if not we abort + if (strtolower($oldDtstart->format('D')) !== Calendar_Model_Rrule::$WEEKDAY_MAP[$byday] + || $oldDtstart->format('n') != $rrule->bymonth) { + break; + } - // check old dtstart matches bymonthday, if not we abort - if ($_oldRecord->dtstart->format('j') != $rrule->bymonthday) { + $dtstartJ = $oldDtstart->format('j'); + // check old dtstart matches bydayPrefix, if not we abort + if ($bydayPrefix === -1) { + if ($oldDtstart->format('t') - $dtstartJ > 6) { + break; + } + } else { + if ($dtstartJ - (($bydayPrefix-1)*7) > 6 || $dtstartJ - (($bydayPrefix-1)*7) < 1) { break; } + } - $rrule->bymonthday = $_record->dtstart->format('j'); - $_record->rrule = $rrule; + if ($newDtstart->format('j') > 28 || ($bydayPrefix === -1 && $newDtstart->format('t') - $newDtstart->format('j') < 7)) { + // keep -1 => last X + $prefix = '-1'; + } else { + $prefix = floor(($newDtstart->format('j') - 1) / 7) + 1; } - break; + $rrule->byday = $prefix . Calendar_Model_Rrule::$WEEKDAY_MAP_REVERSE[strtolower($newDtstart->format('D'))]; + $rrule->bymonth = $newDtstart->format('n'); + $success = true; - case Calendar_Model_Rrule::FREQ_YEARLY: + } else { // only do simple rules - if (! empty($rrule->byday) || empty($rrule->bymonth) || empty($rrule->bymonthday) || strpos($rrule->bymonth, ',') !== false || - strpos($rrule->bymonthday, ',') !== false || + if (strpos($rrule->bymonthday, ',') !== false || // check old dtstart matches the date - $_oldRecord->dtstart->format('j') != $rrule->bymonthday || $_oldRecord->dtstart->format('n') != $rrule->bymonth + $oldDtstart->format('j') != $rrule->bymonthday || $oldDtstart->format('n') != $rrule->bymonth ) { break; } - $rrule->bymonthday = $_record->dtstart->format('j'); - $rrule->bymonth = $_record->dtstart->format('n'); - $_record->rrule = $rrule; + $rrule->bymonthday = $newDtstart->format('j'); + $rrule->bymonth = $newDtstart->format('n'); + $success = true; + } - break; - } + break; + } + + if (!$success) { + // _('The new recurrence rule is unpredictable. Please choose a valid recurrence rule') + throw new Tinebase_Exception_SystemGeneric( + 'The new recurrence rule is unpredictable. Please choose a valid recurrence rule'); } - Calendar_Controller_Poll::getInstance()->inspectBeforeUpdateEvent($_record, $_oldRecord); } /** @@ -2325,13 +2259,6 @@ protected function _checkGrant($_record, $_action, $_throw = TRUE, $_errorMessag break; } - if (! $hasGrant && Tinebase_Core::getUser()->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES)) { - $container = Tinebase_Container::getInstance()->getContainerById($_record->container_id); - if (isset($container->xprops()['Calendar']['Resource']['resource_id'])) { - $hasGrant = true; - } - } - if (! $hasGrant && 'get' === $_action) { $_record->doFreeBusyCleanup(); } @@ -2705,8 +2632,7 @@ protected function _createAttender(Calendar_Model_Attender $attender, Calendar_M try { $resource = $resourceController->get($attender->user_id); if (! Tinebase_Container::getInstance()->hasGrant(Tinebase_Core::getUser(), $resource->container_id, - Calendar_Model_ResourceGrants::RESOURCE_INVITE) && ! Tinebase_Core::getUser()->hasRight( - 'Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES)) { + Calendar_Model_ResourceGrants::RESOURCE_INVITE)) { throw new Tinebase_Exception_AccessDenied('you do not have permission to invite this resource'); } } finally { @@ -2719,8 +2645,7 @@ protected function _createAttender(Calendar_Model_Attender $attender, Calendar_M // check if user is allowed to set status if ($attender->user_type === Calendar_Model_Attender::USERTYPE_RESOURCE) { if (! $preserveStatus && !Tinebase_Core::getUser()->hasGrant($attender->displaycontainer_id, - Calendar_Model_ResourceGrants::EVENTS_EDIT) && - ! Tinebase_Core::getUser()->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES)) { + Calendar_Model_ResourceGrants::EVENTS_EDIT)) { //If resource has an default status use this $attender->status = isset($resource->status) ? $resource->status : Calendar_Model_Attender::STATUS_NEEDSACTION; } @@ -2827,9 +2752,7 @@ protected function _updateAttender($attender, $currentAttender, $event, $isResch !$this->_keepAttenderStatus) { if ($attender->user_type === Calendar_Model_Attender::USERTYPE_RESOURCE) { if (!Tinebase_Core::getUser()->hasGrant($attender->displaycontainer_id, - Calendar_Model_ResourceGrants::EVENTS_EDIT) - && !Tinebase_Core::getUser()->hasRight('Calendar', - Calendar_Acl_Rights::MANAGE_RESOURCES)) { + Calendar_Model_ResourceGrants::EVENTS_EDIT)) { if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Wrong authkey, resetting status (' . $attender->status . ' -> ' . $currentAttender->status . ')'); diff --git a/tine20/Calendar/Controller/EventNotifications.php b/tine20/Calendar/Controller/EventNotifications.php index 90938e7eba7..0d43817b500 100644 --- a/tine20/Calendar/Controller/EventNotifications.php +++ b/tine20/Calendar/Controller/EventNotifications.php @@ -417,6 +417,11 @@ public static function getNotificationPreferences(Calendar_Model_Attender $atten } /** + * Not a resource? = Don't do anything + * Suppress Notifications = Don't send anything. Neither to Users or Resource + * ResourceMailsForEditors = Send to Editors and Resource + * ! ResourceMailsForEditors = Send only to Resource + * * @param $attender * @param $_notificationLevel * @param $recipients @@ -427,27 +432,31 @@ public static function getNotificationPreferences(Calendar_Model_Attender $atten protected function _handleResourceEditors($attender, $_notificationLevel, &$recipients, &$action, &$sendLevel, $_updates) { // Add additional recipients for resources - if ($attender->user_type !== Calendar_Model_Attender::USERTYPE_RESOURCE - || ! Calendar_Config::getInstance()->get(Calendar_Config::RESOURCE_MAIL_FOR_EDITORS)) { - + if ($attender->user_type !== Calendar_Model_Attender::USERTYPE_RESOURCE) { return true; } - - // Set custom status booked - if ($action == 'created') { - $action = 'booked'; - } - + $resource = Calendar_Controller_Resource::getInstance()->get($attender->user_id); + // Suppress all notifications? if ($resource->suppress_notification) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ - . " Do not send Notifications for this resource: ". $resource->name); + . " Do not send Notifications for this resource: ". $resource->name); // $recipients will still contain the resource itself // Edit 13.12.2016 Remove resource as well and supress ALL notifications $recipients = array(); return true; } - + + // Send Mails to Editors? + if (! Calendar_Config::getInstance()->get(Calendar_Config::RESOURCE_MAIL_FOR_EDITORS)) { + return true; + } + + // Set custom status booked + if ($action == 'created') { + $action = 'booked'; + } + // The resource has no account there for the organizer preference (sendLevel) is used. We don't want that $sendLevel = self::NOTIFICATION_LEVEL_EVENT_RESCHEDULE; //handle attendee status change diff --git a/tine20/Calendar/Controller/MSEventFacade.php b/tine20/Calendar/Controller/MSEventFacade.php index 4b9b4992aee..e4f90a53c42 100644 --- a/tine20/Calendar/Controller/MSEventFacade.php +++ b/tine20/Calendar/Controller/MSEventFacade.php @@ -731,13 +731,14 @@ protected function _filterEventsByDTStarts($_events, $_dtstarts, $dtStartDiff=nu return $filteredSet; } - protected function _resolveData($events) { + protected function _resolveData($events) + { $eventSet = $events instanceof Tinebase_Record_RecordSet ? $events->getClone(true) : new Tinebase_Record_RecordSet('Calendar_Model_Event', array($events)); // get recur exceptions - foreach($eventSet as $event) { + foreach ($eventSet as $event) { if ($event->rrule && !$event->exdate instanceof Tinebase_Record_RecordSet) { $exdates = $this->_eventController->getRecurExceptions($event, TRUE, $this->getEventFilter()); $event->exdate = $exdates; diff --git a/tine20/Calendar/Controller/Poll.php b/tine20/Calendar/Controller/Poll.php index a97d4cf95c6..1aec02fa249 100644 --- a/tine20/Calendar/Controller/Poll.php +++ b/tine20/Calendar/Controller/Poll.php @@ -238,14 +238,15 @@ public function inspectDeleteEvents($events) * inspect event helper * * @param Calendar_Model_Event $event - * @throws Tasks_Exception_UnexpectedValue + * @throws Tinebase_Exception_SystemGeneric */ protected function _inspectEvent($event) { $poll = $event->poll_id; if ($poll instanceof Calendar_Model_Poll) { if ($event->rrule || $event->isRecurException()) { - throw new Tasks_Exception_UnexpectedValue('Polls for recurring events are not supported'); + // _('Polls for recurring events are not supported') + throw new Tinebase_Exception_SystemGeneric('Polls for recurring events are not supported'); } try { $this->_inspectedPoll = $poll->getId(); diff --git a/tine20/Calendar/Controller/Resource.php b/tine20/Calendar/Controller/Resource.php index 9d54c1919ca..fc818c3bc7c 100644 --- a/tine20/Calendar/Controller/Resource.php +++ b/tine20/Calendar/Controller/Resource.php @@ -6,7 +6,7 @@ * @subpackage Controller * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3 * @author Cornelius Weiss - * @copyright Copyright (c) 2009-2014 Metaways Infosystems GmbH (http://www.metaways.de) + * @copyright Copyright (c) 2009-2018 Metaways Infosystems GmbH (http://www.metaways.de) */ /** @@ -48,7 +48,8 @@ class Calendar_Controller_Resource extends Tinebase_Controller_Record_Abstract * * don't use the constructor. use the singleton */ - private function __construct() { + private function __construct() + { $this->_applicationName = 'Calendar'; $this->_modelName = 'Calendar_Model_Resource'; @@ -58,10 +59,6 @@ private function __construct() { )); $this->_backend->setModlogActive(TRUE); - if (Tinebase_Core::getUser()->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES)) { - $this->_doContainerACLChecks = false; - } - $this->_getMultipleGrant = Calendar_Model_ResourceGrants::RESOURCE_READ; $this->_requiredFilterACLget = [Calendar_Model_ResourceGrants::RESOURCE_READ]; $this->_requiredFilterACLupdate = [Calendar_Model_ResourceGrants::RESOURCE_EDIT]; @@ -283,9 +280,8 @@ public function update(Tinebase_Record_Interface $_record, $_duplicateCheck = tr $container = Tinebase_Container::getInstance()->getContainerById($_record->container_id); /** @var Tinebase_Model_Container $eventContainer */ - if (is_array($_record->grants) && - (Tinebase_Core::getUser()->hasGrant($container, Calendar_Model_ResourceGrants::RESOURCE_ADMIN) || - Tinebase_Core::getUser()->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES))) { + if (is_array($_record->grants) && Tinebase_Core::getUser() + ->hasGrant($container, Calendar_Model_ResourceGrants::RESOURCE_ADMIN)) { $grants = $this->convertToEventGrants( new Tinebase_Record_RecordSet(Calendar_Model_ResourceGrants::class, $_record->grants)); @@ -321,26 +317,23 @@ protected function _checkGrant($_record, $_action, $_throw = true, $_errorMessag throw new Tinebase_Exception_AccessDenied('User object required to check grants'); } - if ($user->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES) || ($_action !== self::ACTION_DELETE && - $user->hasGrant($_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_ADMIN))) { - return true; - } - $hasGrant = false; switch ($_action) { case self::ACTION_GET: - $hasGrant = $user->hasGrant($_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_READ); + $hasGrant = $user->hasGrant($_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_READ) || + $user->hasGrant($_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_ADMIN); break; - /* there is no explicit create right, you need manage_resources for it case self::ACTION_CREATE: - break;*/ + $hasGrant = $user->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); + break; case self::ACTION_UPDATE: - $hasGrant = $user->hasGrant($_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_EDIT); + $hasGrant = $user->hasGrant($_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_EDIT) || + $user->hasGrant($_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_ADMIN); break; - /* there is no explicit delete right, you need manage_resources for it case self::ACTION_DELETE: - break;*/ + $hasGrant = $user->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES); + break; } if (! $hasGrant) { @@ -353,29 +346,6 @@ protected function _checkGrant($_record, $_action, $_throw = true, $_errorMessag return $hasGrant; } - /** - * check if user has the right to manage resources - * - * @param string $_action {get|create|update|delete} - * @return void - * @throws Tinebase_Exception_AccessDenied - */ - protected function _checkRight($_action) - { - switch ($_action) { - // only create requires Calendar_Acl_Rights::MANAGE_RESOURCES, all other acl checks are handled in _checkGrant - case 'create': - if (! Tinebase_Core::getUser()->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES)) { - throw new Tinebase_Exception_AccessDenied("You don't have the right to manage resources"); - } - break; - default; - break; - } - - parent::_checkRight($_action); - } - /** * delete linked objects (notes, relations, ...) of record * @@ -392,6 +362,22 @@ protected function _deleteLinkedObjects(Tinebase_Record_Interface $_record) return parent::_deleteLinkedObjects($_record); } + /** + * delete one record + * + * @param Tinebase_Record_Interface $_record + * @throws Tinebase_Exception_AccessDenied + */ + protected function _deleteRecord(Tinebase_Record_Interface $_record) + { + // event needs to be fired before the actual delete - otherwise for example the resource attender is no longer found ... + $event = new Calendar_Event_DeleteResource(); + $event->resource = $_record; + Tinebase_Event::fireEvent($event); + + parent::_deleteRecord($_record); + } + /** * returns recipients for a resource notification * diff --git a/tine20/Calendar/Convert/Resource/Json.php b/tine20/Calendar/Convert/Resource/Json.php index 630196c538a..24b88b71dfe 100644 --- a/tine20/Calendar/Convert/Resource/Json.php +++ b/tine20/Calendar/Convert/Resource/Json.php @@ -34,8 +34,7 @@ public function fromTine20Model(Tinebase_Record_Abstract $_record) ->getGrantsOfAccount(Tinebase_Core::getUser(), $_record->container_id)->toArray(); $user = Tinebase_Core::getUser(); - if ($user->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES) || - Tinebase_Container::getInstance()->hasGrant($user->getId(), $_record->container_id, + if (Tinebase_Container::getInstance()->hasGrant($user->getId(), $_record->container_id, Calendar_Model_ResourceGrants::RESOURCE_READ)) { $jsonData['grants'] = Tinebase_Container::getInstance()->getGrantsOfContainer($_record->container_id, true) ->toArray(); diff --git a/tine20/Calendar/Event/DeleteResource.php b/tine20/Calendar/Event/DeleteResource.php new file mode 100644 index 00000000000..a238a407838 --- /dev/null +++ b/tine20/Calendar/Event/DeleteResource.php @@ -0,0 +1,26 @@ + + * + */ + +/** + * event class for deleted resources + * + * @package Calendar + */ +class Calendar_Event_DeleteResource extends Tinebase_Event_Abstract +{ + /** + * the resource to be deleted + * + * @var Calendar_Model_Resource + */ + public $resource; +} diff --git a/tine20/Calendar/Frontend/ActiveSync.php b/tine20/Calendar/Frontend/ActiveSync.php index ab3c1bc6c36..5cbcc63d1a5 100644 --- a/tine20/Calendar/Frontend/ActiveSync.php +++ b/tine20/Calendar/Frontend/ActiveSync.php @@ -607,13 +607,20 @@ public function toTineModel(Syncroton_Model_IEntry $data, $entry = null) // AttendeeStatus send only on repsonse $parsedName = Addressbook_Model_Contact::splitName($attendee->name); + $status = intval($attendee->attendeeStatus); + if (isset($this->_attendeeStatusMapping[$status])) { + $status = $this->_attendeeStatusMapping[$status]; + } else { + $status = null; + } + $newAttendees[] = array( 'userType' => Calendar_Model_Attender::USERTYPE_USER, 'firstName' => $parsedName['n_given'], 'lastName' => $parsedName['n_family'], - #'partStat' => $status, 'role' => $role, - 'email' => $attendee->email + 'partStat' => $status, + 'email' => $attendee->email, ); } @@ -632,7 +639,7 @@ public function toTineModel(Syncroton_Model_IEntry $data, $entry = null) $oldExdates = $event->exdate instanceof Tinebase_Record_RecordSet ? $event->exdate : new Tinebase_Record_RecordSet('Calendar_Model_Event'); foreach ($data->$syncrotonProperty as $exception) { - $eventException = $this->_getRecurException($oldExdates, $exception); + $eventException = $this->_getRecurException($oldExdates, $exception, $entry); if ($exception->deleted == 0) { $eventException = $this->toTineModel($exception, $eventException); @@ -833,7 +840,8 @@ protected function _handleAlarms($data, $event) * @param Syncroton_Model_EventException $sevent * @return Calendar_Model_Event */ - protected function _getRecurException(Tinebase_Record_RecordSet $oldExdates, Syncroton_Model_EventException $sevent) + protected function _getRecurException(Tinebase_Record_RecordSet $oldExdates, Syncroton_Model_EventException $sevent, + Calendar_Model_Event $baseEvent = null) { // we need to use the user timezone here if this is a DATE (like this: RECURRENCE-ID;VALUE=DATE:20140429) $originalDtStart = new Tinebase_DateTime($sevent->exceptionStartTime); @@ -846,6 +854,7 @@ protected function _getRecurException(Tinebase_Record_RecordSet $oldExdates, Syn return new Calendar_Model_Event(array( 'recurid' => $originalDtStart, + 'attendee' => $baseEvent ? $baseEvent->attendee : null, )); } diff --git a/tine20/Calendar/Frontend/Json.php b/tine20/Calendar/Frontend/Json.php index 4b3ceec4ebb..a959aa672a3 100644 --- a/tine20/Calendar/Frontend/Json.php +++ b/tine20/Calendar/Frontend/Json.php @@ -6,7 +6,7 @@ * @subpackage Frontend * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3 * @author Cornelius Weiss - * @copyright Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de) + * @copyright Copyright (c) 2007-2018 Metaways Infosystems GmbH (http://www.metaways.de) */ /** @@ -386,10 +386,9 @@ public function searchResources($filter, $paging) * * @param array $recordData * @param bool $checkBusyConflicts - * @param string $range * @return array created/updated event */ - public function saveEvent($recordData, $checkBusyConflicts = FALSE, $range = Calendar_Model_Event::RANGE_THIS) + public function saveEvent($recordData, $checkBusyConflicts = FALSE) { $record = new Calendar_Model_Event([], true); $record->setFromJsonInUsersTimezone($recordData); @@ -400,7 +399,7 @@ public function saveEvent($recordData, $checkBusyConflicts = FALSE, $range = Cal if ((empty($record->id))) { $savedRecord = Calendar_Controller_Event::getInstance()->create($record, $checkBusyConflicts, false); } else { - $savedRecord = Calendar_Controller_Event::getInstance()->update($record, $checkBusyConflicts, $range, false); + $savedRecord = Calendar_Controller_Event::getInstance()->update($record, $checkBusyConflicts, false); } return $this->_recordToJson($savedRecord); diff --git a/tine20/Calendar/Model/Attender.php b/tine20/Calendar/Model/Attender.php index f0a3021d376..eb4a529ce8a 100644 --- a/tine20/Calendar/Model/Attender.php +++ b/tine20/Calendar/Model/Attender.php @@ -1156,10 +1156,9 @@ public static function resolveAttendee($eventAttendees, $resolveDisplayContainer // keep authkey if attender is a resource and user has manage_resources if ($attender->user_type === self::USERTYPE_RESOURCE && - (Tinebase_Core::getUser()->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES) || ( isset($attender['displaycontainer_id']) && !is_scalar($attender['displaycontainer_id']) && isset($attender['displaycontainer_id']['account_grants'][Calendar_Model_ResourceGrants::EVENTS_EDIT]) - && $attender['displaycontainer_id']['account_grants'][Calendar_Model_ResourceGrants::EVENTS_EDIT]))) { + && $attender['displaycontainer_id']['account_grants'][Calendar_Model_ResourceGrants::EVENTS_EDIT]) { continue; } diff --git a/tine20/Calendar/Model/ResourceGrants.php b/tine20/Calendar/Model/ResourceGrants.php index 69a73da7723..4e5b7fae6e3 100644 --- a/tine20/Calendar/Model/ResourceGrants.php +++ b/tine20/Calendar/Model/ResourceGrants.php @@ -80,65 +80,35 @@ public static function doSetGrantFailsafeCheck() public static function addCustomGetSharedContainerSQL(Zend_Db_Select $_select, Tinebase_Model_Application $_application, $_accountId, $_grant) { - if (Tinebase_Core::getUser()->hasRight('Calendar', Calendar_Acl_Rights::MANAGE_RESOURCES)) { - $db = $_select->getAdapter(); - $where = join(' ', $_select->getPart(Zend_Db_Select::WHERE)); - $_select->reset(Zend_Db_Select::WHERE); - $_select->where($where); - $_select->orWhere( - $db->quoteInto($db->quoteIdentifier('container.application_id') .' = ?', $_application->getId()) . ' AND ' . - $db->quoteInto($db->quoteIdentifier('container.type') . ' = ?', Tinebase_Model_Container::TYPE_SHARED) . ' AND ' . - $db->quoteIdentifier('container.is_deleted') . ' = 0 AND ' . - $db->quoteIdentifier('container.xprops') . ' LIKE ?','%"Resource":{"resource_id":"%' - ); - } else { - $grants = is_array($_grant) ? $_grant : array($_grant); - if (count($grants) > 1 || $grants[0] !== Tinebase_Model_Grants::GRANT_READ) { - return; - } - - $db = $_select->getAdapter(); - $groupMemberships = Tinebase_Group::getInstance()->getGroupMemberships($_accountId); - $roleMemberships = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($_accountId); - // enforce string for pgsql - array_walk($roleMemberships, function(&$item) {$item = (string)$item;}); - $quotedActId = $db->quoteIdentifier('container_acl.account_id'); - $quotedActType = $db->quoteIdentifier('container_acl.account_type'); - $anyoneSelect = ''; - if (! Tinebase_Config::getInstance()->get(Tinebase_Config::ANYONE_ACCOUNT_DISABLED)) { - $anyoneSelect = ' OR ' . $quotedActType . $db->quoteInto(' = ?', Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE); - } - - $where = join(' ', $_select->getPart(Zend_Db_Select::WHERE)); - $_select->reset(Zend_Db_Select::WHERE); - $_select->where($where); - $_select->orWhere( - $db->quoteInto($db->quoteIdentifier('container.application_id') .' = ?', $_application->getId()) . ' AND ' . - $db->quoteInto($db->quoteIdentifier('container.type') . ' = ?', Tinebase_Model_Container::TYPE_SHARED) . ' AND ' . - $db->quoteIdentifier('container.is_deleted') . ' = 0 AND ' . - $db->quoteIdentifier('container.xprops') . $db->quoteInto(' LIKE ?', '%"Resource":{"resource_id":"%') . ' AND ((' . - $db->quoteInto("{$quotedActId} = ? AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_USER), $_accountId) . ' ) OR (' . - $db->quoteInto("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP), empty($groupMemberships) ? ' ' : $groupMemberships) . ' ) OR (' . - $db->quoteInto("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE), empty($roleMemberships) ? ' ' : $roleMemberships) . ' )' . - $anyoneSelect . ' ) AND ' . $db->quoteIdentifier('container_acl.account_grant') . $db->quoteInto(' LIKE ?', Calendar_Model_ResourceGrants::EVENTS_FREEBUSY) - ); + $grants = is_array($_grant) ? $_grant : array($_grant); + if (count($grants) > 1 || $grants[0] !== Tinebase_Model_Grants::GRANT_READ) { + return; } - } - public function setSpecialGrantsByUser($_accountId) - { - if (Tinebase_User::getInstance()->getUserById($_accountId)->hasRight('Calendar', - Calendar_Acl_Rights::MANAGE_RESOURCES)) { - $myGrants = static::getAllGrants(); - $parentGrants = parent::getAllGrants(); - foreach ($myGrants as $grant) { - if (!in_array($grant, $parentGrants)) { - $this->{$grant} = true; - if (strpos($grant, 'events') === 0) { - $this->{strtolower($grant[6]) . substr($grant, 7)} = true; - } - } - } + $db = $_select->getAdapter(); + $groupMemberships = Tinebase_Group::getInstance()->getGroupMemberships($_accountId); + $roleMemberships = Tinebase_Acl_Roles::getInstance()->getRoleMemberships($_accountId); + // enforce string for pgsql + array_walk($roleMemberships, function(&$item) {$item = (string)$item;}); + $quotedActId = $db->quoteIdentifier('container_acl.account_id'); + $quotedActType = $db->quoteIdentifier('container_acl.account_type'); + $anyoneSelect = ''; + if (! Tinebase_Config::getInstance()->get(Tinebase_Config::ANYONE_ACCOUNT_DISABLED)) { + $anyoneSelect = ' OR ' . $quotedActType . $db->quoteInto(' = ?', Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE); } + + $where = join(' ', $_select->getPart(Zend_Db_Select::WHERE)); + $_select->reset(Zend_Db_Select::WHERE); + $_select->where($where); + $_select->orWhere( + $db->quoteInto($db->quoteIdentifier('container.application_id') .' = ?', $_application->getId()) . ' AND ' . + $db->quoteInto($db->quoteIdentifier('container.type') . ' = ?', Tinebase_Model_Container::TYPE_SHARED) . ' AND ' . + $db->quoteIdentifier('container.is_deleted') . ' = 0 AND ' . + $db->quoteIdentifier('container.xprops') . $db->quoteInto(' LIKE ?', '%"Resource":{"resource_id":"%') . ' AND ((' . + $db->quoteInto("{$quotedActId} = ? AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_USER), $_accountId) . ' ) OR (' . + $db->quoteInto("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP), empty($groupMemberships) ? ' ' : $groupMemberships) . ' ) OR (' . + $db->quoteInto("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE), empty($roleMemberships) ? ' ' : $roleMemberships) . ' )' . + $anyoneSelect . ' ) AND ' . $db->quoteIdentifier('container_acl.account_grant') . $db->quoteInto(' LIKE ?', Calendar_Model_ResourceGrants::EVENTS_FREEBUSY) + ); } } \ No newline at end of file diff --git a/tine20/Calendar/Model/Rrule.php b/tine20/Calendar/Model/Rrule.php index 160ad6c384a..60500fc031a 100644 --- a/tine20/Calendar/Model/Rrule.php +++ b/tine20/Calendar/Model/Rrule.php @@ -6,7 +6,7 @@ * @subpackage Model * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3 * @author Cornelius Weiss - * @copyright Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de) + * @copyright Copyright (c) 2009-2018 Metaways Infosystems GmbH (http://www.metaways.de) */ /** @@ -19,6 +19,12 @@ * * @package Calendar * @subpackage Model + * + * @property string $freq + * @property string $bymonth + * @property string $byday + * @property string $bymonthday + * @property Tinebase_DateTime $until */ class Calendar_Model_Rrule extends Tinebase_Record_Abstract { @@ -751,6 +757,7 @@ public static function computeRecurrenceSet($_event, $_exceptions, $_from, $_unt } } } + $recurSet->sort('dtstart'); break; case self::FREQ_MONTHLY: diff --git a/tine20/Calendar/Setup/DemoData.php b/tine20/Calendar/Setup/DemoData.php index 8907a40ab3d..d06fc2ece76 100644 --- a/tine20/Calendar/Setup/DemoData.php +++ b/tine20/Calendar/Setup/DemoData.php @@ -169,12 +169,32 @@ private function _createSharedCalendar() 'description' => static::$_de ? 'Bis zu 10 Personen' : 'Up to 10 people', 'email' => 'mars@tin20.com', 'is_location' => TRUE, + 'grants' => [[ + 'account_id' => Tinebase_Core::getUser()->getId(), + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER, + Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true, + ],[ + 'account_id' => 0, + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE, + Calendar_Model_ResourceGrants::RESOURCE_INVITE => true, + Calendar_Model_ResourceGrants::RESOURCE_READ => true, + ]] ))); $this->_ressources[] = Calendar_Controller_Resource::getInstance()->create(new Calendar_Model_Resource(array( 'name' => static::$_de ? 'Besprechnungsraum Venus (2.OG)' : 'Meeting Room Venus (second floor)', 'description' => static::$_de ? 'Bis zu 14 Personen' : 'Up to 14 people', 'email' => 'venus@tin20.com', 'is_location' => TRUE, + 'grants' => [[ + 'account_id' => Tinebase_Core::getUser()->getId(), + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER, + Calendar_Model_ResourceGrants::RESOURCE_ADMIN => true, + ],[ + 'account_id' => 0, + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE, + Calendar_Model_ResourceGrants::RESOURCE_INVITE => true, + Calendar_Model_ResourceGrants::RESOURCE_READ => true, + ]] ))); } diff --git a/tine20/Calendar/js/AbstractView.js b/tine20/Calendar/js/AbstractView.js index 17297168759..523c74de136 100644 --- a/tine20/Calendar/js/AbstractView.js +++ b/tine20/Calendar/js/AbstractView.js @@ -116,7 +116,7 @@ Tine.Calendar.AbstractView = Ext.extend(Ext.Container, { /** * @private */ - onAdd : function(ds, records, index){ + onAdd : function(ds, records, index) { for (var i=0; i