From 5fdc1773ff33c78059463eba22d8c7759ef65e1e Mon Sep 17 00:00:00 2001 From: John van Breda Date: Thu, 26 Sep 2024 09:22:04 +0100 Subject: [PATCH 01/19] Syntax fix and code tidy --- prebuilt_forms/species_details_2.php | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/prebuilt_forms/species_details_2.php b/prebuilt_forms/species_details_2.php index d086be83..e949e563 100644 --- a/prebuilt_forms/species_details_2.php +++ b/prebuilt_forms/species_details_2.php @@ -43,70 +43,70 @@ class iform_species_details_2 extends BaseDynamicDetails { /** * Stores a value to indicate of no taxon identified. * - * @var notaxon + * @var bool */ private static $notaxon; /** * Stores the preferred name of the taxon with markup and authority. * - * @var preferred + * @var string */ private static $preferred; /** * Stores the preferred name of the taxon. * - * @var preferredPlain + * @var string */ private static $preferredPlain; /** * Stores the default common name of the taxon. * - * @var defaultCommonName + * @var string */ private static $defaultCommonName; /** * Stores the synonyms of the taxon. * - * @var synonyms + * @var array */ private static $synonyms = []; /** * Stores the common names of the taxon. * - * @var commonNames + * @var array */ private static $commonNames = []; /** * Stores the taxonomy of the taxon. * - * @var taxonomy + * @var array */ private static $taxonomy = []; /** * Stores the taxa_taxon_list_id of the taxon. * - * @var taxaTaxonListId + * @var int */ private static $taxaTaxonListId; /** * Stores the taxon_meaning_id of the taxon. * - * @var taxonMeaningId + * @var int */ private static $taxonMeaningId; /** * Stores the exter_key of the taxon. * - * @var externalKey + * @var string */ private static $externalKey; @@ -1093,7 +1093,7 @@ protected static function get_control_recsthroughyear($auth, $args, $tabalias, $ 'source' => 'recsthroughyearSource', 'functionName' => 'populateRecsThroughYearChart', ]; - $customScript .= ElasticsearchReportHelper::customScript($optionsCustomScript); + $customScript = ElasticsearchReportHelper::customScript($optionsCustomScript); $r = <<

$title

@@ -1595,7 +1595,6 @@ protected static function mapWithoutGeoserver($auth, $args, $tabalias, $options) $params = [ 'taxa_taxon_list_id' => empty($_GET['taxa_taxon_list_id']) ? '' : $_GET['taxa_taxon_list_id'], 'taxon_meaning_id' => empty($_GET['taxon_meaning_id']) ? '' : $_GET['taxon_meaning_id'], - 'sharing' => 'reporting', 'reportGroup' => 'dynamic', 'autoParamsForm' => FALSE, 'sharing' => $sharing, @@ -1760,7 +1759,6 @@ protected static function get_control_speciesphotos($auth, $args, $tabalias, $op /** * Returns a control for picking a species. * - * @global type $indicia_templates * @param array $auth * Read authorisation tokens. * @param array $args From 38c18490f2fd8fb365d81fd68a54c265efc0d22b Mon Sep 17 00:00:00 2001 From: John van Breda Date: Thu, 26 Sep 2024 12:41:51 +0100 Subject: [PATCH 02/19] Remove unused parameter --- prebuilt_forms/group_discovery.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/prebuilt_forms/group_discovery.php b/prebuilt_forms/group_discovery.php index 42d0a250..e195740a 100644 --- a/prebuilt_forms/group_discovery.php +++ b/prebuilt_forms/group_discovery.php @@ -50,13 +50,6 @@ public static function getPageType(): PageType { public static function get_parameters() { return [ - [ - 'name' => 'group_home_path', - 'caption' => 'Path to the group home page', - 'description' => 'Path to the Drupal page which hosts group home pages.', - 'type' => 'text_input', - 'required' => FALSE, - ], [ 'name' => 'default_group_label_plural', 'caption' => 'Default group label (plural)', From 1efe6c5a542aa88d2d4cc3964989880a5daf0516 Mon Sep 17 00:00:00 2001 From: John van Breda Date: Mon, 30 Sep 2024 15:58:39 +0100 Subject: [PATCH 03/19] Adds boundaryLocationId option to leafletMap --- ElasticsearchProxyHelper.php | 45 +++++++++++++++++++++++++++++++++++ ElasticsearchReportHelper.php | 1 + 2 files changed, 46 insertions(+) diff --git a/ElasticsearchProxyHelper.php b/ElasticsearchProxyHelper.php index c420b8fd..61520e3d 100644 --- a/ElasticsearchProxyHelper.php +++ b/ElasticsearchProxyHelper.php @@ -149,6 +149,9 @@ public static function callMethod($method, $nid) { case 'runcustomruleset': return self::proxyRunCustomRuleset($nid); + case 'getLocationBoundaryGeom': + return self::getLocationBoundaryGeom($nid); + default: throw new ElasticsearchProxyAbort('Method not found', 404); } @@ -3222,4 +3225,46 @@ private static function proxyRunCustomRuleset($nid) { return self::curlPost($url, $query); } + /** + * Proxy method for fetching a locations' boundary geom. + * + * Used when the `locationBoundaryId` option is set for a `leafletMap` + * control. The response is cached. + * + * @param int $nid + * Node ID. + * + * @return array + * Array containing HTTP status and response message, plus boundary_geom + * if it worked. + */ + private static function getLocationBoundaryGeom($nid) { + if (empty($_GET['location_id']) || !preg_match('/^\d+$/', $_GET['location_id'])) { + http_response_code(400); + return ['status' => 400, 'msg' => 'Bad Request']; + } + iform_load_helpers(['report_helper']); + $conn = iform_get_connection_details($nid); + $readAuth = helper_base::get_read_auth($conn['website_id'], $conn['password']); + + $response = report_helper::get_report_data([ + 'dataSource' => '/library/locations/locations_combined_boundary_transformed', + 'extraParams' => [ + 'location_ids' => $_GET['location_id'], + ], + 'readAuth' => $readAuth, + 'caching' => TRUE, + 'cachePerUser' => FALSE, + ]); + if (empty($response)) { + http_response_code(404); + return ['status' => 404, 'msg' => 'Not Found']; + } + return [ + 'status' => 200, + 'msg' => 'OK', + 'boundary_geom' => $response[0]['geom'], + ]; + } + } diff --git a/ElasticsearchReportHelper.php b/ElasticsearchReportHelper.php index 7e082222..0b76658f 100644 --- a/ElasticsearchReportHelper.php +++ b/ElasticsearchReportHelper.php @@ -1180,6 +1180,7 @@ public static function leafletMap(array $options) { } $dataOptions = helper_base::getOptionsForJs($options, [ 'baseLayerConfig', + 'boundaryLocationId', 'cookies', 'initialLat', 'initialLng', From dd02bd7ea6456693af46d0b1c63b6fb23aaeccd0 Mon Sep 17 00:00:00 2001 From: John van Breda Date: Mon, 30 Sep 2024 15:58:56 +0100 Subject: [PATCH 04/19] cachePerUser option fix --- helper_base.php | 1 + 1 file changed, 1 insertion(+) diff --git a/helper_base.php b/helper_base.php index f4743d0e..f24eefc0 100644 --- a/helper_base.php +++ b/helper_base.php @@ -3596,6 +3596,7 @@ public static function getCachedGenericCall($url, array $get, array $post, array ]; if (isset($options['cachePerUser']) && !$options['cachePerUser']) { $excludedParams[] = 'user_id'; + $excludedParams[] = 'currentUser'; } $cacheOpts = array_diff_key(array_merge($get, $post), array_combine($excludedParams, $excludedParams)); $cacheOpts['serviceCallPath'] = self::$base_url . $url; From d227e4a37638074982b75faddb1b72eeea1ed37e Mon Sep 17 00:00:00 2001 From: John van Breda Date: Tue, 1 Oct 2024 10:56:51 +0100 Subject: [PATCH 05/19] Consistency of ES AJAX Proxy response format --- ElasticsearchProxyHelper.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ElasticsearchProxyHelper.php b/ElasticsearchProxyHelper.php index 61520e3d..fa2bc634 100644 --- a/ElasticsearchProxyHelper.php +++ b/ElasticsearchProxyHelper.php @@ -286,7 +286,10 @@ private static function proxyDoesUserSeeNotifications($nid) { iform_load_helpers(['VerificationHelper']); $conn = iform_get_connection_details($nid); $readAuth = helper_base::get_read_auth($conn['website_id'], $conn['password']); - return ['msg' => VerificationHelper::doesUserSeeNotifications($readAuth, $_GET['user_id'])]; + return [ + 'message' => 'OK', + 'result' => VerificationHelper::doesUserSeeNotifications($readAuth, $_GET['user_id']), + ]; } /** @@ -660,7 +663,8 @@ private static function proxyVerificationQueryEmail() { $success = hostsite_send_email($_POST['to'], $_POST['subject'], $emailBody); return [ - 'status' => $success ? 'OK' : 'Fail', + 'message' => $success ? 'OK' : 'Fail', + 'code' => $success ? 200 : 500, ]; } @@ -2867,6 +2871,12 @@ private static function bulkEditIds($nid, array $ids, array $updates, array $opt * batch to fetch if paging. */ private static function proxyBulkEditAll($nid) { + return [ + 'code' => 409, + 'message' => 'Conflict', + 'info' => 'Shared sample', + 'errorCode' => 'SAMPLES_CONTAIN_OTHER_OCCURRENCES', + ]; $batchInfo = self::getOccurrenceIdPageFromFilter( $nid, $_POST['occurrence:idsFromElasticFilter'], @@ -3239,9 +3249,10 @@ private static function proxyRunCustomRuleset($nid) { * if it worked. */ private static function getLocationBoundaryGeom($nid) { + return ['code' => 400, 'message' => 'Bad Request']; if (empty($_GET['location_id']) || !preg_match('/^\d+$/', $_GET['location_id'])) { http_response_code(400); - return ['status' => 400, 'msg' => 'Bad Request']; + return ['code' => 400, 'message' => 'Bad Request']; } iform_load_helpers(['report_helper']); $conn = iform_get_connection_details($nid); @@ -3258,11 +3269,10 @@ private static function getLocationBoundaryGeom($nid) { ]); if (empty($response)) { http_response_code(404); - return ['status' => 404, 'msg' => 'Not Found']; + return ['code' => 404, 'message' => 'Not Found']; } return [ - 'status' => 200, - 'msg' => 'OK', + 'message' => 'OK', 'boundary_geom' => $response[0]['geom'], ]; } From 143ebcfe420d6189fd6f8a037cedc0f3a576896d Mon Sep 17 00:00:00 2001 From: John van Breda Date: Tue, 1 Oct 2024 11:05:30 +0100 Subject: [PATCH 06/19] Removed debug code --- ElasticsearchProxyHelper.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ElasticsearchProxyHelper.php b/ElasticsearchProxyHelper.php index fa2bc634..31a6456f 100644 --- a/ElasticsearchProxyHelper.php +++ b/ElasticsearchProxyHelper.php @@ -2871,12 +2871,6 @@ private static function bulkEditIds($nid, array $ids, array $updates, array $opt * batch to fetch if paging. */ private static function proxyBulkEditAll($nid) { - return [ - 'code' => 409, - 'message' => 'Conflict', - 'info' => 'Shared sample', - 'errorCode' => 'SAMPLES_CONTAIN_OTHER_OCCURRENCES', - ]; $batchInfo = self::getOccurrenceIdPageFromFilter( $nid, $_POST['occurrence:idsFromElasticFilter'], @@ -2898,7 +2892,7 @@ private static function proxyBulkEditAll($nid) { return $response; } -/** + /** * Bulk edit a list of selected records. * * @param int $nid @@ -3249,7 +3243,6 @@ private static function proxyRunCustomRuleset($nid) { * if it worked. */ private static function getLocationBoundaryGeom($nid) { - return ['code' => 400, 'message' => 'Bad Request']; if (empty($_GET['location_id']) || !preg_match('/^\d+$/', $_GET['location_id'])) { http_response_code(400); return ['code' => 400, 'message' => 'Bad Request']; From 023eacf2f396ae7e61920a64073126037fd8fcbb Mon Sep 17 00:00:00 2001 From: John van Breda Date: Wed, 2 Oct 2024 10:21:02 +0100 Subject: [PATCH 07/19] Use Drupal cache for location boundary if possible --- ElasticsearchProxyHelper.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ElasticsearchProxyHelper.php b/ElasticsearchProxyHelper.php index 31a6456f..4e62901e 100644 --- a/ElasticsearchProxyHelper.php +++ b/ElasticsearchProxyHelper.php @@ -3267,6 +3267,10 @@ private static function getLocationBoundaryGeom($nid) { return [ 'message' => 'OK', 'boundary_geom' => $response[0]['geom'], + '#cache' => [ + 'max-age' => 3600, + 'contexts' => ['route'], + ], ]; } From 95644c46985729517f70f05229c7342e684b2a65 Mon Sep 17 00:00:00 2001 From: Rich Burkmar Date: Thu, 10 Oct 2024 15:21:31 +0100 Subject: [PATCH 08/19] Fixes to problems with species_details_2 incl iRec iss #1727 --- prebuilt_forms/js/species_details_2.js | 2 +- prebuilt_forms/species_details_2.php | 30 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/prebuilt_forms/js/species_details_2.js b/prebuilt_forms/js/species_details_2.js index 8b87b6ed..d81197fd 100644 --- a/prebuilt_forms/js/species_details_2.js +++ b/prebuilt_forms/js/species_details_2.js @@ -223,7 +223,7 @@ jQuery(document).ready(function($) { if (existing) { existing.recs += h.recs; existing.minYear = h.minYear < existing.minYear ? h.minYear : existing.minYear; - existing.maxYear = h.maxYear < existing.maxYear ? h.maxYear : existing.maxYear; + existing.maxYear = h.maxYear > existing.maxYear ? h.maxYear : existing.maxYear; } else { a.push({gr: h.gr, recs: h.recs, minYear: h.minYear, maxYear: h.maxYear}); } diff --git a/prebuilt_forms/species_details_2.php b/prebuilt_forms/species_details_2.php index 50b8d56a..c54f7055 100644 --- a/prebuilt_forms/species_details_2.php +++ b/prebuilt_forms/species_details_2.php @@ -43,70 +43,70 @@ class iform_species_details_2 extends BaseDynamicDetails { /** * Stores a value to indicate of no taxon identified. * - * @var bool + * @var notaxon */ private static $notaxon; /** * Stores the preferred name of the taxon with markup and authority. * - * @var string + * @var preferred */ private static $preferred; /** * Stores the preferred name of the taxon. * - * @var string + * @var preferredPlain */ private static $preferredPlain; /** * Stores the default common name of the taxon. * - * @var string + * @var defaultCommonName */ private static $defaultCommonName; /** * Stores the synonyms of the taxon. * - * @var array + * @var synonyms */ private static $synonyms = []; /** * Stores the common names of the taxon. * - * @var array + * @var commonNames */ private static $commonNames = []; /** * Stores the taxonomy of the taxon. * - * @var array + * @var taxonomy */ private static $taxonomy = []; /** * Stores the taxa_taxon_list_id of the taxon. * - * @var int + * @var taxaTaxonListId */ private static $taxaTaxonListId; /** * Stores the taxon_meaning_id of the taxon. * - * @var int + * @var taxonMeaningId */ private static $taxonMeaningId; /** * Stores the exter_key of the taxon. * - * @var string + * @var externalKey */ private static $externalKey; @@ -527,7 +527,13 @@ protected static function get_form_html($args, $auth, $attributes) { } else { // Get information on species names self::getNames($auth); - + if (is_null(self::$externalKey)){ + // Some lists have no TVKs + self::$notaxon = TRUE; + \Drupal::messenger()->addMessage('There is no external key corresponding to this taxon.'); + return parent::get_form_html($args, $auth, $attributes); + } + // In Drupal 9, markup cannot be used in page title, so remove em tags. $repArray = ['', '']; $preferredClean = str_replace($repArray, '', self::$preferred); @@ -1596,6 +1602,7 @@ protected static function mapWithoutGeoserver($auth, $args, $tabalias, $options) $params = [ 'taxa_taxon_list_id' => empty($_GET['taxa_taxon_list_id']) ? '' : $_GET['taxa_taxon_list_id'], 'taxon_meaning_id' => empty($_GET['taxon_meaning_id']) ? '' : $_GET['taxon_meaning_id'], + 'sharing' => 'reporting', 'reportGroup' => 'dynamic', 'autoParamsForm' => FALSE, 'sharing' => $sharing, @@ -1766,6 +1773,7 @@ protected static function get_control_speciesphotos($auth, $args, $tabalias, $op /** * Returns a control for picking a species. * + * @global type $indicia_templates * @param array $auth * Read authorisation tokens. * @param array $args From 54a062cd92cb3d41171cc08eec610444c5556a97 Mon Sep 17 00:00:00 2001 From: Andrew van Breda Date: Sun, 27 Oct 2024 19:04:24 +0000 Subject: [PATCH 09/19] Import reverser code --- import_helper_2.php | 214 +++++++++++++++++++++++++++++++++- prebuilt_forms/importer_2.php | 9 ++ 2 files changed, 222 insertions(+), 1 deletion(-) diff --git a/import_helper_2.php b/import_helper_2.php index ce4505a1..64b16944 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -130,7 +130,13 @@ public static function importer($options) { self::$indiciaData['step'] = $nextImportStep; switch ($nextImportStep) { case 'fileSelectForm': - return self::fileSelectForm($options); + $r = self::fileSelectForm($options); + // The reverser currently assumes occurrence entity. + if ($options['entity'] === 'occurrence' && + (!empty($options['allow_import_reverse']) && $options['allow_import_reverse'] == TRUE)) { + $r .= self::importToReverseDropDown($options); + } + return $r; case 'globalValuesForm': return self::globalValuesForm($options); @@ -150,6 +156,12 @@ public static function importer($options) { case 'doImportPage': return self::doImportPage($options); + case 'reversalModeWarning': + return self::reversalModeWarning($options); + + case 'reversalResult': + return self::reversalResult($options); + default: throw new exception('Invalid next-import-step parameter'); } @@ -496,6 +508,206 @@ private static function fileSelectForm(array $options) { return $r; } + /** + * Fetch the HTML for the import reversal drop-down. + * + * @param array $options + * Options array for the control. + * + * @return string + * HTML. + */ + private static function importToReverseDropDown(array $options) { + iform_load_helpers(['report_helper']); + if (!function_exists('hostsite_get_user_field') || !hostsite_get_user_field('indicia_user_id')) { + return ''; + } + // We only show user their own imports. + $indiciaUserID = hostsite_get_user_field('indicia_user_id'); + $extraParams = [ + 'currentUser' => $indiciaUserID, + ]; + // This will be empty if importer is running on Warehouse. + // In that case all imports for the logged-in user will be shown. + if (!empty($options['website_id'])) { + $extraParams = array_merge( + $extraParams, ['website_id' => $options['website_id']] + ); + } + // Get a list of imports that are reversible. + $lookupData = report_helper::get_report_data([ + 'dataSource' => 'library/imports/reversible_imports_list', + 'extraParams' => $options['readAuth'] + $extraParams, + ]); + // Construct label for the drop-down from the date/time and import_guid. + $reversableImports = []; + foreach ($lookupData as $importRow) { + $reversableImports[$importRow['import_guid']] = 'Date: ' . $importRow['import_date_time'] . ' (Import ID: ' . $importRow['import_guid'] . ')'; + } + $r = '
'; + $r .= '
Or select a previous import to reverse

'; + if (empty($reversableImports)) { + $r .= '
There are no previous imports available for you to reverse
'; + } + // If there are some reversible imports, show user a drop-down of imports + // and a run the reverse button. + else { + $r .= '
'; + $r .= data_entry_helper::select([ + 'id' => 'reverse-guid', + 'fieldname' => 'reverse-guid', + 'label' => lang::get('Import to reverse'), + 'lookupValues' => $reversableImports, + 'blankText' => lang::get(''), + ]); + $r .= '

Please note that imports are not always reversible.
+ This may include old imports, imports that have been updated by another import, + or imports where a reversal has already been attempted.

'; + $r .= ''; + $r .= ''; + $r .= ''; + $r .= ''; + } + // Don't allow reverse to be run until an import has been selected. + data_entry_helper::$javascript = " + $('#reverse-guid').on('change', function() { + if ($(this).val()) { + $('#run-reverse').prop('disabled', false); + $('.reverse-instructions-1').show(); + } + else { + $('#run-reverse').prop('disabled', true); + $('.reverse-instructions-1').hide(); + } + }); + + $('#run-reverse').on('click', function() { + if (confirm('Are you sure you want to reverse the selected import?') == true) { + return true; + } else { + return false; + } + });"; + return $r; + } + + /** + * Allow user to select import reverse options. + * + * If applicable, allow the user to select whether to reverse all data, + * or just data that has not been changed since importing. + * + * @param array $options + * Options array for the control. + * + * @return string + * HTML. + */ + private static function reversalModeWarning(array $options) { + iform_load_helpers(['report_helper']); + $extraParams = []; + if (!empty($_POST['reverse-guid'])) { + $extraParams['import_guid'] = $_POST['reverse-guid']; + } + else { + $extraParams['import_guid'] = ''; + } + // Has any of the data been changed since the import was done. + $smpsChangedSinceImport = report_helper::get_report_data([ + 'dataSource' => 'library/imports/changed_smps_since_import', + 'extraParams' => $options['readAuth'] + $extraParams, + ]); + $occsChangedSinceImport = report_helper::get_report_data([ + 'dataSource' => 'library/imports/changed_occs_since_import', + 'extraParams' => $options['readAuth'] + $extraParams, + ]); + // If no changes have been made to the imported data, + // then we can skip straight to the result page. + if (empty($smpsChangedSinceImport) && empty($occsChangedSinceImport)) { + return self::reversalResult($options); + } + // If import data has been changed since import, then give the user + // the following options. + // Reverse all data, reverse only unchanged data, or abort reverse. + $r .= '
'; + $r .= '

Changes have been made to the data since it was imported.

'; + $r .= '

How do you wish to proceed?

'; + $r .= '
'; + $r .= '

'; + $r .= '

'; + $r .= '


'; + $r .= ''; + $r .= ''; + $r .= ''; + $r .= '
'; + $r .= '
'; + // Change the button label depending if user has chosen to abort. + // Also change the next import step to be the first step again. + data_entry_helper::$javascript .= " + $('[name=\"reverse-mode\"').on('change', function() { + if ($(this).val() == 'abort_reverse') { + $('[name=\"next-import-step\"').val('fileSelectForm'); + $('#run-reverse').prop('value','Abort'); + } + else { + $('[name=\"next-import-step\"').val('reversalResult'); + $('#run-reverse').prop('value','Continue'); + }; + }); + "; + return $r; + } + + /** + * Send reversal information to the Warehouse, and display result. + * + * @param array $options + * Options array for the control. + * + * @return string + * HTML. + */ + private static function reversalResult(array $options) { + $indiciaUserID = hostsite_get_user_field('indicia_user_id'); + $extraParams = [ + 'currentUser' => $indiciaUserID, + ]; + $data['warehouse_user_id'] = $indiciaUserID; + $serviceUrl = self ::$base_url . 'index.php/services/import_2/importreverse'; + if (!empty($_POST['reverse-guid'])) { + $data['guid_to_reverse'] = $_POST['reverse-guid']; + } + if (!empty($_POST['reverse-mode'])) { + $data['reverse_mode'] = $_POST['reverse-mode']; + } + $response = self::http_post($serviceUrl, $data, FALSE); + $output = json_decode($response['output'], TRUE); + $r .= '
'; + if (!$response['result']) { + $r .= '

An error has occurred during the import reversal.

'; + $r .= '

The response from the database is:

'; + $r .= '

' . print_r($response, TRUE) . '

'; + } + else { + // Result includes links to files which contain + // changed and unchanged sample/occurrence rows. + $r .= '

The reversal of import ' . $_POST['reverse-guid'] . ' is complete

'; + $r .= '

' . $response['output'] . '

'; + } + $r .= '
'; + $r .= '
'; + $r .= '
'; + return $r; + } + /** * Fetch the HTML form for the settings form that captures global values. * diff --git a/prebuilt_forms/importer_2.php b/prebuilt_forms/importer_2.php index 2d9ccdb1..8b202f8b 100644 --- a/prebuilt_forms/importer_2.php +++ b/prebuilt_forms/importer_2.php @@ -128,6 +128,7 @@ public static function get_parameters(array $readAuth) { sample:fk_location:id sample:input_form sample:privacy_precision +sample:import_guid sample:record_status sample:sensitivity_precision TXT; @@ -235,6 +236,14 @@ public static function get_parameters(array $readAuth) { 'default' => $default_terms['import2requiredFieldsIntro'], 'required' => FALSE, ], + [ + 'name' => 'allow_import_reverse', + 'caption' => 'Allow import reversals?', + 'group' => 'Import reverser', + 'type' => 'boolean', + 'default' => TRUE, + 'required' => FALSE, + ], ]; $requestParams = $readAuth + ['entity' => 'occurrence']; $request = helper_base::$base_url . 'index.php/services/import_2/get_plugins?' . http_build_query($requestParams); From a8c8959e4733d159e84c44cbd803c008c7604a30 Mon Sep 17 00:00:00 2001 From: Andrew van Breda Date: Tue, 29 Oct 2024 21:16:22 +0000 Subject: [PATCH 10/19] More elegant html, and JS moved to uploader.js --- import_helper_2.php | 207 ++++++++++++++++++++++++++++---------------- 1 file changed, 131 insertions(+), 76 deletions(-) diff --git a/import_helper_2.php b/import_helper_2.php index 64b16944..16c8b518 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -539,20 +539,45 @@ private static function importToReverseDropDown(array $options) { 'dataSource' => 'library/imports/reversible_imports_list', 'extraParams' => $options['readAuth'] + $extraParams, ]); + self::addLanguageStringsToJs('import_helper_2', [ + 'are_you_sure_reverse' => 'Are you sure you want to reverse the selected import?', + ]); + $lang = [ + 'select_a_previous_import_to_reverse' => lang::get('Or select a previous import to reverse'), + 'no_previous_imports_available_for_you_to_reverse' => lang::get('There are no previous imports available for you to reverse'), + 'imports_are_not_always_reversible' => lang::get('Please note that imports are not always reversible.'), + 'non_reversible_import_reasons' => lang::get('This may include old imports, imports that have been updated by another import, + or imports where a reversal has already been attempted.'), + 'reverse_import' => lang::get('Reverse import'), + 'new_records_will_be_reversed' => lang::get('Only new records created by the original import will be reversed.'), + 'updated_records_will_not_be_reversed' => lang::get('Any records that were selected for an update or deletion during that import will not be reversed.'), + ]; // Construct label for the drop-down from the date/time and import_guid. $reversableImports = []; foreach ($lookupData as $importRow) { $reversableImports[$importRow['import_guid']] = 'Date: ' . $importRow['import_date_time'] . ' (Import ID: ' . $importRow['import_guid'] . ')'; } - $r = '
'; - $r .= '
Or select a previous import to reverse

'; + $r .= << +

+ $lang[select_a_previous_import_to_reverse] +

+ HTML; if (empty($reversableImports)) { - $r .= '
There are no previous imports available for you to reverse
'; + $r .= << + + $lang[no_previous_imports_available_for_you_to_reverse] + + + HTML; } // If there are some reversible imports, show user a drop-down of imports // and a run the reverse button. else { - $r .= '
'; + $r .= << + HTML; $r .= data_entry_helper::select([ 'id' => 'reverse-guid', 'fieldname' => 'reverse-guid', @@ -560,36 +585,28 @@ private static function importToReverseDropDown(array $options) { 'lookupValues' => $reversableImports, 'blankText' => lang::get(''), ]); - $r .= '

Please note that imports are not always reversible.
- This may include old imports, imports that have been updated by another import, - or imports where a reversal has already been attempted.

'; - $r .= ''; - $r .= ''; - $r .= ''; - $r .= ''; - } - // Don't allow reverse to be run until an import has been selected. - data_entry_helper::$javascript = " - $('#reverse-guid').on('change', function() { - if ($(this).val()) { - $('#run-reverse').prop('disabled', false); - $('.reverse-instructions-1').show(); - } - else { - $('#run-reverse').prop('disabled', true); - $('.reverse-instructions-1').hide(); - } - }); - - $('#run-reverse').on('click', function() { - if (confirm('Are you sure you want to reverse the selected import?') == true) { - return true; - } else { - return false; - } - });"; + $r .= << + + + $lang[imports_are_not_always_reversible] +
+ $lang[non_reversible_import_reasons] +
+
+

+ + + + + HTML; + } return $r; } @@ -607,6 +624,11 @@ private static function importToReverseDropDown(array $options) { */ private static function reversalModeWarning(array $options) { iform_load_helpers(['report_helper']); + self::addLanguageStringsToJs('import_helper_2', [ + 'abort' => 'Abort', + 'continue' => 'Continue', + 'are_you_sure_reverse' => 'Are you sure you wish to continue?', + ]); $extraParams = []; if (!empty($_POST['reverse-guid'])) { $extraParams['import_guid'] = $_POST['reverse-guid']; @@ -628,41 +650,58 @@ private static function reversalModeWarning(array $options) { if (empty($smpsChangedSinceImport) && empty($occsChangedSinceImport)) { return self::reversalResult($options); } + $lang = [ + 'changes_have_been_made' => lang::get('Changes have been made to the data since it was imported.'), + 'how_do_you_wish_to_proceed' => lang::get('How do you wish to proceed?'), + 'reverse_all_rows' => lang::get('Reverse all rows'), + 'reverse_unchanged_rows' => lang::get('Reverse unchanged rows'), + 'abort_the_reverse' => lang::get('Abort the reverse'), + 'continue' => lang::get('Continue'), + ]; // If import data has been changed since import, then give the user // the following options. // Reverse all data, reverse only unchanged data, or abort reverse. - $r .= '
'; - $r .= '

Changes have been made to the data since it was imported.

'; - $r .= '

How do you wish to proceed?

'; - $r .= '
'; - $r .= '

'; - $r .= '

'; - $r .= '


'; - $r .= ''; - $r .= ''; - $r .= ''; - $r .= '
'; - $r .= '
'; - // Change the button label depending if user has chosen to abort. - // Also change the next import step to be the first step again. - data_entry_helper::$javascript .= " - $('[name=\"reverse-mode\"').on('change', function() { - if ($(this).val() == 'abort_reverse') { - $('[name=\"next-import-step\"').val('fileSelectForm'); - $('#run-reverse').prop('value','Abort'); - } - else { - $('[name=\"next-import-step\"').val('reversalResult'); - $('#run-reverse').prop('value','Continue'); - }; - }); - "; + $reverseGuid = $_POST['reverse-guid']; + $r = << +

+ $lang[changes_have_been_made] +

+

+ $lang[how_do_you_wish_to_proceed] +

+
+

+ +

+

+ +

+

+ +

+
+ + + +
+ + HTML; return $r; } @@ -690,21 +729,37 @@ private static function reversalResult(array $options) { } $response = self::http_post($serviceUrl, $data, FALSE); $output = json_decode($response['output'], TRUE); - $r .= '
'; + $reverseGuid = $_POST['reverse-guid']; + $r .= << + HTML; if (!$response['result']) { - $r .= '

An error has occurred during the import reversal.

'; - $r .= '

The response from the database is:

'; - $r .= '

' . print_r($response, TRUE) . '

'; + $printedResponse = print_r($response, TRUE); + $r .= <<An error has occurred during the import reversal. +

+ The response from the database is: +

+

$printedResponse

+ HTML; } else { + $printedResponseOutput = print_r($response['output'], TRUE); // Result includes links to files which contain // changed and unchanged sample/occurrence rows. - $r .= '

The reversal of import ' . $_POST['reverse-guid'] . ' is complete

'; - $r .= '

' . $response['output'] . '

'; - } - $r .= '
'; - $r .= '
'; - $r .= '
'; + $r .= <<The reversal of import $reverseGuid is complete +

$printedResponseOutput

+ HTML; + } + $r .= << +
+ + +
+ + HTML; return $r; } From d8dd9d4d31d14c04c1413101e2cfa5516d957017 Mon Sep 17 00:00:00 2001 From: Andrew van Breda Date: Wed, 30 Oct 2024 11:03:58 +0000 Subject: [PATCH 11/19] Improved variable name format --- import_helper_2.php | 2 +- prebuilt_forms/importer_2.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/import_helper_2.php b/import_helper_2.php index 16c8b518..43429275 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -133,7 +133,7 @@ public static function importer($options) { $r = self::fileSelectForm($options); // The reverser currently assumes occurrence entity. if ($options['entity'] === 'occurrence' && - (!empty($options['allow_import_reverse']) && $options['allow_import_reverse'] == TRUE)) { + (!empty($options['allowImportReverse']) && $options['allowImportReverse'] == TRUE)) { $r .= self::importToReverseDropDown($options); } return $r; diff --git a/prebuilt_forms/importer_2.php b/prebuilt_forms/importer_2.php index 8b202f8b..3a86b781 100644 --- a/prebuilt_forms/importer_2.php +++ b/prebuilt_forms/importer_2.php @@ -237,7 +237,7 @@ public static function get_parameters(array $readAuth) { 'required' => FALSE, ], [ - 'name' => 'allow_import_reverse', + 'name' => 'allowImportReverse', 'caption' => 'Allow import reversals?', 'group' => 'Import reverser', 'type' => 'boolean', From 427523b6695c38c93ce400f1684d2da43f2e9a07 Mon Sep 17 00:00:00 2001 From: Andrew van Breda Date: Wed, 30 Oct 2024 12:06:00 +0000 Subject: [PATCH 12/19] Improved function name --- import_helper_2.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/import_helper_2.php b/import_helper_2.php index 43429275..2aa9c179 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -156,8 +156,8 @@ public static function importer($options) { case 'doImportPage': return self::doImportPage($options); - case 'reversalModeWarning': - return self::reversalModeWarning($options); + case 'reversalModeWarningOrSkipToResult': + return self::reversalModeWarningOrSkipToResult($options); case 'reversalResult': return self::reversalResult($options); @@ -603,7 +603,7 @@ private static function importToReverseDropDown(array $options) { $lang[updated_records_will_not_be_reversed]

- + HTML; } @@ -615,6 +615,7 @@ private static function importToReverseDropDown(array $options) { * * If applicable, allow the user to select whether to reverse all data, * or just data that has not been changed since importing. + * Skips directly to result page if there is nothing to ask the user. * * @param array $options * Options array for the control. @@ -622,7 +623,7 @@ private static function importToReverseDropDown(array $options) { * @return string * HTML. */ - private static function reversalModeWarning(array $options) { + private static function reversalModeWarningOrSkipToResult(array $options) { iform_load_helpers(['report_helper']); self::addLanguageStringsToJs('import_helper_2', [ 'abort' => 'Abort', From b2cdb909198a06486532fa90848eda89b052e254 Mon Sep 17 00:00:00 2001 From: John van Breda Date: Thu, 31 Oct 2024 11:46:32 +0000 Subject: [PATCH 13/19] Tidies display of field names on import summary page See https://github.com/BiologicalRecordsCentre/iRecord/issues/1745 --- import_helper_2.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/import_helper_2.php b/import_helper_2.php index ce4505a1..b26f6764 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -1262,7 +1262,7 @@ private static function getReadableWarehouseField($warehouseField) { * labels. * */ - private static function getWarehouseFieldLabel($field, array $availableFields) { + private static function getWarehouseFieldLabel($field, array $availableFields, $includeTablePrefix = FALSE) { $label = lang::get($field); if ($label === $field) { // No translation provided. @@ -1273,7 +1273,14 @@ private static function getWarehouseFieldLabel($field, array $availableFields) { $label = self::getReadableWarehouseField($field); } } - return $label; + if ($includeTablePrefix) { + $fieldParts = explode(':', $field); + $tablePrefix = lang::get("optionGroup-$fieldParts[0]-shortLabel"); + if (substr($tablePrefix, 0, 12) !== 'optionGroup-') { + $label = "$tablePrefix $label"; + } + } + return str_replace(' id', ' ID', ucfirst(strtolower($label))); } /** @@ -1320,7 +1327,7 @@ private static function summaryPage(array $options) { $existingMatchFields = []; foreach ($config['columns'] as $columnLabel => $info) { $arrow = self::getSummaryColumnArrow($info); - $warehouseFieldLabel = self::getWarehouseFieldLabel($info['warehouseField'], $availableFields); + $warehouseFieldLabel = self::getWarehouseFieldLabel($info['warehouseField'], $availableFields, TRUE); $mappingRows[] = "$columnLabel$arrow$warehouseFieldLabel"; if (preg_match('/:(id|external_key)$/', $info['warehouseField'])) { $existingMatchFields[] = $warehouseFieldLabel; @@ -1448,8 +1455,9 @@ private static function globalValuesAsTableRows(array $config, array $readAuth, // Foreign key filters were used during matching, not actually for import. // Also exclude settings such as allowUpdates/deletes. if (strpos($field, 'fkFilter') === FALSE && strpos($field, 'config:') !== 0) { - $field = $availableFields[$field] ?? $field; - $globalRows[] = "$displayLabel$arrow$field"; + $displayField = self::getWarehouseFieldLabel($field, $availableFields, TRUE); + + $globalRows[] = "$displayLabel$arrow$displayField"; } } return $globalRows; From e2e02150e2b7ef1b84c15383fdeba5308bf41305 Mon Sep 17 00:00:00 2001 From: John van Breda Date: Thu, 31 Oct 2024 11:49:39 +0000 Subject: [PATCH 14/19] Documented new feature. --- import_helper_2.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/import_helper_2.php b/import_helper_2.php index 7d87b0f3..1c1e8ca1 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -96,6 +96,8 @@ class import_helper_2 extends helper_base { * ID or external key field mapping. Only affects the user's own data. * * allowDeletes = set to true to enable mapping to a deleted flag for the * user's own data. Requires the allowUpdates option to be set. + * * allowImportReverse - adds a control to the file upload page which allows + * a previous export to be selected and reversed. */ public static function importer($options) { if (empty($options['entity'])) { @@ -588,7 +590,7 @@ private static function importToReverseDropDown(array $options) { $r .= << - + $lang[imports_are_not_always_reversible]
$lang[non_reversible_import_reasons] From 68b9234a907752aaf41620e9b5f9e7ca9c79e33f Mon Sep 17 00:00:00 2001 From: John van Breda Date: Thu, 31 Oct 2024 12:33:45 +0000 Subject: [PATCH 15/19] Change list lookup icon https://github.com/BiologicalRecordsCentre/iRecord/issues/1747 --- import_helper_2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/import_helper_2.php b/import_helper_2.php index 1c1e8ca1..1db9803e 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -1498,7 +1498,7 @@ private static function getSummaryColumnArrow(array $info) { $arrow = ""; } elseif (!empty($info['isFkField'])) { - $arrow = ""; + $arrow = ""; } else { $arrow = ""; From 6c63785ecec98cfd7a256538ae98a47a38209a65 Mon Sep 17 00:00:00 2001 From: Andrew van Breda Date: Thu, 31 Oct 2024 14:48:53 +0000 Subject: [PATCH 16/19] Fix warnings appearing in Drupal log, not functionality differences --- import_helper_2.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/import_helper_2.php b/import_helper_2.php index 1db9803e..77c7d6f4 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -539,7 +539,8 @@ private static function importToReverseDropDown(array $options) { // Get a list of imports that are reversible. $lookupData = report_helper::get_report_data([ 'dataSource' => 'library/imports/reversible_imports_list', - 'extraParams' => $options['readAuth'] + $extraParams, + 'readAuth' => $options['readAuth'], + 'extraParams' => $extraParams, ]); self::addLanguageStringsToJs('import_helper_2', [ 'are_you_sure_reverse' => 'Are you sure you want to reverse the selected import?', @@ -559,7 +560,7 @@ private static function importToReverseDropDown(array $options) { foreach ($lookupData as $importRow) { $reversableImports[$importRow['import_guid']] = 'Date: ' . $importRow['import_date_time'] . ' (Import ID: ' . $importRow['import_guid'] . ')'; } - $r .= <<

$lang[select_a_previous_import_to_reverse] @@ -642,11 +643,13 @@ private static function reversalModeWarningOrSkipToResult(array $options) { // Has any of the data been changed since the import was done. $smpsChangedSinceImport = report_helper::get_report_data([ 'dataSource' => 'library/imports/changed_smps_since_import', - 'extraParams' => $options['readAuth'] + $extraParams, + 'readAuth' => $options['readAuth'], + 'extraParams' => $extraParams, ]); $occsChangedSinceImport = report_helper::get_report_data([ 'dataSource' => 'library/imports/changed_occs_since_import', - 'extraParams' => $options['readAuth'] + $extraParams, + 'readAuth' => $options['readAuth'], + 'extraParams' => $extraParams, ]); // If no changes have been made to the imported data, // then we can skip straight to the result page. @@ -733,7 +736,7 @@ private static function reversalResult(array $options) { $response = self::http_post($serviceUrl, $data, FALSE); $output = json_decode($response['output'], TRUE); $reverseGuid = $_POST['reverse-guid']; - $r .= << HTML; if (!$response['result']) { From 52fe24fddf2c98a301a6e6e9fed3f5392b6fc373 Mon Sep 17 00:00:00 2001 From: John van Breda Date: Fri, 8 Nov 2024 14:27:43 +0000 Subject: [PATCH 17/19] Minor bug fixes for new import reversal code Also a minor text string improvement. --- import_helper_2.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/import_helper_2.php b/import_helper_2.php index 1db9803e..a757afe4 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -538,8 +538,9 @@ private static function importToReverseDropDown(array $options) { } // Get a list of imports that are reversible. $lookupData = report_helper::get_report_data([ + 'readAuth' => $options['readAuth'], 'dataSource' => 'library/imports/reversible_imports_list', - 'extraParams' => $options['readAuth'] + $extraParams, + 'extraParams' => $extraParams, ]); self::addLanguageStringsToJs('import_helper_2', [ 'are_you_sure_reverse' => 'Are you sure you want to reverse the selected import?', @@ -559,7 +560,7 @@ private static function importToReverseDropDown(array $options) { foreach ($lookupData as $importRow) { $reversableImports[$importRow['import_guid']] = 'Date: ' . $importRow['import_date_time'] . ' (Import ID: ' . $importRow['import_guid'] . ')'; } - $r .= <<

$lang[select_a_previous_import_to_reverse] @@ -1401,7 +1402,7 @@ private static function lookupMatchingForm(array $options) { 'matchesToLocation' => 'Matches to location', 'matchesToTaxon' => 'Matches to species or taxon name', 'matchesToTerm' => 'Matches to term', - 'matchingPanelFor' => 'List of values to match for {1}', + 'matchingPanelFor' => 'List of values to match for {1} column', 'pleaseMatchAllValues' => 'Matches saved, but there are more matches required for {1}.', 'pleaseMatchValues' => 'Please match the values in the list before saving them.', 'pleaseSelect' => '- Please select -', From 843da9b88cd3c9968f1ba866b6b4cb7fad2314a1 Mon Sep 17 00:00:00 2001 From: John van Breda Date: Fri, 8 Nov 2024 15:19:00 +0000 Subject: [PATCH 18/19] Add support for location name in multiple place form sub-samples --- data_entry_helper.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/data_entry_helper.php b/data_entry_helper.php index 032e928c..b4535a08 100644 --- a/data_entry_helper.php +++ b/data_entry_helper.php @@ -5066,6 +5066,7 @@ public static function preload_species_checklist_occurrences($sampleId, $readAut data_entry_helper::$entity_to_load['sc:' . $idx . ':' . $subSample['id'] . ':sample:geom'] = $subSample['wkt']; data_entry_helper::$entity_to_load['sc:' . $idx . ':' . $subSample['id'] . ':sample:wkt'] = $subSample['wkt']; data_entry_helper::$entity_to_load['sc:' . $idx . ':' . $subSample['id'] . ':sample:location_id'] = $subSample['location_id']; + data_entry_helper::$entity_to_load['sc:' . $idx . ':' . $subSample['id'] . ':sample:location_name'] = $subSample['location_name']; data_entry_helper::$entity_to_load['sc:' . $idx . ':' . $subSample['id'] . ':sample:entered_sref'] = $subSample['entered_sref']; data_entry_helper::$entity_to_load['sc:' . $idx . ':' . $subSample['id'] . ':sample:entered_sref_system'] = $subSample['entered_sref_system']; if ($spatialRefPrecisionAttrId) { @@ -6042,6 +6043,8 @@ public static function get_species_checklist_clonable_row(array $options, array * the default. Options same as sampleOnClusterButtonContents. * * **samplePhotos** - set to true to add a photos upload control for each * sub-sample. + * * **location_name** - set to true to add a location name input control for + * each sub-sample. */ public static function multiple_places_species_checklist($options) { if (empty($options['spatialSystem'])) { @@ -6074,7 +6077,14 @@ public static function multiple_places_species_checklist($options) { $attr['fieldname'] = "sc:n::$attr[fieldname]"; $attr['id'] = "sc:n::$attr[id]"; } - $sampleCtrls = get_attribute_html($sampleAttrs, [], ['extraParams' => $options['readAuth']], NULL, $attrOptions); + $sampleCtrls = ''; + if (!empty($options['locationName'])) { + $sampleCtrls .= self::text_input([ + 'label' => lang::get('Location name at this position'), + 'fieldname' => "sc:n::sample:location_name", + ]); + } + $sampleCtrls .= get_attribute_html($sampleAttrs, [], ['extraParams' => $options['readAuth']], NULL, $attrOptions); // Add a template for the form section for a new subsample. $r .= "

$sampleCtrls
"; } @@ -6269,7 +6279,16 @@ private static function getMultiplePlacesSpeciesChecklistButtons($options) { $attr['id'] = "sc:$a[1]:$a[2]:$attr[id]"; } $attrOptions = self::getAttrSpecificOptions($options); - $sampleCtrls = get_attribute_html($sampleAttrs, [], ['extraParams' => $options['readAuth']], NULL, $attrOptions); + $sampleCtrls = ''; + if (!empty($options['locationName'])) { + $locationName = data_entry_helper::$entity_to_load["$a[0]:$a[1]:$sampleId:sample:location_name"] ?? ''; + $sampleCtrls .= self::text_input([ + 'label' => lang::get('Location name at this position'), + 'fieldname' => "$a[0]:$a[1]:$sampleId:sample:location_name", + 'default' => $locationName, + ]); + } + $sampleCtrls .= get_attribute_html($sampleAttrs, [], ['extraParams' => $options['readAuth']], NULL, $attrOptions); $blocks .= << $sampleCtrls From 65d0d8f9846bac53ba06260cef0c8171ea85e9b5 Mon Sep 17 00:00:00 2001 From: John van Breda Date: Fri, 29 Nov 2024 13:57:48 +0000 Subject: [PATCH 19/19] Import reverser tidy --- import_helper_2.php | 31 +++++++++++++++++-------------- prebuilt_forms/importer_2.php | 3 ++- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/import_helper_2.php b/import_helper_2.php index 155ec43f..b2c039df 100644 --- a/import_helper_2.php +++ b/import_helper_2.php @@ -558,7 +558,8 @@ private static function importToReverseDropDown(array $options) { // Construct label for the drop-down from the date/time and import_guid. $reversableImports = []; foreach ($lookupData as $importRow) { - $reversableImports[$importRow['import_guid']] = 'Date: ' . $importRow['import_date_time'] . ' (Import ID: ' . $importRow['import_guid'] . ')'; + $affected = $importRow['inserted'] + $importRow['updated']; + $reversableImports[$importRow['import_guid']] = lang::get('{1}, {2} rows (Import ID: {3}', $importRow['import_date_time'], $affected, $importRow['import_guid']); } $r = << @@ -722,11 +723,8 @@ private static function reversalModeWarningOrSkipToResult(array $options) { */ private static function reversalResult(array $options) { $indiciaUserID = hostsite_get_user_field('indicia_user_id'); - $extraParams = [ - 'currentUser' => $indiciaUserID, - ]; $data['warehouse_user_id'] = $indiciaUserID; - $serviceUrl = self ::$base_url . 'index.php/services/import_2/importreverse'; + $serviceUrl = self ::$base_url . 'index.php/services/import_2/import_reverse'; if (!empty($_POST['reverse-guid'])) { $data['guid_to_reverse'] = $_POST['reverse-guid']; } @@ -739,23 +737,28 @@ private static function reversalResult(array $options) { $r = << HTML; - if (!$response['result']) { - $printedResponse = print_r($response, TRUE); + global $indicia_templates; + if (!isset($response['result']) || $output['status'] !== 'OK') { + $printedResponse = empty($output['msg']) ? print_r($response, TRUE) : $output['msg']; + $r .= str_replace('{message}', 'An error has occurred during the import reversal.', $indicia_templates['warningBox']); $r .= <<An error has occurred during the import reversal.

The response from the database is:

-

$printedResponse

+
$printedResponse
HTML; } else { - $printedResponseOutput = print_r($response['output'], TRUE); - // Result includes links to files which contain - // changed and unchanged sample/occurrence rows. + // Result includes links to files which contain changed and unchanged + // sample/occurrence rows. + $samplesDetails = '

' . implode('

', $output['samplesDetails']); + $occurrencesDetails = '

' . implode('

', $output['occurrencesDetails']); $r .= <<The reversal of import $reverseGuid is complete -

$printedResponseOutput

+

The reversal of import $reverseGuid is complete

+

$output[samplesOutcome]

+ $samplesDetails +

$output[occurrencesOutcome]

+ $occurrencesDetails HTML; } $r .= << 'allowImportReverse', 'caption' => 'Allow import reversals?', + 'description' => 'Provides an option for a user to select one of their previous imports to reverse, i.e. remove the added records.', 'group' => 'Import reverser', 'type' => 'boolean', - 'default' => TRUE, + 'default' => FALSE, 'required' => FALSE, ], ];