From 95bed5254b761a8efc321328a3406d3db7ea82c2 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 7 Dec 2012 04:05:39 +0000 Subject: [PATCH 1/9] Warning no longer displayed when attempting to call islandora_load_object with an existing object in the datastream_load function. --- islandora.module | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/islandora.module b/islandora.module index a67efcf3..19b933da 100644 --- a/islandora.module +++ b/islandora.module @@ -167,7 +167,6 @@ 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 +529,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 +546,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 +581,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 @@ -591,12 +590,10 @@ function islandora_tokened_datastream_load($datastream_id, $map) { * object, otherwise it returns NULL which triggers drupal_page_not_found(). */ function islandora_datastream_load($datastream_id, $object_id) { - $object = islandora_object_load($object_id); - + $object = is_object($object_id) ? $object_id : islandora_object_load($object_id); if (!$object) { return NULL; } - return $object[$datastream_id]; } @@ -875,7 +872,7 @@ function islandora_post_delete_datastream(FedoraObject $object, $datastream_id) /** * Implements hook_cron() - * + * * Removes expired authentication tokens. */ function islandora_cron() { From a330997c6387c1042178c3cbc1ab06b72f94aba3 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 6 Dec 2012 23:12:20 +0000 Subject: [PATCH 2/9] 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 3/9] 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 e1a5a3559029395ec772f2b1ce9966e55fde7c0d Mon Sep 17 00:00:00 2001 From: William Panting Date: Fri, 7 Dec 2012 19:15:46 -0400 Subject: [PATCH 4/9] Token authentication. --- includes/islandora_authtokens.inc | 107 ++++++++++++++++++++++++++++++ islandora.install | 80 ++++++++++++++++++++-- islandora.module | 57 ++++++++++++++-- 3 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 includes/islandora_authtokens.inc diff --git a/includes/islandora_authtokens.inc b/includes/islandora_authtokens.inc new file mode 100644 index 00000000..7acdb898 --- /dev/null +++ b/includes/islandora_authtokens.inc @@ -0,0 +1,107 @@ +fields( + array( + 'token' => $token, + 'uid' => $user->uid, + 'pid' => $pid, + 'dsid' => $dsid, + 'time' => $time, + 'remaining_uses' => $uses, + ))->execute(); + + return $token; +} + +/** + * Submit a token to islandora for authentication. Supply islandora with the + * token and the object/datastream it is for and you will receive access if + * authentication passes. Tokens can only be redeemed in a short window after + * their creation. + * + * @param string $pid + * The PID of the object to retrieve. + * @param string $dsid + * The datastream id to retrieve. + * @param string $token + * The registered token that allows access to this object. + * + * @return mixed + * The user credentials for access if the token validation passes, + * FALSE otherwise + */ +function islandora_validate_object_token($pid, $dsid, $token) { + // Check for database token. + $time = time(); + $query = db_select('islandora_authtokens', 'tokens'); + $query->join('users', 'u', 'tokens.uid = u.uid'); + // The results will look like user objects. + $result = $query + ->fields('u', array('uid', 'name', 'pass')) + ->fields('tokens', array('remaining_uses')) + ->condition('token', $token, '=') + ->condition('pid', $pid, '=') + ->condition('dsid', $dsid, '=') + ->condition('time', $time, '<=') + ->condition('time', $time - TOKEN_TIMEOUT, '>') + ->execute() + ->fetchAll(); + if ($result) { + $remaining_uses = $result[0]->remaining_uses; + $remaining_uses--; + // Remove the authentication token so it can't be used again. + if ($remaining_uses == 0) { + db_delete("islandora_authtokens") + ->condition('token', $token, '=') + ->condition('pid', $pid, '=') + ->condition('dsid', $dsid, '=') + ->execute(); + } + // Decrement authentication token uses. + else { + db_update("islandora_authtokens") + ->fields(array('remaining_uses' => $remaining_uses)) + ->condition('token', $token, '=') + ->condition('pid', $pid, '=') + ->condition('dsid', $dsid, '=') + ->execute(); + } + unset($result[0]->remaining_uses); + return $result[0]; + } + else { + return FALSE; + } +} diff --git a/islandora.install b/islandora.install index 88549ce5..549f7e14 100644 --- a/islandora.install +++ b/islandora.install @@ -1,15 +1,13 @@ 'The hub for all islandora authentication tokens', + 'fields' => array( + 'id' => array( + 'description' => 'key', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'token' => array( + 'description' => 'a unique identifier for this token', + 'type' => 'varchar', + 'length' => 64, + ), + 'remaining_uses' => array( + 'description' => 'How many uses until this should be removed', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'uid' => array( + 'description' => 'the user id that requested this token', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'time' => array( + 'description' => 'when this token was created', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'pid' => array( + 'description' => 'the pid of the object this token unlocks', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + ), + 'dsid' => array( + 'description' => 'the datasteram id of the object this token unlocks', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), + ), + 'unique keys' => array( + 'id' => array('id'), + ), + 'primary key' => array('id'), + ); + return $schema; +} + +/** + * Implements hook_update_N(). + * + * Add the required table for handling authentication tokens. + * This is the first instance that has this table. + */ +function islandora_update_7001(&$sandbox) { + drupal_install_schema('islandora'); + $t = get_t(); + return $t("Islandora database updates complete"); +} diff --git a/islandora.module b/islandora.module index 2b553da8..797e183f 100644 --- a/islandora.module +++ b/islandora.module @@ -161,10 +161,13 @@ function islandora_menu() { 'access arguments' => array(FEDORA_VIEW, 2, 4), 'load arguments' => array(2), ); - $items['islandora/object/%islandora_object/datastream/%islandora_datastream/view'] = array( + // This menu item uses token authentication in islandora_tokened_object. + $items['islandora/object/%islandora_tokened_object/datastream/%islandora_tokened_datastream/view'] = array( 'title' => 'View datastream', + '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', @@ -518,6 +521,50 @@ function islandora_object_load($object_id) { return NULL; } +/** + * A helper function to get a connection and return an object using a token + * for authentication. + * + * @param string $object_id + * The PID of an object in the menu path identified by + * '%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 + */ +function islandora_tokened_object_load($object_id, $map) { + if (array_key_exists('token', $_GET)) { + $token = $_GET['token']; + if ($token) { + module_load_include('inc', 'islandora', 'includes/islandora_authtokens'); + $token_user = islandora_validate_object_token($object_id, $map[4], $token); + islandora_get_tuque_connection($user = $token_user); + } + } + return islandora_object_load($object_id); +} + +/** + * 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. + * + * @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 + */ +function islandora_tokened_datastream_load($datastream_id, $map) { + return islandora_datastream_load($datastream_id, $map[2]); +} + /** * A helper function to get an datastream specified as '%islandora_datastream' * for the object specified in the menu path as '%islandora_object'. @@ -526,11 +573,13 @@ function islandora_object_load($object_id) { * drupal_access_denied() when appropriate. * * @param string $datastream_id - * The dsid of the datastream specified as '%islandora_datastream' to fetch + * The DSID of the datastream specified as '%islandora_datastream' to fetch * from the given object in the menu path identified by '%islandora_object'. * - * $param string $object_id - * The object to load the datastream from. + * @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() + * returning the PID. * * @return FedoraDatastream * If the given datastream ID exists then this returns a FedoraDatastream From bf3945ecf84e5f1b226b66b350726d3fdd35ddac Mon Sep 17 00:00:00 2001 From: William Panting Date: Mon, 10 Dec 2012 09:36:01 -0400 Subject: [PATCH 5/9] more secure authentication tokens and better docs --- includes/islandora_authtokens.inc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/includes/islandora_authtokens.inc b/includes/islandora_authtokens.inc index 7acdb898..44b02578 100644 --- a/includes/islandora_authtokens.inc +++ b/includes/islandora_authtokens.inc @@ -6,8 +6,9 @@ * such as Djatoka that do not pass through credentials. */ -// Token lifespan: after this duration the token expires. -define('TOKEN_TIMEOUT', 30000); +// Token lifespan(seconds): after this duration the token expires. +// 5 minutes. +define('TOKEN_TIMEOUT', 300); /** * Request Islandora to construct an object/datastream authentication token. @@ -30,7 +31,11 @@ define('TOKEN_TIMEOUT', 30000); function islandora_get_object_token($pid, $dsid, $uses = 1) { global $user; $time = time(); - $token = hash("sha256", mt_rand()); + // The function mt_rand is not considered cryptographically secure + // and openssl_rando_pseudo_bytes() is only available in PHP > 5.3. + // We might be safe in this case because mt_rand should never be using + // the same seed, but this is still more secure. + $token = hash("sha256", mt_rand() . $time); $id = db_insert("islandora_authtokens")->fields( array( From b25d691d8da3d0e759d54e557d066d7dc81180d8 Mon Sep 17 00:00:00 2001 From: William Panting Date: Mon, 10 Dec 2012 10:18:22 -0400 Subject: [PATCH 6/9] more secure, more documented tokens --- islandora.module | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/islandora.module b/islandora.module index 797e183f..388b2895 100644 --- a/islandora.module +++ b/islandora.module @@ -536,7 +536,7 @@ function islandora_object_load($object_id) { */ function islandora_tokened_object_load($object_id, $map) { if (array_key_exists('token', $_GET)) { - $token = $_GET['token']; + $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING); if ($token) { module_load_include('inc', 'islandora', 'includes/islandora_authtokens'); $token_user = islandora_validate_object_token($object_id, $map[4], $token); @@ -550,7 +550,12 @@ 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 * the function islandora_tokened_object_load needs DSID. It uses - * the path %map to avoid duplicate parameters. + * 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: + * 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 From c6e35cb8b57bf5f4fd7c016e7560c2025a85b7c5 Mon Sep 17 00:00:00 2001 From: William Panting Date: Mon, 10 Dec 2012 12:27:58 -0400 Subject: [PATCH 7/9] better docs, implemented cron to remove expired authentication tokens from the database --- README | 8 ++++++++ includes/islandora_authtokens.inc | 10 ++++++++++ islandora.module | 10 ++++++++++ 3 files changed, 28 insertions(+) diff --git a/README b/README index 1a3479e8..c9784a95 100644 --- a/README +++ b/README @@ -38,6 +38,14 @@ INSTALLATION CONFIGURATION ------------- +The islandora_drupal_filter passes the username of 'anonymous' through to +Fedora for unauthenticated Drupal Users. A user with the name of 'anonymous' +may have XACML policies applied to them that are meant to be applied to Drupal +users that are not logged in or vice-versa. This is a potential security issue +that can be plugged by creating a user named 'anonymous' and restricting access +to the account. + +Drupal's cron will can be ran to remove expired authentication tokens. CUSTOMIZATION ------------- diff --git a/includes/islandora_authtokens.inc b/includes/islandora_authtokens.inc index 44b02578..f7861a0b 100644 --- a/includes/islandora_authtokens.inc +++ b/includes/islandora_authtokens.inc @@ -110,3 +110,13 @@ function islandora_validate_object_token($pid, $dsid, $token) { return FALSE; } } + +/** + * Will remove any expired authentication tokens. + */ +function islandora_remove_expired_tokens() { + $time = time(); + db_delete("islandora_authtokens") + ->condition('time', $time - TOKEN_TIMEOUT, '<') + ->execute(); +} diff --git a/islandora.module b/islandora.module index 388b2895..a67efcf3 100644 --- a/islandora.module +++ b/islandora.module @@ -872,3 +872,13 @@ function islandora_post_delete_datastream(FedoraObject $object, $datastream_id) module_invoke_all($hook, $object, $datastream_id); } } + +/** + * Implements hook_cron() + * + * Removes expired authentication tokens. + */ +function islandora_cron() { + module_load_include('inc', 'islandora', 'includes/islandora_authtokens'); + islandora_remove_expired_tokens(); +} From d0de6d6b9096fe57162a5846d6902383e1577979 Mon Sep 17 00:00:00 2001 From: jonathangreen Date: Tue, 11 Dec 2012 13:52:38 -0400 Subject: [PATCH 8/9] 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); } From 6c0a918291e2f39200591c1f807dbd00543dd50d Mon Sep 17 00:00:00 2001 From: William Panting Date: Tue, 11 Dec 2012 16:59:23 -0400 Subject: [PATCH 9/9] islandora_check_object_status now handling Fedora's whitespacing --- includes/solution_packs.inc | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index 6b5310e2..21e22619 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -253,8 +253,7 @@ function islandora_install_solution_pack($module, $op = 'install') { return; } $connection = islandora_get_tuque_connection(); - $islandora_required_objects = $module . '_islandora_required_objects'; - $required_objects = $islandora_required_objects($connection); + $required_objects = module_invoke($module, 'islandora_required_objects', $connection); $objects = $required_objects[$module]['objects']; $status_messages = array( 'up_to_date' => 'The object already exists and is up-to-date', @@ -322,8 +321,7 @@ function islandora_uninstall_solution_pack($module) { return; } $connection = islandora_get_tuque_connection(); - $islandora_required_objects = $module . '_islandora_required_objects'; - $required_objects = $islandora_required_objects($connection); + $required_objects = module_invoke($module, 'islandora_required_objects', $connection); $objects = $required_objects[$module]['objects']; $existing_objects = array_filter($objects, function($o) use($connection) { $param = "pid={$o->id}"; @@ -379,7 +377,8 @@ function islandora_check_object_status(NewFedoraObject $object_definition) { $out_of_date_datastreams[] = $ds->id; } } - if(count($out_of_date_datastreams)) { + + 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); } @@ -388,31 +387,36 @@ function islandora_check_object_status(NewFedoraObject $object_definition) { // 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 ($ds->mimetype == 'text/xml' + || $ds->mimetype == 'application/rdf+xml' + || $ds->mimetype == 'application/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. + // C14N also doesn't normalize whitespace at the end of lines and Fedora + // may add some whitespace on some lines. $object_definition_dom = new DOMDocument(); + $object_definition_dom->preserveWhiteSpace = FALSE; $object_definition_dom->loadXML(str_replace('info:', 'http://', $ds->content)); $object_actual_dom = new DOMDocument(); + $object_actual_dom->preserveWhiteSpace = FALSE; $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()) { + // Fedora changes the xml structure so we need to cannonize it. + 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) { + if ($object_definition_hash != $object_actual_hash) { $modified_datastreams[] = $ds->id;; } } } - if(count($modified_datastreams)) { + 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); }