From 744e09a3063dfdcf65024c5c8f70d13e6b629f1a Mon Sep 17 00:00:00 2001 From: Claudiu Cristea <clau.cristea@gmail.com> Date: Mon, 3 Mar 2025 13:11:49 +0200 Subject: [PATCH 01/10] ISAICP-9538: Calendar fix. --- config/sync/views.view.calendar.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/sync/views.view.calendar.yml b/config/sync/views.view.calendar.yml index 88649e1fb5..e9d808ccd9 100644 --- a/config/sync/views.view.calendar.yml +++ b/config/sync/views.view.calendar.yml @@ -343,7 +343,7 @@ display: value: min: '' max: '' - value: '-1 month' + value: '-40 day' type: offset group: 1 exposed: false @@ -388,7 +388,7 @@ display: value: min: '' max: '' - value: '+1 month' + value: '+40 day' type: offset group: 1 exposed: false -- GitLab From 9653c1318404314dfab21993967a5dd6f4654301 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Tue, 25 Feb 2025 13:39:46 +0200 Subject: [PATCH 02/10] ISAICP-9526: The suffix of a URI should be after a ?, not a /. There are IRIs that have the / as a separator. --- .../src/EventSubscriber/LegacyRdfUrlSubscriber.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php index 863a4056fe..406f5fff61 100644 --- a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php +++ b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php @@ -61,11 +61,16 @@ public function on404(ExceptionEvent $event): void { */ public function getTargetEntity(string $path): array { // Extracts the encoded URI and everything that comes after. - $pattern = '@^(?<prefix>/rdf_entity/|/taxonomy/term/)(?<uri>http_e_f_f[^/]*)(?<suffix>/(?:.*))?$@'; + $pattern = '@^(?<prefix>/rdf_entity/|/taxonomy/term/)(?<uri>http_e_f_f[^?]*)(?<suffix>/(?:.*))?$@'; if (!preg_match($pattern, $path, $found)) { return [NULL, NULL]; } + // Cases like eGovEra are redirected with a / in their URI. We need to fix + // this by replacing the prefix. + if ($found['uri'] && str_contains($found['uri'], '_fegovera/')) { + $found['uri'] = str_replace('/', '_f', $found['uri']); + } if (!$entity = $this->getEntity($found['uri'])) { return [NULL, NULL]; } -- GitLab From bde32f0304267e7e96ccc37ce6876468c06a6c9d Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Tue, 25 Feb 2025 13:45:33 +0200 Subject: [PATCH 03/10] ISAICP-9526: Provide a test for the redirection. --- tests/features/update/ISAICP-9396.feature | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/features/update/ISAICP-9396.feature b/tests/features/update/ISAICP-9396.feature index 254fbd5769..9d1ee7b5f0 100644 --- a/tests/features/update/ISAICP-9396.feature +++ b/tests/features/update/ISAICP-9396.feature @@ -19,6 +19,13 @@ Feature: Library import check. # Text from the EIRA properties. And I should see the text "eira:InformationBusinessObject" + # Ensure the upstream redirection works as well. + Given I go to "/taxonomy/term/http_e_f_fdata_ceuropa_ceu_fdr8_fegovera/InvoiceBusinessObject" + Then I should see the heading "Invoice" + And I should see the text "EIRA properties" + # Text from the EIRA properties. + And I should see the text "egovera:InvoiceBusinessObject" + @update:joinup_core_deploy_200301 Scenario: Sample check that options are available in the solution type field. Given I am logged in as a moderator -- GitLab From 0239bad8c32fae71dc8bf7a9f34613869804e076 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Tue, 25 Feb 2025 18:04:10 +0200 Subject: [PATCH 04/10] ISAICP-9526: Clean up the code a bit. We no longer have /rdf_entity/, there is no point to encode right before decode. Allow / in the suffix. --- .../custom/joinup_core/src/Controller/IdRedirect.php | 1 - .../src/EventSubscriber/LegacyRdfUrlSubscriber.php | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/web/modules/custom/joinup_core/src/Controller/IdRedirect.php b/web/modules/custom/joinup_core/src/Controller/IdRedirect.php index e8d5c53225..c646a24a5c 100644 --- a/web/modules/custom/joinup_core/src/Controller/IdRedirect.php +++ b/web/modules/custom/joinup_core/src/Controller/IdRedirect.php @@ -77,7 +77,6 @@ public static function getEntityTypeFromPersistentUriNamespace(?string $namespac $namespaces = [ 'w21' => 'node', 'dr8' => 'taxonomy_term', - 'dr8/egovera' => 'taxonomy_term', ]; if (!$namespace) { diff --git a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php index 406f5fff61..f444cd744b 100644 --- a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php +++ b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php @@ -61,16 +61,11 @@ public function on404(ExceptionEvent $event): void { */ public function getTargetEntity(string $path): array { // Extracts the encoded URI and everything that comes after. - $pattern = '@^(?<prefix>/rdf_entity/|/taxonomy/term/)(?<uri>http_e_f_f[^?]*)(?<suffix>/(?:.*))?$@'; + $pattern = '@^(?<prefix>/taxonomy/term/)(?<uri>http_e_f_f[^?]*)(?<suffix>/(?:.*))?$@'; if (!preg_match($pattern, $path, $found)) { return [NULL, NULL]; } - // Cases like eGovEra are redirected with a / in their URI. We need to fix - // this by replacing the prefix. - if ($found['uri'] && str_contains($found['uri'], '_fegovera/')) { - $found['uri'] = str_replace('/', '_f', $found['uri']); - } if (!$entity = $this->getEntity($found['uri'])) { return [NULL, NULL]; } -- GitLab From 13f9d8a9fc72a66da2408d9086576f2a3a05e3fa Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Wed, 26 Feb 2025 02:53:03 +0200 Subject: [PATCH 05/10] ISAICP-9526: Restore used functionality. --- .../joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php index f444cd744b..3b32375f5a 100644 --- a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php +++ b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php @@ -61,7 +61,7 @@ public function on404(ExceptionEvent $event): void { */ public function getTargetEntity(string $path): array { // Extracts the encoded URI and everything that comes after. - $pattern = '@^(?<prefix>/taxonomy/term/)(?<uri>http_e_f_f[^?]*)(?<suffix>/(?:.*))?$@'; + $pattern = '@^(?<prefix>/rdf_entity/|/taxonomy/term/)(?<uri>http_e_f_f[^?]*)(?<suffix>/(?:.*))?$@'; if (!preg_match($pattern, $path, $found)) { return [NULL, NULL]; } -- GitLab From 9350f7eb7e314d9ed25b5b5dc4aeaa15dcd4db55 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Wed, 26 Feb 2025 04:09:46 +0200 Subject: [PATCH 06/10] ISAICP-9526: Use the logic of subpath_auto to construct the IRI both in case where the suffix is and is not part of the base IRI. --- .../LegacyRdfUrlSubscriber.php | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php index 3b32375f5a..df6d8accce 100644 --- a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php +++ b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php @@ -61,16 +61,20 @@ public function on404(ExceptionEvent $event): void { */ public function getTargetEntity(string $path): array { // Extracts the encoded URI and everything that comes after. - $pattern = '@^(?<prefix>/rdf_entity/|/taxonomy/term/)(?<uri>http_e_f_f[^?]*)(?<suffix>/(?:.*))?$@'; + $pattern = '@^(?<prefix>/rdf_entity/|/taxonomy/term/)(?<uri>http_e_f_f[^/]*)(?<suffix>/(?:.*))?$@'; if (!preg_match($pattern, $path, $found)) { return [NULL, NULL]; } - if (!$entity = $this->getEntity($found['uri'])) { + if (!$entity = $this->getEntity($found['uri'], $found['suffix'])) { return [NULL, NULL]; } - return [$entity, $found['suffix'] ?? '']; + // In case we found that the suffix is part of the IRI, then, return an + // empty string as suffix. + return str_ends_with($entity->getUri(), $found['suffix']) + ? [$entity, ''] + : [$entity, $found['suffix']]; } /** @@ -78,20 +82,36 @@ public function getTargetEntity(string $path): array { * * @param string $encodedUri * The entity encoded URI path. + * @param string $suffix + * The URL suffix that might be part of the URL. * * @return \Drupal\joinup_rdf\Entity\RdfSyncEntityInterface|null * The entity or NULL if the request URL doesn't designate a mapped entity. */ - protected function getEntity(string $encodedUri): ?RdfSyncEntityInterface { + protected function getEntity(string $encodedUri, string $suffix): ?RdfSyncEntityInterface { // With the legacy storage, RDF entity IDs were encoded URIs. $uri = strtr($encodedUri, self::MAP); - if (!$entity = $this->mapper->getEntityByUri($uri)) { - return NULL; + // Sometimes, the URI is served to us including '/' characters, like the + // eGovERA. Other times, the UUID is already properly set, like internal + // URLs. Try to construct the URL by adding parts of the suffix if the + // entity is not found with the solid URI. + $uriParts = array_filter(explode('/', $suffix)); + $candidate = $uri; + $entity = $this->mapper->getEntityByUri($candidate); + if ($entity instanceof RdfSyncEntityInterface) { + return $entity; } - // Make sure is a mapped entity. - return $entity instanceof RdfSyncEntityInterface ? $entity : NULL; + while ($uriParts) { + $part = array_shift($uriParts); + $candidate .= '/' . $part; + $entity = $this->mapper->getEntityByUri($candidate); + if ($entity instanceof RdfSyncEntityInterface) { + return $entity; + } + } + return NULL; } /** -- GitLab From 8b50865bd979b92f8355f30c72b586945e2fc5c8 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Wed, 26 Feb 2025 13:52:06 +0200 Subject: [PATCH 07/10] ISAICP-9526: Fix some cases where the suffix is empty. --- .../src/EventSubscriber/LegacyRdfUrlSubscriber.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php index df6d8accce..6b2df4b284 100644 --- a/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php +++ b/web/modules/custom/joinup_rdf/src/EventSubscriber/LegacyRdfUrlSubscriber.php @@ -66,12 +66,16 @@ public function getTargetEntity(string $path): array { return [NULL, NULL]; } - if (!$entity = $this->getEntity($found['uri'], $found['suffix'])) { + if (!$entity = $this->getEntity($found['uri'], $found['suffix'] ?? '')) { return [NULL, NULL]; } // In case we found that the suffix is part of the IRI, then, return an // empty string as suffix. + if (!isset($found['suffix'])) { + return [$entity, '']; + } + return str_ends_with($entity->getUri(), $found['suffix']) ? [$entity, ''] : [$entity, $found['suffix']]; @@ -88,7 +92,7 @@ public function getTargetEntity(string $path): array { * @return \Drupal\joinup_rdf\Entity\RdfSyncEntityInterface|null * The entity or NULL if the request URL doesn't designate a mapped entity. */ - protected function getEntity(string $encodedUri, string $suffix): ?RdfSyncEntityInterface { + protected function getEntity(string $encodedUri, string $suffix = ''): ?RdfSyncEntityInterface { // With the legacy storage, RDF entity IDs were encoded URIs. $uri = strtr($encodedUri, self::MAP); -- GitLab From faffe6f2bb54f94e193594e60e65cdc68a8a68c5 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Mon, 3 Mar 2025 13:14:24 +0200 Subject: [PATCH 08/10] ISAICP-9536: Add an update hook to delete the orphaned entities. --- .../group.delete_with_content.feature | 34 +++++++++++++++++++ tests/features/update/ISAICP-9536.feature | 6 ++++ tests/src/Context/DebugContext.php | 21 ++++++++++++ .../custom/joinup_core/joinup_core.deploy.php | 33 ++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 tests/features/joinup_group/group.delete_with_content.feature create mode 100644 tests/features/update/ISAICP-9536.feature diff --git a/tests/features/joinup_group/group.delete_with_content.feature b/tests/features/joinup_group/group.delete_with_content.feature new file mode 100644 index 0000000000..063429b11e --- /dev/null +++ b/tests/features/joinup_group/group.delete_with_content.feature @@ -0,0 +1,34 @@ +@api @group-e +Feature: + In order to not leave orphaned entities + As a collection owner + I need to be unable to delete groups that have group content. + + Scenario Outline: Delete a group with group content + Given <group type> content: + | title | state | + | <group label> with children | <state> | + And news content: + | title | state | + | News | published | + And event content: + | title | state | + | Event | published | + And document content: + | title | state | + | Document | published | + And discussion content: + | title | state | + | Discussion | published | + + When I am logged in as a moderator + And I go to the delete form of the "<group label> with children" <group type> + And I press "Delete" + Then I should see the text "The <group label> <group label> with children has been deleted." + When I go to the "Discussion" discussion + Then I should not see the heading "Discussion" + + Examples: + | group label | group type | + | Collection | collection | + | Solution | solution | diff --git a/tests/features/update/ISAICP-9536.feature b/tests/features/update/ISAICP-9536.feature new file mode 100644 index 0000000000..ef8f1f5b84 --- /dev/null +++ b/tests/features/update/ISAICP-9536.feature @@ -0,0 +1,6 @@ +@api @group-clone +Feature: Test orphaned content + + @update:joinup_core_deploy_200305 + Scenario: Assert that no orphaned content exist in the website. + Then no orphaned content should exist in the website diff --git a/tests/src/Context/DebugContext.php b/tests/src/Context/DebugContext.php index c4a9490bc4..cc1c071a94 100644 --- a/tests/src/Context/DebugContext.php +++ b/tests/src/Context/DebugContext.php @@ -24,4 +24,25 @@ public function printMessages(): void { } } + /** + * Asserts that no orphaned content exists in the website. + * + * @Then /^no orphaned content should exist in the website$/ + */ + public function noOprhanedContentShouldExistInTheWebsite(): void { + $query = <<<SQL +SELECT n1.nid, n1.type, n1.title, n2.type, n2.title +FROM node_field_data n1 + LEFT JOIN node__og_audience oa ON n1.nid = oa.entity_id + LEFT JOIN node_field_data n2 ON oa.og_audience_target_id = n2.nid +WHERE n1.type IN('news', 'discussion', 'document', 'event', 'solution', 'distribution', 'release') + AND n2.nid IS NULL +SQL; + + $nids = \Drupal::database()->query($query)->fetchCol(); + if (!empty($nids)) { + throw new \Exception('Orphaned content found: ' . implode(', ', $nids)); + } + } + } diff --git a/web/modules/custom/joinup_core/joinup_core.deploy.php b/web/modules/custom/joinup_core/joinup_core.deploy.php index b989aa0b66..0130856b02 100644 --- a/web/modules/custom/joinup_core/joinup_core.deploy.php +++ b/web/modules/custom/joinup_core/joinup_core.deploy.php @@ -183,3 +183,36 @@ function joinup_core_deploy_200304(): string { \Drupal::state()->delete('joinup_core_deploy_200200'); return 'Solution type field and revision values have been restored.'; } + +/** + * Delete content of an orphaned group. + */ +function joinup_core_deploy_200305(array &$sandbox): string { + if (empty($sandbox['nids'])) { + $query = <<<SQL +SELECT n1.nid, n1.type, n1.title, n2.type, n2.title +FROM node_field_data n1 + LEFT JOIN node__og_audience oa ON n1.nid = oa.entity_id + LEFT JOIN node_field_data n2 ON oa.og_audience_target_id = n2.nid +WHERE n1.type IN('news', 'discussion', 'document', 'event', 'solution', 'distribution', 'release') + AND n2.nid IS NULL +SQL; + + $sandbox['nids'] = \Drupal::database()->query($query)->fetchCol(); + if (empty($sandbox['nids'])) { + return 'No orphaned content found.'; + } + + $sandbox['total'] = count($sandbox['nids']); + $sandbox['progress'] = 0; + } + + $nids = array_splice($sandbox['nids'], 0, 100); + $storage = \Drupal::entityTypeManager()->getStorage('node'); + $storage->delete($storage->loadMultiple($nids)); + + $sandbox['progress'] += count($nids); + $sandbox['#finished'] = (int) empty($sandbox['nids']); + + return "Deleted {$sandbox['progress']} out of {$sandbox['total']} orphaned content."; +} -- GitLab From 40e354dd5a33bc5bca012a143313320d6e6d3366 Mon Sep 17 00:00:00 2001 From: Ilias Dimopoulos <idimopoulos@hotmail.com> Date: Mon, 3 Mar 2025 13:20:11 +0200 Subject: [PATCH 09/10] ISAICP-9536: Lower the number of nids per batch. --- web/modules/custom/joinup_core/joinup_core.deploy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/modules/custom/joinup_core/joinup_core.deploy.php b/web/modules/custom/joinup_core/joinup_core.deploy.php index 0130856b02..f7429ad7f8 100644 --- a/web/modules/custom/joinup_core/joinup_core.deploy.php +++ b/web/modules/custom/joinup_core/joinup_core.deploy.php @@ -207,7 +207,7 @@ function joinup_core_deploy_200305(array &$sandbox): string { $sandbox['progress'] = 0; } - $nids = array_splice($sandbox['nids'], 0, 100); + $nids = array_splice($sandbox['nids'], 0, 50); $storage = \Drupal::entityTypeManager()->getStorage('node'); $storage->delete($storage->loadMultiple($nids)); -- GitLab From be1a87824a25368b70a801144f10c3d8163991a6 Mon Sep 17 00:00:00 2001 From: Claudiu Cristea <clau.cristea@gmail.com> Date: Mon, 3 Mar 2025 13:37:41 +0200 Subject: [PATCH 10/10] ISAICP-9536: QA remarks --- .../group.delete_with_content.feature | 34 ------------------- tests/src/Context/DebugContext.php | 14 ++++---- .../custom/joinup_core/joinup_core.deploy.php | 17 ++++------ 3 files changed, 14 insertions(+), 51 deletions(-) delete mode 100644 tests/features/joinup_group/group.delete_with_content.feature diff --git a/tests/features/joinup_group/group.delete_with_content.feature b/tests/features/joinup_group/group.delete_with_content.feature deleted file mode 100644 index 063429b11e..0000000000 --- a/tests/features/joinup_group/group.delete_with_content.feature +++ /dev/null @@ -1,34 +0,0 @@ -@api @group-e -Feature: - In order to not leave orphaned entities - As a collection owner - I need to be unable to delete groups that have group content. - - Scenario Outline: Delete a group with group content - Given <group type> content: - | title | state | - | <group label> with children | <state> | - And news content: - | title | state | - | News | published | - And event content: - | title | state | - | Event | published | - And document content: - | title | state | - | Document | published | - And discussion content: - | title | state | - | Discussion | published | - - When I am logged in as a moderator - And I go to the delete form of the "<group label> with children" <group type> - And I press "Delete" - Then I should see the text "The <group label> <group label> with children has been deleted." - When I go to the "Discussion" discussion - Then I should not see the heading "Discussion" - - Examples: - | group label | group type | - | Collection | collection | - | Solution | solution | diff --git a/tests/src/Context/DebugContext.php b/tests/src/Context/DebugContext.php index cc1c071a94..1e0c628991 100644 --- a/tests/src/Context/DebugContext.php +++ b/tests/src/Context/DebugContext.php @@ -31,13 +31,13 @@ public function printMessages(): void { */ public function noOprhanedContentShouldExistInTheWebsite(): void { $query = <<<SQL -SELECT n1.nid, n1.type, n1.title, n2.type, n2.title -FROM node_field_data n1 - LEFT JOIN node__og_audience oa ON n1.nid = oa.entity_id - LEFT JOIN node_field_data n2 ON oa.og_audience_target_id = n2.nid -WHERE n1.type IN('news', 'discussion', 'document', 'event', 'solution', 'distribution', 'release') - AND n2.nid IS NULL -SQL; + SELECT n1.nid, n1.type, n1.title, n2.type, n2.title + FROM node_field_data n1 + LEFT JOIN node__og_audience oa ON n1.nid = oa.entity_id + LEFT JOIN node_field_data n2 ON oa.og_audience_target_id = n2.nid + WHERE n1.type IN('news', 'discussion', 'document', 'event', 'solution', 'distribution', 'release') + AND n2.nid IS NULL + SQL; $nids = \Drupal::database()->query($query)->fetchCol(); if (!empty($nids)) { diff --git a/web/modules/custom/joinup_core/joinup_core.deploy.php b/web/modules/custom/joinup_core/joinup_core.deploy.php index f7429ad7f8..19b32e7fb3 100644 --- a/web/modules/custom/joinup_core/joinup_core.deploy.php +++ b/web/modules/custom/joinup_core/joinup_core.deploy.php @@ -190,18 +190,15 @@ function joinup_core_deploy_200304(): string { function joinup_core_deploy_200305(array &$sandbox): string { if (empty($sandbox['nids'])) { $query = <<<SQL -SELECT n1.nid, n1.type, n1.title, n2.type, n2.title -FROM node_field_data n1 - LEFT JOIN node__og_audience oa ON n1.nid = oa.entity_id - LEFT JOIN node_field_data n2 ON oa.og_audience_target_id = n2.nid -WHERE n1.type IN('news', 'discussion', 'document', 'event', 'solution', 'distribution', 'release') - AND n2.nid IS NULL -SQL; + SELECT n1.nid, n1.type, n1.title, n2.type, n2.title + FROM node_field_data n1 + LEFT JOIN node__og_audience oa ON n1.nid = oa.entity_id + LEFT JOIN node_field_data n2 ON oa.og_audience_target_id = n2.nid + WHERE n1.type IN('news', 'discussion', 'document', 'event', 'solution', 'distribution', 'release') + AND n2.nid IS NULL + SQL; $sandbox['nids'] = \Drupal::database()->query($query)->fetchCol(); - if (empty($sandbox['nids'])) { - return 'No orphaned content found.'; - } $sandbox['total'] = count($sandbox['nids']); $sandbox['progress'] = 0; -- GitLab