diff --git a/src/CiviEntityStorage.php b/src/CiviEntityStorage.php index 59c30fe4..6d905ab3 100644 --- a/src/CiviEntityStorage.php +++ b/src/CiviEntityStorage.php @@ -11,6 +11,8 @@ use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Language\LanguageInterface; +use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem; +use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; use Drupal\field\FieldStorageConfigInterface; /** @@ -164,6 +166,7 @@ protected function doLoadMultiple(array $ids = NULL) { // get all the fields $fields = $this->getCiviCrmApi()->getFields($this->entityType->get('civicrm_entity')); + $field_names = []; foreach ($fields as $field) { $field_names[] = $field['name']; } @@ -362,13 +365,15 @@ protected function initFieldValues(ContentEntityInterface $entity, array $values // Handle if the value provided is a timestamp. // @note: This only occurred during test migrations. elseif (is_numeric($item[$main_property_name])) { - $item_values[$delta][$main_property_name] = (new \DateTime())->setTimestamp($item[$main_property_name])->format(DATETIME_DATETIME_STORAGE_FORMAT); + $item_values[$delta][$main_property_name] = (new \DateTime())->setTimestamp($item[$main_property_name])->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT); } else { + $datetime_format = $definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT; // CiviCRM gives us the datetime in the users timezone (or no // timezone at all) but Drupal expects it in UTC. So, we need to // convert from the users timezone into UTC. - $item_values[$delta][$main_property_name] = (new \DateTime($item[$main_property_name], new \DateTimeZone(drupal_get_user_timezone())))->setTimezone(new \DateTimeZone('UTC'))->format(DATETIME_DATETIME_STORAGE_FORMAT); + $datetime_value = (new \DateTime($item[$main_property_name], new \DateTimeZone(drupal_get_user_timezone())))->setTimezone(new \DateTimeZone('UTC'))->format($datetime_format); + $item_values[$delta][$main_property_name] = $datetime_value; } } $items->setValue($item_values); diff --git a/tests/src/Kernel/CivicrmBaseFieldTest.php b/tests/src/Kernel/CivicrmBaseFieldTest.php index d39ca568..759d1948 100644 --- a/tests/src/Kernel/CivicrmBaseFieldTest.php +++ b/tests/src/Kernel/CivicrmBaseFieldTest.php @@ -21,8 +21,8 @@ public function testBaseFields() { $this->assertTrue(isset($base_fields['id'])); $this->assertEquals('integer', $base_fields['id']->getType()); $this->assertEquals('civicrm_contact', $base_fields['id']->getTargetEntityTypeId()); - $this->assertTrue(isset($base_fields['title'])); - $this->assertEquals('string', $base_fields['title']->getType()); + $this->assertTrue(isset($base_fields['display_name'])); + $this->assertEquals('string', $base_fields['display_name']->getType()); $this->assertTrue(isset($base_fields['phone_number'])); $this->assertEquals('string', $base_fields['phone_number']->getType()); $this->assertTrue(isset($base_fields['birth_date'])); diff --git a/tests/src/Kernel/CivicrmEntityTestBase.php b/tests/src/Kernel/CivicrmEntityTestBase.php index 36551dfd..f30d0d67 100644 --- a/tests/src/Kernel/CivicrmEntityTestBase.php +++ b/tests/src/Kernel/CivicrmEntityTestBase.php @@ -47,7 +47,15 @@ protected function setUp() { */ protected function mockCiviCrmApi() { $civicrm_api_mock = $this->prophesize(CiviCrmApiInterface::class); - $civicrm_api_mock->get('event', Argument::type('array'))->willReturn($this->sampleEventsData()); + $civicrm_api_mock->get('event', [ + 'id' => 1, + 'return' => array_keys($this->sampleEventsGetFields()), + ])->willReturn($this->sampleEventsData()); + $civicrm_api_mock->get('contact', [ + 'id' => 10, + 'return' => array_keys($this->sampleContactGetFields()), + ])->willReturn($this->sampleContactData()); + $civicrm_api_mock->getFields('event')->willReturn($this->sampleEventsGetFields()); $civicrm_api_mock->getFields('event', 'create')->willReturn($this->sampleEventsGetFields()); $civicrm_api_mock->getFields('contact')->willReturn($this->sampleContactGetFields()); @@ -76,6 +84,7 @@ protected function mockCiviCrmApi() { $civicrm_api_mock->save('event', Argument::type('array'))->willReturn(TRUE); $civicrm_api_mock->delete('event', Argument::type('array'))->willReturn(TRUE); + $this->container->set('civicrm_entity.api', $civicrm_api_mock->reveal()); } @@ -1436,17 +1445,26 @@ protected function sampleContactGetFields() { '0' => 'contact_id', ], ], - 'title' => [ - 'name' => 'title', + 'display_name' => [ + 'name' => 'display_name', 'type' => 2, - 'title' => 'Group Title', - 'description' => 'Name of Group.', - 'maxlength' => 64, + 'title' => 'Display Name', + 'description' => 'Formatted name representing preferred format for display/print/other output.', + 'maxlength' => 128, 'size' => 30, - 'table_name' => 'civicrm_group', - 'entity' => 'Group', - 'bao' => 'CRM_Contact_BAO_Group', - 'api.required' => 1, + 'where' => 'civicrm_contact.display_name', + 'export' => TRUE, + 'table_name' => 'civicrm_contact', + 'entity' => 'Contact', + 'bao' => 'CRM_Contact_BAO_Contact', + 'localizable' => 0, + 'html' => + [ + 'type' => 'Text', + 'maxlength' => 128, + 'size' => 30, + ], + 'is_core_field' => TRUE, ], 'phone_number' => [ 'name' => 'phone_number', @@ -1592,6 +1610,90 @@ protected function minimalGenericGetFields() { ]; } + /** + * Provides sample contacts data. + * + * @return array + * The events data. + */ + protected function sampleContactData() { + return [ + 0 => [ + 'contact_id' => '10', + 'contact_type' => 'Individual', + 'contact_sub_type' => '', + 'sort_name' => 'Neal, Emma', + 'display_name' => 'Emma Neal', + 'do_not_email' => '0', + 'do_not_phone' => '0', + 'do_not_mail' => '0', + 'do_not_sms' => '0', + 'do_not_trade' => '0', + 'is_opt_out' => '0', + 'legal_identifier' => '', + 'external_identifier' => '', + 'nick_name' => '', + 'legal_name' => '', + 'image_URL' => '', + 'preferred_communication_method' => + [ + 0 => '5', + ], + 'preferred_language' => '', + 'preferred_mail_format' => 'Both', + 'first_name' => 'Emma', + 'middle_name' => '', + 'last_name' => 'Neal', + 'prefix_id' => '', + 'suffix_id' => '', + 'formal_title' => '', + 'communication_style_id' => '', + 'job_title' => '', + 'gender_id' => '2', + 'birth_date' => '1982-06-28', + 'is_deceased' => '0', + 'deceased_date' => '', + 'household_name' => '', + 'organization_name' => '', + 'sic_code' => '', + 'contact_is_deleted' => '0', + 'current_employer' => '', + 'address_id' => '36', + 'street_address' => '2262 Frances Ct', + 'supplemental_address_1' => '', + 'supplemental_address_2' => '', + 'supplemental_address_3' => '', + 'city' => 'Memphis', + 'postal_code_suffix' => '', + 'postal_code' => '68042', + 'geo_code_1' => '41.095604', + 'geo_code_2' => '-96.43168', + 'state_province_id' => '1026', + 'country_id' => '1228', + 'phone_id' => '62', + 'phone_type_id' => '1', + 'phone' => '(555) 555-555', + 'email_id' => '62', + 'email' => 'emma@example.com', + 'on_hold' => '0', + 'im_id' => '', + 'provider_id' => '', + 'im' => '', + 'worldregion_id' => '2', + 'world_region' => 'America South, Central, North and Caribbean', + 'languages' => '', + 'individual_prefix' => '', + 'individual_suffix' => '', + 'communication_style' => '', + 'gender' => 'Male', + 'state_province_name' => 'Nebraska', + 'state_province' => 'NE', + 'country' => 'United States', + 'id' => '10', + ], + ]; + } + /** * Provides sample events data. * diff --git a/tests/src/Kernel/CivicrmStorageGetTest.php b/tests/src/Kernel/CivicrmStorageGetTest.php index 89369d49..872e0014 100644 --- a/tests/src/Kernel/CivicrmStorageGetTest.php +++ b/tests/src/Kernel/CivicrmStorageGetTest.php @@ -2,7 +2,10 @@ namespace Drupal\Tests\civicrm_entity\Kernel; +use Drupal\civicrm_entity\CiviCrmApiInterface; use Drupal\civicrm_entity\Entity\CivicrmEntity; +use Drupal\Core\TypedData\Type\DateTimeInterface; +use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; /** * Tests the storage. @@ -15,15 +18,23 @@ class CivicrmStorageGetTest extends CivicrmEntityTestBase { * Tests getting a single entity. */ public function testGet() { - $result = $this->container->get('civicrm_entity.api') - ->get('event', ['id' => 1]); + $result = $this->container->get('civicrm_entity.api')->get('event', [ + 'id' => 1, + 'return' => array_keys($this->sampleEventsGetFields()), + ]); $this->assertEquals('Fall Fundraiser Dinner', $result[0]['title']); + + $result = $this->container->get('civicrm_entity.api')->get('contact', [ + 'id' => 10, + 'return' => array_keys($this->sampleContactGetFields()), + ]); + $this->assertEquals('Emma Neal', $result[0]['display_name']); } /** * Tests loading an entity through storage. */ - public function testLoad() { + public function testLoadEvent() { $storage = $this->container->get('entity_type.manager') ->getStorage('civicrm_event'); $entity = $storage->load(1); @@ -35,4 +46,89 @@ public function testLoad() { $this->assertTrue($entity->get('is_public')->value); } + /** + * Tests loading a contact. + */ + public function testLoadContact() { + $storage = $this->container->get('entity_type.manager')->getStorage('civicrm_contact'); + $entity = $storage->load(10); + $this->assertInstanceOf(CivicrmEntity::class, $entity); + $this->assertEquals($entity->id(), 10); + $this->assertEquals($entity->get('display_name')->value, 'Emma Neal'); + $this->assertEquals('1982/06/27', $entity->get('birth_date')->date->format('Y/m/d')); + } + + /** + * Tests datetime fields and timezone conversions. + * + * CiviCRM stores times in the user's timezone. However Drupal assumes all + * times are in UTC. When loading a date time, CiviEntityStorage converts + * the time into UTC so that Drupal handles the timezone correctly. + * + * @dataProvider datetimeTimezoneDataProvider + * + * @param array $original_datetimes + * @param array $expected_utc_datetime + * @param $timezone + * + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + */ + public function testDatetimeTimezone(array $original_datetimes, array $expected_utc_datetime, $timezone) { + date_default_timezone_set($timezone); + $civicrm_api_mock = $this->prophesize(CiviCrmApiInterface::class); + $civicrm_api_mock->get('event', [ + 'id' => 1, + 'return' => array_keys($this->sampleEventsGetFields()), + ])->willReturn([$original_datetimes]); + + $storage = $this->container->get('entity_type.manager') + ->getStorage('civicrm_event'); + $entity = $storage->load(1); + foreach ($expected_utc_datetime as $field_name => $field_data) { + $this->assertEquals( + $field_data, + $entity->get($field_name)->date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT) + ); + } + } + + public function datetimeTimezoneDataProvider() { + yield [ + [ + 'start_date' => '2018-05-02 17:00:00', + 'end_date' => '2018-05-04 17:00:00', + ], + // America/Chicago is UTC-5 + [ + 'start_date' => '2018-05-02T22:00:00', + 'end_date' => '2018-05-04T22:00:00', + ], + 'America/Chicago', + ]; + yield [ + [ + 'start_date' => '2018-05-02 17:00:00', + 'end_date' => '2018-05-04 17:00:00', + ], + [ + 'start_date' => '2018-05-02T17:00:00', + 'end_date' => '2018-05-04T17:00:00', + ], + 'UTC', + ]; + yield [ + [ + 'start_date' => '2018-05-02 17:00:00', + 'end_date' => '2018-05-04 17:00:00', + ], + // Europe/Berlin if UTC-2 + [ + 'start_date' => '2018-05-02T15:00:00', + 'end_date' => '2018-05-04T15:00:00', + ], + 'Europe/Berlin', + ]; + } + }