From a330997c6387c1042178c3cbc1ab06b72f94aba3 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 6 Dec 2012 23:12:20 +0000 Subject: [PATCH 1/3] Solution pack required objects now takes NewFedoraObjects rather than arrays. --- includes/solution_packs.inc | 715 +++++++++++++++--------------------- includes/utilities.inc | 14 + 2 files changed, 306 insertions(+), 423 deletions(-) diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index d6fab446..e482c388 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -9,157 +9,140 @@ /** * Solution pack admin page callback. + * + * @return string + * The html repersentation of all solution pack forms for required objects. */ function islandora_solution_packs_admin() { module_load_include('inc', 'islandora', 'includes/utilities'); - drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css'); - if (!islandora_describe_repository()) { - $message = t('Could not connect to the repository. Please check the settings on the ' . - 'Islandora configuration page.', - array('@config_url' => url('admin/islandora/configure'))); - drupal_set_message($message, 'error'); + if (($connection = islandora_get_tuque_connection()) === NULL) { + islandora_display_repository_inaccessible_message(); + return; } - $enabled_solution_packs = module_invoke_all('islandora_required_objects'); + drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css'); $output = ''; + $enabled_solution_packs = module_invoke_all('islandora_required_objects', $connection); foreach ($enabled_solution_packs as $solution_pack_module => $solution_pack_info) { - $objects = array(); - foreach ($solution_pack_info as $field => $value) { - switch ($field) { - case 'title': - $solution_pack_name = $value; - break; - case 'objects': - $objects = $value; - break; - } - } - $form_array = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects); - $output .= drupal_render($form_array); + // @todo We should probably get the title of the solution pack from the + // systems table for consistency in the interface. + $solution_pack_name = $solution_pack_info['title']; + $objects = array_filter($solution_pack_info['objects']); + $form = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects); + $output .= drupal_render($form); } return $output; } /** - * Solution pack admin page + * A solution pack form for the given module, it lists all the given objects and + * their status, allowing the user to re-ingest them. + * + * @param array $form + * The Drupal form definition. + * @param array $form_state + * The Drupal form state. + * @param string $solution_pack_module + * The module which requires the given objects. + * @param string $solution_pack_name + * The name of the solution pack to display to the users. + * @param array $objects + * An array of NewFedoraObjects which describe the state in which objects + * should exist. + * + * @return array + * The Drupal form definition. */ -function islandora_solution_pack_form($form, &$form_state, $solution_pack_module, $solution_pack_name, $objects = array()) { - global $base_url; - $needs_update = FALSE; - $needs_install = FALSE; - $could_not_connect = FALSE; - $form = array(); - - $form['solution_pack'] = array( - '#type' => 'fieldset', - '#collapsible' => FALSE, - '#collapsed' => FALSE, - '#attributes' => array('class' => array('islandora-solution-pack-fieldset')), +function islandora_solution_pack_form(array $form, array &$form_state, $solution_pack_module, $solution_pack_name, $objects = array()) { + // The order is important in terms of severity of the status, where higher + // index indicates the status is more serious, this will be used to determine + // what messages get displayed to the user. + $ok_image = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array())); + $warning_image = theme_image(array('path' => 'misc/watchdog-warning.png', 'attributes' => array())); + $status_info = array( + 'up_to_date' => array( + 'object' => t('Up-to-date'), + 'solution_pack' => t('All required objects are installed and up-to-date.'), + 'image' => $ok_image, + 'button' => t("Force reinstall objects"), + ), + 'out_of_date' => array( + 'object' => t('Out-of-date'), + 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'), + 'image' => $warning_image, + 'button' => t("Reinstall objects") + ), + 'missing_datastream' => array( + 'object' => t('Missing Datastream'), + 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'), + 'image' => $warning_image, + 'button' => t("Reinstall objects") + ), + 'missing' => array( + 'object' => t('Missing'), + 'solution_pack' => t( 'Some objects are missing and must be installed. See objects list for details.'), + 'image' => $warning_image, + 'button' => t("Install objects") + ), ); - - // adding values - $form['solution_pack']['solution_pack_module'] = array( - '#type' => 'value', - '#value' => $solution_pack_module, - ); - $form['solution_pack']['solution_pack_name'] = array( - '#type' => 'value', - '#value' => $solution_pack_name, - ); - $form['solution_pack']['objects'] = array( - '#type' => 'value', - '#value' => $objects, - ); - - $table_header = array(t('Label'), t('PID'), t('Status')); + $status_severities = array_keys($status_info); + $solution_pack_status_severity = array_search('up_to_date', $status_severities); $table_rows = array(); - foreach ($objects as $object) { - $datastreams = NULL; - if (isset($object['pid'])) { - $pid = $object['pid']; - $table_row = array(); - $object_status = islandora_check_object_status($object); - switch ($object_status) { - case 'up_to_date': - $object_status = t('Up-to-date'); - break; - case 'missing': - $object_status = t('Missing'); - $needs_install = TRUE; - break; - case 'missing_datastream': - $object_status = t('Missing datastream'); - $needs_update = TRUE; - break; - case 'out_of_date': - $object_status = t('Out-of-date'); - $needs_update = TRUE; - break; - case 'could_not_connect': - $object_status = t('Could not connect'); - $could_not_connect = TRUE; - break; - } - if ($needs_install OR $could_not_connect) { - $label = $object['label'] ? $object['label'] : ''; - } - else { - $label = $object['label'] ? l($object['label'], $base_url . '/islandora/object/' . $pid) : ''; - } - $table_row[] = $label; - $table_row[] = $pid; - $table_row[] = $object_status; - $table_rows[] = $table_row; - } + $object_status = islandora_check_object_status($object); + $object_status_info = $status_info[$object_status]; + $object_status_severity = array_search($object_status, $status_severities); + // The solution pack status severity will be the highest severity of the objects. + $solution_pack_status_severity = max($solution_pack_status_severity, $object_status_severity); + $exists = $object_status != 'missing'; + $label = $exists ? l($object->label, "islandora/object/{$object->id}") : $object->label; + $status_msg = "{$object_status_info['image']} {$object_status_info['object']}"; + $table_rows[] = array($label, $object->id, $status_msg); } - - // title - if (!$form_state['submitted']) { - $form['solution_pack']['solution_pack_label'] = array( - '#markup' => filter_xss($solution_pack_name), - '#prefix' => '

', - '#suffix' => '

', - ); - - $form['solution_pack']['install_status'] = array( - '#markup' => '' . t('Object status:') . ' ', - '#prefix' => '
', - '#suffix' => '
', - ); - if (!$needs_install AND !$needs_update AND !$could_not_connect) { - $form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-ok.png')) . ' ' . t('All required objects are installed and up-to-date.'); - $submit_button_text = t("Force reinstall objects"); - } - elseif ($needs_install) { - $form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-warning.png')) . ' ' . t('Some objects are missing and must be installed. See objects list for details.'); - $submit_button_text = t("Install objects"); - } - elseif ($needs_update) { - $form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-warning.png')) . ' ' . t('Some objects must be reinstalled. See objects list for details.'); - $submit_button_text = t("Reinstall objects"); - } - elseif ($could_not_connect) { - $form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-error.png')) . ' ' . t('Could not connect to the repository.'); - $submit_button_text = ''; - } - - $form['solution_pack']['table'] = array( - '#type' => 'item', - '#markup' => theme('table', array('header' => $table_header, 'rows' => $table_rows)), - ); - } - - if (!$could_not_connect) { - $form['solution_pack']['submit'] = array( - '#value' => $submit_button_text, - '#type' => 'submit', - '#name' => $solution_pack_module, - '#attributes' => array('class' => array('islandora-solution-pack-submit')), - '#weight' => 40, - ); - $form['solution_pack']['#submit'] = array('islandora_solution_pack_form_submit'); - } - return $form; + $solution_pack_status = $status_severities[$solution_pack_status_severity]; + $solution_pack_status_info = $status_info[$solution_pack_status]; + return array( + 'solution_pack' => array( + '#type' => 'fieldset', + '#collapsible' => FALSE, + '#collapsed' => FALSE, + '#attributes' => array('class' => array('islandora-solution-pack-fieldset')), + 'solution_pack_module' => array( + '#type' => 'value', + '#value' => $solution_pack_module, + ), + 'solution_pack_name' => array( + '#type' => 'value', + '#value' => $solution_pack_name, + ), + 'objects' => array( + '#type' => 'value', + '#value' => $objects, + ), + 'solution_pack_label' => array( + '#markup' => $solution_pack_name, + '#prefix' => '

', + '#suffix' => '

', + ), + 'install_status' => array( + '#markup' => t('Object status: !image !status', array( + '!image' => $solution_pack_status_info['image'], + '!status' => $solution_pack_status_info['solution_pack'] + )), + '#prefix' => '
', + '#suffix' => '
', + ), + 'table' => array( + '#type' => 'item', + '#markup' => theme('table', array('header' => array(t('Label'), t('PID'), t('Status')), 'rows' => $table_rows)) + ), + 'submit' => array( + '#type' => 'submit', + '#name' => $solution_pack_module, + '#value' => $solution_pack_status_info['button'], + '#attributes' => array('class' => array('islandora-solution-pack-submit')), + ) + ) + ); } /** @@ -167,349 +150,235 @@ function islandora_solution_pack_form($form, &$form_state, $solution_pack_modul * * @param array $form * The form submitted. - * @param array_reference $form_state + * @param array $form_state * The state of the form submited. */ -function islandora_solution_pack_form_submit($form, &$form_state) { +function islandora_solution_pack_form_submit(array $form, array &$form_state) { $solution_pack_module = $form_state['values']['solution_pack_module']; $solution_pack_name = $form_state['values']['solution_pack_name']; $objects = $form_state['values']['objects']; - $batch = array( - 'title' => t('Installing / updating solution pack objects'), + 'title' => t('Installing / Updating solution pack objects'), 'file' => drupal_get_path('module', 'islandora') . '/includes/solution_packs.inc', 'operations' => array(), ); - foreach ($objects as $object) { - // Add this object to the batch job queue. - $batch['operations'][] = array('islandora_batch_reingest_object', array($object)); + $batch['operations'][] = array('islandora_solution_pack_batch_operation_reingest_object', array($object)); } - batch_set($batch); - // Hook to let solution pack objects be modified. // Not using module_invoke so solution packs can be expanded by other modules. + // @todo shouldn't we send the object list along as well? module_invoke_all('islandora_postprocess_solution_pack', $solution_pack_module); +} +/** + * Batch operation used by the solution pack forms to ingest/reingest required + * object(s) + * + * @param NewFedoraObject $object + * The object to ingest/reingest. + * @param array $context + * The context of this batch operation. + */ +function islandora_solution_pack_batch_operation_reingest_object(NewFedoraObject $object, array &$context) { + $deleted = FALSE; + $existing_object = islandora_object_load($object->id); + if ($existing_object) { + $deleted = islandora_delete_object($existing_object); + $purged = $deleted && $exsiting_object == NULL; + if (!$purged) { + $object_link = l($existing_object->label, "islandora/object/{$existing_object->id}"); + drupal_set_message(t('Failed to purge existing object !object_link.', array( + '!object_link' => $object_link, + )), 'error'); + // Failed to purge don't attempt to ingest. + return; + } + } + // Object was deleted or did not exist. + $pid = $object->id; + $label = $object->label; + $action = $deleted ? 'reinstalled' : 'installed'; + $object_link = l($label, "islandora/object/{$pid}"); + $object = islandora_add_object($object); + $msg = $object ? "Successfully $action !object_link." : "Failed to $action @label identified by @pid."; + $status = $object ? 'status' : 'error'; + drupal_set_message(t($msg, array( + '@pid' => $pid, + '@label' => $label, + '!object_link' => $object_link + )), $status); } /** - * Batch reingest object(s) + * This is to be called from the solution pack's hook_install() + * and hook_uninstall() functions. It provides a convient way to have a + * solution pack's required objects ingested at install time. * - * @param array $object - * @param type $context - * @return type + * @param string $module_name + * The name of the module that is calling this function in its + * install/unistall hooks. + * @param string $op + * The operation to perform, either install or uninstall. + * + * @todo Implement hook_modules_installed/hook_modules_uninstalled instead of + * calling this function directly. + * @todo Remove the second parameter and have two seperate functions. */ -function islandora_batch_reingest_object($object_model, &$context) { +function islandora_install_solution_pack($module, $op = 'install') { + if ($op == 'uninstall') { + islandora_uninstall_solution_pack($module); + return; + } + module_load_include('module', 'islandora', 'islandora'); module_load_include('inc', 'islandora', 'includes/utilities'); - global $base_url; - $connection = islandora_get_tuque_connection(); - if (!$connection) { + module_load_include('module', $module, $module); + $info_file = drupal_get_path('module', $module) . "/{$module}.info"; + $info_array = drupal_parse_info_file($info_file); + $module_name = $info_array['name']; + $admin_link = l(t('Solution Pack admin'), 'admin/islandora/solution_packs'); + $config_link = l(t('Islandora configuration'), 'admin/islandora/configure'); + if (!islandora_describe_repository()) { + $msg = '@module: Did not install any objects. Could not connect to the '; + $msg .= 'repository. Please check the settings on the !config_link page '; + $msg .= 'and install the required objects manually on the !admin_link page.'; + drupal_set_message(st($msg, array( + '@module' => $module_name, + '!config_link' => $config_link, + '@admin_link' => $admin_link + )), 'error'); return; } - if (!empty($object_model) && is_array($object_model)) { - $pid = $object_model['pid']; - if (!islandora_is_valid_pid($pid)) { - return NULL; - } - - // purge object - // check if object already exits - $object_query = $connection->api->a->findObjects('query', 'pid=' . $pid); - $reinstall = FALSE; - if (!empty($object_query['results'])) { - $object = islandora_object_load($pid); - if (isset($object)) { - islandora_delete_object($object); - } - $reinstall = TRUE; + $connection = islandora_get_tuque_connection(); + $islandora_required_objects = $module . '_islandora_required_objects'; + $required_objects = $islandora_required_objects($connection); + $objects = $required_objects[$module]['objects']; + $status_messages = array( + 'up_to_date' => 'The object already exists and is up-to-date', + 'missing_datastream' => 'The object already exists but is missing a datastream. Please reinstall the object on the !admin_link page', + 'out_of_date' => 'The object already exists but is out-of-date. Please update the object on the !admin_link page' + ); + foreach ($objects as $object) { + $query = $connection->api->a->findObjects('query', 'pid=' . $object->id); + $already_exists = !empty($query['results']); + $label = $object->label; + $object_link = l($label, "islandora/object/{$object->id}"); + if ($already_exists) { + $object_status = islandora_check_object_status($object); + $status_msg = $status_messages[$object_status]; + drupal_set_message(st("@module: Did not install !object_link. $status_msg.", array( + '@module' => $module_name, + '!object_link' => $object_link, + '!admin_link' => $admin_link, + )), 'warning'); } - - // build and ingest new object - try { - $object = islandora_solution_pack_add_object($object_model); - $object_name = $object->label; - if ($reinstall) { - drupal_set_message(t('Successfully reinstalled @object_name.', array('@object_name' => $object_name, '@pid' => $pid))); + else { + $object = islandora_add_object($object); + if ($object) { + drupal_set_message(t('@module: Successfully installed. !object_link.', array( + '@module' => $module_name, + '!object_link' => $object_link, + )), 'status'); } else { - drupal_set_message(t('Successfully installed @object_name.', array('@object_name' => $object_name, '@pid' => $pid))); + drupal_set_message(t('@module: Failed to install. @label.', array( + '@module' => $module_name, + '@label' => $label, + )), 'warning'); } } - catch (Exception $e) { - drupal_set_message(t('Installation of object @pid failed', array('@pid' => $pid)), 'error'); - } } } - /** - * Callback function that can be called from the solution pack's hook_install() and hook_uninstall() functions. + * Uninstalls the given solution pack. * - * @TODO: add documentation + * @param string $module + * The solution pack to uninstall. + * + * @todo Implement hook_modules_uninstalled instead of calling this function + * directly for each solution pack. */ -function islandora_install_solution_pack($module_name = NULL, $op = 'install') { - // check if a module name is given. // @TODO: check module name for existance - if (!empty($module_name)) { - module_load_include('module', 'islandora', 'islandora'); - module_load_include('inc', 'islandora', 'includes/utilities'); - module_load_include('module', $module_name, $module_name); - - // set globals - global $base_url; - global $user; - - // set variables - $sp_admin = url($base_url . '/admin/islandora/solution_packs'); - $config_url = url('admin/islandora/configure'); - - // get module info - $info_file = drupal_get_path('module', $module_name) . '/' . $module_name . '.info'; - $info_array = drupal_parse_info_file($info_file); - $module_label = $info_array['name']; - - // check connection - $url = variable_get('islandora_base_url', 'http://localhost:8080/fedora'); - $info = islandora_describe_repository($url); - if (!$info) { - // operation - switch ($op) { - case 'install': - drupal_set_message(st('@module_label: Did not install any objects. Could not connect to the repository. Please check the settings on the Islandora configuration page and install the required objects manually on the solution pack admin page.', array('@module_label' => $module_label, '@config_url' => $config_url, '@sp_url' => $sp_admin)), 'error'); - break; - - case 'uninstall': - drupal_set_message(st('@module_label: Did not uninstall any objects. Could not connect to the repository. Please check the settings on the Islandora configuration page and uninstall the required objects manually if necessary.', array('@module_label' => $module_label, '@config_url' => $config_url)), 'error'); - break; - } - return; - } - - $connection = islandora_get_tuque_connection(); - // get object models - $enabled_solution_packs = module_invoke_all('islandora_required_objects'); - $islandora_required_objects = $module_name . '_islandora_required_objects'; - $required_objects = $islandora_required_objects(); - $objects = $required_objects[$module_name]['objects']; - - // loop over object models - foreach ($objects as $object) { - // set variables - $pid = $object['pid']; - $label = isset($object['label']) ? $object['label'] : st('Object'); - // check if object already exists - $query = $connection->api->a->findObjects('query', 'pid=' . $pid); - // object url - $object_url = url($base_url . '/islandora/object/' . $pid); - - // operation: install or uninstall - switch ($op) { - case 'install': - // if object exists, don't re-ingest - if (!empty($query['results'])) { - // check object status - $object_status = islandora_check_object_status($object); - // set messages - switch ($object_status) { - case 'up_to_date': - drupal_set_message(st('@module_label: did not install @label. The object already exists and is up-to-date.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@object_url' => $object_url))); - break; - case 'missing_datastream': - drupal_set_message(st('@module_label: did not install @label. The object already exists but is missing a datastream. Please reinstall the object on the solution pack admin page.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@objecturl' => $object_url, '@sp_admin' => $sp_admin)), 'warning'); - break; - case 'out_of_date': - drupal_set_message(st('@module_label: did not install @label. The object already exists but is out-of-date. Please update the object on the solution pack admin page.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@object_url' => $object_url, '@sp_admin' => $sp_admin)), 'warning'); - break; - } - } - else { - islandora_solution_pack_add_object($object); - drupal_set_message(st('@module_label: installed @label object.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@object_url' => $object_url))); - } - break; - - case 'uninstall': - // if object exists, set message - if (!empty($query['results'])) { - $object_url = url($base_url . '/islandora/object/' . $pid); - drupal_set_message(st('@module_label: did not remove @label. It may be used by other sites.', array('@pid' => $pid, '@object_url' => $object_url, '@label' => $label, '@module_label' => $module_label)), 'warning'); - } - break; - } - } +function islandora_uninstall_solution_pack($module) { + module_load_include('module', 'islandora', 'islandora'); + module_load_include('inc', 'islandora', 'includes/utilities'); + module_load_include('module', $module, $module); + $admin_link = l(t('Solution Pack admin'), 'admin/islandora/solution_packs'); + $config_link = l(t('Islandora configuration'), 'admin/islandora/configure'); + $info_file = drupal_get_path('module', $module) . "/{$module}.info"; + $info_array = drupal_parse_info_file($info_file); + $module_name = $info_array['name']; + if (!islandora_describe_repository()) { + $msg = '@module: Did not uninstall any objects. Could not connect to the '; + $msg .= 'repository. Please check the settings on the !config_link page '; + $msg .= 'and uninstall the required objects manually if necessary.'; + drupal_set_message(st($msg, array( + '@module' => $module_name, + '!config_link' => $config_link + )), 'error'); + return; + } + $connection = islandora_get_tuque_connection(); + $islandora_required_objects = $module . '_islandora_required_objects'; + $required_objects = $islandora_required_objects($connection); + $objects = $required_objects[$module]['objects']; + $existing_objects = array_filter($objects, function($o) use($connection) { + $param = "pid={$o->id}"; + $query = $connection->api->a->findObjects('query', $param); + return !empty($query['results']); + } + ); + foreach ($existing_objects as $object) { + $msg = '@module: Did not remove !object_link. It may be used by other sites.'; + $object_link = l($object->label, "islandora/object/{$object->id}"); + drupal_set_message(st($msg, array( + '!object_link' => $object_link, + '@module' => $module_name + )), 'warning'); } } - /** * Function to check the status of an object against an object model array. * - * @param array $object_model - * an array describing an object + * @param NewFedoraObject $object_definition + * A new fedora object that defines what the object should contain. + * * @return string * Returns one of the following values: * up_to_date, missing, missing_datastream or out_of_date * You can perform an appropriate action based on this value. - * Returns FALSE if the array is empty * * @see islandora_solution_pack_form() * @see islandora_install_solution_pack() - * @todo: Should this function live in islandora.module so it can be called - * easier without having to include the solution_packs.inc file? - */ -function islandora_check_object_status($object_model = array()) { - if (!empty($object_model)) { - // set variables - $pid = $object_model['pid']; - $object_status = 'up_to_date'; - - // table row - $table_row = array(); - - // check connection - module_load_include('inc', 'islandora', 'includes/utilities'); - $url = variable_get('islandora_base_url', 'http://localhost:8080/fedora'); - $info = islandora_describe_repository($url); - if (!$info) { - $object_status = 'could_not_connect'; - } - else { - - // load object - $object = islandora_object_load($pid); - // check if object exists - if (!$object) { - $object_status = 'missing'; - } - else { - // object defined with single datastream file - // @TODO: should dsversion be mandatory for the check to valid? - if (isset($object_model['dsid']) && isset($object_model['datastream_file']) && isset($object_model['dsversion'])) { - $datastreams = array( - array( - 'dsid' => $object_model['dsid'], - 'datastream_file' => $object_model['datastream_file'], - 'dsversion' => $object_model['dsversion'], - ), - ); - } - // object defined with multiple datastreams (using an array) - elseif (!empty($object_model['datastreams'])) { - $datastreams = $object_model['datastreams']; - } - if (!empty($datastreams) && is_array($datastreams)) { - // loop over defined datastreams - foreach ($datastreams as $ds) { - $ds_id = $ds['dsid']; - // check if defined datastream exists in the object - if (!$object[$ds_id]) { - $object_status = 'missing_datastream'; - break; - } - elseif (isset($ds['dsversion'])) { - // Check if the datastream is versioned and needs updating. - $installed_version = islandora_get_islandora_datastream_version($object, $ds['dsid']); - $available_version = islandora_get_islandora_datastream_version(NULL, NULL, $ds['datastream_file']); - - if ($available_version > $installed_version) { - $object_status = 'out_of_date'; - break; - } - } - } - } - } - } - return $object_status; - } - else { - return FALSE; - } -} - -/** - * Converts the given definition into an object and add's it to the repository. - * - * @param array $object_definition - * An associative array containing the necessary parameters to create the - * desired object: - * - pid: The PID with which the object will be created. - * - label: An optional label to apply to the object. - * - datastreams: Same as the "datastreams" array accepted by - * islandora_prepare_new_object(). - * - cmodel: Either an array of content models as accepted by - * islandora_preprare_new_object(), or a single content model PID to add - * to the object. - * - parent: Either an array of parents, or a single parent PID to which to - * relate to; uses isMemberOfCollection by default. - * - relationships: An array of relationships as accepted by - * islandora_prepare_new_object(). - * - * @return FedoraObject - * The newly created object. - */ -function islandora_solution_pack_add_object(array $object_definition) { - $object = islandora_solution_pack_prepare_new_object($object_definition); - return islandora_add_object($object); -} - -/** - * Prepares a new object based on the solution pack style of declaring them as arrays. - * - * @param array $object_definition - * An associative array containing the necessary parameters to create the - * desired object: - * - pid: The PID with which the object will be created. - * - label: An optional label to apply to the object. - * - datastreams: Same as the "datastreams" array accepted by - * islandora_prepare_new_object(). - * - cmodel: Either an array of content models as accepted by - * islandora_prepare_new_object(), or a single content model PID to add - * to the object. - * - parent: Either an array of parents, or a single parent PID to which to - * relate to; uses isMemberOfCollection by default. - * - relationships: An array of relationships as accepted by - * islandora_prepare_new_object(). - * - * @return NewFedoraObject - * An NewFedoraObject which has been initalized with the given properties. + * @todo Should this function live in islandora.module so it can be called + * easier without having to include the solution_packs.inc file? */ -function islandora_solution_pack_prepare_new_object(array $object_definition) { - module_load_include('inc', 'islandora', 'includes/utilities'); - $namespace = $object_definition['pid']; - $label = !empty($object_definition['label']) ? $object_definition['label'] : NULL; - $datastreams = array(); - if (!empty($object_definition['datastreams']) AND is_array($object_definition['datastreams'])) { - $datastreams = $object_definition['datastreams']; - } - $content_models = array(); - if (!empty($object_definition['cmodel'])) { - if (is_array($object_definition['cmodel'])) { - $content_models = $object_definition['cmodel']; - } - else { - $content_models[] = $object_definition['cmodel']; - } - } - $relationships = array(); - if (!empty($object_definition['parent']) AND !is_array($object_definition['parent'])) { - $relationships[] = array('relationship' => 'isMemberOfCollection', 'pid' => $object_definition['parent']); +function islandora_check_object_status(NewFedoraObject $object_definition) { + $existing_object = islandora_object_load($object_definition->id); + if (!$existing_object) { + return 'missing'; } - if (!empty($object_definition['parents']) AND is_array($object_definition['parents'])) { - foreach ($object_definition['parents'] as $parent) { - $relationships[] = array('relationship' => 'isMemberOfCollection', 'pid' => $parent); - } + $existing_datastreams = array_keys(iterator_to_array($existing_object)); + $expected_datastreams = array_keys(iterator_to_array($object_definition)); + $datastream_diff = array_diff($expected_datastreams, $existing_datastreams); + if (!empty($datastream_diff)) { + return 'missing_datastream'; } - if (!empty($object_definition['relationships']) AND is_array($object_definition['relationships'])) { - foreach ($object_definition['relationships'] as $relationship) { - $relationships[] = array('relationship' => $relationship['relationship'], 'pid' => $relationship['pid']); + $is_xml_datastream = function($ds) { return $ds->mimetype == 'text/xml'; }; + $xml_datastreams = array_filter(iterator_to_array($object_definition), $is_xml_datastream); + foreach ($xml_datastreams as $ds) { + $installed_version = islandora_get_islandora_datastream_version($existing_object, $ds->id); + $available_version = islandora_get_islandora_datastream_version($object_definition, $ds->id); + if ($available_version > $installed_version) { + return 'out_of_date'; } } - return islandora_prepare_new_object($namespace, $label, $datastreams, $content_models, $relationships); + // If not anything else we can assume its up to date. + return 'up_to_date'; } - /** * @defgroup viewer-functions * @{ diff --git a/includes/utilities.inc b/includes/utilities.inc index 537c5dd8..02c131db 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -413,3 +413,17 @@ function islandora_prepare_new_object($namespace = NULL, $label = NULL, $datastr } return $object; } + +/** + * Displays the repository is inaccessible message + * + * Use anywhere we want to ensure a consitent error message when the repository + * is not accessible. + */ +function islandora_display_repository_inaccessible_message() { + $text = t('Islandora configuration'); + $link = l($text, 'admin/islandora/configure', array('attributes' => array('title' => $text))); + $message = t('Could not connect to the repository. Please check the settings on the @link page.', + array('@link' => $link)); + drupal_set_message($message, 'error'); +} From 65fd37cbaca17ad6f7b0fd9b2bbf842dcb3c6ea3 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 7 Dec 2012 04:42:22 +0000 Subject: [PATCH 2/3] Updated require objects hook to use NewFedoraObjects. --- islandora.module | 65 +++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/islandora.module b/islandora.module index a67efcf3..99c84fcf 100644 --- a/islandora.module +++ b/islandora.module @@ -167,7 +167,7 @@ function islandora_menu() { 'load arguments' => array('%map'), 'type' => MENU_DEFAULT_LOCAL_TASK, ); - + $items['islandora/object/%islandora_object/datastream/%islandora_datastream/download'] = array( 'title' => 'Download datastream', 'page callback' => 'islandora_download_datastream', @@ -530,7 +530,7 @@ function islandora_object_load($object_id) { * '%islandora_tokened_object'. * @param array $map * Used to extract the Fedora object's DSID at $map[4]. - * + * * @return FedoraObject * A token authenticated object. @see islandora_object_load */ @@ -547,21 +547,21 @@ function islandora_tokened_object_load($object_id, $map) { } /** - * This datastream load must take in arguments in a different - * order than the usual islandora_datastream_load. This is because + * This datastream load must take in arguments in a different + * order than the usual islandora_datastream_load. This is because * the function islandora_tokened_object_load needs DSID. It uses * the path %map to avoid duplicate parameters. The menu system * passes 'load arguments' to both islandora_tokened_object_load * and this function and the first parameter is positional with the token. - * An alternative: + * An alternative: * islandora_tokened_object_load(PID, DSID, PID) * islandora_tokened_datastream_load(DSID, DSID, PID) - * + * * @param mixed $datastream_id * %islandora_tokened_datastream @see islandora_datastream_load * @param array $map * Used to extract the Fedora object's PID at $map[2]. - * + * * @return FedoraDatastream * A datastream from Fedora. * @see islandora_datastream_load @@ -582,8 +582,8 @@ function islandora_tokened_datastream_load($datastream_id, $map) { * from the given object in the menu path identified by '%islandora_object'. * * @param mixed $object_id - * The object to load the datastream from. This can be a Fedora PID or - * an instantiated IslandoraFedoraObject as it implements __toString() + * The object to load the datastream from. This can be a Fedora PID or + * an instantiated IslandoraFedoraObject as it implements __toString() * returning the PID. * * @return FedoraDatastream @@ -632,35 +632,32 @@ function islandora_get_islandora_datastream_version($item = NULL, $dsid = NULL, /** * Implements hook_islandora_required_objects(). */ -function islandora_islandora_required_objects() { +function islandora_islandora_required_objects(IslandoraTuque $connection) { $module_path = drupal_get_path('module', 'islandora'); + // Root Collection + $root_collection = $connection->repository->constructObject('islandora:root'); + $root_collection->owner = 'fedoraAdmin'; + $root_collection->label = 'Top-level Collection'; + $root_collection->models = 'islandora:collectionCModel'; + // Collection Policy Datastream + $datastream = $root_collection->constructDatastream('COLLECTION_POLICY', 'X'); + $datastream->label = 'Collection policy'; + $datastream->mimetype = 'text/xml'; + $datastream->setContentFromFile("$module_path/xml/islandora_collection_policy.xml", FALSE); + $root_collection->ingestDatastream($datastream); + // TN Datastream + $datastream = $root_collection->constructDatastream('TN', 'M'); + $datastream->label = 'Thumbnail'; + $datastream->mimetype = 'image/png'; + $datastream->setContentFromFile("$module_path/images/folder.png", FALSE); + $root_collection->ingestDatastream($datastream); return array( 'islandora' => array( 'title' => 'Islandora', 'objects' => array( - array( - 'pid' => 'islandora:root', - 'label' => 'Top-level collection', - 'cmodel' => 'islandora:collectionCModel', - 'datastreams' => array( - array( - 'dsid' => 'COLLECTION_POLICY', - 'label' => 'Collection policy', - 'mimetype' => 'text/xml', - 'control_group' => 'X', - 'datastream_file' => "$module_path/xml/islandora_collection_policy.xml", - ), - array( - 'dsid' => 'TN', - 'label' => 'Thumbnail', - 'mimetype' => 'image/png', - 'control_group' => 'M', - 'datastream_file' => "$module_path/images/folder.png", - ), - ), - ), - ), - ), + $root_collection + ) + ) ); } @@ -875,7 +872,7 @@ function islandora_post_delete_datastream(FedoraObject $object, $datastream_id) /** * Implements hook_cron() - * + * * Removes expired authentication tokens. */ function islandora_cron() { From d0de6d6b9096fe57162a5846d6902383e1577979 Mon Sep 17 00:00:00 2001 From: jonathangreen Date: Tue, 11 Dec 2012 13:52:38 -0400 Subject: [PATCH 3/3] Modified solution_packs.inc to return datastreams The table printed by solution_packs.inc used to not mention which datastreams were modified. We do that now. We also test the MD5 of the datastream to make sure that it is actually the same. --- includes/solution_packs.inc | 85 ++++++++++++++++++++++++++++--------- includes/utilities.inc | 6 +-- islandora.module | 6 +++ 3 files changed, 73 insertions(+), 24 deletions(-) diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index e482c388..6b5310e2 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -15,10 +15,12 @@ */ function islandora_solution_packs_admin() { module_load_include('inc', 'islandora', 'includes/utilities'); - if (($connection = islandora_get_tuque_connection()) === NULL) { + if (!islandora_describe_repository()) { islandora_display_repository_inaccessible_message(); - return; + return ''; } + + $connection = islandora_get_tuque_connection(); drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css'); $output = ''; $enabled_solution_packs = module_invoke_all('islandora_required_objects', $connection); @@ -60,25 +62,26 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solutio $warning_image = theme_image(array('path' => 'misc/watchdog-warning.png', 'attributes' => array())); $status_info = array( 'up_to_date' => array( - 'object' => t('Up-to-date'), 'solution_pack' => t('All required objects are installed and up-to-date.'), 'image' => $ok_image, 'button' => t("Force reinstall objects"), ), + 'modified_datastream' => array( + 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'), + 'image' => $warning_image, + 'button' => t("Reinstall objects") + ), 'out_of_date' => array( - 'object' => t('Out-of-date'), 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'), 'image' => $warning_image, 'button' => t("Reinstall objects") ), 'missing_datastream' => array( - 'object' => t('Missing Datastream'), 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'), 'image' => $warning_image, 'button' => t("Reinstall objects") ), 'missing' => array( - 'object' => t('Missing'), 'solution_pack' => t( 'Some objects are missing and must be installed. See objects list for details.'), 'image' => $warning_image, 'button' => t("Install objects") @@ -89,13 +92,13 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solutio $table_rows = array(); foreach ($objects as $object) { $object_status = islandora_check_object_status($object); - $object_status_info = $status_info[$object_status]; - $object_status_severity = array_search($object_status, $status_severities); + $object_status_info = $status_info[$object_status['status']]; + $object_status_severity = array_search($object_status['status'], $status_severities); // The solution pack status severity will be the highest severity of the objects. $solution_pack_status_severity = max($solution_pack_status_severity, $object_status_severity); - $exists = $object_status != 'missing'; + $exists = $object_status['status'] != 'missing'; $label = $exists ? l($object->label, "islandora/object/{$object->id}") : $object->label; - $status_msg = "{$object_status_info['image']} {$object_status_info['object']}"; + $status_msg = "{$object_status_info['image']} {$object_status['status_friendly']}"; $table_rows[] = array($label, $object->id, $status_msg); } $solution_pack_status = $status_severities[$solution_pack_status_severity]; @@ -155,7 +158,6 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solutio */ function islandora_solution_pack_form_submit(array $form, array &$form_state) { $solution_pack_module = $form_state['values']['solution_pack_module']; - $solution_pack_name = $form_state['values']['solution_pack_name']; $objects = $form_state['values']['objects']; $batch = array( 'title' => t('Installing / Updating solution pack objects'), @@ -186,7 +188,7 @@ function islandora_solution_pack_batch_operation_reingest_object(NewFedoraObject $existing_object = islandora_object_load($object->id); if ($existing_object) { $deleted = islandora_delete_object($existing_object); - $purged = $deleted && $exsiting_object == NULL; + $purged = $deleted && $existing_object == NULL; if (!$purged) { $object_link = l($existing_object->label, "islandora/object/{$existing_object->id}"); drupal_set_message(t('Failed to purge existing object !object_link.', array( @@ -257,7 +259,8 @@ function islandora_install_solution_pack($module, $op = 'install') { $status_messages = array( 'up_to_date' => 'The object already exists and is up-to-date', 'missing_datastream' => 'The object already exists but is missing a datastream. Please reinstall the object on the !admin_link page', - 'out_of_date' => 'The object already exists but is out-of-date. Please update the object on the !admin_link page' + 'out_of_date' => 'The object already exists but is out-of-date. Please update the object on the !admin_link page', + 'modified_datastream' => 'The object already exists but datastreams are modified. Please reinstall the object on the !admin_link page', ); foreach ($objects as $object) { $query = $connection->api->a->findObjects('query', 'pid=' . $object->id); @@ -266,7 +269,7 @@ function islandora_install_solution_pack($module, $op = 'install') { $object_link = l($label, "islandora/object/{$object->id}"); if ($already_exists) { $object_status = islandora_check_object_status($object); - $status_msg = $status_messages[$object_status]; + $status_msg = $status_messages[$object_status['status']]; drupal_set_message(st("@module: Did not install !object_link. $status_msg.", array( '@module' => $module_name, '!object_link' => $object_link, @@ -304,7 +307,6 @@ function islandora_uninstall_solution_pack($module) { module_load_include('module', 'islandora', 'islandora'); module_load_include('inc', 'islandora', 'includes/utilities'); module_load_include('module', $module, $module); - $admin_link = l(t('Solution Pack admin'), 'admin/islandora/solution_packs'); $config_link = l(t('Islandora configuration'), 'admin/islandora/configure'); $info_file = drupal_get_path('module', $module) . "/{$module}.info"; $info_array = drupal_parse_info_file($info_file); @@ -352,31 +354,72 @@ function islandora_uninstall_solution_pack($module) { * * @see islandora_solution_pack_form() * @see islandora_install_solution_pack() - * @todo Should this function live in islandora.module so it can be called - * easier without having to include the solution_packs.inc file? */ function islandora_check_object_status(NewFedoraObject $object_definition) { $existing_object = islandora_object_load($object_definition->id); if (!$existing_object) { - return 'missing'; + return array('status' => 'missing', 'status_friendly' => t('Missing')); } + $existing_datastreams = array_keys(iterator_to_array($existing_object)); $expected_datastreams = array_keys(iterator_to_array($object_definition)); $datastream_diff = array_diff($expected_datastreams, $existing_datastreams); if (!empty($datastream_diff)) { - return 'missing_datastream'; + $status_friendly = format_plural(count($datastream_diff), 'Missing Datastream: %dsids.', 'Missing Datastreams: %dsids.', array('%dsids' => implode(', ', $datastream_diff))); + return array('status' => 'missing_datastream', 'status_friendly' => $status_friendly, 'data' => $datastream_diff); } + $is_xml_datastream = function($ds) { return $ds->mimetype == 'text/xml'; }; $xml_datastreams = array_filter(iterator_to_array($object_definition), $is_xml_datastream); + $out_of_date_datastreams = array(); foreach ($xml_datastreams as $ds) { $installed_version = islandora_get_islandora_datastream_version($existing_object, $ds->id); $available_version = islandora_get_islandora_datastream_version($object_definition, $ds->id); if ($available_version > $installed_version) { - return 'out_of_date'; + $out_of_date_datastreams[] = $ds->id; } } + if(count($out_of_date_datastreams)) { + $status_friendly = format_plural(count($out_of_date_datastreams), 'Datastream out of date: %dsids.', 'Datastreams out of date: %dsids.', array('%dsids' => implode(', ', $out_of_date_datastreams))); + return array('status' => 'out_of_date', 'status_friendly' => $status_friendly, 'data' => $out_of_date_datastreams); + } + + // This is a pretty heavy function, but I'm not sure a better way. If we have + // performance trouble, we should maybe remove this. + $modified_datastreams = array(); + foreach ($object_definition as $ds) { + if($ds->mimetype == 'text/xml' || $ds->mimetype == 'application/rdf+xml') { + // If the datastream is XML we use the domdocument C14N cannonicalization + // function to test if they are equal, because the strings likely won't + // be equal as Fedora does some XML mangling. In order for C14N to work + // we need to replace the info:fedora namespace, as C14N hates it. + $object_definition_dom = new DOMDocument(); + $object_definition_dom->loadXML(str_replace('info:', 'http://', $ds->content)); + $object_actual_dom = new DOMDocument(); + $object_actual_dom->loadXML(str_replace('info:', 'http://', $existing_object[$ds->id]->content)); + + // We have to use the shutup function here. C14N throws warnings about the + // info:fedora namespace, but they are mostly useless. + if($object_actual_dom->C14N() != $object_definition_dom->C14N()) { + $modified_datastreams[] = $ds->id; + } + } + else { + $object_definition_hash = md5($ds->content); + $object_actual_hash = md5($existing_object[$ds->id]->content); + if($object_definition_hash != $object_actual_hash) { + $modified_datastreams[] = $ds->id;; + } + } + } + if(count($modified_datastreams)) { + $status_friendly = format_plural(count($modified_datastreams), 'Modified Datastream: %dsids.', 'Modified Datastreams: %dsids.', array('%dsids' => implode(', ', $modified_datastreams))); + return array('status' => 'modified_datastream', 'data' => $modified_datastreams, 'status_friendly' => $status_friendly); + } + + // If not anything else we can assume its up to date. - return 'up_to_date'; + return array('status' => 'up_to_date', 'status_friendly' => t('Up-to-date')); } /** diff --git a/includes/utilities.inc b/includes/utilities.inc index 02c131db..55ef7562 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -423,7 +423,7 @@ function islandora_prepare_new_object($namespace = NULL, $label = NULL, $datastr function islandora_display_repository_inaccessible_message() { $text = t('Islandora configuration'); $link = l($text, 'admin/islandora/configure', array('attributes' => array('title' => $text))); - $message = t('Could not connect to the repository. Please check the settings on the @link page.', - array('@link' => $link)); - drupal_set_message($message, 'error'); + $message = t('Could not connect to the repository. Please check the settings on the !link page.', + array('!link' => $link)); + drupal_set_message($message, 'error', FALSE); } diff --git a/islandora.module b/islandora.module index 99c84fcf..bdfdb764 100644 --- a/islandora.module +++ b/islandora.module @@ -314,6 +314,12 @@ function islandora_forms($form_id) { */ function islandora_object_access_callback($perm, $object = NULL) { module_load_include('inc', 'islandora', 'includes/utilities'); + + if (!$object && !islandora_describe_repository()) { + islandora_display_repository_inaccessible_message(); + return FALSE; + } + return user_access($perm) && is_object($object) && islandora_namespace_accessible($object->id); }