From da9cd2454fbca7e38a78f7cb1539decaa5c2f4ff Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Tue, 29 Nov 2022 15:09:28 +1300 Subject: [PATCH] Add FormBuilder forms to Civiimport Co-authored-by: colemanw --- .../Event/Subscriber/ImportSubscriber.php | 79 +++++++++++++++++-- ext/civiimport/Civi/BAO/Import.php | 21 +++-- ext/civiimport/civiimport.php | 17 +++- 3 files changed, 97 insertions(+), 20 deletions(-) diff --git a/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php b/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php index 4a267ffc68..4ee6bbbe5e 100644 --- a/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php +++ b/ext/civiimport/Civi/Api4/Event/Subscriber/ImportSubscriber.php @@ -12,19 +12,23 @@ use Civi\API\Event\AuthorizeEvent; use Civi\API\Events; use Civi\Api4\Entity; use Civi\Api4\Managed; +use Civi\Api4\SearchDisplay; use Civi\Api4\UserJob; use Civi\Core\Event\PostEvent; use Civi\Core\Event\GenericHookEvent; +use Civi\Core\Service\AutoService; use CRM_Core_DAO_AllCoreTables; use Civi\Api4\Import; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Civi\API\Exception\UnauthorizedException; +use CRM_Civiimport_ExtensionUtil as E; /** * Listening class that registers each Import table as an entity. + * * @service civi.api4.importSubscriber */ -class ImportSubscriber extends \Civi\Core\Service\AutoService implements EventSubscriberInterface { +class ImportSubscriber extends AutoService implements EventSubscriberInterface { /** * Get the events this class listens to. @@ -36,6 +40,7 @@ class ImportSubscriber extends \Civi\Core\Service\AutoService implements EventSu 'hook_civicrm_post' => 'on_hook_civicrm_post', 'civi.api4.entityTypes' => 'on_civi_api4_entityTypes', 'civi.api.authorize' => [['onApiAuthorize', Events::W_EARLY]], + 'civi.afform.get' => 'on_civi_afform_get', ]; } @@ -59,12 +64,6 @@ class ImportSubscriber extends \Civi\Core\Service\AutoService implements EventSu 'class_args' => [$userJobID], 'label_field' => '_id', 'searchable' => 'secondary', - 'paths' => [ - // 'browse' => "civicrm/eck/entity/list/{$entity_type['name']}", - // 'view' => "civicrm/eck/entity?reset=1&action=view&type={$entity_type['name']}&id=[id]", - // 'update' => "civicrm/eck/entity/edit/{$entity_type['name']}/[subtype:name]#?{$entity_type['entity_name']}=[id]", - // 'add' => "civicrm/eck/entity/edit/{$entity_type['name']}/[subtype:name]", - ], 'class' => Import::class, 'icon' => 'fa-upload', ]; @@ -80,6 +79,7 @@ class ImportSubscriber extends \Civi\Core\Service\AutoService implements EventSu $exists = Entity::get(FALSE)->addWhere('name', '=', 'Import_' . $event->id)->selectRowCount()->execute()->count(); if (!$exists || $event->action === 'delete') { // Flush entities cache key so our new Import will load as an entity. + unset(Civi::$statics['civiimport_tables']); Civi::cache('metadata')->delete('api4.entities.info'); Civi::cache('metadata')->delete('civiimport_tables'); CRM_Core_DAO_AllCoreTables::flush(); @@ -112,4 +112,69 @@ class ImportSubscriber extends \Civi\Core\Service\AutoService implements EventSu } } + /** + * Get an array of FormBuilder forms for viewing imports. + * + * @param \Civi\Core\Event\GenericHookEvent $event + * + * @throws \CRM_Core_Exception + * + * @noinspection PhpUnused + */ + public static function on_civi_afform_get(GenericHookEvent $event): void { + // We're only providing afforms of type 'search' + if ($event->getTypes && !in_array('search', $event->getTypes, TRUE)) { + return; + } + + $importForms = self::getImportForms(); + if (!empty($importForms) && $importForms !== $event->afforms) { + $event->afforms = array_merge($event->afforms ?? [], $importForms); + } + } + + /** + * Get an array of FormBuilder forms for viewing imports. + * + * @return array + * + * @throws \CRM_Core_Exception + */ + public static function getImportForms(): array { + $cacheKey = 'civiimport_forms_' . \CRM_Core_Config::domainID() . '_' . (int) \CRM_Core_Session::getLoggedInContactID(); + if (\Civi::cache('metadata')->has($cacheKey)) { + return \Civi::cache('metadata')->get($cacheKey); + } + $forms = []; + try { + $importSearches = SearchDisplay::get() + ->addWhere('saved_search_id.name', 'LIKE', 'Import\_Summary\_%') + ->addWhere('saved_search_id.expires_date', '>', 'now') + ->addSelect('name', 'label') + ->execute(); + foreach ($importSearches as $importSearch) { + $userJobID = str_replace('Import_Summary_', '', $importSearch['name']); + $forms[$importSearch['name']] = [ + 'name' => $importSearch['name'], + 'type' => 'search', + 'title' => $importSearch['label'], + 'base_module' => E::LONG_NAME, + 'is_dashlet' => FALSE, + 'is_public' => FALSE, + 'is_token' => FALSE, + 'permission' => 'access CiviCRM', + 'requires' => ['crmSearchDisplayTable'], + 'layout' => '
+ +
', + ]; + } + } + catch (UnauthorizedException $e) { + // No access - return the empty array. + } + \Civi::cache('metadata')->set($cacheKey, $forms); + return $forms; + } + } diff --git a/ext/civiimport/Civi/BAO/Import.php b/ext/civiimport/Civi/BAO/Import.php index 715c672f20..eff6f36c4b 100644 --- a/ext/civiimport/Civi/BAO/Import.php +++ b/ext/civiimport/Civi/BAO/Import.php @@ -44,21 +44,20 @@ class Import extends CRM_Core_DAO { /** * Get the array of import tables in the database. * - * Note that this can be required early (e.g EntityTypes, even - * before caching & class loading is fully available (the latter could be resolved now). - * Where that is the case `_civiimport_civicrm_get_import_tables` should be called directly. + * Caching is a challenge here as the tables are loaded by the entityTypes hook + * before the cache & full class loading is necessarily available. We did have + * caching in this function but removed it recently in favour of a static cache in + * the other function as that function was 'doing it's work' from the entityTypes + * hook anyway. + * + * In general, call this function from any code that runs late enough in the boot + * order that caches/ class loading is available in case it diverges once again + * from the lower level function. * * @return array */ public static function getImportTables(): array { - // This calls a function on the extension file as it is called from `entityTypes` - // which can be called very early, before this class is available to that hook. - if (!\Civi::cache('metadata')->has('civiimport_tables')) { - $tables = _civiimport_civicrm_get_import_tables(); - \Civi::cache('metadata')->set('civiimport_tables', $tables); - return $tables; - } - return \Civi::cache('metadata')->get('civiimport_tables'); + return _civiimport_civicrm_get_import_tables(); } /** diff --git a/ext/civiimport/civiimport.php b/ext/civiimport/civiimport.php index 3eb2c99911..ded5220c2c 100644 --- a/ext/civiimport/civiimport.php +++ b/ext/civiimport/civiimport.php @@ -86,6 +86,10 @@ function civiimport_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes */ function civiimport_civicrm_entityTypes(array &$entityTypes): void { + // This is the uncached function :-( Because we can't tell if it is being + // called pre-boot. Currently both this and the cached functions rely on the + // static cache - but since it keeps changing practice is to call this + // function when we know caching is likely to be scary. $importEntities = _civiimport_civicrm_get_import_tables(); foreach ($importEntities as $userJobID => $table) { @@ -104,11 +108,19 @@ function civiimport_civicrm_entityTypes(array &$entityTypes): void { * have class loading set up by the time it runs. * * Where the database is fully booted already it is better to call - * `Civi\BAO\Import::getImportTables()` which has caching. + * `Civi\BAO\Import::getImportTables()` which is expected to have caching. + * + * Currently both functions share the Civi::statics caching in this function - + * but we have had lots of back & forth so the principle is - call this if + * we know caching could be scary - call the other for 'whatever caching is + * most performant'. * * @return array */ function _civiimport_civicrm_get_import_tables(): array { + if (isset(Civi::$statics['civiimport_tables'])) { + return Civi::$statics['civiimport_tables']; + } // We need to avoid the api here as it is called early & could cause loops. $tables = CRM_Core_DAO::executeQuery(' SELECT `user_job`.`id` AS id, `metadata`, `name`, `job_type`, `user_job`.`created_id`, `created_id`.`display_name`, `user_job`.`created_date`, `user_job`.`expires_date` @@ -146,6 +158,7 @@ function _civiimport_civicrm_get_import_tables(): array { 'description' => $tables->created_date . $createdBy, ]; } + Civi::$statics['civiimport_tables'] = $importEntities; return $importEntities; } @@ -200,7 +213,7 @@ function civiimport_civicrm_alterTemplateFile($formName, $form, $type, &$templat * @noinspection PhpUnused */ function civiimport_civicrm_searchKitTasks(array &$tasks, bool $checkPermissions, ?int $userId) { - foreach (_civiimport_civicrm_get_import_tables() as $import) { + foreach (Import::getImportTables() as $import) { $tasks['Import_' . $import['user_job_id']]['validate'] = [ 'title' => E::ts('Validate'), 'icon' => 'fa-check', -- 2.25.1