From 87fec90422c9fd56f0ae4f92bdb66fa9c19d9511 Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Tue, 20 Sep 2022 15:29:42 +0800 Subject: [PATCH 01/41] Add workaround for views. --- civicrm_entity.views.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/civicrm_entity.views.inc b/civicrm_entity.views.inc index d79cd496..5f60e404 100644 --- a/civicrm_entity.views.inc +++ b/civicrm_entity.views.inc @@ -143,6 +143,7 @@ function civicrm_entity_views_data_alter(&$data) { $data['civicrm_contact']['current_employer']['real field'] = 'organization_name'; $data['civicrm_contact']['employer_id']['field']['id'] = 'standard'; + $data['civicrm_contribution']['contribution_source']['real field'] = 'source'; } /** From 4605d700c6dd779c43371bc35487c8f4d86e401b Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Tue, 20 Sep 2022 15:27:06 +0800 Subject: [PATCH 02/41] Add workaround for contribution source not displaying. --- src/CiviCrmApi.php | 12 ++++++++++++ src/SupportedEntities.php | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/CiviCrmApi.php b/src/CiviCrmApi.php index 74c9f1a5..10c7b822 100644 --- a/src/CiviCrmApi.php +++ b/src/CiviCrmApi.php @@ -31,6 +31,12 @@ public function __construct(Civicrm $civicrm) { */ public function get($entity, array $params = []) { $this->initialize(); + + if ($entity == 'contribution') { + $params['return'][] = 'contribution_source'; + $params['return'] = array_diff($params['return'], ['source']); + } + $result = civicrm_api3($entity, 'get', $params); return $result['values']; } @@ -73,6 +79,12 @@ public function getFields($entity, $action = '') { // 'sequential' => 1, 'action' => $action, ]); + + if ($entity == 'contribution' && isset($result['values']['source'])) { + $result['values']['contribution_source'] = $result['values']['source']; + unset($result['values']['source']); + } + return $result['values']; } diff --git a/src/SupportedEntities.php b/src/SupportedEntities.php index dca8a5c2..9bb86fd6 100644 --- a/src/SupportedEntities.php +++ b/src/SupportedEntities.php @@ -143,7 +143,7 @@ public static function getInfo() { $civicrm_entity_info['civicrm_contribution'] = [ 'civicrm entity label' => t('Contribution'), 'civicrm entity name' => 'contribution', - 'label property' => 'source', + 'label property' => 'contribution_source', 'permissions' => [ 'view' => ['access CiviContribute', 'administer CiviCRM'], 'edit' => ['edit contributions', 'administer CiviCRM'], From 8badf803255523f41af9916aa8418c5cce4b9fed Mon Sep 17 00:00:00 2001 From: Arnold French Date: Thu, 13 Apr 2023 17:58:28 +0800 Subject: [PATCH 03/41] Disable creation of entity tables. --- src/Entity/Sql/CivicrmEntityStorageSchema.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Entity/Sql/CivicrmEntityStorageSchema.php b/src/Entity/Sql/CivicrmEntityStorageSchema.php index 7184558d..2f42e4a0 100644 --- a/src/Entity/Sql/CivicrmEntityStorageSchema.php +++ b/src/Entity/Sql/CivicrmEntityStorageSchema.php @@ -50,4 +50,11 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac return FALSE; } + /** + * {@inheritdoc} + */ + public function onFieldableEntityTypeCreate(EntityTypeInterface $entity_type, array $field_storage_definitions) { + return; + } + } From ae29aa3a3edfe82b2d16dde1f3d459f46263eff8 Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Thu, 23 Mar 2023 03:35:10 +0800 Subject: [PATCH 04/41] Add support for field_group. --- civicrm_entity.module | 67 +++++++++++++++++++++++++++++++++ src/Routing/RouteSubscriber.php | 37 +++++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/civicrm_entity.module b/civicrm_entity.module index e5317c5d..02bb9dd8 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -30,7 +30,9 @@ use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\query\QueryPluginBase; use Drupal\Core\Database\Database; +use Drupal\Core\Entity\Display\EntityDisplayInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Form\FormStateInterface; /** * Implements hook_theme(). @@ -241,6 +243,71 @@ function civicrm_entity_entity_view_display_alter(EntityViewDisplayInterface $di } } +/** + * Implements hook_entity_view_alter(). + */ +function civicrm_entity_entity_view_alter(array &$build, EntityInterface $entity, EntityDisplayInterface $display) { + $entity_type = $entity->getEntityType(); + if ($entity_type->get('civicrm_entity') && $entity_type->hasKey('bundle') && \Drupal::moduleHandler()->moduleExists('field_group')) { + $entity_display_repository = \Drupal::service('entity_display.repository'); + $entity_view_mode_ids = array_keys($entity_display_repository->getViewModeOptions($entity_type->id())); + + $context = [ + 'entity_type' => $display->getTargetEntityTypeId(), + 'bundle' => $entity_type->id(), + 'entity' => $entity, + 'display_context' => 'view', + 'mode' => in_array($display->getMode(), $entity_view_mode_ids) ? $display->getMode() : $entity_display_repository::DEFAULT_DISPLAY_MODE, + ]; + + field_group_attach_groups($build, $context); + } +} + +/** + * Implements hook_form_alter(). + */ +function civicrm_entity_form_alter(array &$form, FormStateInterface $form_state, $form_id) { + $form_object = $form_state->getFormObject(); + if ($form_object instanceof CivicrmEntityForm) { + + /** + * @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display + */ + $storage = $form_state->getStorage(); + if (!empty($storage['form_display'])) { + $form_display = $storage['form_display']; + $entity = $form_object->getEntity(); + + if ($entity->getEntityType()->hasKey('bundle')) { + $context = [ + 'entity_type' => $entity->getEntityTypeId(), + 'bundle' => $entity->getEntityTypeId(), + 'entity' => $entity, + 'context' => 'form', + 'display_context' => 'form', + 'mode' => $form_display->getMode(), + ]; + + field_group_attach_groups($form, $context); + } + } + } +} + +/** + * Implements hook_module_implements_alter(). + */ +function civicrm_entity_module_implements_alter(&$implementations, $hook) { + switch ($hook) { + case 'form_alter': + $group = $implementations['civicrm_entity']; + unset($implementations['civicrm_entity']); + $implementations['civicrm_entity'] = $group; + break; + } +} + /** * Implements callback_allowed_values_function(). * diff --git a/src/Routing/RouteSubscriber.php b/src/Routing/RouteSubscriber.php index 8a4f3b41..76dc6ae2 100644 --- a/src/Routing/RouteSubscriber.php +++ b/src/Routing/RouteSubscriber.php @@ -50,6 +50,7 @@ protected function alterRoutes(RouteCollection $collection) { } $has_layout_builder = $this->moduleHandler->moduleExists('layout_builder'); + $has_field_group = $this->moduleHandler->moduleExists('field_group'); foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { if (!$entity_type->get('civicrm_entity_ui_exposed')) { continue; @@ -109,6 +110,40 @@ protected function alterRoutes(RouteCollection $collection) { ]; } + if ($has_field_group) { + $field_ui_routes["field_ui.field_group_add_$entity_type_id.form_display"] = [ + 'bundle' => $entity_type_id, + ]; + + $field_ui_routes["field_ui.field_group_add_$entity_type_id.form_display.form_mode"] = [ + 'bundle' => $entity_type_id, + ]; + + $field_ui_routes["field_ui.field_group_add_$entity_type_id.display"] = [ + 'bundle' => $entity_type_id, + ]; + + $field_ui_routes["field_ui.field_group_add_$entity_type_id.display.view_mode"] = [ + 'bundle' => $entity_type_id, + ]; + + $field_ui_routes["field_ui.field_group_delete_$entity_type_id.form_display"] = [ + 'bundle' => $entity_type_id, + ]; + + $field_ui_routes["field_ui.field_group_delete_$entity_type_id.form_display.form_mode"] = [ + 'bundle' => $entity_type_id, + ]; + + $field_ui_routes["field_ui.field_group_delete_$entity_type_id.display"] = [ + 'bundle' => $entity_type_id, + ]; + + $field_ui_routes["field_ui.field_group_delete_$entity_type_id.display.view_mode"] = [ + 'bundle' => $entity_type_id, + ]; + } + foreach ($field_ui_routes as $route_name => $defaults) { $route = $collection->get($route_name); assert($route !== NULL); @@ -125,7 +160,7 @@ protected function alterRoutes(RouteCollection $collection) { public static function getSubscribedEvents() : array { $events = parent::getSubscribedEvents(); // Field UI's route subscriber runs at -100. - $events[RoutingEvents::ALTER] = ['onAlterRoutes', -200]; + $events[RoutingEvents::ALTER] = ['onAlterRoutes', -250]; return $events; } From b316f4c760ca4857fd93e4c84896209200d918ee Mon Sep 17 00:00:00 2001 From: Arnold French Date: Tue, 9 May 2023 14:59:38 +0800 Subject: [PATCH 05/41] Set weight of the module. --- civicrm_entity.install | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 civicrm_entity.install diff --git a/civicrm_entity.install b/civicrm_entity.install new file mode 100644 index 00000000..a5eec453 --- /dev/null +++ b/civicrm_entity.install @@ -0,0 +1,20 @@ + Date: Wed, 10 May 2023 02:50:38 +0800 Subject: [PATCH 06/41] Add a check for field_group module existence. --- civicrm_entity.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/civicrm_entity.module b/civicrm_entity.module index 02bb9dd8..3ede5aa0 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -279,7 +279,7 @@ function civicrm_entity_form_alter(array &$form, FormStateInterface $form_state, $form_display = $storage['form_display']; $entity = $form_object->getEntity(); - if ($entity->getEntityType()->hasKey('bundle')) { + if ($entity->getEntityType()->hasKey('bundle') && \Drupal::moduleHandler()->moduleExists('field_group')) { $context = [ 'entity_type' => $entity->getEntityTypeId(), 'bundle' => $entity->getEntityTypeId(), From dfe15ff0b856538ee4e5a1c1d5bceab200deb73a Mon Sep 17 00:00:00 2001 From: Finn Lewis Date: Wed, 1 Mar 2023 13:48:41 +0000 Subject: [PATCH 07/41] Replace isset with is_numeric in prepareItemsByDelta() when $row->delta is false, isset(false) seems to return true, so we are sometimes returning an array or items like [0 => null] which causes 'TypeError: Unsupported operand types: null + array' --- src/Plugin/views/field/CustomEntityField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/views/field/CustomEntityField.php b/src/Plugin/views/field/CustomEntityField.php index 90a2c054..87d262a8 100644 --- a/src/Plugin/views/field/CustomEntityField.php +++ b/src/Plugin/views/field/CustomEntityField.php @@ -219,7 +219,7 @@ protected function prepareItemsByDelta(array $all_values) { if ($this->limit_values) { $row = $this->view->result[$this->view->row_index]; - if (!$this->options['group_rows'] && isset($row->delta)) { + if (!$this->options['group_rows'] && is_numeric($row->delta)) { return [$all_values[$row->delta]]; } } From 6f47d4eab02d66725914588e0fb50229cf60e24c Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Sun, 28 May 2023 15:33:31 +0800 Subject: [PATCH 08/41] Require specific version of drupal core. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index d38923d6..991eb42f 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,7 @@ "homepage": "http://drupal.org/project/civicrm_entity", "license": "GPL-2.0+", "require": { + "drupal/core": "^10", "civicrm/civicrm-drupal-8": "*" }, "require-dev": { From ae2629e507407712d82b087f5bb42def410a25b7 Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Fri, 2 Jun 2023 02:59:53 +0800 Subject: [PATCH 09/41] Add option to disable links. (#414) * Add option to disable links. * Update settings page. * Clear render cache after saving. * Add settings for disabling Drupal pages per CE type. * Alter entity types for disabling drupal pages. * Add fix for bundles for "add" operation. * Check if route exists when defining local tasks. * Only add checks for canonical since that is used as base route. * Add a check when saving. * Fix schema. * Update tests. * Add check if route exists. * Add fix for list builder for bundles. * Add a check for edit form. * Fix WSOD and add some tests. * Reverse behavior. * Update test. * Update test. --------- Co-authored-by: Arnold French --- civicrm_entity.install | 21 +++++ civicrm_entity.module | 34 +++++++- config/install/civicrm_entity.settings.yml | 2 + config/schema/civicrm_entity.schema.yml | 16 ++++ src/CivicrmEntityListBuilder.php | 16 ++-- src/Form/CivicrmEntityForm.php | 37 ++++++-- src/Form/CivicrmEntitySettings.php | 84 ++++++++++++++++--- .../Derivative/CivicrmEntityLocalAction.php | 21 +++-- src/Plugin/Derivative/DynamicLocalTasks.php | 60 ++++++++++--- src/Routing/CiviCrmEntityRouteProvider.php | 2 +- src/Routing/RouteSubscriber.php | 8 +- .../CivicrmEntitySettingsFormTest.php | 19 +++++ .../CivicrmEntityTestBase.php | 2 +- 13 files changed, 267 insertions(+), 55 deletions(-) diff --git a/civicrm_entity.install b/civicrm_entity.install index a5eec453..d6a77240 100644 --- a/civicrm_entity.install +++ b/civicrm_entity.install @@ -18,3 +18,24 @@ function civicrm_entity_install() { function civicrm_entity_update_8001() { module_set_weight('civicrm_entity', 1); } + +/** + * Enable Drupal pages for enabled entity types. + */ +function civicrm_entity_update_8002() { + $config = \Drupal::service('config.factory')->getEditable('civicrm_entity.settings'); + $enable_links_per_type = []; + + foreach ($config->get('enabled_entity_types') as $entity_type) { + $enable_links_per_type[$entity_type]['values'] = [ + 'view' => 'view', + 'add' => 'add', + 'edit' => 'edit', + 'delete' => 'delete', + ]; + } + + $config + ->set('enable_links_per_type', $enable_links_per_type) + ->save(); +} diff --git a/civicrm_entity.module b/civicrm_entity.module index 3ede5aa0..b1ca5383 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -58,6 +58,7 @@ function civicrm_entity_entity_type_build(array &$entity_types) { $supported_entities = SupportedEntities::getInfo(); $config = \Drupal::config('civicrm_entity.settings'); $enabled_entity_types = $config->get('enabled_entity_types') ?: []; + $enable_links_per_type = $config->get('enable_links_per_type') ?: []; foreach ($supported_entities as $entity_type_id => $civicrm_entity_info) { $clean_entity_type_id = str_replace('_', '-', $entity_type_id); $civicrm_entity_name = $civicrm_entity_info['civicrm entity name']; @@ -114,11 +115,40 @@ function civicrm_entity_entity_type_build(array &$entity_types) { 'canonical' => sprintf('/%s/{%s}', $clean_entity_type_id, $entity_type_id), 'delete-form' => sprintf('/%s/{%s}/delete', $clean_entity_type_id, $entity_type_id), 'edit-form' => sprintf('/%s/{%s}/edit', $clean_entity_type_id, $entity_type_id), - 'add-form' => sprintf('/%s/add', $clean_entity_type_id), + 'add-form' => sprintf('/%s/add', $clean_entity_type_id, $entity_type_id), 'collection' => sprintf('/admin/structure/civicrm-entity/%s', $clean_entity_type_id), ], 'field_ui_base_route' => "entity.$entity_type_id.collection", ]); + + if (!empty($enable_links_per_type) && in_array($entity_type_id, array_keys($enable_links_per_type))) { + $enable_links = array_filter($enable_links_per_type[$entity_type_id]['values']); + + if (!in_array('view', $enable_links)) { + unset($entity_type_info['links']['canonical']); + } + + if (!in_array('delete', $enable_links)) { + unset($entity_type_info['links']['delete-form']); + } + + if (!in_array('edit', $enable_links)) { + unset($entity_type_info['links']['edit-form']); + } + + if (!in_array('add', $enable_links)) { + unset($entity_type_info['links']['add-form']); + } + } + + if ($config->get('disable_links')) { + unset( + $entity_type_info['links']['canonical'], + $entity_type_info['links']['delete-form'], + $entity_type_info['links']['edit-form'], + $entity_type_info['links']['add-form'], + ); + } } // If this entity has bundle support, we define the bundle field as "bundle" @@ -130,7 +160,7 @@ function civicrm_entity_entity_type_build(array &$entity_types) { if (!empty($civicrm_entity_info['bundle property'])) { $entity_type_info['entity_keys']['bundle'] = 'bundle'; $entity_type_info['civicrm_bundle_property'] = $civicrm_entity_info['bundle property']; - if (isset($entity_type_info['links'])) { + if (isset($entity_type_info['links']['add-form'])) { // For entities with bundles that are exposed, add the `bundle` key to // the add-form route. In CiviCrmEntityRouteProvider::getAddFormRoute // we default the value, so that it isn't actually required in the URL. diff --git a/config/install/civicrm_entity.settings.yml b/config/install/civicrm_entity.settings.yml index 493fca28..2d14cc6e 100644 --- a/config/install/civicrm_entity.settings.yml +++ b/config/install/civicrm_entity.settings.yml @@ -1,3 +1,5 @@ filter_format: '' enabled_entity_types: { } disable_hooks: FALSE +disable_links: FALSE +enable_links_per_type: { } diff --git a/config/schema/civicrm_entity.schema.yml b/config/schema/civicrm_entity.schema.yml index 57bbaaff..b0d295e5 100644 --- a/config/schema/civicrm_entity.schema.yml +++ b/config/schema/civicrm_entity.schema.yml @@ -14,3 +14,19 @@ civicrm_entity.settings: disable_hooks: type: boolean label: 'Disable pre/post hooks' + enable_links_per_type: + type: sequence + label: 'Enable Drupal pages per type' + sequence: + type: mapping + label: 'Entity type' + mapping: + values: + type: sequence + label: 'Values' + sequence: + type: string + label: 'Value' + disable_links: + type: boolean + label: 'Disable Drupal pages' diff --git a/src/CivicrmEntityListBuilder.php b/src/CivicrmEntityListBuilder.php index 913d0d87..ec58f24e 100644 --- a/src/CivicrmEntityListBuilder.php +++ b/src/CivicrmEntityListBuilder.php @@ -40,12 +40,12 @@ public function buildRow(EntityInterface $entity) { return [ 'id' => $entity->id(), 'bundle' => $entity->bundle(), - 'label' => $entity->toLink(), + 'label' => $entity->hasLinkTemplate('canonical') ? $entity->toLink() : $entity->label(), ] + parent::buildRow($entity); } return [ 'id' => $entity->id(), - 'label' => $entity->toLink(), + 'label' => $entity->hasLinkTemplate('canonical') ? $entity->toLink() : $entity->label(), ] + parent::buildRow($entity); } @@ -55,11 +55,13 @@ public function buildRow(EntityInterface $entity) { protected function getDefaultOperations(EntityInterface $entity) { $operations = parent::getDefaultOperations($entity); - $operations['view'] = [ - 'title' => $this->t('View'), - 'weight' => 50, - 'url' => $entity->toUrl(), - ]; + if ($entity->hasLinkTemplate('canonical')) { + $operations['view'] = [ + 'title' => $this->t('View'), + 'weight' => 0, + 'url' => $entity->toUrl(), + ]; + } return $operations; } diff --git a/src/Form/CivicrmEntityForm.php b/src/Form/CivicrmEntityForm.php index 7d14a710..17dd73bf 100644 --- a/src/Form/CivicrmEntityForm.php +++ b/src/Form/CivicrmEntityForm.php @@ -10,8 +10,10 @@ use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Routing\RouteProvider; use Drupal\Core\Session\AccountInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Routing\Exception\RouteNotFoundException; /** * Form object for CiviCRM Entities. @@ -25,6 +27,13 @@ class CivicrmEntityForm extends ContentEntityForm { */ protected $currentUser; + /** + * The route provider. + * + * @var \Drupal\Core\Routing\RouteProvider + */ + protected $routeProvider; + /** * Constructs a CivicrmEntityForm object. * @@ -36,10 +45,13 @@ class CivicrmEntityForm extends ContentEntityForm { * The time service. * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. + * @param \Drupal\Core\Routing\RouteProvider $route_provider + * The route provider. */ - public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, AccountInterface $current_user) { + public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, AccountInterface $current_user, RouteProvider $route_provider) { parent::__construct($entity_repository, $entity_type_bundle_info, $time); $this->currentUser = $current_user; + $this->routeProvider = $route_provider; } /** @@ -50,7 +62,8 @@ public static function create(ContainerInterface $container) { $container->get('entity.repository'), $container->get('entity_type.bundle.info'), $container->get('datetime.time'), - $container->get('current_user') + $container->get('current_user'), + $container->get('router.route_provider') ); } @@ -130,17 +143,27 @@ public function save(array $form, FormStateInterface $form_state) { $insert = $this->entity->isNew(); $result = $this->entity->save(); - $t_args = ['%title' => $this->entity->toLink()->toString()]; + try { + if ($this->routeProvider->getRouteByName("entity.{$this->entity->getEntityTypeId()}.canonical")) { + $form_state->setRedirect( + "entity.{$this->entity->getEntityTypeId()}.canonical", + [$this->entity->getEntityTypeId() => $this->entity->id()] + ); + + $t_args = ['%title' => $this->entity->toLink()->toString()]; + } + } + catch (RouteNotFoundException $e) { + $t_args = ['%title' => $this->entity->label()]; + } + if ($insert) { $this->messenger()->addMessage($this->t('%title has been created.', $t_args)); } else { $this->messenger()->addMessage($this->t('%title has been updated.', $t_args)); } - $form_state->setRedirect( - "entity.{$this->entity->getEntityTypeId()}.canonical", - [$this->entity->getEntityTypeId() => $this->entity->id()] - ); + return $result; } diff --git a/src/Form/CivicrmEntitySettings.php b/src/Form/CivicrmEntitySettings.php index 8691300e..94459743 100644 --- a/src/Form/CivicrmEntitySettings.php +++ b/src/Form/CivicrmEntitySettings.php @@ -3,6 +3,7 @@ namespace Drupal\civicrm_entity\Form; use Drupal\civicrm_entity\SupportedEntities; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\ConfigFormBase; @@ -54,6 +55,13 @@ class CivicrmEntitySettings extends ConfigFormBase { */ protected $menuLinkManager; + /** + * The render cache manager. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $cacheRender; + /** * Constructs a \Drupal\system\ConfigFormBase object. * @@ -69,14 +77,17 @@ class CivicrmEntitySettings extends ConfigFormBase { * The local task manager. * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager * The menu link manager. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_render + * The render cache manager. */ - public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, RouteBuilderInterface $route_builder, LocalActionManager $local_action_manager, LocalTaskManager $local_task_manager, MenuLinkManagerInterface $menu_link_manager) { + public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, RouteBuilderInterface $route_builder, LocalActionManager $local_action_manager, LocalTaskManager $local_task_manager, MenuLinkManagerInterface $menu_link_manager, CacheBackendInterface $cache_render) { parent::__construct($config_factory); $this->entityTypeManager = $entity_type_manager; $this->routeBuilder = $route_builder; $this->localActionManager = $local_action_manager; $this->localTaskManager = $local_task_manager; $this->menuLinkManager = $menu_link_manager; + $this->cacheRender = $cache_render; } /** @@ -89,7 +100,8 @@ public static function create(ContainerInterface $container) { $container->get('router.builder'), $container->get('plugin.manager.menu.local_action'), $container->get('plugin.manager.menu.local_task'), - $container->get('plugin.manager.menu.link') + $container->get('plugin.manager.menu.link'), + $container->get('cache.render') ); } @@ -127,19 +139,49 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; $civicrm_entity_types = SupportedEntities::getInfo(); - // @todo Use tableselect so we can display entity descriptions. - $options = array_map(function (array $entity_info) { - return $entity_info['civicrm entity label']; - }, $civicrm_entity_types); - asort($options); $form['enabled_entity_types'] = [ - '#type' => 'checkboxes', + '#tree' => TRUE, + '#type' => 'fieldset', '#title' => $this->t('Enabled entity types'), - '#options' => $options, - '#default_value' => $config->get('enabled_entity_types'), + '#collapsible' => FALSE, ]; + $enabled_entity_types = $config->get('enabled_entity_types') ?? []; + $enable_links_per_type = $config->get('enable_links_per_type') ?? []; + foreach ($civicrm_entity_types as $key => $entity_info) { + $form['enabled_entity_types'][$key]['#type'] = 'fieldset'; + + $form['enabled_entity_types'][$key]['enabled'] = [ + '#type' => 'checkbox', + '#title' => $entity_info['civicrm entity label'], + '#default_value' => in_array($key, $enabled_entity_types), + ]; + + $form['enabled_entity_types'][$key]['enable_links'] = [ + '#states' => [ + 'visible' => [ + ':input[name="enabled_entity_types[' . $key . '][enabled]"]' => ['checked' => TRUE], + ], + ], + '#type' => 'checkboxes', + '#title' => $this->t('Enable Drupal pages'), + // @todo Should this be a list of dynamic operations? + '#options' => [ + 'view' => $this->t('View'), + 'add' => $this->t('Add'), + 'edit' => $this->t('Edit'), + 'delete' => $this->t('Delete'), + ], + '#default_value' => $enable_links_per_type[$key]['values'] ?? [ + 'view', + 'add', + 'edit', + 'delete', + ], + ]; + } + $form['advanced_settings'] = [ '#type' => 'details', '#title' => $this->t('Advanced settings'), @@ -153,6 +195,13 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#description' => $this->t('Not intended for normal use. Provided to temporarily disable Drupal entity hooks for CiviCRM Entity types for special cases, such as migrations. Only disable if you know you need to.'), ]; + $form['advanced_settings']['disable_links'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Disable Drupal pages'), + '#default_value' => $config->get('disable_links'), + '#description' => $this->t('Globally disables Drupal versions of view page and, add, edit, and delete forms for all enabled entity types. This option overrides the "per type" Drupal pages.'), + ]; + return $form; } @@ -161,11 +210,21 @@ public function buildForm(array $form, FormStateInterface $form_state) { */ public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); - $enabled_entity_type = array_filter($form_state->getValue('enabled_entity_types')); + $enabled_entity_types = []; + $enable_links_per_type = []; + foreach ($form_state->getValue('enabled_entity_types') as $entity_type => $value) { + if ($value['enabled']) { + $enabled_entity_types[] = $entity_type; + $enable_links_per_type[$entity_type]['values'] = $value['enable_links']; + } + } + $this->config('civicrm_entity.settings') ->set('filter_format', $form_state->getValue('filter_format')) - ->set('enabled_entity_types', $enabled_entity_type) + ->set('enabled_entity_types', $enabled_entity_types) ->set('disable_hooks', $form_state->getValue('disable_hooks')) + ->set('disable_links', $form_state->getValue('disable_links')) + ->set('enable_links_per_type', $enable_links_per_type) ->save(); // Need to rebuild derivative routes. @@ -173,6 +232,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $this->routeBuilder->rebuild(); $this->localActionManager->clearCachedDefinitions(); $this->localTaskManager->clearCachedDefinitions(); + $this->cacheRender->invalidateAll(); } } diff --git a/src/Plugin/Derivative/CivicrmEntityLocalAction.php b/src/Plugin/Derivative/CivicrmEntityLocalAction.php index 594fa9ab..e6025eaa 100644 --- a/src/Plugin/Derivative/CivicrmEntityLocalAction.php +++ b/src/Plugin/Derivative/CivicrmEntityLocalAction.php @@ -54,15 +54,18 @@ public function getDerivativeDefinitions($base_plugin_definition) { /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */ foreach ($civicrm_entities as $entity_type_id => $entity_type) { - $this->derivatives["civicrm_entity_add_$entity_type_id"] = [ - 'route_name' => "entity.$entity_type_id.add_form", - 'title' => $this->t('Add :label', [':label' => $entity_type->getLabel()]), - 'appears_on' => ["entity.$entity_type_id.collection"], - ] + $base_plugin_definition; - if ($entity_type->hasKey('bundle')) { - $this->derivatives["civicrm_entity_add_$entity_type_id"]['route_parameters'] = [ - $entity_type->getKey('bundle') => $entity_type_id, - ]; + if ($entity_type->hasLinkTemplate('add-form')) { + $this->derivatives["civicrm_entity_add_$entity_type_id"] = [ + 'route_name' => "entity.$entity_type_id.add_form", + 'title' => $this->t('Add :label', [':label' => $entity_type->getLabel()]), + 'appears_on' => ["entity.$entity_type_id.collection"], + ] + $base_plugin_definition; + + if ($entity_type->hasKey('bundle')) { + $this->derivatives["civicrm_entity_add_$entity_type_id"]['route_parameters'] = [ + $entity_type->getKey('bundle') => $entity_type_id, + ]; + } } } diff --git a/src/Plugin/Derivative/DynamicLocalTasks.php b/src/Plugin/Derivative/DynamicLocalTasks.php index f331cc10..453755ad 100644 --- a/src/Plugin/Derivative/DynamicLocalTasks.php +++ b/src/Plugin/Derivative/DynamicLocalTasks.php @@ -6,9 +6,11 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; +use Drupal\Core\Routing\RouteProvider; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Routing\Exception\RouteNotFoundException; /** * Generates moderation-related local tasks. @@ -31,6 +33,13 @@ class DynamicLocalTasks extends DeriverBase implements ContainerDeriverInterface */ protected $entityTypeManager; + /** + * The route provider. + * + * @var \Drupal\Core\Routing\RouteProvider + */ + protected $routeProvider; + /** * Creates an FieldUiLocalTask object. * @@ -40,11 +49,14 @@ class DynamicLocalTasks extends DeriverBase implements ContainerDeriverInterface * The entity type manager. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The translation manager. + * @param \Drupal\Core\Routing\RouteProvider $route_provider + * The route provider. */ - public function __construct($base_plugin_id, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) { + public function __construct($base_plugin_id, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation, RouteProvider $route_provider) { $this->entityTypeManager = $entity_type_manager; $this->stringTranslation = $string_translation; $this->basePluginId = $base_plugin_id; + $this->routeProvider = $route_provider; } /** @@ -54,7 +66,8 @@ public static function create(ContainerInterface $container, $base_plugin_id) { return new static( $base_plugin_id, $container->get('entity_type.manager'), - $container->get('string_translation') + $container->get('string_translation'), + $container->get('router.route_provider') ); } @@ -68,23 +81,44 @@ public function getDerivativeDefinitions($base_plugin_definition) { return $type->getProvider() == 'civicrm_entity' && $type->get('civicrm_entity_ui_exposed'); }); - foreach ($civicrm_entities as $entity_type_id => $entity_type) { - $this->derivatives["$entity_type_id.canonical"] = [ - 'route_name' => "entity.$entity_type_id.canonical", - 'title' => $this->t('View'), - 'base_route' => "entity.$entity_type_id.canonical", - ] + $base_plugin_definition; - $this->derivatives["$entity_type_id.edit_form"] = [ - 'route_name' => "entity.$entity_type_id.edit_form", - 'title' => $this->t('Edit'), - 'base_route' => "entity.$entity_type_id.canonical", - ] + $base_plugin_definition; + foreach (array_keys($civicrm_entities) as $entity_type_id) { + try { + if ($this->routeProvider->getRouteByName("entity.$entity_type_id.edit_form")) { + $this->derivatives["entity.$entity_type_id.edit_form"] = [ + 'route_name' => "entity.$entity_type_id.edit_form", + 'title' => $this->t('Edit'), + 'base_route' => "entity.$entity_type_id.canonical", + ] + $base_plugin_definition; + } + } + catch (RouteNotFoundException $e) { + // No-op. + } + $this->derivatives["entity.$entity_type_id.collection"] = [ 'route_name' => "entity.$entity_type_id.collection", 'title' => $this->t('List'), 'base_route' => "entity.$entity_type_id.collection", 'weight' => -10, ] + $base_plugin_definition; + + try { + if ($this->routeProvider->getRouteByName("entity.$entity_type_id.canonical")) { + $this->derivatives["$entity_type_id.canonical"] = [ + 'route_name' => "entity.$entity_type_id.canonical", + 'title' => $this->t('View'), + 'base_route' => "entity.$entity_type_id.canonical", + ] + $base_plugin_definition; + } + else { + if ($this->routeProvider->getRouteByName("entity.$entity_type_id.edit_form")) { + $this->derivatives["entity.$entity_type_id.edit_form"]['base_route'] = "entity.$entity_type_id.edit_form"; + } + } + } + catch (RouteNotFoundException $e) { + // No-op. + } } return $this->derivatives; diff --git a/src/Routing/CiviCrmEntityRouteProvider.php b/src/Routing/CiviCrmEntityRouteProvider.php index 733c99e9..ce2a1696 100644 --- a/src/Routing/CiviCrmEntityRouteProvider.php +++ b/src/Routing/CiviCrmEntityRouteProvider.php @@ -28,7 +28,7 @@ public function getRoutes(EntityTypeInterface $entity_type) { protected function getAddFormRoute(EntityTypeInterface $entity_type) { $has_bundles = $entity_type->hasKey('bundle'); $entity_add_form_route = parent::getAddFormRoute($entity_type); - if ($has_bundles) { + if ($has_bundles && $entity_add_form_route) { // This ensures the form receives a default bundle from the // CivicrmEntity::preCreate method, avoiding the need for the `add_page` // route for selecting a bundle. diff --git a/src/Routing/RouteSubscriber.php b/src/Routing/RouteSubscriber.php index 76dc6ae2..b3346b5a 100644 --- a/src/Routing/RouteSubscriber.php +++ b/src/Routing/RouteSubscriber.php @@ -146,9 +146,11 @@ protected function alterRoutes(RouteCollection $collection) { foreach ($field_ui_routes as $route_name => $defaults) { $route = $collection->get($route_name); - assert($route !== NULL); - foreach ($defaults as $name => $default) { - $route->setDefault($name, $default); + + if ($route) { + foreach ($defaults as $name => $default) { + $route->setDefault($name, $default); + } } } } diff --git a/tests/src/FunctionalJavascript/CivicrmEntitySettingsFormTest.php b/tests/src/FunctionalJavascript/CivicrmEntitySettingsFormTest.php index 6deedc0a..0ae18257 100644 --- a/tests/src/FunctionalJavascript/CivicrmEntitySettingsFormTest.php +++ b/tests/src/FunctionalJavascript/CivicrmEntitySettingsFormTest.php @@ -26,6 +26,25 @@ public function testEnableNewEntityTypes() { $this->drupalGet(Url::fromRoute('civicrm_entity.admin')); $this->assertSession()->linkExists('CiviCRM Activity'); $this->assertSession()->linkExists('CiviCRM Event'); + + $this->drupalGet(Url::fromRoute('civicrm_entity.settings')); + $page = $this->getSession()->getPage(); + foreach (['civicrm_contact'] as $entity_type) { + $page->checkField("enabled_entity_types[$entity_type][enabled]"); + $page->uncheckField("enabled_entity_types[$entity_type][enable_links][view]"); + $page->uncheckField("enabled_entity_types[$entity_type][enable_links][add]"); + $page->uncheckField("enabled_entity_types[$entity_type][enable_links][edit]"); + $page->uncheckField("enabled_entity_types[$entity_type][enable_links][delete]"); + } + $page->pressButton('Save configuration'); + $this->drupalGet('/civicrm-contact/add'); + $this->assertSession()->responseContains('Page not found'); + $this->drupalGet('/civicrm-contact/1'); + $this->assertSession()->responseContains('Page not found'); + $this->drupalGet('/civicrm-contact/1/edit'); + $this->assertSession()->responseContains('Page not found'); + $this->drupalGet('/civicrm-contact/1/delete'); + $this->assertSession()->responseContains('Page not found'); } /** diff --git a/tests/src/FunctionalJavascript/CivicrmEntityTestBase.php b/tests/src/FunctionalJavascript/CivicrmEntityTestBase.php index da1a6e68..845c5491 100644 --- a/tests/src/FunctionalJavascript/CivicrmEntityTestBase.php +++ b/tests/src/FunctionalJavascript/CivicrmEntityTestBase.php @@ -34,7 +34,7 @@ protected function enableCivicrmEntityTypes(array $entity_types): void { $this->drupalGet(Url::fromRoute('civicrm_entity.settings')); $page = $this->getSession()->getPage(); foreach ($entity_types as $entity_type) { - $page->checkField("enabled_entity_types[$entity_type]"); + $page->checkField("enabled_entity_types[$entity_type][enabled]"); } $page->pressButton('Save configuration'); $this->assertSession()->pageTextContains('The configuration options have been saved.'); From 5aaa1ab694ec774e99e2f4aab284182048daadc6 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Thu, 1 Jun 2023 14:02:13 -0500 Subject: [PATCH 10/41] =?UTF-8?q?make=20sure=20CiviCRM=20custom=20tables?= =?UTF-8?q?=20get=20fully=20qualified=20database=20name=20eve=E2=80=A6=20(?= =?UTF-8?q?#431)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * make sure CiviCRM custom tables get fully qualified database name even if contains double underscore * make sure CiviCRM custom tables get fully qualified database name even if contains double underscore * make sure CiviCRM custom tables get fully qualified database name even if contains double underscore * sql plugin fix --- civicrm_entity.module | 4 ++-- src/Plugin/views/query/CivicrmSql.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/civicrm_entity.module b/civicrm_entity.module index b1ca5383..4a87587f 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -616,10 +616,10 @@ function civicrm_entity_views_query_alter(ViewExecutable $view, QueryPluginBase $civicrm_connection = Database::getConnection('default', $civicrm_connection_name); $table_queue =& $query->getTableQueue(); foreach ($table_queue as $alias => &$table_info) { - if (strpos($table_info['table'], 'civicrm_') === 0 && strpos($table_info['table'], '.') === FALSE && strpos($table_info['table'], '__') === FALSE) { + if (!empty($table_info['table']) && ((strpos($table_info['table'], 'civicrm_') === 0 && strpos($table_info['table'], '.') === FALSE && strpos($table_info['table'], '__') === FALSE) || strpos($table_info['table'], 'civicrm_value_') === 0)) { $table_info['table'] = $civicrm_connection->getFullQualifiedTableName($table_info['table']); } - if (!empty($table_info['join']->table) && strpos($table_info['join']->table, 'civicrm_') === 0 && strpos($table_info['join']->table, '.') === FALSE && strpos($table_info['join']->table, '__') === FALSE) { + if (!empty($table_info['join']->table) && ((strpos($table_info['join']->table, 'civicrm_') === 0 && strpos($table_info['join']->table, '.') === FALSE && strpos($table_info['join']->table, '__') === FALSE) || strpos($table_info['join']->table, 'civicrm_value_') === 0)) { $table_info['join']->table = $civicrm_connection->getFullQualifiedTableName($table_info['join']->table); } } diff --git a/src/Plugin/views/query/CivicrmSql.php b/src/Plugin/views/query/CivicrmSql.php index c429bb75..bb7b96cf 100644 --- a/src/Plugin/views/query/CivicrmSql.php +++ b/src/Plugin/views/query/CivicrmSql.php @@ -82,7 +82,7 @@ public function query($get_count = FALSE) { // and convert it to a fully qualified table name. But, make sure it has // not already been converted. // Also do not convert any drupal custom fields. - if ((strpos($table['table'], 'civicrm_') !== 0 && strpos($table['table'], '.') === FALSE) || (strpos($table['table'], 'civicrm_') === 0 && strpos($table['table'], '__') !== FALSE)) { + if ((strpos($table['table'], 'civicrm_') !== 0 && strpos($table['table'], '.') === FALSE) || ((strpos($table['table'], 'civicrm_') === 0 && strpos($table['table'], '__') !== FALSE)) || strpos($table['table'], 'civicrm_value_') === 0) { $table['table'] = $connection->getFullQualifiedTableName($table['table']); } } From 5e52c3f2938bf53dd059bf338b125cd6839327b1 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Fri, 9 Jun 2023 09:20:42 -0500 Subject: [PATCH 11/41] fix error installing Media Library or other modules - Dynamic Task route provider (#433) --- src/Plugin/Derivative/DynamicLocalTasks.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Plugin/Derivative/DynamicLocalTasks.php b/src/Plugin/Derivative/DynamicLocalTasks.php index 453755ad..14be7c29 100644 --- a/src/Plugin/Derivative/DynamicLocalTasks.php +++ b/src/Plugin/Derivative/DynamicLocalTasks.php @@ -6,7 +6,7 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; -use Drupal\Core\Routing\RouteProvider; +use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -36,7 +36,7 @@ class DynamicLocalTasks extends DeriverBase implements ContainerDeriverInterface /** * The route provider. * - * @var \Drupal\Core\Routing\RouteProvider + * @var \Drupal\Core\Routing\RouteProviderInterface */ protected $routeProvider; @@ -49,10 +49,10 @@ class DynamicLocalTasks extends DeriverBase implements ContainerDeriverInterface * The entity type manager. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The translation manager. - * @param \Drupal\Core\Routing\RouteProvider $route_provider + * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider * The route provider. */ - public function __construct($base_plugin_id, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation, RouteProvider $route_provider) { + public function __construct($base_plugin_id, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation, RouteProviderInterface $route_provider) { $this->entityTypeManager = $entity_type_manager; $this->stringTranslation = $string_translation; $this->basePluginId = $base_plugin_id; From 2cdc2663502610fa8460766574816ce5c1dee150 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Mon, 7 Aug 2023 14:47:14 -0500 Subject: [PATCH 12/41] Add Rule Condition ContactInGroup and options provider CiviCRM Group Options (#437) --- src/Plugin/Condition/ContactInGroup.php | 104 ++++++++++++++++++ src/TypedData/Options/CivicrmGroupOptions.php | 60 ++++++++++ 2 files changed, 164 insertions(+) create mode 100644 src/Plugin/Condition/ContactInGroup.php create mode 100644 src/TypedData/Options/CivicrmGroupOptions.php diff --git a/src/Plugin/Condition/ContactInGroup.php b/src/Plugin/Condition/ContactInGroup.php new file mode 100644 index 00000000..8a443b68 --- /dev/null +++ b/src/Plugin/Condition/ContactInGroup.php @@ -0,0 +1,104 @@ +civicrmApi = $civicrm_api; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('civicrm_entity.api') + ); + } + + /** + * Check if contact is in group. + * + * @param \Drupal\civicrm_entity\Entity\CivicrmEntity $civicrm_contact + * The CiviCRM contact to check. + * @param string $group + * The group id. + * + * @return bool + * TRUE if the contact is in a CiviCRM group. + */ + protected function doEvaluate(CivicrmEntity $civicrm_contact, string $group) { + try { + $id = $civicrm_contact->get('id')->getString(); + if (!empty($id) && is_numeric($id)) { + $result = $this->civicrmApi->get('GroupContact', [ + 'sequential' => 1, + 'contact_id' => (int) $id, + 'group_id' => $group, + 'status' => "Added", + ]); + if (!empty($result[0]['id'])) { + return TRUE; + } + } + } + catch (\CiviCRM_API3_Exception $e) { + return FALSE; + } + return FALSE; + } + +} diff --git a/src/TypedData/Options/CivicrmGroupOptions.php b/src/TypedData/Options/CivicrmGroupOptions.php new file mode 100644 index 00000000..80faac5e --- /dev/null +++ b/src/TypedData/Options/CivicrmGroupOptions.php @@ -0,0 +1,60 @@ +civicrmApi = $civicrm_api; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('civicrm_entity.api') + ); + } + + /** + * {@inheritdoc} + */ + public function getPossibleOptions(AccountInterface $account = NULL) { + $options = []; + + // Load all the node types. + $groups = $this->civicrmApi->get('group', []); + + foreach ($groups as $group_id => $group) { + $options[$group_id] = $group['title']; + } + + // Sort the result by value for ease of locating and selecting. + asort($options); + + return $options; + } + +} From 345700b874df29b9b1cceaea8f311330b3bae36b Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Tue, 15 Aug 2023 10:53:31 -0500 Subject: [PATCH 13/41] Load Linked User Rules Action (#440) --- src/Plugin/RulesAction/LoadLinkedUser.php | 99 +++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/Plugin/RulesAction/LoadLinkedUser.php diff --git a/src/Plugin/RulesAction/LoadLinkedUser.php b/src/Plugin/RulesAction/LoadLinkedUser.php new file mode 100644 index 00000000..98f5a21c --- /dev/null +++ b/src/Plugin/RulesAction/LoadLinkedUser.php @@ -0,0 +1,99 @@ +entityTypeManager = $entity_type_manager; + $this->civicrmApi = $civicrm_api; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('civicrm_entity.api') + ); + } + + /** + * Executes the action with the given context. + * + * @param int $contact_id + * The contact id. + */ + protected function doExecute($contact_id) { + $result = $this->civicrmApi->get('UfMatch', [ + 'sequential' => TRUE, + 'contact_id' => $contact_id, + ]); + if (!empty($result[0]['uf_id'])) { + $user_storage = $this->entityTypeManager->getStorage('user'); + $account = $user_storage->load($result[0]['uf_id']); + $this->setProvidedValue('user_fetched', $account); + } + } + +} From 4b373c792011be6f0eec977755e5921c7125a6a6 Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Wed, 13 Sep 2023 00:27:17 +0800 Subject: [PATCH 14/41] Add phone type. (#444) --- src/CivicrmEntityViewsData.php | 2 +- .../EntityReverseLocationPhone.php | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/Plugin/views/relationship/EntityReverseLocationPhone.php diff --git a/src/CivicrmEntityViewsData.php b/src/CivicrmEntityViewsData.php index 37d4fd40..8fc9cb14 100644 --- a/src/CivicrmEntityViewsData.php +++ b/src/CivicrmEntityViewsData.php @@ -490,7 +490,7 @@ protected function processViewsDataForSpecialFields(array &$views_field, $base_t case 'civicrm_phone': if (isset($views_field['civicrm_contact']['reverse__civicrm_phone__contact_id']['relationship'])) { - $views_field['civicrm_contact']['reverse__civicrm_phone__contact_id']['relationship']['id'] = 'civicrm_entity_reverse_location'; + $views_field['civicrm_contact']['reverse__civicrm_phone__contact_id']['relationship']['id'] = 'civicrm_entity_reverse_location_phone'; $views_field['civicrm_contact']['reverse__civicrm_phone__contact_id']['relationship']['label'] = $this->t('Phone'); } diff --git a/src/Plugin/views/relationship/EntityReverseLocationPhone.php b/src/Plugin/views/relationship/EntityReverseLocationPhone.php new file mode 100644 index 00000000..2e7d72eb --- /dev/null +++ b/src/Plugin/views/relationship/EntityReverseLocationPhone.php @@ -0,0 +1,66 @@ +phoneTypes = \CRM_Core_BAO_Phone::buildOptions('phone_type_id'); + + if (!empty($this->options['phone_type'])) { + $this->definition['extra'][] = [ + 'field' => 'phone_type_id', + 'value' => $this->options['phone_type'], + 'numeric' => TRUE, + ]; + } + } + + /** + * {@inheritdoc} + */ + protected function defineOptions() { + $options = parent::defineOptions(); + $options['phone_type'] = ['default' => 0]; + return $options; + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + $form['phone_type'] = [ + '#type' => 'radios', + '#title' => $this->t('Phone type'), + '#options' => [0 => $this->t('Any')] + $this->phoneTypes, + '#default_value' => isset($this->options['phone_type']) ? (int) $this->options['phone_type'] : 0, + '#weight' => -2, + ]; + + parent::buildOptionsForm($form, $form_state); + } + +} From d00990e125167ebd2729d4945fcdfc7dc8410b1f Mon Sep 17 00:00:00 2001 From: Jitendra Purohit Date: Tue, 12 Sep 2023 22:17:26 +0530 Subject: [PATCH 15/41] Unsupported operand types: null + array error on custom field view (#443) --- src/Plugin/views/field/CustomEntityField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/views/field/CustomEntityField.php b/src/Plugin/views/field/CustomEntityField.php index 87d262a8..aa83a63c 100644 --- a/src/Plugin/views/field/CustomEntityField.php +++ b/src/Plugin/views/field/CustomEntityField.php @@ -219,7 +219,7 @@ protected function prepareItemsByDelta(array $all_values) { if ($this->limit_values) { $row = $this->view->result[$this->view->row_index]; - if (!$this->options['group_rows'] && is_numeric($row->delta)) { + if (!$this->options['group_rows'] && isset($all_values[$row->delta]) && is_numeric($row->delta)) { return [$all_values[$row->delta]]; } } From ccdc3611f56a2ad011e881695dabe9bdd635f0df Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Tue, 19 Sep 2023 06:45:23 +1000 Subject: [PATCH 16/41] Ensure that only if we have a custom field that we pass it to getCustomFieldMetadata (#447) --- src/Entity/CivicrmEntity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/CivicrmEntity.php b/src/Entity/CivicrmEntity.php index 2700a41d..5e1a9ae3 100644 --- a/src/Entity/CivicrmEntity.php +++ b/src/Entity/CivicrmEntity.php @@ -115,7 +115,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields[$name] = $field_definition_provider->getBaseFieldDefinition($civicrm_field); $fields[$name]->setRequired(isset($civicrm_required_fields[$name])); - if ($values = \Drupal::service('civicrm_entity.api')->getCustomFieldMetadata($name)) { + if (str_starts_with($name, 'custom_') && $values = \Drupal::service('civicrm_entity.api')->getCustomFieldMetadata($name)) { $fields[$name]->setSetting('civicrm_entity_field_metadata', $values); $fields[$name]->setRequired((bool) $civicrm_field['is_required']); } From f7a21b09552bb08e2e5edd1bc47cb4fcfd7cad9b Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Tue, 19 Sep 2023 12:04:23 -0500 Subject: [PATCH 17/41] fix route error on entity types with dynamic bundle type (#448) --- src/Routing/RouteSubscriber.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Routing/RouteSubscriber.php b/src/Routing/RouteSubscriber.php index b3346b5a..9c3a7446 100644 --- a/src/Routing/RouteSubscriber.php +++ b/src/Routing/RouteSubscriber.php @@ -75,6 +75,9 @@ protected function alterRoutes(RouteCollection $collection) { "field_ui.field_storage_config_add_$entity_type_id" => [ 'bundle' => $entity_type_id, ], + "field_ui.field_storage_config_reuse_{$entity_type_id}" => [ + 'bundle' => $entity_type_id, + ], "entity.entity_form_display.{$entity_type_id}.default" => [ 'bundle' => $entity_type_id, ], From 9856816514c486c1820ce8406c5abaccd6ce0193 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Wed, 4 Oct 2023 09:40:58 -0500 Subject: [PATCH 18/41] adding Views relationship plugin for Website entity type with Website Type condition (#445) --- src/CivicrmEntityViewsData.php | 8 +- .../relationship/EntityReverseWebsiteType.php | 104 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/Plugin/views/relationship/EntityReverseWebsiteType.php diff --git a/src/CivicrmEntityViewsData.php b/src/CivicrmEntityViewsData.php index 8fc9cb14..4fc66ce5 100644 --- a/src/CivicrmEntityViewsData.php +++ b/src/CivicrmEntityViewsData.php @@ -495,7 +495,13 @@ protected function processViewsDataForSpecialFields(array &$views_field, $base_t } break; - + case 'civicrm_website': + if (isset($views_field['civicrm_contact']['reverse__civicrm_website__contact_id']['relationship'])) { + $views_field['civicrm_contact']['reverse__civicrm_website__contact_id']['relationship']['id'] = 'civicrm_entity_reverse_website_type'; + $views_field['civicrm_contact']['reverse__civicrm_website__contact_id']['relationship']['label'] = $this->t('Website'); + } + + break; case 'civicrm_address': if (isset($views_field['civicrm_contact']['reverse__civicrm_address__contact_id']['relationship'])) { $views_field['civicrm_contact']['reverse__civicrm_address__contact_id']['relationship']['id'] = 'civicrm_entity_reverse_location'; diff --git a/src/Plugin/views/relationship/EntityReverseWebsiteType.php b/src/Plugin/views/relationship/EntityReverseWebsiteType.php new file mode 100644 index 00000000..52d7fea9 --- /dev/null +++ b/src/Plugin/views/relationship/EntityReverseWebsiteType.php @@ -0,0 +1,104 @@ +civicrmApi = $civicrm_api; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('plugin.manager.views.join'), + $container->get('civicrm_entity.api') + ); + } + + /** + * {@inheritdoc} + */ + public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) { + parent::init($view, $display, $options); + $this->civicrmApi->civicrmInitialize(); + $this->websiteTypes = \CRM_Core_BAO_Website::buildOptions('website_type_id'); + + if (!empty($this->options['website_type'])) { + $this->definition['extra'][] = [ + 'field' => 'website_type_id', + 'value' => $this->options['website_type'], + 'numeric' => TRUE, + ]; + } + } + + /** + * {@inheritdoc} + */ + protected function defineOptions() { + $options = parent::defineOptions(); + $options['website_type'] = ['default' => 0]; + return $options; + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + $form['website_type'] = [ + '#type' => 'radios', + '#title' => $this->t('Website type'), + '#options' => [0 => $this->t('Any')] + $this->websiteTypes, + '#default_value' => isset($this->options['website_type']) ? (int) $this->options['website_type'] : 0, + '#weight' => -2, + ]; + + parent::buildOptionsForm($form, $form_state); + } + +} From 64cf61749dfc9377066097b9a94023fe09058fdb Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Wed, 18 Oct 2023 02:20:51 +0800 Subject: [PATCH 19/41] Add search api fix for separate civi database. (#450) * Add search api fix for separate civi database. * Check if connection exists. * Check if connection exists. --- civicrm_entity.module | 13 ++++++++++ .../search_api/datasource/CivicrmEntity.php | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/Plugin/search_api/datasource/CivicrmEntity.php diff --git a/civicrm_entity.module b/civicrm_entity.module index 4a87587f..ac8a054c 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -33,6 +33,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\Entity\Display\EntityDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\civicrm_entity\Plugin\search_api\datasource\CivicrmEntity as DatasourceCivicrmEntity; /** * Implements hook_theme(). @@ -732,3 +733,15 @@ function civicrm_entity_rules_action_info_alter(&$rules_actions) { ])); } } + +/** + * Implements hook_search_api_datasource_info_alter(). + */ +function civicrm_entity_search_api_datasource_info_alter(array &$infos) { + foreach ($infos as $entity_type => &$info) { + if (strpos($entity_type, 'entity:civicrm_') !== FALSE) { + unset($info['deriver']); + $info['class'] = DatasourceCivicrmEntity::class; + } + } +} diff --git a/src/Plugin/search_api/datasource/CivicrmEntity.php b/src/Plugin/search_api/datasource/CivicrmEntity.php new file mode 100644 index 00000000..8116758d --- /dev/null +++ b/src/Plugin/search_api/datasource/CivicrmEntity.php @@ -0,0 +1,26 @@ +setDatabaseConnection(Database::getConnection('default', $civicrm_connection_name)); + } + + return $datasource; + } + +} From b2fb23cba8befd26f3f2f8cb65dc0ed9822608fd Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:29:53 +0800 Subject: [PATCH 20/41] Add check for objectref. (#451) --- civicrm_entity.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/civicrm_entity.module b/civicrm_entity.module index ac8a054c..eb77d361 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -548,7 +548,7 @@ function civicrm_entity_civicrm_post($op, $objectName, $objectId, &$objectRef) { } else { // Special handling for EntityTag objects. - if ($entityType == 'civicrm_entity_tag') { + if ($entityType == 'civicrm_entity_tag' && is_array($objectRef)) { foreach ($objectRef[0] as $entityTag) { $object = new CRM_Core_BAO_EntityTag(); $object->entity_id = $entityTag; From ec5aedead8f42254300915c13f53cd6253f372de Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Wed, 18 Oct 2023 20:32:51 -0500 Subject: [PATCH 21/41] Add state/province entity type to list of supported types (#452) --- src/SupportedEntities.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/SupportedEntities.php b/src/SupportedEntities.php index 9bb86fd6..a003cf54 100644 --- a/src/SupportedEntities.php +++ b/src/SupportedEntities.php @@ -605,6 +605,18 @@ public static function getInfo() { 'delete' => ['administer CiviCampaign'], ], ]; + $civicrm_entity_info['civicrm_state_province'] = [ + 'civicrm entity label' => t('State/Province'), + 'civicrm entity name' => 'state_province', + 'label property' => 'name', + 'permissions' => [ + 'view' => ['view all contacts'], + 'edit' => [], + 'update' => [], + 'create' => [], + 'delete' => [], + ], + ]; $civicrm_entity_info['civicrm_tag'] = [ 'civicrm entity label' => t('Tag'), 'civicrm entity name' => 'tag', From c1b2f5f05b8ce8649cd517a433c70988c2f803ae Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Tue, 28 Nov 2023 07:52:40 -0600 Subject: [PATCH 22/41] Readme update (#457) * README updates * README updates * additional README updates --- README.md | 97 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 816ab742..385ceb2d 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,8 @@ -# CiviCRM Entity for Drupal 8 +# CiviCRM Entity for Drupal 10 -## Installing 8.x-3.0-Beta2 or later. +## Installing 4.0.0-alpha7 or later. -From 8.x-3.0-Beta2 onwards this module should be installable with normal Drupal composer install steps, `composer require drupal/civicrm_entity`. - -## Installing 8.x-3.0-Beta1 or earlier Drupal 8 versions. - -Due to a [bug](https://www.drupal.org/project/project_composer/issues/3051746) with Drupal.org's composer facade. It's not possible to simply `composer require drupal/civicrm_entity` without some preparation. - -To get CiviCRM Entity installed with composer the following steps will work: - -1. Add the CiviCRM Entity repository to your composer.json: - `composer config repositories.civicrm_entity vcs https://github.com/eileenmcnaughton/civicrm_entity` -2. Require CiviCRM Entity's `8.x-3.x` git branch: - `composer require drupal/civicrm_entity:dev-8.x-3.x` +This module is installable with normal Drupal composer install steps, `composer require drupal/civicrm_entity`. **Note:** @@ -22,8 +11,82 @@ You should ensure that your `composer.json` file has versioned requirements for For example: ``` json - "civicrm/civicrm-core": "^5.19", - "civicrm/civicrm-drupal-8": "^5.19", + "civicrm/civicrm-core": "^5.63", + "civicrm/civicrm-packages": "^5.63", + "civicrm/civicrm-drupal-8": "^5.63", ``` +## CiviCRM Entity Leaflet upgrade to Drupal 10 + +In the 4.0.x version the civicrm_entity_leaflet module has been removed from the main module repo and put into its own module project http://drupal.org/project/civicrm_entity_leaflet + +If upgrading from Drupal 9 to Drupal 10, simply include that module: +`composer require drupal/civicrm_entity_leaflet` + +## Version support + +8.x-3.x for Drupal 9 Known to work with CiviCRM 5.51+ +4.0.x for Drupal 10 Requires CiviCRM 5.60+ + +We do our best to support as many versions of CiviCRM Core as is feasible. + +CiviCRM Entity is primarily tested and developed with a focus on Extended Support Release (ESR) versions of CiviCRM. Updates needed due to CiviCRM Core are typically driven by reported issues or contributed changes. While it's uncommon for a newer CiviCRM version to encounter issues, for best success use the ESR version. +https://civicrm.org/esr + +## Issues + +For bug reports, support requests, or feature requests please create an issue in the Drupal.org issue queue https://www.drupal.org/project/issues/civicrm_entity + + +## Contribution + +Primary development happens in the github repo: https://github.com/eileenmcnaughton/civicrm_entity + +Please make PRs against the 4.0.x branch first. Changes will be merged there, and then backported to the 8.x-3.x branch. + +We do plan on shifting primary development to the drupal.org Gitlab infrastructure in time. + + +### Develop with the most recent version versions. + +To get CiviCRM Entity github repo installed with composer the following steps will work: + +1. Add the CiviCRM Entity repository to your composer.json: + `composer config repositories.civicrm_entity vcs https://github.com/eileenmcnaughton/civicrm_entity` +2. Require CiviCRM Entity's `4.0.x` git branch: + `composer require drupal/civicrm_entity:dev-4.0.x` + +## Get support now + +CiviCRM Entity is open source software. Its support and improvement depends upon the good will and contribution of open source developers' time and the investment of money by individuals and organizations. + +We do our best to answer requests and fix bugs, and are committed to continuing development and supporting the module as both Drupal Core and CiviCRM evolve. + +Primary development is managed by [Skvare](https://skvare.com), but a community of developers supports and contributes to the module. + +If you or your organization has specific or immediate needs, or simply wishes to support the continued development and maintenance of CiviCRM Entity you can [Contact Skvare](https://skvare.com/contact) and our dedicated team of account managers, business analysts, project managers, and developers will work to get the solution you need, as fast as we can. + +## Developers + +[Mark Hanna](https://www.drupal.org/u/markusa), Architect and Senior Developer at Skvare, module maintainer. + +[Arnold French](https://www.drupal.org/u/dsdeiz), Developer at Skvare + +[Eileen Mcnaughton](https://github.com/eileenmcnaughton) Original creator of CiviCRM Entity, and tireless contributor to CiviCRM core and a multitude of CiviCRM Extensions + +[Matt Glaman](https://www.drupal.org/u/mglaman) Initial development of the Drupal 8 version was a massive contribution. + +[Jitendra Purohit](https://www.drupal.org/u/jitendrapurohit) Developer and contributor + +See all contributors: https://github.com/eileenmcnaughton/civicrm_entity/graphs/contributors + +## Supporting Organizations + +[Skvare](https://skvare.com) Skvare Supporting and managing CiviCRM since 2008. [Every Skvare team member](https://skvare.com/about) has a hand in CiviCRM Entity's continued development. + +[Fuzion](https://www.drupal.org/fuzion) Founding organization of the initial versions of CiviCRM Entity, and steady support and contribution throughout the years. + +[SemperIT](https://semper-it.com/) Sponsors contribution from D8 onwards + +[CiviCRM Core Team](https://civicrm.org/about/core-team) For creating CiviCRM, and supporting us all and Drupal integration in general. -By default the **RoundEarth** method uses `dev-master` for `civicrm/civicrm-drupal-8` - this is [dangerous and not recommended](https://lab.civicrm.org/dev/drupal/issues/87#note_23534)! +[MyDropWizard](https://www.drupal.org/mydropwizard) Original funding for the Drupal 8 version, and the composerization of CiviCRM From 65c053fce06a8a5cd0f86c1fdacf9d6d80c8cc57 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Mon, 12 Feb 2024 10:40:46 -0600 Subject: [PATCH 23/41] update Add Contact to Group Views Bulk operations action plugin access handler (#464) --- src/Plugin/Action/CivicrmContactAddToGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/Action/CivicrmContactAddToGroup.php b/src/Plugin/Action/CivicrmContactAddToGroup.php index 40cb0632..29f80f36 100644 --- a/src/Plugin/Action/CivicrmContactAddToGroup.php +++ b/src/Plugin/Action/CivicrmContactAddToGroup.php @@ -141,7 +141,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta * {@inheritdoc} */ public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { - return $account->hasPermission('edit all contacts'); + return $object->access('update', $account, $return_as_object); } /** From 28bc26e03d64cd520230220588ccdd46bea8e31d Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Thu, 15 Feb 2024 18:05:58 -0600 Subject: [PATCH 24/41] fix Views field custom date field output for year only (#439) --- src/Plugin/views/field/CustomEntityField.php | 39 +++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Plugin/views/field/CustomEntityField.php b/src/Plugin/views/field/CustomEntityField.php index aa83a63c..9857b199 100644 --- a/src/Plugin/views/field/CustomEntityField.php +++ b/src/Plugin/views/field/CustomEntityField.php @@ -301,8 +301,7 @@ protected function getItemValue($value, FieldDefinitionInterface $definition) { switch ($definition->getType()) { case 'datetime': if (!empty($value)) { - $datetime_format = $definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT; - return (new \DateTime($value, new \DateTimeZone(date_default_timezone_get())))->setTimezone(new \DateTimeZone('UTC'))->format($datetime_format); + return $this->convertToUtc($definition, $value); } break; @@ -335,4 +334,40 @@ protected function getDelta($id) { return 0; } + /** + * Check if date field should be converted to UTC or not. + * + * @param \Drupal\Core\Field\FieldDefinitionInterface $definition + * The field definition. + * @param string $date_value + * The date value. + * + * @return string + * The converted value. + */ + public function convertToUtc(FieldDefinitionInterface $definition, $date_value) { + $datetime_format = $definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT; + $default_timezone = date_default_timezone_get(); + + $utc = TRUE; + // If the field is custom and meant to store only year value, + // Avoid converting to any timezone and display it as stored in database. + if (strpos($definition->getName(), "custom_") === 0) { + [, $custom_field_id] = explode('_', $definition->getName()); + $params = [ + 'sequential' => 1, + 'id' => $custom_field_id, + ]; + $date_field = $this->civicrmApi->get('CustomField', $params); + if (!empty($date_field[0]['date_format']) && $date_field[0]['date_format'] === 'yy') { + $utc = FALSE; + } + } + + if ($utc) { + return (new \DateTime($date_value, new \DateTimeZone($default_timezone)))->setTimezone(new \DateTimeZone('UTC'))->format($datetime_format); + } + return (new \DateTime($date_value, new \DateTimeZone($default_timezone)))->format($datetime_format); + } + } From 6bc3005012b246dd4d75ece32554ca71e9d11af6 Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Fri, 16 Feb 2024 08:13:19 +0800 Subject: [PATCH 25/41] Add reset to the file URLs generated. (#458) --- src/Plugin/views/field/CustomFile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/views/field/CustomFile.php b/src/Plugin/views/field/CustomFile.php index e0b7914f..bb5d1373 100644 --- a/src/Plugin/views/field/CustomFile.php +++ b/src/Plugin/views/field/CustomFile.php @@ -76,7 +76,7 @@ public function render(ResultRow $values) { $entity_id = $this->getValue($values, 'entity_id'); $file_hash = \CRM_Core_BAO_File::generateFileHash($entity_id, $value); - $query = ['id' => $value, 'eid' => $entity_id, 'fcs' => $file_hash]; + $query = ['id' => $value, 'eid' => $entity_id, 'fcs' => $file_hash, 'reset' => 1]; return \CRM_Utils_System::url($path, UrlHelper::buildQuery($query), TRUE, FALSE, FALSE, TRUE); } From 01a0c7102818a5df86b260c23fdffb61f65c5fcc Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Fri, 16 Feb 2024 08:15:34 +0800 Subject: [PATCH 26/41] From #378 for 4.0.x (#466) * Fix loading of contact reference custom field value for anonymous users * Use fieldformatter to access custom contact reference field values --------- Co-authored-by: jitendrapurohit --- .../ContactReferenceFormatter.php | 35 +++++++++++++++++++ src/Plugin/views/field/CustomEntityField.php | 3 ++ 2 files changed, 38 insertions(+) create mode 100644 src/Plugin/Field/FieldFormatter/ContactReferenceFormatter.php diff --git a/src/Plugin/Field/FieldFormatter/ContactReferenceFormatter.php b/src/Plugin/Field/FieldFormatter/ContactReferenceFormatter.php new file mode 100644 index 00000000..6f97f146 --- /dev/null +++ b/src/Plugin/Field/FieldFormatter/ContactReferenceFormatter.php @@ -0,0 +1,35 @@ +id()); + return AccessResult::allowedIfHasPermissions($account, $permissions, 'OR'); + } + +} diff --git a/src/Plugin/views/field/CustomEntityField.php b/src/Plugin/views/field/CustomEntityField.php index 9857b199..62012e76 100644 --- a/src/Plugin/views/field/CustomEntityField.php +++ b/src/Plugin/views/field/CustomEntityField.php @@ -113,6 +113,9 @@ protected function defineOptions() { if (in_array($this->fieldMetadata['html_type'], ['Multi-Select', 'CheckBox'])) { $options['type']['default'] = 'civicrm_entity_custom_multi_value'; } + if ($this->fieldMetadata['data_type'] == 'ContactReference') { + $options['type']['default'] = 'civicrm_entity_contact_reference'; + } return $options; } From f94404bd1d1fd43d808b31a29d55c5139c959e84 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Fri, 16 Feb 2024 05:46:39 +0530 Subject: [PATCH 27/41] Clean value on custom field for Float (Number) and Money field type (#463) --- src/CiviEntityStorage.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/CiviEntityStorage.php b/src/CiviEntityStorage.php index 89fcec49..5da37bf6 100644 --- a/src/CiviEntityStorage.php +++ b/src/CiviEntityStorage.php @@ -429,7 +429,21 @@ protected function initFieldValues(ContentEntityInterface $entity, array $values // Handle special cases for field definitions. foreach ($field_definitions as $definition) { - if (($field_metadata = $definition->getSetting('civicrm_entity_field_metadata')) && isset($field_metadata['custom_group_id']) && $field_metadata['data_type'] === 'File') { + if (($field_metadata = $definition->getSetting('civicrm_entity_field_metadata')) && isset($field_metadata['custom_group_id']) && in_array($field_metadata['data_type'],['Float', 'Money'])) { + $items = $entity->get($definition->getName()); + $item_values = $items->getValue(); + if (!empty($item_values)) { + $ret = []; + foreach ($item_values as $value) { + $v = \CRM_Utils_Rule::cleanMoney($value['value']); + $ret[] = ['value' => $v]; + } + if (!empty($ret)) { + $items->setValue($ret); + } + } + } + elseif (($field_metadata = $definition->getSetting('civicrm_entity_field_metadata')) && isset($field_metadata['custom_group_id']) && $field_metadata['data_type'] === 'File') { $items = $entity->get($definition->getName()); $item_values = $items->getValue(); From 963299ef0875e8784d39607f0c2a197cfadb2cb6 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Fri, 1 Mar 2024 14:08:25 -0600 Subject: [PATCH 28/41] update test versions of Drupal 10.2 and CiviCRM 5.69 (#468) * update test versions of Drupal 10.2 and CiviCRM 5.69 * update CivicrmAddressViewsTest zip code to string --- .github/workflows/main.yml | 4 +++- .../FunctionalJavascript/Views/CivicrmAddressViewsTest.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ae7491d4..169ba9c4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,9 @@ jobs: fail-fast: false matrix: include: - - drupal: '10.0.*' + - drupal: '10.2.*' + civicrm: '5.69.*' + - drupal: '10.2.*' civicrm: 'dev-master' name: Drupal ${{ matrix.drupal }} | CiviCRM ${{ matrix.civicrm }} services: diff --git a/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php b/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php index 11e206e0..f7230e92 100644 --- a/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php +++ b/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php @@ -193,7 +193,7 @@ protected function assertViewWithArgumentsResults(array $arguments) { case 2: $assert_session->pageTextContainsOnce('Billing'); $assert_session->pageTextContains('United States'); - $assert_session->pageTextContainsOnce(75001); + $assert_session->pageTextContainsOnce('75001'); $assert_session->pageTextContainsOnce('Texas'); $assert_session->pageTextContainsOnce('3820 Vitruvian Way'); break; From 14f93cb71df74962f241dd5bc267feb292740cc1 Mon Sep 17 00:00:00 2001 From: Sunil Pawar Date: Fri, 19 Apr 2024 18:30:07 +0530 Subject: [PATCH 29/41] update contact entity referece field on contact merge (#456) --- civicrm_entity.module | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/civicrm_entity.module b/civicrm_entity.module index eb77d361..5e26f79c 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -745,3 +745,56 @@ function civicrm_entity_search_api_datasource_info_alter(array &$infos) { } } } + +/** + * Implements hook_civicrm_merge(). + */ +function civicrm_entity_civicrm_merge($mode, &$sqls, $mainId = NULL, $otherId = NULL, $tables = NULL) { + if ($mode == 'sqls' && !empty($mainId) && !empty($otherId)) { + // Get List if civicrm contact entity reference field. + $entityRefFields = \Drupal::entityTypeManager()->getStorage('field_storage_config') + ->loadByProperties([ + 'type' => 'entity_reference', + 'settings' => ['target_type' => 'civicrm_contact'], + ]); + + // Iterate fields and prepare the array. + foreach ($entityRefFields as $entityRefField) { + // Get Field name. + $fieldName = $entityRefField->getName(); + // Get Type, Node etc.. + $entityRefType = $entityRefField->getTargetEntityTypeId(); + $options[$entityRefType][] = $fieldName; + } + + foreach ($options as $type => $fields) { + foreach ($fields as $field) { + try { + // $otherId is duplicate contact ID + $ids = \Drupal::entityQuery($type) + ->condition($field, $otherId, '=') + ->accessCheck(FALSE) + ->execute(); + if (!empty($ids)) { + $entityRecords = Drupal::entityTypeManager()->getStorage($type) + ->loadMultiple($ids); + foreach ($entityRecords as $entityRecord) { + // Update new contact. + // Target Contact. + try { + $entityRecord->set($field, $mainId); + $entityRecord->save(); + } + catch (\Exception $e) { + \Drupal::logger('civicrm_entity')->error($e->getMessage()); + } + } + } + } + catch (\Exception $e) { + \Drupal::logger('civicrm_entity')->error($e->getMessage()); + } + } + } + } +} From 4e56f8b689bbb626708f8998c65abddb15629ad3 Mon Sep 17 00:00:00 2001 From: Pradeep Nayak Date: Fri, 19 Apr 2024 14:02:29 +0100 Subject: [PATCH 30/41] Ignore convert to UTC if custom field is date only (#469) --- src/Plugin/views/field/CustomEntityField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugin/views/field/CustomEntityField.php b/src/Plugin/views/field/CustomEntityField.php index 62012e76..cdfe63e0 100644 --- a/src/Plugin/views/field/CustomEntityField.php +++ b/src/Plugin/views/field/CustomEntityField.php @@ -362,7 +362,7 @@ public function convertToUtc(FieldDefinitionInterface $definition, $date_value) 'id' => $custom_field_id, ]; $date_field = $this->civicrmApi->get('CustomField', $params); - if (!empty($date_field[0]['date_format']) && $date_field[0]['date_format'] === 'yy') { + if (empty($date_field[0]['time_format'])) { $utc = FALSE; } } From 1bd5a7b3a872122b5967112e43eae577f2bd4982 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Fri, 19 Apr 2024 08:21:53 -0500 Subject: [PATCH 31/41] port to 4.0.x from 436: Include dblocale table names in list of civicrm entity info (#438) --- src/SupportedEntities.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/SupportedEntities.php b/src/SupportedEntities.php index a003cf54..d089b77c 100644 --- a/src/SupportedEntities.php +++ b/src/SupportedEntities.php @@ -727,6 +727,22 @@ public static function getInfo() { if (!in_array($entity_info['civicrm entity name'], $api_entity_types)) { unset($civicrm_entity_info[$entity_type]); } + // Insert dblocale table names + $multilingual = \CRM_Core_I18n::isMultilingual(); + if ($multilingual) { + // @codingStandardsIgnoreStart + global $dbLocale; + // @codingStandardsIgnoreEnd + if ($dbLocale) { + $tables = \CRM_Core_I18n_Schema::schemaStructureTables(); + if (in_array($entity_type, $tables)) { + $locale_table_name = $entity_type . $dbLocale; + if (strlen($locale_table_name) <= 32) { + $civicrm_entity_info[$locale_table_name] = $entity_info; + } + } + } + } } return $civicrm_entity_info; } From 516d855628711ce21f7725bf32311c882eac196f Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Fri, 19 Apr 2024 09:50:38 -0500 Subject: [PATCH 32/41] fix testLoadContact birthdate assertion (#472) --- tests/src/Kernel/CivicrmStorageGetTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/Kernel/CivicrmStorageGetTest.php b/tests/src/Kernel/CivicrmStorageGetTest.php index 38e6d309..084add5d 100644 --- a/tests/src/Kernel/CivicrmStorageGetTest.php +++ b/tests/src/Kernel/CivicrmStorageGetTest.php @@ -62,7 +62,7 @@ public function testLoadContact() { $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')); + $this->assertEquals('1982/06/28', $entity->get('birth_date')->date->format('Y/m/d')); } /** From 33f7df5dc07de259360c5724ec2c550c71a28afd Mon Sep 17 00:00:00 2001 From: Nick Perkins Date: Sat, 20 Apr 2024 01:50:51 +1000 Subject: [PATCH 33/41] Update default timezone handling for dates in CiviEntityStorage.php (#461) --- src/CiviEntityStorage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CiviEntityStorage.php b/src/CiviEntityStorage.php index 5da37bf6..5c84be28 100644 --- a/src/CiviEntityStorage.php +++ b/src/CiviEntityStorage.php @@ -418,7 +418,7 @@ protected function initFieldValues(ContentEntityInterface $entity, array $values } else { $datetime_format = $definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::DATE_STORAGE_FORMAT : DateTimeItemInterface::DATETIME_STORAGE_FORMAT; - $default_timezone = \Drupal::config('system.date')->get('timezone.default') ?? date_default_timezone_get(); + $default_timezone = $definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE ? DateTimeItemInterface::STORAGE_TIMEZONE : \Drupal::config('system.date')->get('timezone.default') ?? date_default_timezone_get(); $datetime_value = (new \DateTime($item[$main_property_name], new \DateTimeZone($default_timezone)))->setTimezone(new \DateTimeZone('UTC'))->format($datetime_format); $item_values[$delta][$main_property_name] = $datetime_value; } From 267ae6d3f06c05d38567aff8e8083a4b899e871a Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Sat, 20 Apr 2024 00:07:52 +0800 Subject: [PATCH 34/41] Add filter for state province. (#453) * Add filter for state province. * Add schema. * Minor update. * Fix default value for states and province. * Remove all options on initialization. --- civicrm_entity.libraries.yml | 9 ++ civicrm_entity.views.inc | 1 + config/schema/civicrm_entity.views.schema.yml | 3 + js/civicrm_entity.states.js | 33 +++++ src/Plugin/views/filter/StateProvince.php | 119 ++++++++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 js/civicrm_entity.states.js create mode 100644 src/Plugin/views/filter/StateProvince.php diff --git a/civicrm_entity.libraries.yml b/civicrm_entity.libraries.yml index 06b3ef2b..3c92fa4e 100644 --- a/civicrm_entity.libraries.yml +++ b/civicrm_entity.libraries.yml @@ -3,3 +3,12 @@ form: css: theme: css/civicrm_entity.form.css: {} + +states: + version: VERSION + js: + js/civicrm_entity.states.js: {} + dependencies: + - core/drupal + - core/jquery + - core/once diff --git a/civicrm_entity.views.inc b/civicrm_entity.views.inc index 5f60e404..1d7c805b 100644 --- a/civicrm_entity.views.inc +++ b/civicrm_entity.views.inc @@ -144,6 +144,7 @@ function civicrm_entity_views_data_alter(&$data) { $data['civicrm_contact']['current_employer']['real field'] = 'organization_name'; $data['civicrm_contact']['employer_id']['field']['id'] = 'standard'; $data['civicrm_contribution']['contribution_source']['real field'] = 'source'; + $data['civicrm_address']['state_province_id']['filter']['id'] = 'civicrm_entity_civicrm_address_state_province'; } /** diff --git a/config/schema/civicrm_entity.views.schema.yml b/config/schema/civicrm_entity.views.schema.yml index 58d291aa..9f4a4801 100644 --- a/config/schema/civicrm_entity.views.schema.yml +++ b/config/schema/civicrm_entity.views.schema.yml @@ -63,6 +63,9 @@ views.filter_value.civicrm_entity_civicrm_address_proximity: type: string label: 'Distance unit' +views.filter.civicrm_entity_civicrm_address_state_province: + type: views.filter.many_to_one + views.argument_validator.entity:civicrm_contact: type: views.argument_validator_entity label: 'Civicrm contact' diff --git a/js/civicrm_entity.states.js b/js/civicrm_entity.states.js new file mode 100644 index 00000000..49df755e --- /dev/null +++ b/js/civicrm_entity.states.js @@ -0,0 +1,33 @@ +/** + * @file + * civicrm_entity.states.js + */ + +(function ($, Drupal) { + + /** + * Sets states depending on the chosen country. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + */ + Drupal.behaviors.civicrmEntityStates = { + attach(context, settings) { + $(once('loadStates', '.views-exposed-form [data-drupal-selector="edit-' + settings.civicrm_entity.country_identifier + '"]', context)).on('change', function() { + var countries = $(this).val(); + + var $stateElement = $('[data-drupal-selector="edit-' + settings.civicrm_entity.states_identifier + '"]', $(this).parents('form')); + $stateElement.find('option').not(':first').remove(); + + if (countries != Drupal.t('All')) { + countries = Array.isArray(countries) ? countries : [countries]; + countries.forEach((v) => { + $stateElement.append(settings.civicrm_entity.states[v]); + }); + } + }).trigger('change'); + }, + }; + +})(jQuery, Drupal, drupalSettings); diff --git a/src/Plugin/views/filter/StateProvince.php b/src/Plugin/views/filter/StateProvince.php new file mode 100644 index 00000000..a53b59b2 --- /dev/null +++ b/src/Plugin/views/filter/StateProvince.php @@ -0,0 +1,119 @@ +civicrmApi = $civicrm_api; + + $this->civicrmApi->civicrmInitialize(); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('civicrm_entity.api') + ); + } + + /** + * {@inheritdoc} + */ + protected function valueForm(&$form, FormStateInterface $form_state) { + $exposed = $form_state->get('exposed'); + $view = $this->view; + $handler = $view->getHandler($this->view->current_display, 'filter', 'country_id'); + + if ($exposed && !empty($handler) && $handler['table'] == 'civicrm_address' && $handler['field'] == 'country_id') { + $user_input = $form_state->getUserInput(); + + $selected = []; + if (isset($user_input[$this->options['expose']['identifier']])) { + $selected = is_array($user_input[$this->options['expose']['identifier']]) ? $user_input[$this->options['expose']['identifier']] : [$user_input[$this->options['expose']['identifier']]]; + } + + if ($handler['exposed']) { + $countries = array_keys(\CRM_Core_PseudoConstant::country()); + $country_states = $this->getStates($countries); + + // Convert to HTML options. + $js_country_states = []; + foreach ($country_states as $country_id => $states) { + if (empty($js_country_states[$country_id])) { + $js_country_states[$country_id] = ''; + } + + foreach ($states as $k => $v) { + $js_country_states[$country_id] .= ''; + } + } + $form['#attached']['drupalSettings']['civicrm_entity']['states_identifier'] = Html::cleanCssIdentifier($this->options['expose']['identifier']); + $form['#attached']['drupalSettings']['civicrm_entity']['country_identifier'] = Html::cleanCssIdentifier($handler['expose']['identifier']); + $form['#attached']['drupalSettings']['civicrm_entity']['states'] = $js_country_states; + $form['#attached']['library'][] = 'civicrm_entity/states'; + } + else { + $selected_countries = is_array($handler['value']) ? $handler['value'] : [$handler['value']]; + $country_states = $this->getStates($selected_countries); + $this->valueOptions = []; + foreach ($country_states as $states) { + $this->valueOptions += $states; + } + } + } + + parent::valueForm($form, $form_state); + } + + /** + * Gets the corresponding states. + * + * @param array $countries + * The countries. + * + * @return array + * The states keyed by country_id. + */ + protected function getStates(array $countries): array { + $states = []; + + $countries = implode(', ', $countries); + $query = "SELECT id, name, country_id FROM civicrm_state_province WHERE country_id IN ($countries) ORDER BY name ASC"; + $result = \CRM_Core_DAO::executeQuery($query); + + while ($result->fetch()) { + $states[$result->country_id][$result->id] = $result->name; + } + + return $states; + } + +} From 613fcb8532b089d9caf4874c9918c31ea364ef06 Mon Sep 17 00:00:00 2001 From: Sadashiv Date: Fri, 19 Apr 2024 21:42:15 +0530 Subject: [PATCH 35/41] Adding a raw value function to return value from values array (#465) --- src/Entity/CivicrmEntity.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Entity/CivicrmEntity.php b/src/Entity/CivicrmEntity.php index 5e1a9ae3..dcbeeafa 100644 --- a/src/Entity/CivicrmEntity.php +++ b/src/Entity/CivicrmEntity.php @@ -229,4 +229,8 @@ public function civicrmApiNormalize() { return $params; } + public function getRawValue($field) { + return $this->values[$field] ?? ''; + } + } From 8a5b3de29f6a73c25ba20bceb14a0a5743c2d8ab Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Sat, 20 Apr 2024 00:46:57 +0800 Subject: [PATCH 36/41] 4.0.x field mappings (#430) * Massage data for field mappings. * Add missing fields. --- src/CiviEntityStorage.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/CiviEntityStorage.php b/src/CiviEntityStorage.php index 5c84be28..c617b187 100644 --- a/src/CiviEntityStorage.php +++ b/src/CiviEntityStorage.php @@ -158,17 +158,13 @@ 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']; - } $ids = $this->cleanIds($ids); if (!empty($ids)) { $options = [ 'id' => ['IN' => $ids], - 'return' => $field_names, + 'return' => array_keys($fields), 'options' => ['limit' => 0], ]; @@ -195,7 +191,14 @@ protected function doLoadMultiple(array $ids = NULL) { $civicrm_entity = $temporary; } - $entity = $this->prepareLoadedEntity($civicrm_entity); + $massaged_civicrm_entity = []; + foreach ($fields as $name => $field) { + if (isset($civicrm_entity[$name])) { + $massaged_civicrm_entity[$field['name']] = $civicrm_entity[$name]; + } + } + + $entity = $this->prepareLoadedEntity($civicrm_entity + $massaged_civicrm_entity); $entities[$entity->id()] = $entity; } } From 25d711870d0f7a80b1eea72f19ecee7259da2590 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Fri, 19 Apr 2024 14:17:22 -0500 Subject: [PATCH 37/41] fix test to use string instead of integer for zip code (#474) --- .../src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php b/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php index f7230e92..2386adc0 100644 --- a/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php +++ b/tests/src/FunctionalJavascript/Views/CivicrmAddressViewsTest.php @@ -159,7 +159,7 @@ protected function assertViewWithSortsResults() { $assert_session = $this->assertSession(); $assert_session->elementTextContains('css', '.views-row:first-child', 'Texas'); $assert_session->elementTextContains('css', '.views-row:first-child', 'United States'); - $assert_session->elementTextContains('css', '.views-row:first-child', 75001); + $assert_session->elementTextContains('css', '.views-row:first-child', '75001'); $assert_session->elementTextContains('css', '.views-row:first-child', '3820 Vitruvian Way'); } From abeccede9ac5287a36a588df126a0d715614e0ec Mon Sep 17 00:00:00 2001 From: puresyntax71 <34715246+puresyntax71@users.noreply.github.com> Date: Sat, 20 Apr 2024 03:30:39 +0800 Subject: [PATCH 38/41] Pathauto (#470) * Fix database not finding. * Add fix for bulk delete as well. --- civicrm_entity.module | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/civicrm_entity.module b/civicrm_entity.module index 5e26f79c..84e62ece 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -15,6 +15,7 @@ use Drupal\civicrm_entity\Entity\Sql\CivicrmEntityStorageSchema; use Drupal\civicrm_entity\Form\CivicrmEntityForm; use Drupal\civicrm_entity\Routing\CiviCrmEntityRouteProvider; use Drupal\civicrm_entity\SupportedEntities; +use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Entity\ContentEntityDeleteForm; use Drupal\Core\Entity\ContentEntityType; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; @@ -798,3 +799,35 @@ function civicrm_entity_civicrm_merge($mode, &$sqls, $mainId = NULL, $otherId = } } } + +/** + * Implements hook_query_TAG_alter(). + */ +function civicrm_entity_query_pathauto_bulk_update_alter(AlterableInterface $query) { + $tables = &$query->getTables(); + + if (strpos($tables['base_table']['table'], 'civicrm_') !== FALSE) { + $civicrm_connection_name = drupal_valid_test_ua() ? 'civicrm_test' : 'civicrm'; + $civicrm_database_info = Database::getConnectionInfo($civicrm_connection_name); + if (isset($civicrm_database_info['default'])) { + $connection = Database::getConnection('default', $civicrm_connection_name); + $tables['base_table']['table'] = $connection->getFullQualifiedTableName($tables['base_table']['table']); + } + } +} + +/** + * Implements hook_query_TAG_alter(). + */ +function civicrm_entity_query_pathauto_bulk_delete_alter(AlterableInterface $query) { + $tables = &$query->getTables(); + + if (strpos($tables['base_table']['table'], 'civicrm_') !== FALSE) { + $civicrm_connection_name = drupal_valid_test_ua() ? 'civicrm_test' : 'civicrm'; + $civicrm_database_info = Database::getConnectionInfo($civicrm_connection_name); + if (isset($civicrm_database_info['default'])) { + $connection = Database::getConnection('default', $civicrm_connection_name); + $tables['base_table']['table'] = $connection->getFullQualifiedTableName($tables['base_table']['table']); + } + } +} From 95900591a9a297f2ab8334405d1ae150bce7cbed Mon Sep 17 00:00:00 2001 From: Luke Stewart Date: Tue, 23 Apr 2024 10:49:12 +1200 Subject: [PATCH 39/41] Adds is Empty/NULL operators (#423) * Adds is Empty/NULL operators * require Condition for In/Operator Empty null condition * Mirror parent class conditional - only allow empty is set in definition. --- src/Plugin/views/filter/InOperator.php | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/Plugin/views/filter/InOperator.php b/src/Plugin/views/filter/InOperator.php index bd23c688..d4841bcc 100644 --- a/src/Plugin/views/filter/InOperator.php +++ b/src/Plugin/views/filter/InOperator.php @@ -3,6 +3,7 @@ namespace Drupal\civicrm_entity\Plugin\views\filter; use Drupal\Core\Database\Connection; +use Drupal\Core\Database\Query\Condition; use Drupal\civicrm_entity\CiviCrmApiInterface; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\filter\InOperator as BaseInOperator; @@ -137,4 +138,45 @@ protected function opSimple() { } } + public function operators() { + $operators = parent::operators(); + if (!empty($this->definition['allow empty'])) { + $operators += [ + 'empty string' => [ + 'title' => $this->t('Is EMPTY/NULL'), + 'method' => 'opEmptyString', + 'short' => $this->t('empty string'), + 'values' => 0, + ], + 'not empty string' => [ + 'title' => $this->t('Is not EMPTY/NULL'), + 'method' => 'opEmptyString', + 'short' => $this->t('not empty string'), + 'values' => 0, + ], + ]; + } + return $operators; + } + + protected function opEmptyString() { + $this->ensureMyTable(); + $field = "$this->tableAlias.$this->realField"; + + if ($this->operator == 'empty string') { + $operator = "="; + $nullOP = 'IS NULL'; + } + else { + $operator = "!="; + $nullOP = 'IS NOT NULL'; + } + + $condition = new Condition('OR'); + $condition->condition($field, '', $operator); + $condition->condition($field, NULL, $nullOP); + + $this->query->addWhere($this->options['group'], $condition); + } + } From 0838fb18c24e92044cbf80e479a2d0f1856b40e0 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Thu, 25 Apr 2024 15:07:27 -0600 Subject: [PATCH 40/41] only check for data in enabled components on module uninstall (#476) --- src/CiviEntityStorage.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CiviEntityStorage.php b/src/CiviEntityStorage.php index c617b187..d6421270 100644 --- a/src/CiviEntityStorage.php +++ b/src/CiviEntityStorage.php @@ -308,7 +308,12 @@ public function hasData() { return !in_array($component, $components) ? FALSE : $this->getCiviCrmApi()->getCount($this->entityType->get('civicrm_entity')) > 0; } - return $this->getCiviCrmApi()->getCount($this->entityType->get('civicrm_entity')) > 0; + else { + $enabled_components_as_entity = $this->getConfigFactory()->get('civicrm_entity.settings')->get('enabled_entity_types'); + $component = $this->entityType->get('civicrm_entity'); + return !in_array($component, $enabled_components_as_entity) ? FALSE : + $this->getCiviCrmApi()->getCount($this->entityType->get('civicrm_entity')) > 0; + } } /** From 52e97b8d4cb19394bbe65d6224b9a072d8250a40 Mon Sep 17 00:00:00 2001 From: Mark Hanna Date: Thu, 25 Apr 2024 15:08:27 -0600 Subject: [PATCH 41/41] patch from issue 3420771 (#475) --- civicrm_entity.module | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/civicrm_entity.module b/civicrm_entity.module index 84e62ece..12176250 100644 --- a/civicrm_entity.module +++ b/civicrm_entity.module @@ -35,6 +35,7 @@ use Drupal\Core\Entity\Display\EntityDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\civicrm_entity\Plugin\search_api\datasource\CivicrmEntity as DatasourceCivicrmEntity; +use Drupal\Core\Field\Entity\BaseFieldOverride; /** * Implements hook_theme(). @@ -236,6 +237,14 @@ function civicrm_entity_entity_bundle_field_info(EntityTypeInterface $entity_typ $result[$field_instance->getName()] = $field_instance; } } + // Ensure all fields have a definition. + foreach ($base_field_definitions as $field_name => $definition) { + if (isset($result[$field_name])) { + continue; + } + $field = BaseFieldOverride::createFromBaseFieldDefinition($base_field_definitions[$field_name], $bundle); + $result[$field_name] = $field; + } return $result; }