diff --git a/.travis.yml b/.travis.yml index adedf7b1..17a22707 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,10 @@ php: - 5.4 branches: only: - - 7.x + - /^7.x/ env: - FEDORA_VERSION="3.5" + - FEDORA_VERSION="3.6.2" before_install: - export ISLANDORA_DIR=$TRAVIS_BUILD_DIR - $TRAVIS_BUILD_DIR/tests/scripts/travis_setup.sh diff --git a/includes/admin.form.inc b/includes/admin.form.inc index a1f22652..d34bd5d5 100644 --- a/includes/admin.form.inc +++ b/includes/admin.form.inc @@ -19,139 +19,111 @@ function islandora_repository_admin(array $form, array &$form_state) { module_load_include('inc', 'islandora', 'includes/utilities'); drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css'); + $url = islandora_system_settings_form_default_value('islandora_base_url', 'http://localhost:8080/fedora', $form_state); + $restrict_namespaces = islandora_system_settings_form_default_value('islandora_namespace_restriction_enforced', FALSE, $form_state); + $confirmation_message = islandora_admin_settings_form_repository_access_message($url); + $form = array( + 'islandora_tabs' => array( + '#type' => 'vertical_tabs', + 'islandora_general' => array( + '#type' => 'fieldset', + '#title' => t('General Configuarion'), + 'wrapper' => array( + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'markup', + 'islandora_base_url' => array( + '#type' => 'textfield', + '#title' => t('Fedora base URL'), + '#default_value' => variable_get('islandora_base_url', 'http://localhost:8080/fedora'), + '#description' => t('The URL to use for REST connections
!confirmation_message', array( + '!confirmation_message' => $confirmation_message)), + '#required' => TRUE, + '#ajax' => array( + 'callback' => 'islandora_update_url_div', + 'wrapper' => 'islandora-url', + 'effect' => 'fade', + 'event' => 'blur', + 'progress' => array('type' => 'throbber'), + ), + ), + ), + 'islandora_repository_pid' => array( + '#type' => 'textfield', + '#title' => t('Root Collection PID'), + '#default_value' => variable_get('islandora_repository_pid', 'islandora:root'), + '#description' => t('The PID of the Root Collection Object'), + '#required' => TRUE, + ), + ), + 'islandora_namespace' => array( + '#type' => 'fieldset', + '#title' => t('Namespaces'), + 'islandora_namespace_restriction_enforced' => array( + '#type' => 'checkbox', + '#title' => t('Enforce namespace restrictions'), + '#description' => t("Allow administrator to restrict user's access to the PID namepaces listed below"), + '#default_value' => $restrict_namespaces, + ), + 'islandora_pids_allowed' => array( + '#type' => 'textfield', + '#title' => t('PID namespaces allowed in this Drupal install'), + '#description' => t('A list of PID namespaces, separated by spaces, that users are permitted to access from this Drupal installation.
This could be more than a simple namespace, e.g. demo:mydemos.
The namespace islandora: is reserved, and is always allowed.'), + '#default_value' => variable_get('islandora_pids_allowed', 'default: demo: changeme: ilives: islandora-book: books: newspapers: '), + '#states' => array( + 'invisible' => array( + ':input[name="islandora_namespace_restriction_enforced"]' => array('checked' => FALSE), + ), + ), + ), + ), + ), + ); + return system_settings_form($form); +} - $form = array(); - if (isset($form_state['values']['islandora_base_url'])) { - $url = $form_state['values']['islandora_base_url']; - } - else { - $url = variable_get('islandora_base_url', 'http://localhost:8080/fedora'); - } - +/** + * Gets a message which describes if the repository is accessible. + * + * Also describes if the user is considered an authenticated user by the + * repository. + * + * @param string $url + * The url to the Fedora Repository. + * + * @return string + * A message describing the accessibility of the repository at the given url. + */ +function islandora_admin_settings_form_repository_access_message($url) { + $info = $dc = FALSE; $connection = islandora_get_tuque_connection(NULL, $url); - if (!$connection) { - return; - } - - try { - $info = $connection->api->a->describeRepository(); - } - catch (RepositoryException $e) { - $info = FALSE; - } - - if ($info) { + if ($connection) { try { + $info = $connection->api->a->describeRepository(); + // If we are able to successfully call API-M::getDatastream, assume we are + // an authenticated user, as API-M is usally locked down. $dc = $connection->api->m->getDatastream('fedora-system:ContentModel-3.0', 'DC'); } catch (RepositoryException $e) { - $dc = FALSE; + // Ignore, we only testing to see what is accessible. } } - - if ($info) { - if ($dc) { - $confirmation_message = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array())); - $confirmation_message .= t('Successfully connected to Fedora Server (Version !version).', array( - '!version' => $info['repositoryVersion'])); - } - else { - $confirmation_message = theme_image(array('path' => 'misc/watchdog-warning.png', 'attributes' => array())); - $confirmation_message .= t('Unable to authenticate when connecting to to Fedora Server (Version !version). Please configure the !filter.', - array('!version' => $info['repositoryVersion'], '!filter' => 'Drupal Filter')); - } + if ($info && $dc) { + $confirmation_message = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array())); + $confirmation_message .= t('Successfully connected to Fedora Server (Version !version).', array( + '!version' => $info['repositoryVersion'])); + } + elseif ($info) { + $confirmation_message = theme_image(array('path' => 'misc/watchdog-warning.png', 'attributes' => array())); + $confirmation_message .= t('Unable to authenticate when connecting to to Fedora Server (Version !version). Please configure the !filter.', array( + '!version' => $info['repositoryVersion'], '!filter' => 'Drupal Filter')); } else { $confirmation_message = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())); $confirmation_message .= t('Unable to connect to Fedora server at !islandora_url', array( '!islandora_url' => $url)); } - - $form['islandora_tabs'] = array( - '#type' => 'vertical_tabs', - ); - - $form['islandora_tabs']['islandora_general'] = array( - '#type' => 'fieldset', - '#title' => t('General Configuarion'), - ); - - // Ajax wrapper for url checking. - $form['islandora_tabs']['islandora_general']['wrapper'] = array( - '#prefix' => '
', - '#suffix' => '
', - '#type' => 'markup', - ); - - $form['islandora_tabs']['islandora_general']['wrapper']['islandora_base_url'] = array( - '#type' => 'textfield', - '#title' => t('Fedora base URL'), - '#default_value' => variable_get('islandora_base_url', 'http://localhost:8080/fedora'), - '#description' => t('The URL to use for REST connections
!confirmation_message', array( - '!confirmation_message' => $confirmation_message, - )), - '#required' => TRUE, - '#ajax' => array( - 'callback' => 'islandora_update_url_div', - 'wrapper' => 'islandora-url', - 'effect' => 'fade', - 'event' => 'blur', - 'progress' => array('type' => 'throbber'), - ), - ); - - $form['islandora_tabs']['islandora_general']['islandora_repository_pid'] = array( - '#type' => 'textfield', - '#title' => t('Root Collection PID'), - '#default_value' => variable_get('islandora_repository_pid', 'islandora:root'), - '#description' => t('The PID of the Root Collection Object'), - '#required' => TRUE, - ); - - $form['islandora_tabs']['islandora_namespace'] = array( - '#type' => 'fieldset', - '#title' => t('Namespaces'), - ); - - $form['islandora_tabs']['islandora_namespace']['wrapper'] = array( - '#type' => 'markup', - '#prefix' => '
', - '#suffix' => '
', - ); - - $form['islandora_tabs']['islandora_namespace']['wrapper']['islandora_namespace_restriction_enforced'] = array( - '#weight' => -1, - '#type' => 'checkbox', - '#title' => t('Enforce namespace restrictions'), - '#description' => t("Allow administrator to restrict user's access to the PID namepaces listed below"), - '#default_value' => variable_get('islandora_namespace_restriction_enforced', FALSE), - '#ajax' => array( - 'callback' => 'islandora_update_namespace_div', - 'wrapper' => 'islandora-namespace', - 'effect' => 'fade', - 'event' => 'change', - 'progress' => array('type' => 'throbber'), - ), - ); - - if (isset($form_state['values']['islandora_namespace_restriction_enforced'])) { - $namespaces = $form_state['values']['islandora_namespace_restriction_enforced']; - } - else { - $namespaces = variable_get('islandora_namespace_restriction_enforced', FALSE); - } - - if ($namespaces) { - $form['islandora_tabs']['islandora_namespace']['wrapper']['islandora_pids_allowed'] = array( - '#type' => 'textfield', - '#title' => t('PID namespaces allowed in this Drupal install'), - '#default_value' => variable_get('islandora_pids_allowed', 'default: demo: changeme: ilives: islandora-book: books: newspapers: '), - '#description' => t('A list of PID namespaces, separated by spaces, that users are permitted to access from this Drupal installation.
This could be more than a simple namespace, e.g. demo:mydemos.
The namespace islandora: is reserved, and is always allowed.'), - '#weight' => 0, - ); - } - - return system_settings_form($form); + return $confirmation_message; } /** @@ -168,18 +140,3 @@ function islandora_repository_admin(array $form, array &$form_state) { function islandora_update_url_div(array $form, array $form_state) { return $form['islandora_tabs']['islandora_general']['wrapper']; } - -/** - * Get the element to render for the AJAX event that triggered this callback. - * - * @param array $form - * The Drupal form definition. - * @param array $form_state - * The Drupal form state. - * - * @return array - * The form element to render. - */ -function islandora_update_namespace_div(array $form, array $form_state) { - return $form['islandora_tabs']['islandora_namespace']['wrapper']; -} diff --git a/includes/dublin_core.inc b/includes/dublin_core.inc index 14d25392..ec058181 100644 --- a/includes/dublin_core.inc +++ b/includes/dublin_core.inc @@ -133,6 +133,7 @@ class DublinCore { $dc_array[$field]['label'] = $element_label; $dc_array[$field]['value'] = $value; $dc_array[$field]['class'] = drupal_strtolower(preg_replace('/[^A-Za-z0-9]/', '-', $field)); + $dc_array[$field]['dcterms'] = preg_replace('/^dc/', 'dcterms', $field); } } } diff --git a/includes/ingest.form.inc b/includes/ingest.form.inc index 00783934..6faa642c 100644 --- a/includes/ingest.form.inc +++ b/includes/ingest.form.inc @@ -703,7 +703,7 @@ function islandora_ingest_form_submit(array $form, array &$form_state) { islandora_ingest_form_execute_consecutive_callback_steps($form, $form_state, $step); } // Ingest the objects. - foreach ($form_state['islandora']['objects'] as $object) { + foreach ($form_state['islandora']['objects'] as &$object) { try { islandora_add_object($object); $form_state['redirect'] = "islandora/object/{$object->id}"; @@ -715,6 +715,9 @@ function islandora_ingest_form_submit(array $form, array &$form_state) { drupal_set_message(t('A problem occured while ingesting "@label" (ID: @pid), please notifiy the administrator.', array('@label' => $object->label, '@pid' => $object->id)), 'error'); } } + // XXX: Foreaching with references can be weird... The reference exists in + // the scope outside. + unset($object); } /** diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index 26bd77b5..e1cf9493 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -5,11 +5,55 @@ * Admin and callback functions for solution pack management. */ +/** + * Get the information about required object. + * + * @param string $module + * An optional string, identifying a module for which to get the required + * object information. + * + * @return array + * An associative array of info describing the required objects. If $module + * is not provided (or is NULL), then we provide the info for all modules. If + * $module is provided and we have info for the given module, only the info + * for that module is provided. If $module is provided and we have no info + * for the given module, we throw an exception. + */ +function islandora_solution_packs_get_required_objects($module = NULL) { + // Should make this statically cache, after figuring out how exactly it + // should be called... We occasionally load a module and attempt to install + // it's object right away (in the same request)... This would require + // resetting of the cache. Let's just not cache for now... + $required_objects = array(); + + if (!$required_objects) { + $connection = islandora_get_tuque_connection(); + $required_objects = module_invoke_all('islandora_required_objects', $connection); + } + + if ($module !== NULL) { + if (isset($required_objects[$module])) { + return $required_objects[$module]; + } + else { + watchdog('islandora', 'Attempted to get required objects for %module... %module does not appear to have any required objects. Clear caches?', array( + '%module' => $module, + )); + throw new Exception(t('Module "@module" has no required objects!', array( + '@module' => $module, + ))); + } + } + else { + return $required_objects; + } +} + /** * Solution pack admin page callback. * - * @return string - * The html repersentation of all solution pack forms for required objects. + * @return array + * Renderable array of all solution pack forms for required objects. */ function islandora_solution_packs_admin() { module_load_include('inc', 'islandora', 'includes/utilities'); @@ -18,17 +62,15 @@ function islandora_solution_packs_admin() { 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); + $output = array(); + $enabled_solution_packs = islandora_solution_packs_get_required_objects(); foreach ($enabled_solution_packs as $solution_pack_module => $solution_pack_info) { // @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); + $output[$solution_pack_module] = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects); } return $output; } @@ -161,20 +203,38 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solution */ function islandora_solution_pack_form_submit(array $form, array &$form_state) { $solution_pack_module = $form_state['values']['solution_pack_module']; - $objects = $form_state['values']['objects']; + + $batch = islandora_solution_pack_get_batch($solution_pack_module); + 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); +} + +/** + * Get the batch definition to reinstall all the objects for a given module. + * + * @param string $module + * The name of the modules of which to grab the required objects for to setup + * the batch. + * + * @return array + * An array defining a batch which can be passed on to batch_set(). + */ +function islandora_solution_pack_get_batch($module) { $batch = array( 'title' => t('Installing / Updating solution pack objects'), 'file' => drupal_get_path('module', 'islandora') . '/includes/solution_packs.inc', 'operations' => array(), ); - foreach ($objects as $object) { + + $info = islandora_solution_packs_get_required_objects($module); + foreach ($info['objects'] as $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); + + return $batch; } /** @@ -245,14 +305,16 @@ function islandora_solution_pack_batch_operation_reingest_object(AbstractObject * install/unistall hooks. * @param string $op * The operation to perform, either install or uninstall. + * @param bool $force + * Force the (un)installation of object. * * @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_install_solution_pack($module, $op = 'install') { +function islandora_install_solution_pack($module, $op = 'install', $force = FALSE) { if ($op == 'uninstall') { - islandora_uninstall_solution_pack($module); + islandora_uninstall_solution_pack($module, $force); return; } @@ -267,9 +329,9 @@ function islandora_install_solution_pack($module, $op = 'install') { '!admin_link' => $admin_link, ); - module_load_include('module', 'islandora', 'islandora'); + drupal_load('module', 'islandora'); module_load_include('inc', 'islandora', 'includes/utilities'); - module_load_include('module', $module, $module); + drupal_load('module', $module); $info_file = drupal_get_path('module', $module) . "/{$module}.info"; $info_array = drupal_parse_info_file($info_file); $module_name = $info_array['name']; @@ -279,8 +341,8 @@ function islandora_install_solution_pack($module, $op = 'install') { return; } $connection = islandora_get_tuque_connection(); - $required_objects = module_invoke($module, 'islandora_required_objects', $connection); - $objects = $required_objects[$module]['objects']; + $required_objects = islandora_solution_packs_get_required_objects($module); + $objects = $required_objects['objects']; $status_messages = array( 'up_to_date' => $t('The object already exists and is up-to-date.', $t_params), 'missing_datastream' => $t('The object already exists but is missing a datastream. Please reinstall the object on the !admin_link page.', $t_params), @@ -288,26 +350,41 @@ function islandora_install_solution_pack($module, $op = 'install') { 'modified_datastream' => $t('The object already exists but datastreams are modified. Please reinstall the object on the !admin_link page.', $t_params), ); foreach ($objects as $object) { - $query = $connection->api->a->findObjects('query', 'pid=' . $object->id); - $already_exists = !empty($query['results']); + $already_exists = islandora_object_load($object->id); + $label = $object->label; $object_link = l($label, "islandora/object/{$object->id}"); + $deleted = FALSE; if ($already_exists) { - $object_status = islandora_check_object_status($object); - $here_params = array( - '!summary' => $t("@module: Did not install !object_link.", array( - '!object_link' => $object_link, - ) + $t_params), - '!description' => $status_messages[$object_status['status']], - ); - drupal_set_message(filter_xss(format_string('!summary !description', $here_params)), 'warning'); + if (!$force) { + $object_status = islandora_check_object_status($object); + $here_params = array( + '!summary' => $t("@module: Did not install !object_link.", array( + '!object_link' => $object_link, + ) + $t_params), + '!description' => $status_messages[$object_status['status']], + ); + drupal_set_message(filter_xss(format_string('!summary !description', $here_params)), 'warning'); + continue; + } + else { + $deleted = islandora_delete_object($already_exists); + } } - else { + + if ($already_exists && $deleted || !$already_exists) { $object = islandora_add_object($object); if ($object) { - drupal_set_message(filter_xss($t('@module: Successfully installed. !object_link.', array( - '!object_link' => $object_link, - ) + $t_params)), 'status'); + if ($deleted) { + drupal_set_message(filter_xss($t('@module: Successfully reinstalled. !object_link.', array( + '!object_link' => $object_link, + ) + $t_params)), 'status'); + } + else { + drupal_set_message(filter_xss($t('@module: Successfully installed. !object_link.', array( + '!object_link' => $object_link, + ) + $t_params)), 'status'); + } } else { drupal_set_message($t('@module: Failed to install. @label.', array( @@ -315,6 +392,11 @@ function islandora_install_solution_pack($module, $op = 'install') { ) + $t_params), 'warning'); } } + else { + drupal_set_message($t('@module: "@label" already exists and failed to be deleted.', array( + '@label' => $label, + ) + $t_params), 'warning'); + } } } @@ -323,15 +405,17 @@ function islandora_install_solution_pack($module, $op = 'install') { * * @param string $module * The solution pack to uninstall. + * @param bool $force + * Force the objects to be removed. * * @todo Implement hook_modules_uninstalled instead of calling this function * directly for each solution pack. */ -function islandora_uninstall_solution_pack($module) { +function islandora_uninstall_solution_pack($module, $force = FALSE) { $t = get_t(); - module_load_include('module', 'islandora', 'islandora'); + drupal_load('module', 'islandora'); module_load_include('inc', 'islandora', 'includes/utilities'); - module_load_include('module', $module, $module); + drupal_load('module', $module); $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); @@ -345,22 +429,35 @@ function islandora_uninstall_solution_pack($module) { return; } $connection = islandora_get_tuque_connection(); - $required_objects = module_invoke($module, 'islandora_required_objects', $connection); - $objects = $required_objects[$module]['objects']; + $required_objects = islandora_solution_packs_get_required_objects($module); + $objects = $required_objects['objects']; $filter = function($o) use($connection) { $param = "pid={$o->id}"; $query = $connection->api->a->findObjects('query', $param); return !empty($query['results']); }; $existing_objects = array_filter($objects, $filter); - foreach ($existing_objects as $object) { - $object_link = l($object->label, "islandora/object/{$object->id}"); - $msg = $t('@module: Did not remove !object_link. It may be used by other sites.', array( - '!object_link' => $object_link, - '@module' => $module_name, - )); - drupal_set_message(filter_xss($msg), 'warning'); + if (!$force) { + foreach ($existing_objects as $object) { + $object_link = l($object->label, "islandora/object/{$object->id}"); + $msg = $t('@module: Did not remove !object_link. It may be used by other sites.', array( + '!object_link' => $object_link, + '@module' => $module_name, + )); + + drupal_set_message(filter_xss($msg), 'warning'); + } + } + else { + foreach ($existing_objects as $object) { + $params = array( + '@id' => $object->id, + '@module' => $module_name, + ); + islandora_delete_object($object); + drupal_set_message($t('@module: Deleted @id.', $params)); + } } } @@ -430,13 +527,21 @@ function islandora_check_object_status(AbstractObject $object_definition) { // 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. + // will sometimes replace new-lines with white-space. So first we strip + // leading/tailing white-space and replace all new-lines within the xml + // document to account for Fedora's weird formatting. + $xsl = new DOMDocument(); + $xsl->load(drupal_get_path('module', 'islandora') . '/xml/strip_newlines_and_whitespace.xsl'); + $xslt = new XSLTProcessor(); + $xslt->importStyleSheet($xsl); $object_definition_dom = new DOMDocument(); $object_definition_dom->preserveWhiteSpace = FALSE; $object_definition_dom->loadXML(str_replace('info:', 'http://', $ds->content)); + $object_definition_dom = $xslt->transformToDoc($object_definition_dom); $object_actual_dom = new DOMDocument(); $object_actual_dom->preserveWhiteSpace = FALSE; $object_actual_dom->loadXML(str_replace('info:', 'http://', $existing_object[$ds->id]->content)); + $object_actual_dom = $xslt->transformToDoc($object_actual_dom); // Fedora changes the xml structure so we need to cannonize it. if ($object_actual_dom->C14N() != $object_definition_dom->C14N()) { diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 8563070f..96f9919c 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -82,13 +82,26 @@ class IslandoraFedoraRepository extends FedoraRepository { * @see FedoraRepository::ingestObject() */ public function ingestObject(NewFedoraObject &$object) { - $context = array( - 'action' => 'ingest', - 'block' => FALSE, - ); - islandora_alter_object($object, $context); try { - if ($context['block']) { + foreach ($object as $dsid => $datastream) { + $datastream_context = array( + 'action' => 'ingest', + 'block' => FALSE, + ); + islandora_alter_datastream($object, $datastream, $datastream_context); + if ($datastream_context['block']) { + throw new Exception(t('Object ingest blocked due to ingest of @dsid being blocked.', array( + '@dsid' => $dsid, + ))); + } + } + + $object_context = array( + 'action' => 'ingest', + 'block' => FALSE, + ); + islandora_alter_object($object, $object_context); + if ($object_context['block']) { throw new Exception('Ingest Object was blocked.'); } $ret = parent::ingestObject($object); @@ -194,6 +207,9 @@ class IslandoraFedoraApiM extends FedoraApiM { 'params' => $params, ); islandora_alter_datastream($object, $datastream, $context); + if (isset($params['lastModifiedDate'])) { + $params['lastModifiedDate'] = (string) $object[$dsid]->createdDate; + } try { if ($context['block']) { throw new Exception('Modify Datastream was blocked.'); @@ -269,7 +285,6 @@ class IslandoraFedoraApiM extends FedoraApiM { switch ($action) { case 'block': throw new Exception('Purge Datastream was blocked.'); - break; case 'delete': $object[$dsid]->state = 'D'; @@ -313,7 +328,6 @@ class IslandoraFedoraApiM extends FedoraApiM { switch ($action) { case 'block': throw new Exception('Purge object was blocked.'); - break; case 'delete': $object->state = 'D'; diff --git a/includes/utilities.inc b/includes/utilities.inc index 66c03d26..5ce2ca14 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -865,3 +865,48 @@ function islandora_deprecated($release, $solution = NULL) { } return $message; } + +/** + * Transform recursively-merged array of strings to renderable arrays. + * + * Renderable arrays are passed-through as-is. + * + * Previously, functions/hooks like islandora_view_object would return an + * associative array with string values containing markup. These values were + * then imploded into one large piece of markup. Here, we transform this older + * structure which was generated into a renderable array, because renderable + * arrays are awesome! + * + * @param array $markup_array + * An associative array of which the values are either a string or an array + * of strings, which we transform to renderable markup elements (by + * reference). + */ +function islandora_as_renderable_array(&$markup_array) { + foreach ($markup_array as &$value) { + if (!is_array($value)) { + // Not a renderable array, just a string. Let's convert it to a + // renderable '#markup' element. + $value = array( + '#markup' => $value, + ); + } + elseif (!isset($value['#type']) && !isset($value['#markup'])) { + // A simple array--possibly the result of a recursive merge? Let's + // look at each, to possibly convert them to a renderable '#markup' + // elements. + foreach ($value as &$inner) { + if (!is_array($inner)) { + // If it is an array at this level, we can assume that it is a + // renderable array. If it is not an array, convert to a renderable + // '#markup' element. + $inner = array( + '#markup' => $inner, + ); + } + } + unset($inner); + } + } + unset($value); +} diff --git a/islandora.drush.inc b/islandora.drush.inc new file mode 100644 index 00000000..b2e75c47 --- /dev/null +++ b/islandora.drush.inc @@ -0,0 +1,142 @@ + dt('Install Solution Pack objects.'), + 'options' => array( + 'module' => array( + 'description' => dt('The module for which to install the required objects.'), + 'required' => TRUE, + ), + 'force' => array( + 'description' => dt('Force reinstallation of the objects.'), + ), + ), + 'aliases' => array('ispiro'), + 'drupal dependencies' => array( + 'islandora', + ), + 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN, + ); + $commands['islandora-solution-pack-uninstall-required-objects'] = array( + 'description' => dt('Uninstall Solution Pack objects.'), + 'options' => array( + 'module' => array( + 'description' => dt('The module for which to uninstall the required objects.'), + 'required' => TRUE, + ), + 'force' => array( + 'description' => dt('Force reinstallation of the objects.'), + ), + ), + 'aliases' => array('ispuro'), + 'drupal dependencies' => array( + 'islandora', + ), + 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN, + ); + $commands['islandora-solution-pack-required-objects-status'] = array( + 'description' => dt('Get Solution Pack object status.'), + 'options' => array( + 'module' => array( + 'description' => dt('The module for which to get the status of the required objects.'), + ), + ), + 'aliases' => array('ispros'), + 'drupal dependencies' => array( + 'islandora', + ), + 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN, + ); + + return $commands; +} + +/** + * Command callback to install required objects. + */ +function drush_islandora_solution_pack_install_required_objects() { + module_load_include('inc', 'islandora', 'includes/solution_packs'); + + $module = drush_get_option('module'); + if (module_exists($module)) { + islandora_install_solution_pack( + $module, + 'install', + drush_get_option('force', FALSE) + ); + } + else { + drush_log(dt('"@module" is not installed/enabled?...', array( + '@module' => $module, + ))); + } +} + +/** + * Command callback to uninstall required objects. + */ +function drush_islandora_solution_pack_uninstall_required_objects() { + module_load_include('inc', 'islandora', 'includes/solution_packs'); + + $module = drush_get_option('module'); + if (module_exists($module)) { + islandora_uninstall_solution_pack( + $module, + drush_get_option('force', FALSE) + ); + } + else { + drush_log(dt('"@module" is not installed/enabled?...', array( + '@module' => $module, + ))); + } +} + +/** + * Command callback for object status. + */ +function drush_islandora_solution_pack_required_objects_status() { + module_load_include('inc', 'islandora', 'includes/solution_packs'); + + $module = drush_get_option('module', FALSE); + $required_objects = array(); + if ($module && module_exists($module)) { + $required_objects[$module] = islandora_solution_packs_get_required_objects($module); + } + elseif ($module === FALSE) { + $required_objects = islandora_solution_packs_get_required_objects(); + } + else { + drush_log(dt('"@module" is not installed/enabled?...', array( + '@module' => $module, + ))); + return; + } + + $header = array('PID', 'Machine Status', 'Readable Status'); + $widths = array(30, 20, 20); + foreach ($required_objects as $module => $info) { + $rows = array(); + drush_print($info['title']); + foreach ($info['objects'] as $object) { + $status = islandora_check_object_status($object); + $rows[] = array( + $object->id, + $status['status'], + $status['status_friendly'], + ); + } + drush_print_table($rows, $header, $widths); + } +} diff --git a/islandora.module b/islandora.module index d1f15542..8a8ee6be 100644 --- a/islandora.module +++ b/islandora.module @@ -668,6 +668,7 @@ function islandora_manage_overview_object(AbstractObject $object) { $output = array(); foreach (islandora_build_hook_list(ISLANDORA_OVERVIEW_HOOK, $object->models) as $hook) { $temp = module_invoke_all($hook, $object); + islandora_as_renderable_array($temp); if (!empty($temp)) { $output = array_merge_recursive($output, $temp); } @@ -678,7 +679,8 @@ function islandora_manage_overview_object(AbstractObject $object) { } arsort($output); drupal_alter(ISLANDORA_OVERVIEW_HOOK, $object, $output); - return implode('', $output); + islandora_as_renderable_array($output); + return $output; } /** @@ -693,7 +695,11 @@ function islandora_manage_overview_object(AbstractObject $object) { */ function islandora_default_islandora_manage_overview_object(AbstractObject $object) { $output = theme('islandora_default_overview', array('islandora_object' => $object)); - return array('Default overview output' => $output); + return array( + 'Default overview output' => array( + '#markup' => $output, + ), + ); } /** @@ -719,6 +725,7 @@ function islandora_edit_object(AbstractObject $object) { $output = array(); foreach (islandora_build_hook_list(ISLANDORA_EDIT_HOOK, $object->models) as $hook) { $temp = module_invoke_all($hook, $object); + islandora_as_renderable_array($temp); if (!empty($temp)) { $output = array_merge_recursive($output, $temp); } @@ -729,7 +736,8 @@ function islandora_edit_object(AbstractObject $object) { } arsort($output); drupal_alter(ISLANDORA_EDIT_HOOK, $object, $output); - return implode('', $output); + islandora_as_renderable_array($output); + return $output; } /** @@ -744,7 +752,11 @@ function islandora_edit_object(AbstractObject $object) { */ function islandora_default_islandora_edit_object(AbstractObject $object) { $output = theme('islandora_default_edit', array('islandora_object' => $object)); - return array('Default Edit output' => $output); + return array( + 'Default Edit output' => array( + '#markup' => $output, + ), + ); } /** @@ -787,6 +799,7 @@ function islandora_view_object(AbstractObject $object) { // @todo Remove page number and size from this hook, implementers of the // hook should use drupal page handling directly. $temp = module_invoke_all($hook, $object, $page_number, $page_size); + islandora_as_renderable_array($temp); if (!empty($temp)) { $output = array_merge_recursive($output, $temp); } @@ -797,7 +810,8 @@ function islandora_view_object(AbstractObject $object) { } arsort($output); drupal_alter($hooks, $object, $output); - return implode('', $output); + islandora_as_renderable_array($output); + return $output; } @@ -830,7 +844,11 @@ function islandora_drupal_title(AbstractObject $object) { */ function islandora_default_islandora_view_object($object) { $output = theme('islandora_default', array('islandora_object' => $object)); - return array('Default output' => $output); + return array( + 'Default output' => array( + '#markup' => $output, + ), + ); } /** diff --git a/tests/hooks.test b/tests/hooks.test index 3133d656..d08c537b 100644 --- a/tests/hooks.test +++ b/tests/hooks.test @@ -75,6 +75,8 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { /** * Test ALL THE HOOKS!. + * + * Covers the majority of cases... */ public function testHooks() { // Test ingesting with FedoraRepository::ingestObject(). @@ -85,6 +87,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called "hook_islandora_object_alter" when ingesting via FedoraRepository::ingestObject.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called ISLANDORA_OBJECT_INGESTED_HOOK when ingesting via FedoraRepository::ingestObject.'); $this->repository->purgeObject($object->id); + // Test blocking the ingest. $object = $this->repository->constructObject('test:testIngestedObjectHook'); $object->label = 'block'; @@ -100,6 +103,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called "hook_islandora_object_alter" when blocking ingesting via FedoraRepository::ingestObject.'); $this->assertFalse($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Did not called ISLANDORA_OBJECT_INGESTED_HOOK when blocking ingesting via FedoraRepository::ingestObject.'); } + // Test modifying via set magic functions. $object = $this->repository->constructObject('test:testModifiedObjectHook'); $this->repository->ingestObject($object); @@ -108,6 +112,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $object->label = "New Label!"; $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called "hook_islandora_object_alter" when modifying via set magic functions.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called ISLANDORA_OBJECT_MODIFIED_HOOK when modifying via set magic functions.'); + // Test blocking the modification. try { $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE; @@ -122,6 +127,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assertFALSE($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called ISLANDORA_OBJECT_MODIFIED_HOOK when blocking modifying via set magic functions.'); } $this->repository->purgeObject($object->id); + // Test purging with FedoraRepository::purgeObject(). $object = $this->repository->constructObject('test:testPurgedObjectHook'); $this->repository->ingestObject($object); @@ -130,6 +136,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->repository->purgeObject($object->id); $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when purging via FedoraRepository::purgeObject.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when purging via FedoraRepository::purgeObject.'); + // Test deleting. $object = $this->repository->constructObject('test:testPurgedObjectHook'); $this->repository->ingestObject($object); @@ -139,6 +146,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when deleting via FedoraObject::delete.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when purging via FedoraObject::delete.'); $this->repository->purgeObject($object->id); + // Test alter blocking. $object = $this->repository->constructObject('test:testPurgedObjectHook'); $this->repository->ingestObject($object); @@ -154,6 +162,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when blocking purge via FedoraRepository::purgeObject.'); $this->assertFalse($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when blocking purge via FedoraRepository::purgeObject.'); } + // Test alter delete. $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; @@ -164,6 +173,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when preventing purge and deleting.'); $object->label = 'Something other than delete'; $this->repository->purgeObject($object->id); + // Test ingesting with FedoraRepository::ingestObject(). $object = $this->repository->constructObject('test:testIngestedDatastreamHook'); $this->repository->ingestObject($object); @@ -174,6 +184,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called "hook_islandora_datastream_alter" when ingesting via FedoraObject::ingestDatastream.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called ISLANDORA_DATASTREAM_INGESTED_HOOK when ingesting via FedoraObject::ingestDatastream.'); $this->repository->purgeObject($object->id); + // Test modifying a datastream. $object = $this->repository->constructObject('test:testModifiedDatastreamHook'); $this->repository->ingestObject($object); @@ -184,6 +195,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $ds->label = "New Label!"; $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called "hook_islandora_datastream_alter" when modifying via set magic functions.'); $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when modifying via set magic functions.'); + // Test blocking modifying. try { $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE; @@ -197,6 +209,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->assertFALSE($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when blocking modifying via set magic functions.'); } $this->repository->purgeObject($object->id); + // Test purging with FedoraRepository::purgeObject(). $object = $this->repository->constructObject('test:testPurgedDatastreamHook'); $this->repository->ingestObject($object); @@ -210,4 +223,21 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->repository->purgeObject($object->id); } + /** + * Test ALL THE HOOKS!. + * + * Ensure hooks are triggered properly in "New" objects. + */ + public function testNewIngestHooks() { + // Test ingesting with FedoraRepository::ingestObject(). + $object = $this->repository->constructObject('test:testIngestedDatastreamHook'); + $ds = $object->constructDatastream('TEST'); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = FALSE; + $object->ingestDatastream($ds); + $this->repository->ingestObject($object); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called "hook_islandora_datastream_alter" when ingesting via FedoraObject::ingestDatastream.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called ISLANDORA_DATASTREAM_INGESTED_HOOK when ingesting via FedoraObject::ingestDatastream.'); + $this->repository->purgeObject($object->id); + } } diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 45dc3ab9..19c7db78 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash mysql -u root -e 'create database drupal;' mysql -u root -e "create database fedora;" @@ -11,8 +11,8 @@ cd islandora_tomcat export CATALINA_HOME='.' ./bin/startup.sh cd $HOME -pear upgrade –force Console_Getopt -pear upgrade –force pear +pear upgrade --force Console_Getopt +pear upgrade --force pear pear upgrade-all pear channel-discover pear.drush.org pear channel-discover pear.drush.org diff --git a/xml/strip_newlines_and_whitespace.xsl b/xml/strip_newlines_and_whitespace.xsl new file mode 100644 index 00000000..4d4e1057 --- /dev/null +++ b/xml/strip_newlines_and_whitespace.xsl @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file