diff --git a/includes/admin.form.inc b/includes/admin.form.inc index 7324f6c6..0d6ae539 100644 --- a/includes/admin.form.inc +++ b/includes/admin.form.inc @@ -127,6 +127,16 @@ function islandora_repository_admin(array $form, array &$form_state) { ), ), ), + 'islandora_orphaned_objects_backend' => array( + '#type' => 'radios', + '#title' => t('Orphaned Objects query'), + '#description' => t('How the Orphaned Islandora Objects list is generated.'), + '#default_value' => variable_get('islandora_orphaned_objects_backend', 'SPARQL'), + '#options' => array( + 'Solr' => t('Solr'), + 'SPARQL' => t('SPARQL'), + ), + ), 'islandora_risearch_use_itql_when_necessary' => array( '#type' => 'checkbox', '#title' => t('Use iTQL for particular queries'), diff --git a/includes/orphaned_objects.inc b/includes/orphaned_objects.inc index f17f4185..f33de1b7 100644 --- a/includes/orphaned_objects.inc +++ b/includes/orphaned_objects.inc @@ -54,11 +54,20 @@ function islandora_manage_orphaned_objects_form(array $form, array $form_state) or a variety of other reasons. Some of these orphans may exist intentionally. Please be cautious when deleting, as this action is irreversible.'), 'warning'); $orphaned_objects = islandora_get_orphaned_objects(); + $query_method = variable_get('islandora_orphaned_objects_backend', 'SPARQL'); + module_load_include('inc', 'islandora', 'includes/utilities'); $rows = array(); foreach ($orphaned_objects as $orphaned_object) { - $pid = $orphaned_object['object']['value']; + if ($query_method == 'SPARQL') { + $pid = $orphaned_object['object']['value']; + $title = $orphaned_object['title']['value']; + } + elseif ($query_method == 'Solr') { + $pid = $orphaned_object['PID']; + $title = $orphaned_object['object_label']; + } if (islandora_namespace_accessible($pid)) { - $rows[$pid] = array(l($orphaned_object['title']['value'] . " (" . $pid . ")", "islandora/object/$pid")); + $rows[$pid] = array(l($title . " (" . $pid . ")", "islandora/object/$pid")); } } ksort($rows); @@ -144,9 +153,86 @@ function islandora_manage_orphaned_objects_confirm_submit(array $form, array &$f * An array containing the results of the orphaned objects queries. */ function islandora_get_orphaned_objects() { - $connection = islandora_get_tuque_connection(); - // SPARQL: get orphaned objects, exclude any with a living parent. - $object_query = <<buildQuery($query); + $qp->solrParams['fl'] = $params; + $qp->solrLimit = 1000000000; + + // Check islandora_compound_object filters to include compound children. + if (variable_get('islandora_compound_object_hide_child_objects_solr', TRUE)) { + $fq = variable_get('islandora_compound_object_solr_fq', '-RELS_EXT_isConstituentOf_uri_mt:[* TO *]'); + if (!empty($fq)) { + // Delete islandora_compound_object_solr_fq from the list of filters. + $filters = $qp->solrParams['fq']; + if (($key = array_search($fq, $filters)) !== FALSE) { + unset($filters[$key]); + $qp->solrParams['fq'] = $filters; + } + } + } + $qp->executeQuery(FALSE); + + try { + $results = $qp->islandoraSolrResult['response']['objects']; + } + catch (Exception $e) { + watchdog_exception('Islandora', $e, 'Got an exception searching for parent objects .', array(), WATCHDOG_ERROR); + $results = array(); + } + $orphaned_objects = array(); + $already_checked = array(); + $missing_parents = array(); + // Check all results for PIDs that don't exist. + foreach ($results as $result) { + if (array_key_exists($collection_field, $result['solr_doc'])) { + foreach ($result['solr_doc'][$collection_field] as $collection) { + if (in_array($collection, $missing_parents)) { + $orphaned_objects[] = $result; + } + elseif (!in_array($collection, $already_checked)) { + $test = islandora_identify_missing_parents($collection); + if (!$test) { + $orphaned_objects[] = $result; + $missing_parents[] = $collection; + } + $already_checked[] = $collection; + } + } + } + if (array_key_exists($member_field, $result['solr_doc'])) { + foreach ($result['solr_doc'][$member_field] as $membership) { + if (in_array($membership, $missing_parents)) { + $orphaned_objects[] = $result; + } + elseif (!in_array($membership, $already_checked)) { + $test = islandora_identify_missing_parents($membership); + if (!$test) { + $orphaned_objects[] = $result; + $missing_parents[] = $membership; + } + $already_checked[] = $membership; + } + } + } + } + $results = $orphaned_objects; + } + + elseif ($query_method == "SPARQL") { + $connection = islandora_get_tuque_connection(); + // SPARQL: get orphaned objects, exclude any with a living parent. + $object_query = << !empty($optionals) ? ('OPTIONAL {{' . implode('} UNION {', $optionals) . '}}') : '', + '!filters' => !empty($filters) ? implode(' ', array_map($filter_map, $filters)) : '', + '!dead_parent_relationships' => '?p = ' . implode(' || ?p = ', $parent_relationships['predicate']), + '!live_parent_relationships' => '{' . implode(' } UNION { ', array_map($parent_map, $parent_relationships['predicate'])) . '}', + '!prefix' => implode("\n", $parent_relationships['prefix']), + )); + $results = $connection->repository->ri->sparqlQuery($sparql_query_objects); } - $filter_map = function ($filter) { - return "FILTER($filter)"; - }; - $parent_map = function ($parent) { - return "?object $parent ?liveparent"; - }; - // Use separate queries for different object types. - $sparql_query_objects = format_string($object_query, array( - '!optionals' => !empty($optionals) ? ('OPTIONAL {{' . implode('} UNION {', $optionals) . '}}') : '', - '!filters' => !empty($filters) ? implode(' ', array_map($filter_map, $filters)) : '', - '!dead_parent_relationships' => '?p = ' . implode(' || ?p = ', $parent_relationships['predicate']), - '!live_parent_relationships' => '{' . implode(' } UNION { ', array_map($parent_map, $parent_relationships['predicate'])) . '}', - '!prefix' => implode("\n", $parent_relationships['prefix']), - )); - $results = $connection->repository->ri->sparqlQuery($sparql_query_objects); return $results; } @@ -230,6 +317,29 @@ function islandora_delete_orphaned_objects_create_batch(array $pids) { return $batch; } +/** + * Solr query to check for deceased parents. + */ +function islandora_identify_missing_parents($parent) { + $parent_params = "PID"; + $parent_test = substr($parent, strpos($parent, '/') + 1); + $parent_query = 'PID:"' . $parent_test . '"'; + $qp = new islandoraSolrQueryProcessor(); + $qp->buildQuery($parent_query); + $qp->solrParams['fl'] = $parent_params; + $qp->solrLimit = 1000000000; + $qp->executeQuery(FALSE); + try { + $parent_results = $qp->islandoraSolrResult['response']['objects']; + } + catch (Exception $e) { + watchdog_exception('Islandora', $e, 'Got an exception searching for parent objects .', array(), WATCHDOG_ERROR); + $parent_results = array(); + } + return ($parent_results); +} + + /** * Constructs and performs the deleting batch operation. * diff --git a/islandora.install b/islandora.install index cc0cc49f..c08d06d6 100644 --- a/islandora.install +++ b/islandora.install @@ -59,6 +59,7 @@ function islandora_uninstall() { 'islandora_semaphore_period', 'islandora_require_obj_upload', 'islandora_breadcrumbs_backends', + 'islandora_orphaned_objects_backend', 'islandora_render_context_ingeststep', 'islandora_deny_inactive_and_deleted', ); diff --git a/tests/datastream_validator_tests.test b/tests/datastream_validator_tests.test index d0176485..81d36c9b 100644 --- a/tests/datastream_validator_tests.test +++ b/tests/datastream_validator_tests.test @@ -224,7 +224,7 @@ class PrefixDatastreamValidatorTestCase extends IslandoraWebTestCase { 'dsid' => $prefix, 'path' => $this->path . $filename, 'control_group' => 'M', - ), + ), ); $object = $this->ingestConstructedObject(array(), $datastreams); return $object; diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 5e8e4543..f798a4a3 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -42,7 +42,7 @@ if [ "$(phpenv version-name)" == "5.3.3" ]; then composer global require drupal/coder:7.* else composer global require drush/drush:7.* - composer global require drupal/coder + composer global require drupal/coder:7.* fi # Symlink the things