diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..adedf7b1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: php +php: + - 5.3.3 + - 5.4 +branches: + only: + - 7.x +env: + - FEDORA_VERSION="3.5" +before_install: + - export ISLANDORA_DIR=$TRAVIS_BUILD_DIR + - $TRAVIS_BUILD_DIR/tests/scripts/travis_setup.sh + - cd $HOME/drupal-* +script: + - ant -buildfile sites/all/modules/islandora/build.xml lint + - $ISLANDORA_DIR/tests/scripts/line_endings.sh sites/all/modules/islandora + - drush coder-review --reviews=production,security,style,i18n,potx,sniffer islandora + - phpcpd --names *.module,*.inc,*.test sites/all/modules/islandora + - drush test-run --uri=http://localhost:8081 Islandora diff --git a/README.txt b/README.md similarity index 79% rename from README.txt rename to README.md index ce276d7b..0515a848 100644 --- a/README.txt +++ b/README.md @@ -1,16 +1,10 @@ -CONTENTS OF THIS FILE ---------------------- - - * summary - * requirements - * installation - * configuration - * customization - * troubleshooting - * faq - * contact - * sponsors +BUILD STATUS +------------ +Current build status: +[![Build Status](https://travis-ci.org/Islandora/islandora.png?branch=7.x)](https://travis-ci.org/Islandora/islandora) +CI Server: +http://jenkins.discoverygarden.ca SUMMARY ------- @@ -41,7 +35,7 @@ should be copied into the Fedora global XACML policies folder. This will allow CONFIGURATION ------------- -The islandora_drupal_filter passes the username of 'anonymous' through to +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 @@ -53,6 +47,7 @@ Drupal's cron will can be ran to remove expired authentication tokens. CUSTOMIZATION ------------- +[Customize ingest forms](http://github.com/Islandora/islandora/wiki/Multi-paged-Ingest-Forms) TROUBLESHOOTING --------------- diff --git a/build.xml b/build.xml index 854d3a0a..273f8ca8 100644 --- a/build.xml +++ b/build.xml @@ -1,110 +1,84 @@ - + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - + + + + + + + + + + - - - - - - - + + + + + - - - - - - - - + + + + + - - - - - - - - + + + + + - - - - - - - - - - + + + + + - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - + + + + + diff --git a/build/Doxyfile b/build/Doxyfile index 1d4e3693..f41dc285 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -27,6 +27,9 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Islandora 7.x +# Put the git hash here using sed before generating the documentation. +PROJECT_NUMBER = + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location diff --git a/css/islandora.objects.css b/css/islandora.objects.css new file mode 100644 index 00000000..5e03d3f7 --- /dev/null +++ b/css/islandora.objects.css @@ -0,0 +1,39 @@ +/** + * @file + * Styles for rendering grids/lists of objects. + */ +.islandora-objects-display-switch { + float: right; +} +.islandora-objects-grid-item { + display: inline-block; + width: 20%; + min-width: 100px; + min-height: 180px; + display: -moz-inline-stack; + display: inline-block; + vertical-align: top; + margin: 1.5em 1.84%; + zoom: 1; + *display: inline; + _height: 180px; +} +.islandora-objects-list-item { + padding-bottom: 1.5em; + border-bottom: 1px solid #ddd; +} +.islandora-objects-list-item .islandora-object-thumb { + clear: left; + float: left; + padding: 3px 0 0; + text-align: center; + width: 100px; +} +.islandora-objects-list-item .islandora-object-caption, .islandora-objects-list-item .islandora-object-description { + margin: 0 0 0 130px; + padding-top: 2px; + padding-bottom: 2px; +} +.islandora-object-thumb img { + width: 100%; +} diff --git a/includes/add_datastream.form.inc b/includes/add_datastream.form.inc index fa94148f..d19d5757 100644 --- a/includes/add_datastream.form.inc +++ b/includes/add_datastream.form.inc @@ -39,7 +39,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, FedoraOb '#collapsed' => FALSE, 'dsid' => array( '#title' => 'Datastream ID', - '#description' => t("An ID for this stream that is unique to this object. Must start with a letter and contain only alphanumeric characters and dashes and underscores. Datastreams that are defined by the content model don't currently exist: @unused_dsids.", array('@unused_dsids' => $unused_datastreams)), + '#description' => t("An ID for this stream that is unique to this object. Must start with a letter and contain only alphanumeric characters, dashes and underscores. The following datastreams are defined by this content model but don't currently exist: @unused_dsids.", array('@unused_dsids' => $unused_datastreams)), ), '#type' => 'textfield', '#size' => 64, @@ -57,7 +57,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, FedoraOb '#required' => TRUE, '#size' => 64, '#maxlength' => 64, - '#description' => t('A Human readable label'), + '#description' => t('A human-readable label'), '#type' => 'textfield', '#element_validate' => array('islandora_add_datastream_form_field_does_not_contain_a_forward_slash'), ), diff --git a/includes/admin.form.inc b/includes/admin.form.inc index d3bccd5a..a1f22652 100644 --- a/includes/admin.form.inc +++ b/includes/admin.form.inc @@ -146,7 +146,7 @@ function islandora_repository_admin(array $form, array &$form_state) { '#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 space separated list of PID namespaces that users are permitted to access from this Drupal installation.
This could be more than a simple namespace ie demo:mydemos.
islandora: is reserved and is always allowed.'), + '#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, ); } diff --git a/includes/breadcrumb.inc b/includes/breadcrumb.inc index dd8544a8..9db4824c 100644 --- a/includes/breadcrumb.inc +++ b/includes/breadcrumb.inc @@ -25,13 +25,8 @@ * drupal_set_breadcrumb(). */ function islandora_get_breadcrumbs($object) { - $breadcrumbs = array(); - islandora_get_breadcrumbs_recursive($object->id, $breadcrumbs, $object->repository); - if (isset($breadcrumbs[0])) { - // Remove the actual object. - unset($breadcrumbs[0]); - } - $breadcrumbs = array_reverse($breadcrumbs); + $breadcrumbs = islandora_get_breadcrumbs_recursive($object->id, $object->repository); + array_pop($breadcrumbs); return $breadcrumbs; } @@ -40,30 +35,33 @@ function islandora_get_breadcrumbs($object) { * * @todo Make fully recursive... * - * @todo Could use some clean up, can't be called multiple times safely due to - * the use of static variables. - * * @param string $pid - * THe object id whose parent will be fetched for the next link. - * @param array $breadcrumbs - * The list of existing bread-crumb links in reverse order. + * The object id whose parent will be fetched for the next link. * @param FedoraRepository $repository * The fedora repository. + * @param array $context + * An associative array of context for internal use when recursing. Currently + * only used to track a single value: + * - level: The number of child-parent relationships to follow. Defaults to + * 10. + * + * @return array + * An array of links representing the breadcrumb trail, "root" first. */ -function islandora_get_breadcrumbs_recursive($pid, array &$breadcrumbs, FedoraRepository $repository) { +function islandora_get_breadcrumbs_recursive($pid, FedoraRepository $repository, array &$context = NULL) { // Before executing the query, we have a base case of accessing the top-level // collection. - static $max_level = 10; - static $level = -1; - - if (count($breadcrumbs) === 0) { - $level = $max_level; + if ($context === NULL) { + $context['level'] = 10; } $root = variable_get('islandora_repository_pid', 'islandora:root'); if ($pid == $root) { - $breadcrumbs[] = l(menu_get_active_title(), 'islandora'); - $breadcrumbs[] = l(t('Home'), ''); + $item = menu_get_item('islandora'); + return array( + l(t('Home'), ''), + l($item['title'], 'islandora'), + ); } else { $query_string = 'select $parentObject $title $content from <#ri> @@ -82,7 +80,7 @@ function islandora_get_breadcrumbs_recursive($pid, array &$breadcrumbs, FedoraRe order by $title desc'; $results = $repository->ri->itqlQuery($query_string); - if (count($results) > 0 && $level > 0) { + if (count($results) > 0 && $context['level'] > 0) { $parent = $results[0]['parentObject']['value']; $this_title = $results[0]['title']['value']; @@ -90,16 +88,23 @@ function islandora_get_breadcrumbs_recursive($pid, array &$breadcrumbs, FedoraRe $this_title = t('-'); } - $breadcrumbs[] = l($this_title, "islandora/object/$pid"); - - $level--; - islandora_get_breadcrumbs_recursive($parent, $breadcrumbs, $repository); + $context['level']--; + return array_merge( + islandora_get_breadcrumbs_recursive($parent, $repository, $context), + array( + l($this_title, "islandora/object/$pid"), + ) + ); } else { - // Add an non-link, as we don't know how to get back to the root. - $breadcrumbs[] = '...'; - // And render the last two links and break (on the next pass). - islandora_get_breadcrumbs_recursive($root, $breadcrumbs, $repository); + // Add an non-link, as we don't know how to get back to the root, and + // render the last two links and break (on the next pass). + return array_merge( + islandora_get_breadcrumbs_recursive($root, $repository, $context), + array( + '...', + ) + ); } } } diff --git a/includes/ingest.form.inc b/includes/ingest.form.inc index 9434e9e4..5c9e3e55 100644 --- a/includes/ingest.form.inc +++ b/includes/ingest.form.inc @@ -26,6 +26,8 @@ * be related. * - models: An array of content model PIDs, to which the new object might * subscribe + * - parent: The parent of the child to be ingested. This is needed for XACML + * to correctly apply the parent's POLICY to children. * * @return array * The form definition of the current step. @@ -35,7 +37,7 @@ function islandora_ingest_form(array $form, array &$form_state, array $configura islandora_ingest_form_init_form_state_storage($form_state, $configuration); return islandora_ingest_form_execute_step($form, $form_state); } - catch(Exception $e) { + catch (Exception $e) { drupal_set_message($e->getMessage(), 'error'); return array(array( '#markup' => l(t('Back'), 'javascript:window.history.back();', array('external' => TRUE)))); @@ -82,9 +84,42 @@ function islandora_ingest_form_init_form_state_storage(array &$form_state, array 'shared_storage' => $configuration, 'step_storage' => array(), ); + // Must be called after $form_state['islandora'] is initialized, otherwise, + // the values in 'islandora' would not be availible to the step hooks. + $form_state['islandora']['step_id'] = islandora_ingest_form_get_first_step_id($form_state); } } +/** + * Get the first step ID. + * + * @param array $form_state + * The Drupal form state. + * + * @return string + * The 'id' of the very first step. + */ +function islandora_ingest_form_get_first_step_id(array &$form_state) { + $steps = islandora_ingest_form_get_steps($form_state); + $keys = array_keys($steps); + return array_shift($keys); +} + +/** + * Get the last step ID. + * + * @param array $form_state + * The Drupal form state. + * + * @return string + * The 'id' of the very last step. + */ +function islandora_ingest_form_get_last_step_id(array &$form_state) { + $steps = islandora_ingest_form_get_steps($form_state); + $keys = array_keys($steps); + return array_pop($keys); +} + /** * Prepares a new object based on the given configuration. * @@ -96,27 +131,29 @@ function islandora_ingest_form_init_form_state_storage(array &$form_state, array */ function islandora_ingest_form_prepare_new_object(array $configuration) { module_load_include('inc', 'islandora', 'includes/utilities'); - // ID is more specific than namespace so it will take precedence. - $id = isset($configuration['namespace']) ? $configuration['namespace'] : 'islandora'; - $id = isset($configuration['id']) ? $configuration['id'] : $id; - $label = isset($configuration['label']) ? $configuration['label'] : 'New Object'; - $relationship_map = function($o) { - return array('relationship' => 'isMemberOfCollection', 'pid' => $o); - }; - $relationships = empty($configuration['collections']) ? array() : array_map($relationship_map, $configuration['collections']); - return islandora_prepare_new_object($id, $label, array(), array(), $relationships); + if (empty($configuration['object'])) { + // ID is more specific than namespace so it will take precedence. + $id = isset($configuration['namespace']) ? $configuration['namespace'] : 'islandora'; + $id = isset($configuration['id']) ? $configuration['id'] : $id; + $label = isset($configuration['label']) ? $configuration['label'] : 'New Object'; + $relationship_map = function($o) { + return array('relationship' => 'isMemberOfCollection', 'pid' => $o); + }; + $relationships = empty($configuration['collections']) ? array() : array_map($relationship_map, $configuration['collections']); + return islandora_prepare_new_object($id, $label, array(), array(), $relationships); + } + return $configuration['object']; } /** * Gets the given/current step. * - * The current step is returned if no step ID is given. If the current step is - * not defined it's assume to be the first step. + * If the current step is not defined it's assumed that all steps have executed. * * @param array $form_state * The Drupal form state. * @param string $step_id - * The ID of the step. + * The ID of the step. The current step is returned if no step ID is given. * * @return array * The given/current step if found, NULL otherwise. @@ -130,10 +167,48 @@ function islandora_ingest_form_get_step(array &$form_state, $step_id = NULL) { return NULL; } +/** + * Gets the next step. + * + * If the current step is not defined it's assumed that all steps have executed. + * + * @param array $form_state + * The Drupal form state. + * @param string $step + * The step relative to the result, if not provided the current step is used. + * + * @return string + * The next step if found, NULL otherwise. + */ +function islandora_ingest_form_get_next_step(array &$form_state, array $step = NULL) { + $step = isset($step) ? $step : islandora_ingest_form_get_step($form_state); + $next_step_id = islandora_ingest_form_get_next_step_id($form_state, $step['id']); + return isset($next_step_id) ? islandora_ingest_form_get_step($form_state, $next_step_id) : NULL; +} + +/** + * Gets the previous step. + * + * If the current step is not defined it's assumed that all steps have executed. + * + * @param array $form_state + * The Drupal form state. + * @param string $step + * The step relative to the result, if not provided the current step is used. + * + * @return string + * The next step if found, NULL otherwise. + */ +function islandora_ingest_form_get_previous_step(array &$form_state, array $step = NULL) { + $step = isset($step) ? $step : islandora_ingest_form_get_step($form_state); + $previous_step_id = islandora_ingest_form_get_previous_step_id($form_state, $step['id']); + return isset($previous_step_id) ? islandora_ingest_form_get_step($form_state, $previous_step_id) : NULL; +} + /** * Gets the ID of the current step. * - * If a current step is not defined, its assumed to be the first step. + * If the current step is not defined it's assumed that all steps have executed. * * @param array $form_state * The Drupal form state. @@ -142,26 +217,25 @@ function islandora_ingest_form_get_step(array &$form_state, $step_id = NULL) { * The step ID. */ function islandora_ingest_form_get_current_step_id(array &$form_state) { - if (empty($form_state['islandora']['step_id'])) { - $steps = islandora_ingest_form_get_steps($form_state); - return array_shift(array_keys($steps)); - } return $form_state['islandora']['step_id']; } /** * Gets the ID of the next step. * - * If a current step is not defined, its assumed to be the first step. + * If the current step is not defined it's assumed that all steps have executed. * * @param array $form_state * The Drupal form state. + * @param string $step_id + * The ID of the step relative to the result, if not provided the current + * step_id is used. * * @return string * The next step ID if found, NULL otherwise. */ -function islandora_ingest_form_get_next_step_id(array &$form_state) { - $step_id = islandora_ingest_form_get_current_step_id($form_state); +function islandora_ingest_form_get_next_step_id(array &$form_state, $step_id = NULL) { + $step_id = isset($step_id) ? $step_id : islandora_ingest_form_get_current_step_id($form_state); $step_ids = array_keys(islandora_ingest_form_get_steps($form_state)); $index = array_search($step_id, $step_ids); $count = count($step_ids); @@ -174,16 +248,25 @@ function islandora_ingest_form_get_next_step_id(array &$form_state) { /** * Gets the ID of the previous step. * - * If a current step is not defined, its assumed to be the first step. + * If the current step is not defined it's assumed that all steps have executed. + * In such cases the last step will be returned. * * @param array $form_state * The Drupal form state. + * @param string $step_id + * The ID of the step relative to the result, if not provided the current + * step_id is used. * * @return string * The previous step ID if found, NULL otherwise. */ -function islandora_ingest_form_get_previous_step_id(array &$form_state) { - $step_id = islandora_ingest_form_get_current_step_id($form_state); +function islandora_ingest_form_get_previous_step_id(array &$form_state, $step_id = NULL) { + $step_id = isset($step_id) ? $step_id : islandora_ingest_form_get_current_step_id($form_state); + // If the current step is not defined it's assumed that all steps have + // executed. So return the very last step. + if ($step_id == NULL) { + return islandora_ingest_form_get_last_step_id($form_state); + } $step_ids = array_keys(islandora_ingest_form_get_steps($form_state)); $index = array_search($step_id, $step_ids); if ($index !== FALSE && --$index >= 0) { @@ -203,11 +286,9 @@ function islandora_ingest_form_increment_step(array &$form_state) { // of the current step could have added/removed a step. drupal_static_reset('islandora_ingest_form_get_steps'); $next_step_id = islandora_ingest_form_get_next_step_id($form_state); - if (isset($next_step_id)) { - islandora_ingest_form_stash_info($form_state); - $form_state['islandora']['step_id'] = $next_step_id; - islandora_ingest_form_grab_info($form_state); - } + islandora_ingest_form_stash_info($form_state); + $form_state['islandora']['step_id'] = $next_step_id; + islandora_ingest_form_grab_info($form_state); } /** @@ -218,6 +299,7 @@ function islandora_ingest_form_increment_step(array &$form_state) { */ function islandora_ingest_form_decrement_step(array &$form_state) { $previous_step_id = islandora_ingest_form_get_previous_step_id($form_state); + // Don't decrement passed the first step. if (isset($previous_step_id)) { islandora_ingest_form_stash_info($form_state); $form_state['islandora']['step_id'] = $previous_step_id; @@ -229,16 +311,18 @@ function islandora_ingest_form_decrement_step(array &$form_state) { * Build a list of steps given only configuration. * * XXX: This is used to give an indication of whether there are any steps for a - * given list of content models. + * given configuration. * * @param array $configuration * The list of key/value pairs of configuration. */ function islandora_ingest_get_approximate_steps(array $configuration) { try { + // @todo, we need to expand the configuration before we can validate it? + // I think this need some thinking. islandora_ingest_form_validate_configuration($configuration); } - catch(InvalidArgumentException $e) { + catch (InvalidArgumentException $e) { // Don't log or display exception. return array(); } @@ -266,16 +350,19 @@ function islandora_ingest_get_approximate_steps(array $configuration) { * @return array * The form definition of the current step. */ -function islandora_ingest_form_execute_step(array $form, array &$form_state) { +function islandora_ingest_form_execute_step(array $form, array &$form_state, $step_id = NULL) { // Load any required files for the current step. islandora_ingest_form_load_include($form_state); - $step = islandora_ingest_form_get_step($form_state); + $step = isset($step_id) ? islandora_ingest_form_get_step($form_state) : islandora_ingest_form_get_step($form_state, $step_id); switch ($step['type']) { + case 'callback': + // Execute all the consecutive callbacks, and move then attempt to process + // the next step. + islandora_ingest_form_execute_consecutive_callback_steps($form, $form_state, $step); + return islandora_ingest_form_execute_step($form, $form_state); + case 'form': - $args = array($form, &$form_state); - $args = isset($step['args']) ? array_merge($args, $step['args']) : $args; - $form = call_user_func_array($step['form_id'], $args); - return islandora_ingest_form_stepify($form, $form_state, $step); + return islandora_ingest_form_execute_form_step($form, $form_state, $step); case 'batch': // @todo Implement if possible. @@ -284,6 +371,94 @@ function islandora_ingest_form_execute_step(array $form, array &$form_state) { return array(); } +/** + * Execute the given 'form' step. + * + * Assumes the given step is a 'form' step. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + * + * @return array + * The form definition of the given step. + */ +function islandora_ingest_form_execute_form_step(array $form, array &$form_state, array $step) { + $args = array($form, &$form_state); + $args = isset($step['args']) ? array_merge($args, $step['args']) : $args; + $form = call_user_func_array($step['form_id'], $args); + return islandora_ingest_form_stepify($form, $form_state, $step); +} + +/** + * Execute the given 'callback' step and any consecutive 'callback' steps. + * + * Assumes the given step is a 'callback' step. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + */ +function islandora_ingest_form_execute_consecutive_callback_steps(array $form, array &$form_state, array $step) { + do { + islandora_ingest_form_execute_callback_step($form, $form_state, $step); + islandora_ingest_form_increment_step($form_state); + $step = islandora_ingest_form_get_step($form_state); + } while (isset($step) && $step['type'] == 'callback'); +} + +/** + * Execute the given 'callback' step. + * + * Assumes the given step is a 'callback' step. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + */ +function islandora_ingest_form_execute_callback_step(array $form, array &$form_state, array $step) { + $args = array(&$form_state); + $args = isset($step['do_function']['args']) ? array_merge($args, $step['do_function']['args']) : $args; + call_user_func_array($step['do_function']['function'], $args); +} + +/** + * Undo the given 'callback' step and any consecutive 'callback' steps. + * + * Assumes the given $step is a 'callback' step. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + */ +function islandora_ingest_form_undo_consecutive_callback_steps(array $form, array &$form_state, array $step) { + do { + islandora_ingest_form_undo_callback_step($form, $form_state, $step); + islandora_ingest_form_decrement_step($form_state); + $step = islandora_ingest_form_get_step($form_state); + } while (isset($step) && $step['type'] == 'callback'); +} + +/** + * Undo the given 'callback' step. + * + * Assumes the given $step is a 'callback' step. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + */ +function islandora_ingest_form_undo_callback_step(array $form, array &$form_state, array $step) { + $args = array(&$form_state); + $args = isset($step['undo_function']['args']) ? array_merge($args, $step['undo_function']['args']) : $args; + call_user_func_array($step['undo_function']['function'], $args); +} + /** * Append Prev/Next buttons submit/validation handlers etc. * @@ -293,22 +468,20 @@ function islandora_ingest_form_execute_step(array $form, array &$form_state) { * The Drupal form state. * * @return array - * The stepified drupal form definition for the given step. + * The stepified Drupal form definition for the given step. */ function islandora_ingest_form_stepify(array $form, array &$form_state, $step) { - $first_step = islandora_ingest_form_on_first_step($form_state); - $last_step = islandora_ingest_form_on_last_step($form_state); - $form['prev'] = $first_step ? NULL : islandora_ingest_form_previous_button($form_state); - $form['next'] = $last_step ? islandora_ingest_form_ingest_button($form_state) : islandora_ingest_form_next_button($form_state); - + $first_form_step = islandora_ingest_form_on_first_form_step($form_state); + $last_form_step = islandora_ingest_form_on_last_form_step($form_state); + $form['prev'] = $first_form_step ? NULL : islandora_ingest_form_previous_button($form_state); + $form['next'] = $last_form_step ? islandora_ingest_form_ingest_button($form_state) : islandora_ingest_form_next_button($form_state); // Allow for a hook_form_FORM_ID_alter(). drupal_alter(array('form_' . $step['form_id'], 'form'), $form, $form_state, $step['form_id']); - return $form; } /** - * Checks if we are on the first step. + * Checks if we are on the first form step. * * @param array $form_state * The Drupal form state. @@ -316,14 +489,12 @@ function islandora_ingest_form_stepify(array $form, array &$form_state, $step) { * @return bool * TRUE if we are currently on the first step, FALSE otherwise. */ -function islandora_ingest_form_on_first_step(array &$form_state) { - $step_id = islandora_ingest_form_get_current_step_id($form_state); - $step_ids = array_keys(islandora_ingest_form_get_steps($form_state)); - return array_search($step_id, $step_ids) == 0; +function islandora_ingest_form_on_first_form_step(array &$form_state) { + return !islandora_ingest_form_get_previous_form_step($form_state); } /** - * Checks if we are on the last step. + * Checks if we are on the last form step. * * @param array $form_state * The Drupal form state. @@ -331,11 +502,42 @@ function islandora_ingest_form_on_first_step(array &$form_state) { * @return bool * TRUE if we are currently on the last step, FALSE otherwise. */ -function islandora_ingest_form_on_last_step(array &$form_state) { - $step_id = islandora_ingest_form_get_current_step_id($form_state); - $step_ids = array_keys(islandora_ingest_form_get_steps($form_state)); - $count = count($step_ids); - return array_search($step_id, $step_ids) == --$count; +function islandora_ingest_form_on_last_form_step(array &$form_state) { + return !islandora_ingest_form_get_next_form_step($form_state); +} + +/** + * Get the previous form step relative to the current step. + * + * @param array $form_state + * The Drupal form state. + * + * @return array + * The previous form step if one exists, NULL otherwise. + */ +function islandora_ingest_form_get_previous_form_step(array &$form_state) { + $step = NULL; + do { + $step = islandora_ingest_form_get_previous_step($form_state, $step); + } while (isset($step) && $step['type'] != 'form'); + return $step; +} + +/** + * Get the next form step relative to the current step. + * + * @param array $form_state + * The Drupal form state. + * + * @return array + * The next form step if one exists, NULL otherwise. + */ +function islandora_ingest_form_get_next_form_step(array &$form_state) { + $step = NULL; + do { + $step = islandora_ingest_form_get_next_step($form_state, $step); + } while (isset($step) && $step['type'] != 'form'); + return $step; } /** @@ -350,13 +552,12 @@ function islandora_ingest_form_on_last_step(array &$form_state) { * The previous button for the ingest form. */ function islandora_ingest_form_previous_button(array &$form_state) { - // Before we move back to the previous step we should tell the previous step + // Before we move back to the previous step we should tell the previous steps // to undo whatever its submit handler did. - $prev_step_id = islandora_ingest_form_get_previous_step_id($form_state); - $prev_step = islandora_ingest_form_get_step($form_state, $prev_step_id); - $form_id = $prev_step['form_id']; + $prev_form_step = islandora_ingest_form_get_previous_form_step($form_state); + $form_id = $prev_form_step['form_id']; $submit_callback = $form_id . '_undo_submit'; - $submit = function_exists($submit_callback) ? array($submit_callback, 'islandora_ingest_form_previous_submit') : array('islandora_ingest_form_undo_submit'); + $submit = function_exists($submit_callback) ? array($submit_callback, 'islandora_ingest_form_previous_submit') : array('islandora_ingest_form_previous_submit'); return array( '#type' => 'submit', '#value' => t('Previous'), @@ -387,6 +588,11 @@ function islandora_ingest_form_previous_button(array &$form_state) { */ function islandora_ingest_form_previous_submit(array $form, array &$form_state) { islandora_ingest_form_decrement_step($form_state); + $step = islandora_ingest_form_get_step($form_state); + // Undo all callbacks that occured after the previous step. + if ($step['type'] == 'callback') { + islandora_ingest_form_undo_consecutive_callback_steps($form, $form_state, $step); + } $form_state['rebuild'] = TRUE; } @@ -442,8 +648,10 @@ function islandora_ingest_form_next_submit(array $form, array &$form_state) { */ function islandora_ingest_form_stash_info(array &$form_state) { $storage = &islandora_ingest_form_get_step_storage($form_state); - $storage['values'] = $form_state['values']; - unset($form_state['values']); + if ($storage) { + $storage['values'] = $form_state['values']; + unset($form_state['values']); + } } /** @@ -489,12 +697,17 @@ function islandora_ingest_form_ingest_button(array &$form_state) { * * Attempts to ingest every object built by the previous steps. * - * @param array $form - * The Drupal form. * @param array $form_state * The Drupal form state. */ function islandora_ingest_form_submit(array $form, array &$form_state) { + // Execute any remaining callbacks. + islandora_ingest_form_increment_step($form_state); + $step = islandora_ingest_form_get_step($form_state); + if (isset($step) && $step['type'] == 'callback') { + islandora_ingest_form_execute_consecutive_callback_steps($form, $form_state, $step); + } + // Ingest the objects. foreach ($form_state['islandora']['objects'] as $object) { try { islandora_add_object($object); @@ -503,7 +716,7 @@ function islandora_ingest_form_submit(array $form, array &$form_state) { catch (Exception $e) { // If post hooks throws it may already exist at this point but may be // invalid, so don't say failed. - watchdog('islandora', $e->getMessage(), NULL, WATCHDOG_ERROR); + watchdog('islandora', 'Exception Message: @exception.', array('@exception' => $e->getMessage()), WATCHDOG_ERROR); drupal_set_message(t('A problem occured while ingesting "@label" (ID: @pid), please notifiy the administrator.', array('@label' => $object->label, '@pid' => $object->id)), 'error'); } } @@ -563,7 +776,8 @@ function &islandora_ingest_form_get_step_storage(array &$form_state, $step_id = } return $form_state['islandora']['step_storage'][$step_id]; } - return NULL; + $undefined_step_storage = array(); + return $undefined_step_storage; } /** @@ -621,6 +835,8 @@ function islandora_ingest_form_get_steps(array &$form_state) { $shared_storage = &islandora_ingest_form_get_shared_storage($form_state); foreach (islandora_build_hook_list(ISLANDORA_INGEST_STEP_HOOK, $shared_storage['models']) as $hook) { // Required for pass by reference. + // @todo Change this around so that it isn't passed by reference, there + // Is an alter below that can handle that requirement. foreach (module_implements($hook) as $module) { $function = $module . '_' . $hook; $module_steps = (array) $function($form_state); @@ -631,6 +847,10 @@ function islandora_ingest_form_get_steps(array &$form_state) { foreach (islandora_build_hook_list(ISLANDORA_INGEST_STEP_HOOK, $shared_storage['models']) as $hook) { drupal_alter($hook, $steps, $form_state); } + // Add any defaults. + foreach ($steps as $key => &$step) { + $step['id'] = $key; + } uasort($steps, 'drupal_sort_weight'); return $steps; } diff --git a/includes/ingest.menu.inc b/includes/ingest.menu.inc deleted file mode 100644 index a6f79891..00000000 --- a/includes/ingest.menu.inc +++ /dev/null @@ -1,49 +0,0 @@ -'; - // Redirect back to referer or top level collection. - drupal_goto($redirect); - } -} - -/** - * Fetches the ingest configuration from the $_GET parameters. - * - * Generic parameters as accepted by all ingest processes, other modules may - * add to this list. - * id -> The pid of the object to create. optional. - * models -> Comma delimited list of all the content models the created object - * should have. - * collections -> Comma delimited list of all the collections the created - * object should belong to. - * - * @return array - * The configuration options used to build the multi-paged ingest process. - */ -function islandora_ingest_get_configuration() { - $configuration = $_GET; - unset($configuration['q']); - $convert_to_array_keys = array_intersect(array('models', 'collections'), array_keys($configuration)); - foreach ($convert_to_array_keys as $key) { - $configuration[$key] = explode(',', $configuration[$key]); - } - return $configuration; -} diff --git a/includes/object_properties.form.inc b/includes/object_properties.form.inc index 29875d28..c9de5e8f 100644 --- a/includes/object_properties.form.inc +++ b/includes/object_properties.form.inc @@ -30,7 +30,7 @@ function islandora_object_properties_form(array $form, array &$form_state, Fedor '#title' => t('Item Label'), '#default_value' => $object->label, '#required' => 'TRUE', - '#description' => t('A Human readable label'), + '#description' => t('A human-readable label'), // Double the normal length. '#size' => 120, // Max length for a Fedora Label. @@ -43,14 +43,14 @@ function islandora_object_properties_form(array $form, array &$form_state, Fedor '#title' => t('Owner'), '#default_value' => $object->owner, '#required' => FALSE, - '#description' => t('The owner id'), + '#description' => t("The owner's account name"), '#type' => 'textfield', ), 'object_state' => array( '#title' => t('State'), '#default_value' => $object->state, '#required' => TRUE, - '#description' => t('The items state one of active, inactive or deleted'), + '#description' => t("The object's state (active, inactive or deleted)"), '#type' => 'select', '#options' => array('A' => 'Active', 'I' => 'Inactive', 'D' => 'Deleted'), ), diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index e1cf8478..aa09c8a4 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -187,30 +187,48 @@ function islandora_solution_pack_form_submit(array $form, array &$form_state) { */ function islandora_solution_pack_batch_operation_reingest_object(NewFedoraObject $object, array &$context) { $existing_object = islandora_object_load($object->id); + $deleted = FALSE; if ($existing_object) { $deleted = islandora_delete_object($existing_object); if (!$deleted) { $object_link = l($existing_object->label, "islandora/object/{$existing_object->id}"); - drupal_set_message(t('Failed to purge existing object !object_link.', array( + drupal_set_message(filter_xss(t('Failed to purge existing object !object_link.', array( '!object_link' => $object_link, - )), 'error'); + ))), '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."; + $params = array( + '@pid' => $pid, + '@label' => $label, + '!object_link' => $object_link, + ); + + $msg = ''; + if ($object) { + if ($deleted) { + $msg = t('Successfully reinstalled !object_link.', $params); + } + else { + $msg = t('Successfully installed !object_link.', $params); + } + } + elseif ($deleted) { + $msg = t('Failed to reinstall @label, identified by @pid.', $params); + } + else { + $msg = t('Failed to install @label, identified by @pid.', $params); + } + $status = $object ? 'status' : 'error'; - drupal_set_message(t($msg, array( - '@pid' => $pid, - '@label' => $label, - '!object_link' => $object_link, - )), $status); + drupal_set_message(filter_xss($msg), $status); } /** @@ -237,33 +255,37 @@ function islandora_install_solution_pack($module, $op = 'install') { islandora_uninstall_solution_pack($module); return; } + + $t = get_t(); + + // Some general replacements. + $admin_link = l($t('Solution Pack admin'), 'admin/islandora/solution_packs'); + $config_link = l($t('Islandora configuration'), 'admin/islandora/configure'); + $t_params = array( + '@module' => $module, + '!config_link' => $config_link, + '!admin_link' => $admin_link, + ); + module_load_include('module', 'islandora', 'islandora'); module_load_include('inc', 'islandora', 'includes/utilities'); 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'); + $msg = $t('@module: Did not install any objects. Could not connect to the repository. Please check the settings on the !config_link page and install the required objects manually on the !admin_link page.', $t_params); + drupal_set_message(filter_xss($msg), 'error'); return; } $connection = islandora_get_tuque_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', - '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', - 'modified_datastream' => 'The object already exists but datastreams are modified. Please reinstall the object on the !admin_link page', + '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), + 'out_of_date' => $t('The object already exists but is out-of-date. Please update the object on the !admin_link page.', $t_params), + '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); @@ -272,26 +294,25 @@ 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']]; - drupal_set_message(st("@module: Did not install !object_link. $status_msg.", array( - '@module' => $module_name, + $here_params = array( + '!summary' => $t("@module: Did not install !object_link.", array( '!object_link' => $object_link, - '!admin_link' => $admin_link, - )), 'warning'); + ) + $t_params), + '!description' => $status_messages[$object_status['status']], + ); + drupal_set_message(filter_xss(format_string('!summary !description', $here_params)), 'warning'); } else { $object = islandora_add_object($object); if ($object) { - drupal_set_message(t('@module: Successfully installed. !object_link.', array( - '@module' => $module_name, + drupal_set_message(filter_xss($t('@module: Successfully installed. !object_link.', array( '!object_link' => $object_link, - )), 'status'); + ) + $t_params)), 'status'); } else { - drupal_set_message(t('@module: Failed to install. @label.', array( - '@module' => $module_name, + drupal_set_message($t('@module: Failed to install. @label.', array( '@label' => $label, - )), 'warning'); + ) + $t_params), 'warning'); } } } @@ -307,21 +328,20 @@ function islandora_install_solution_pack($module, $op = 'install') { * directly for each solution pack. */ function islandora_uninstall_solution_pack($module) { + $t = get_t(); module_load_include('module', 'islandora', 'islandora'); module_load_include('inc', 'islandora', 'includes/utilities'); module_load_include('module', $module, $module); - $config_link = l(t('Islandora configuration'), 'admin/islandora/configure'); + $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'); + $msg = $t('@module: Did not uninstall any objects. Could not connect to the repository. Please check the settings on the !config_link page and uninstall the required objects manually if necessary.', array( + '@module' => $module_name, + '!config_link' => $config_link, + )); + drupal_set_message(filter_xss($msg), 'error'); return; } $connection = islandora_get_tuque_connection(); @@ -334,12 +354,13 @@ function islandora_uninstall_solution_pack($module) { }; $existing_objects = array_filter($objects, $filter); 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'); + $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'); } } diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 8bd3c3e6..14a770e2 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -35,22 +35,20 @@ $islandora_module_path = drupal_get_path('module', 'islandora'); * Allow modules to alter an object before a mutable event occurs. */ function islandora_alter_object(AbstractFedoraObject $object, array &$context) { - $types = array('islandora_object'); - foreach ($object->models as $model) { - $types[] = "{$model}_islandora_object"; - } - drupal_alter($types, $object, $context); + module_load_include('inc', 'islandora', 'includes/utilities'); + drupal_alter(islandora_build_hook_list('islandora_object', $object->models), $object, $context); } /** * Allow modules to alter a datastream before a mutable event occurs. */ function islandora_alter_datastream(AbstractFedoraObject $object, AbstractDatastream $datastream, array &$context) { - $types = array('islandora_datastream'); + module_load_include('inc', 'islandora', 'includes/utilities'); + $types = array(); foreach ($object->models as $model) { - $types[] = "{$model}_{$datastream->id}_islandora_datastream"; + $types[] = "{$model}_{$datastream->id}"; } - drupal_alter($types, $object, $datastream, $context); + drupal_alter(islandora_build_hook_list('islandora_datastream', $types), $object, $datastream, $context); } /** @@ -207,7 +205,7 @@ class IslandoraFedoraApiM extends FedoraApiM { } return $ret; } - catch(Exception $e) { + catch (Exception $e) { watchdog('islandora', 'Failed to modify datastream @dsid from @pid
code: @code
message: @msg', array( '@pid' => $pid, '@dsid' => $dsid, @@ -241,7 +239,7 @@ class IslandoraFedoraApiM extends FedoraApiM { } return $ret; } - catch(Exception $e) { + catch (Exception $e) { watchdog('islandora', 'Failed to modify object: @pid
code: @code
message: @msg', array( '@pid' => $pid, '@code' => $e->getCode(), @@ -283,7 +281,7 @@ class IslandoraFedoraApiM extends FedoraApiM { return $ret; } } - catch(Exception $e) { + catch (Exception $e) { watchdog('islandora', 'Failed to purge datastream @dsid from @pid
code: @code
message: @msg', array( '@pid' => $pid, '@dsid' => $dsid, @@ -327,7 +325,7 @@ class IslandoraFedoraApiM extends FedoraApiM { return $ret; } } - catch(Exception $e) { + catch (Exception $e) { watchdog('islandora', 'Failed to purge object @pid
code: @code
message: @msg', array( '@pid' => $pid, '@code' => $e->getCode(), diff --git a/includes/utilities.inc b/includes/utilities.inc index f061f72c..fbc4585a 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -204,13 +204,13 @@ function islandora_escape_pid_for_function($pid) { // Apparently, case doesn't matter for function calls in PHP, so let's not // really worry about changing the case. return str_replace( - // Any PID characters which are not valid in the name of a PHP function. - array( - ':', - '-', - ), - '_', - $pid + // Any PID characters which are not valid in the name of a PHP function. + array( + ':', + '-', + ), + '_', + $pid ); } @@ -380,18 +380,13 @@ function islandora_get_datastreams_requirements_from_models(array $models) { * determine what datastreams are required. * * @return array - * The DS-COMPOSITE-MODEL defined datastreams that are required for the given - * object. - * - * @code - * array( - * 'DC' => array( - * 'id' => 'DC', - * 'mime' => 'text/xml', - * 'optional' => FALSE, - * ) - * ) - * @endcode + * An associative array mapping datastream IDs to associative arrays + * containing the values parsed from the DS-COMPOSITE-MODEL on the given + * object--of the form: + * - DSID: A datastream ID being described. + * - "id": A string containing ID of the datastream. + * - "mime": A array containing MIME-types the stream may have. + * - "optional": A boolean indicating if the given stream is optional. */ function islandora_get_datastreams_requirements_from_content_model(FedoraObject $object) { if (empty($object[DS_COMP_STREAM])) { @@ -465,20 +460,43 @@ function islandora_prepare_new_object($namespace = NULL, $label = NULL, $datastr if (isset($ds['control_group']) && in_array($ds['control_group'], $groups)) { $control_group = $ds['control_group']; } - $ds_uri = FALSE; + + $as_file = FALSE; if (file_valid_uri($ds['datastream_file'])) { + // A local file with as a Drupal file/stream wrapper URI. $datastream_file = $ds['datastream_file']; - $ds_uri = TRUE; + $as_file = TRUE; + } + elseif (is_readable($ds['datastream_file'])) { + // A local file as a filesystem path. + $datastream_file = drupal_realpath($ds['datastream_file']); + $as_file = TRUE; } else { - $datastream_file = url($ds['datastream_file'], array('absolute' => TRUE)); + $scheme = parse_url($ds['datastream_file'], PHP_URL_SCHEME); + if (in_array($scheme, stream_get_wrappers())) { + // A URI which gets handled by one of the PHP-native stream wrappers. + $datastream_file = $ds['datastream_file']; + $as_file = TRUE; + } + else { + // Schema does not match available php stream wrapper. Attempt to + // set datastream_file by url for the given scheme. Https (SSL) can + // cause this to fail, and trigger an output log in watchdog. + $datastream_file = url($ds['datastream_file'], array('absolute' => TRUE)); + watchdog('islandora', + 'Attempting to ingest %file in islandora_prepare_new_object(), but it does not appear to be readable. We will pass the path through url(), and pass along as such.', + array('%file' => $datastream_file), + WATCHDOG_WARNING); + } } + $datastream = $object->constructDatastream($dsid, $control_group); $datastream->label = $label; $datastream->mimetype = $mimetype; switch ($control_group) { case 'M': - if ($ds_uri) { + if ($as_file) { $datastream->setContentFromFile($datastream_file); } else { @@ -490,8 +508,10 @@ function islandora_prepare_new_object($namespace = NULL, $label = NULL, $datastr $datastream->setContentFromString(file_get_contents($datastream_file)); break; } + $object->ingestDatastream($datastream); } + return $object; } @@ -505,8 +525,9 @@ 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', FALSE); + array('!link' => $link)); + drupal_set_message(check_plain($message), 'error', FALSE); + } /** @@ -595,33 +616,24 @@ function islandora_system_settings_form_default_value($name, $default_value, arr /** * Returns basic information about DS-COMPOSITE-MODEL. * + * @deprecated + * The pre-existing--and more flexible-- + * islandora_get_datastreams_requirements_from_content_model() should be + * preferred, as it addresses the case where a stream can be allowed to have + * one of a set of mimetypes (this functions appears to only return the last + * declared mimetype for a given datastream). + * * @param string $pid - * PID of content model containing DS_COMP stream. + * The PID of content model containing DS_COMP stream. * * @return array - * array of values in the following form - * - * [DC] => Array - * ( - * [mimetype] => text/xml - * ) - * - * [MODS] => Array - * ( - * [optional] => true - * [mimetype] => text/xml - * ) - * - * [RELS-EXT] => Array - * ( - * [mimetype] => application/rdf+xml - * ) - * - * [RELS-INT] => Array - * ( - * [optional] => true - * [mimetype] => application/rdf+xml - * ) + * An associative array mapping datastream IDs to an associative array + * representing the parsed DS-COMPOSITE-MODEL of the form: + * - DSID: A string containing the datastream ID. + * - "mimetype": A string containing the last mimetype declared for the + * given datastream ID. + * - "optional": An optional boolean indicating that the given datastream + * is optional. */ function islandora_get_comp_ds_mappings($pid) { $cm_object = islandora_object_load($pid); @@ -718,14 +730,24 @@ function islandora_get_allowed_namespaces() { function islandora_get_content_models($ignore_system_namespace = TRUE) { module_load_include('inc', 'islandora', 'includes/utilities'); $tuque = islandora_get_tuque_connection(); - $query = 'select $object $label from <#ri> - where ($object $label - and ($object - or $object ) - and $object ) - order by $label'; + $query = "PREFIX fm: <" . FEDORA_MODEL_URI . "> + PREFIX fr: <" . FEDORA_RELS_EXT_URI . "> + SELECT ?object ?label + FROM <#ri> + WHERE { + {?object fm:hasModel ; + fm:state fm:Active + } + UNION{ + ?object fr:isMemberOfCollection ; + fm:state fm:Active + } + OPTIONAL{ + ?object fm:label ?label + } + }"; $content_models = array(); - $results = $tuque->repository->ri->itqlQuery($query, 'unlimited'); + $results = $tuque->repository->ri->sparqlQuery($query, 'unlimited'); foreach ($results as $result) { $content_model = $result['object']['value']; $label = $result['label']['value']; @@ -738,3 +760,60 @@ function islandora_get_content_models($ignore_system_namespace = TRUE) { } return $content_models; } + +/** + * Returns Drupal tableselect element allowing selection of Content Models. + * + * @param string $drupal_variable + * the name of the Drupal variable holding selected content models + * Content models held in this variable will appear at the top of + * the displayed list + * @param array $default_values_array + * default values to display if $drupal_variable is unset + * + * @return array + * Drupal form element allowing content model selection + */ +function islandora_content_model_select_table_form_element($drupal_variable, $default_values_array = array('')) { + $defaults = array(); + $rows = array(); + $content_models = array(); + $options = islandora_get_content_models(TRUE); + foreach ($options as $option) { + $content_models[$option['pid']] = $option['label']; + } + + $selected = array_values(variable_get($drupal_variable, $default_values_array)); + $comparator = function ($a, $b) use ($selected) { + $a_val = $b_val = 0; + if (in_array($a, $selected)) { + $a_val = 1; + } + if (in_array($b, $selected)) { + $b_val = 1; + } + return $b_val - $a_val; + }; + uksort($content_models, $comparator); + foreach ($content_models as $pid => $label) { + $rows[$pid] = array( + 'pid' => $pid, + 'title' => $label, + ); + $defaults[$pid] = in_array($pid, $selected); + } + $header = array( + 'pid' => array('data' => t('PID')), + 'title' => array('data' => t('Content Model')), + ); + // Build and return table element. + $element = array( + '#type' => 'tableselect', + '#header' => $header, + '#options' => $rows, + '#default_value' => $defaults, + '#empty' => t("There are no content models in this Fedora Repository."), + ); + + return $element; +} diff --git a/islandora.api.php b/islandora.api.php index 5e976194..17db59ad 100644 --- a/islandora.api.php +++ b/islandora.api.php @@ -45,11 +45,24 @@ function hook_CMODEL_PID_islandora_view_object($object) { * @param FedoraObject $object * A Tuque FedoraObject being operated on. * @param array $rendered - * An arr of rendered views. + * The array of rendered views. */ function hook_islandora_view_object_alter(&$object, &$rendered) { } +/** + * Alter display output if the object has the given model. + * + * @see hook_islandora_view_object_alter() + * + * @param FedoraObject $object + * A Tuque FedoraObject being operated on. + * @param array $rendered + * The array of rendered views. + */ +function hook_CMODEL_PID_islandora_view_object_alter(&$object, &$rendered) { +} + /** * Generate an object's management display. * @@ -291,7 +304,7 @@ function hook_islandora_datastream_modified(FedoraObject $object, FedoraDatastre } /** - * Notify modules that the given datastream was ingested. + * Notify modules that the given datastream was modified. * * @see hook_islandora_datastream_modified() */ @@ -373,15 +386,26 @@ function hook_islandora_undeletable_datastreams(array $models) { * @return array * An associative array of associative arrays which define each step in the * ingest process. Each step should consist of a unique name mapped to an - * array of properties (keys) including: - * - type: The type of step. Currently, only "form" is implemented. - * - weight: The "weight" of this step--heavier(/"larger") values sink to the - * end of the process while smaller(/"lighter") values are executed first. + * array of properties (keys) which take different paramaters based upon type: + * - type: Type of step. Only "form" and "callback" are implemented so far. + * Required "form" type specific parameters: * - form_id: The form building function to call to get the form structure * for this step. * - args: An array of arguments to pass to the form building function. - * And may optionally include both: + * Required "callback" type specific parameters: + * - do_function: An associate array including: + * - 'function': The callback function to be called. + * - 'args': An array of arguments to pass to the callback function. + * - undo_function: An associate array including: + * - 'function': The callback function to be called to reverse the + * executed action in the ingest steps. + * - 'args': An array of arguments to pass to the callback function. + * Shared parameters between both types: + * - weight: The "weight" of this step--heavier(/"larger") values sink to the + * end of the process while smaller(/"lighter") values are executed first. + * Both types may optionally include: * - module: A module from which we want to load an include. + * "Form" type may optionally include: * - file: A file to include (relative to the module's path, including the * file's extension). */ @@ -393,6 +417,18 @@ function hook_islandora_ingest_steps(array $form_state) { 'form_id' => 'my_cool_form', 'args' => array('arg_one', 'numero deux'), ), + 'my_cool_step_callback' => array( + 'type' => 'callback', + 'weight' => 2, + 'do_function' => array( + 'function' => 'my_cool_execute_function', + 'args' => array('arg_one', 'numero deux'), + ), + 'undo_function' => array( + 'function' => 'my_cool_undo_function', + 'args' => array('arg_one', 'numero deux'), + ), + ), ); } /** diff --git a/islandora.install b/islandora.install index 9a1825b5..caff7d75 100644 --- a/islandora.install +++ b/islandora.install @@ -10,13 +10,14 @@ */ function islandora_requirements($phase) { $requirements = array(); - // Ensure translations don't break at install time + + // Ensure translations don't break at install time. $t = get_t(); if (!class_exists('XSLTProcessor', FALSE)) { $requirements['islandora_xsltprocessor']['title'] = $t('Islandora XSLTProcessor Prerequisite'); $requirements['islandora_xsltprocessor']['value'] = $t('Not installed'); - $link = l($t('PHP XSL extension'), 'http://us2.php.net/manual/en/book.xsl.php', array('attributes' => array('target'=>'_blank'))); + $link = l($t('PHP XSL extension'), 'http://us2.php.net/manual/en/book.xsl.php', array('attributes' => array('target' => '_blank'))); $requirements['islandora_xsltprocessor']['description'] = $t('The !xsllink is required. Check your installed PHP extensions and php.ini file.', array('!xsllink' => $link)); $requirements['islandora_xsltprocessor']['severity'] = REQUIREMENT_ERROR; } diff --git a/islandora.module b/islandora.module index 94aedbe0..ad9e569e 100644 --- a/islandora.module +++ b/islandora.module @@ -120,7 +120,7 @@ function islandora_menu() { FEDORA_METADATA_EDIT, FEDORA_ADD_DS, FEDORA_PURGE, - FEDORA_INGEST + FEDORA_INGEST, ), 2), ); @@ -238,12 +238,13 @@ function islandora_menu() { 'access arguments' => array(FEDORA_VIEW_OBJECTS, 2), 'load arguments' => array(2), ); - $items['islandora/ingest'] = array( - 'title' => 'Add an Object', - 'page callback' => 'islandora_ingest_callback', - 'file' => 'includes/ingest.menu.inc', - 'type' => MENU_SUGGESTED_ITEM, - 'access arguments' => array(FEDORA_INGEST), + $items['islandora/object/%islandora_object/download_clip'] = array( + 'page callback' => 'islandora_download_clip', + 'page arguments' => array(2), + 'type' => MENU_CALLBACK, + 'access callback' => 'islandora_object_access_callback', + 'access arguments' => array(FEDORA_VIEW_OBJECTS, 2), + 'load arguments' => array(2), ); return $items; } @@ -286,6 +287,29 @@ function islandora_theme() { 'file' => 'theme/theme.inc', 'variables' => array('object' => NULL, 'content' => array()), ), + // Render a bunch of objects as either a grid or a list. + 'islandora_objects' => array( + 'file' => 'theme/theme.inc', + 'template' => 'theme/islandora-objects', + 'variables' => array( + 'objects' => NULL, + 'display' => NULL, + 'page_size' => 20, + 'limit' => 10, + ), + ), + // Render a bunch of objects as a grid. + 'islandora_objects_grid' => array( + 'file' => 'theme/theme.inc', + 'template' => 'theme/islandora-objects-grid', + 'variables' => array('objects' => NULL), + ), + // Render a bunch of objects as a list. + 'islandora_objects_list' => array( + 'file' => 'theme/theme.inc', + 'template' => 'theme/islandora-objects-list', + 'variables' => array('objects' => NULL), + ), ); } @@ -334,6 +358,88 @@ function islandora_forms($form_id) { return $forms; } +/** + * Checks whether the user can access the given object. + * + * Checks for repository access, object/datastream existance, namespace access, + * user permissions, content models. + * + * Will check the given user or the user repersented by the GET token parameter, + * failing that it will use the global user. + * + * @global $user + * + * @param mixed $object + * The FedoraObject or FedoraDatastream to test for accessibility, if NULL + * is given the object is assumed to not exist or be inaccessible. + * @param array $permissions + * The required user permissions. + * @param array $content_models + * The required content models. + * @param bool $access_any + * (optional) TRUE to grant access if any single requirement is met from both + * the permissions and content models parameters. FALSE if all requirements + * must be met from both the permissions and content model parameters. + * @param object $account + * (optional) The account to check, if not given check the GET parameters for + * a token to restore the user. If no GET parameter is present use currently + * logged in user. + * + * @return bool + * TRUE if the user is allowed to access this object/datastream, FALSE + * otherwise. + */ +function islandora_user_access($object, array $permissions, $content_models = array(), $access_any = TRUE, $account = NULL) { + module_load_include('inc', 'islandora', 'includes/utilities'); + $is_repository_accessible = &drupal_static(__FUNCTION__); + // If the repository is inaccessible then access always fails. + if (!isset($is_repository_accessible)) { + $is_repository_accessible = islandora_describe_repository(); + if (!$is_repository_accessible) { + // Only display the inaccessible message once. + islandora_display_repository_inaccessible_message(); + return FALSE; + } + } + if (!$is_repository_accessible || !is_object($object)) { + return FALSE; + } + // Determine the user account to test against. + if (!isset($account)) { + $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING); + if ($token) { + module_load_include('inc', 'islandora', 'includes/authtokens'); + $user = islandora_validate_object_token($object->id, $datastream->id, $token); + if ($user) { + $account = user_load($user->uid); + } + } + else { + global $user; + $account = $user; + } + } + // Determine what has been passed as $object. + if (is_subclass_of($object, 'FedoraObject')) { + $object = $object; + } + elseif (is_subclass_of($object, 'FedoraDatastream')) { + $datastream = $object; + $object = $datastream->parent; + } + // Check for access. + $accessible_namespace = islandora_namespace_accessible($object->id); + if ($access_any) { + $has_required_permissions = islandora_user_access_any($permissions, $account); + $has_required_content_models = empty($content_models) ? TRUE : count(array_intersect($object->models, $content_models)) > 0; + } + else { + $has_required_permissions = islandora_user_access_all($permissions, $account); + $has_required_content_models = count(array_diff($content_models, $object->models)) == 0; + } + return $accessible_namespace && $has_required_permissions && $has_required_content_models; +} + /** * Checks whether the user can access the given object. * @@ -570,7 +676,8 @@ function islandora_view_object(FedoraObject $object) { $page_number = (empty($_GET['page'])) ? '1' : $_GET['page']; $page_size = (empty($_GET['pagesize'])) ? '10' : $_GET['pagesize']; $output = array(); - foreach (islandora_build_hook_list(ISLANDORA_VIEW_HOOK, $object->models) as $hook) { + $hooks = islandora_build_hook_list(ISLANDORA_VIEW_HOOK, $object->models); + foreach ($hooks as $hook) { // @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); @@ -583,7 +690,7 @@ function islandora_view_object(FedoraObject $object) { $output = islandora_default_islandora_view_object($object); } arsort($output); - drupal_alter(ISLANDORA_VIEW_HOOK, $object, $output); + drupal_alter($hooks, $object, $output); return implode('', $output); } @@ -859,7 +966,7 @@ function islandora_delete_object(FedoraObject &$object) { $object = NULL; return TRUE; } - catch(Exception $e) { + catch (Exception $e) { // Exception message gets logged in Tuque Wrapper. return FALSE; } @@ -964,7 +1071,29 @@ function islandora_entity_property_info() { * @return array * A renderable array. */ -function islandora_print_object(FedoraObject $object) { +function islandora_print_object(AbstractObject $object) { drupal_set_title($object->label); return theme('islandora_object_print', array('object' => $object)); } + +/** + * Menu callback downloads the given clip. + */ +function islandora_download_clip(AbstractObject $object) { + if (isset($_GET['clip'])) { + $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; + $http_protocol = $is_https ? 'https' : 'http'; + $url = $http_protocol . '://' . $_SERVER['HTTP_HOST'] . $_GET['clip']; + $filename = $object->label; + header("Content-Disposition: attachment; filename=\"{$filename}.jpg\""); + header("Content-type: image/jpeg"); + header("Content-Transfer-Encoding: binary"); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_URL, $url); + $response = curl_exec($ch); + curl_close($ch); + } + exit(); +} diff --git a/tests/README.txt b/tests/README.txt index 797d788b..c03004ae 100644 --- a/tests/README.txt +++ b/tests/README.txt @@ -1,3 +1,4 @@ You can define your own configurations specific to your enviroment by copying default.test_config.ini to test_config.ini, making your changes in the copied -file. \ No newline at end of file +file. These test need write access to the system's $FEDORA_HOME/server/config +directory as well as the filter-drupal.xml file. \ No newline at end of file diff --git a/tests/hooks.test b/tests/hooks.test index 0a667fee..3133d656 100644 --- a/tests/hooks.test +++ b/tests/hooks.test @@ -32,7 +32,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { * @see IslandoraWebTestCase::setUp() */ public function setUp() { - parent::setUp('islandora_hooks_test', 'devel'); + parent::setUp('islandora_hooks_test'); $this->repository = $this->admin->repository; $this->purgeTestObjects(); } @@ -67,7 +67,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $object->label = "Don't Block"; $this->repository->purgeObject($object->id); } - catch(Exception $e) { + catch (Exception $e) { // Meh... Either it didn't exist or the purge failed. } } @@ -95,7 +95,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->fail('Blocked ingest should throw an Exception.'); $this->repository->purgeObject($object->id); } - catch(Exception $e) { + catch (Exception $e) { $this->pass('Ingest blocked and exception thrown.'); $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.'); @@ -115,7 +115,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $object->label = 'block'; $this->fail('Blocked modify should throw an Exception.'); } - catch(Exception $e) { + catch (Exception $e) { $this->pass('Modify blocked and exception thrown.'); $this->assertNotEqual($object->label, 'block', 'Modification did not stick.'); $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called "hook_islandora_object_alter" when blocking modifying via set magic functions.'); @@ -149,7 +149,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $this->repository->purgeObject($object->id); $this->fail('Blocked modify should throw an Exception.'); } - catch(Exception $e) { + catch (Exception $e) { $this->pass('Modify blocked and exception thrown.'); $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.'); @@ -191,7 +191,7 @@ class IslandoraHooksTestCase extends IslandoraWebTestCase { $ds->label = 'block'; $this->fail('Blocked modify should throw an Exception.'); } - catch(Exception $e) { + catch (Exception $e) { $this->pass('Modify blocked and exception thrown.'); $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called "hook_islandora_datastream_alter" when blocking modifying via set magic functions.'); $this->assertFALSE($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when blocking modifying via set magic functions.'); diff --git a/tests/islandora_test.info b/tests/islandora_hooks_test.info similarity index 62% rename from tests/islandora_test.info rename to tests/islandora_hooks_test.info index c6cb57ad..9d211978 100644 --- a/tests/islandora_test.info +++ b/tests/islandora_hooks_test.info @@ -1,6 +1,7 @@ -name = Islandora Test Module +name = Islandora Hook testing description = Tests Hooks. Do not enable. core = 7.x package = Testing hidden = TRUE dependencies[] = islandora +files[] = islandora_hooks_test.module diff --git a/tests/islandora_test.module b/tests/islandora_hooks_test.module similarity index 100% rename from tests/islandora_test.module rename to tests/islandora_hooks_test.module diff --git a/tests/islandora_manage_permissions.test b/tests/islandora_manage_permissions.test index 76f43a6d..8fc1549f 100644 --- a/tests/islandora_manage_permissions.test +++ b/tests/islandora_manage_permissions.test @@ -1,7 +1,17 @@ 'Islandora Manage Permissions', @@ -10,69 +20,117 @@ class IslandoraPermissionsTestCase extends IslandoraWebTestCase { ); } + /** + * Prepares enviroment for testing. + * + * @see IslandoraWebTestCase::setUp() + */ public function setUp() { parent::setUp(array('islandora')); } + /** + * Test manage permissions. + */ public function testManagePermissions() { - - - // permission FEDORA_VIEW_OBJECTS - // create a user with permission + // Test permission FEDORA_VIEW_OBJECTS. + // Create a user with permission. $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS)); - // log the user in + // Log the user in. $this->drupalLogin($user); $this->clickLink(t('Islandora Repository')); $this->assertNoLink('Manage', 'Manage tab is not on current page.'); - - - // permission FEDORA_VIEW_OBJECTS, FEDORA_MANAGE_PROPERTIES + + // Test permission FEDORA_VIEW_OBJECTS, FEDORA_MANAGE_PROPERTIES. $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_MANAGE_PROPERTIES)); - // log the user in $this->drupalLogin($user); $this->clickLink(t('Islandora Repository')); $this->assertLink('Manage', 0, 'Manage tab is on current page.'); $this->clickLink(t('Manage')); $this->assertLink('Properties', 0, 'Properties tab is on current page.'); $this->assertNoLink('Datastreams', 'Datastreams tab is not on current page.'); - $this->assertNoLink('Collection','Collection tab is not on current page.'); + $this->assertNoLink('Collection', 'Collection tab is not on current page.'); - - // permission FEDORA_VIEW_OBJECTS, FEDORA_ADD_DS + // Test permission FEDORA_VIEW_OBJECTS, FEDORA_ADD_DS. $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_ADD_DS)); - // log the user in $this->drupalLogin($user); $this->clickLink(t('Islandora Repository')); $this->assertLink('Manage', 0, 'Manage tab is on current page.'); $this->clickLink(t('Manage')); $this->assertLink('Datastreams', 0, 'Datastreams tab is on current page.'); $this->assertNoLink('Properties', 'Properties tab is not on current page.'); - $this->assertNoLink('Collection','Collection tab is not on current page.'); - - - // permission FEDORA_VIEW_OBJECTS, FEDORA_METADATA_EDIT + $this->assertNoLink('Collection', 'Collection tab is not on current page.'); + + // Test permission FEDORA_VIEW_OBJECTS, FEDORA_METADATA_EDIT. $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_METADATA_EDIT)); - // log the user in - $this->drupalLogin($user); + $this->drupalLogin($user); $this->clickLink(t('Islandora Repository')); $this->assertLink('Manage', 0, 'Manage tab is on current page.'); $this->clickLink(t('Manage')); $this->assertLink('Datastreams', 0, 'Datastreams tab is on current page.'); $this->assertNoLink('Properties', 'Properties tab is not on current page.'); - $this->assertNoLink('Collection','Collection tab is not on current page.'); - - - // permission FEDORA_VIEW_OBJECTS, FEDORA_PURGE + $this->assertNoLink('Collection', 'Collection tab is not on current page.'); + + // Test permission FEDORA_VIEW_OBJECTS, FEDORA_PURGE. $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_PURGE)); - // log the user in $this->drupalLogin($user); $this->clickLink(t('Islandora Repository')); $this->assertLink('Manage', 0, 'Manage tab is on current page.'); $this->clickLink(t('Manage')); $this->assertLink('Datastreams', 0, 'Datastreams tab is on current page.'); $this->assertNoLink('Properties', 'Properties tab is not on current page.'); - $this->assertNoLink('Collection','Collection tab is not on current page.'); - + $this->assertNoLink('Collection', 'Collection tab is not on current page.'); } - } \ No newline at end of file + /** + * Test generic access functions. + * + * Note that we can't test with the Global user as SimpleTest doesn't support + * it. Therefore we can't test the authtoken support. + */ + public function testAccessFunctions() { + $object = islandora_object_load(variable_get('islandora_repository_pid', 'islandora:root')); + // Test islandora_user_access(). + // Test no object/permissions. + $ret = islandora_user_access(NULL, array()); + $this->assertFalse($ret, 'User access denied when no object/permissions are provided.'); + // Test with object no permissions. + $ret = islandora_user_access($object, array()); + $this->assertFalse($ret, 'User access denied when no permissions are provided.'); + // Test access with matching permission. + $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS)); + $ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS), array(), TRUE, $user); + $this->assertTrue($ret, 'User access granted when permissions match.'); + // Test access with matching permission but access any is FALSE. + $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS)); + $ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS, FEDORA_PURGE), array(), FALSE, $user); + $this->assertFalse($ret, 'User access denied for matching permission but with access any set to FALSE.'); + // Test access with non-matching permission. + $user = $this->drupalCreateUser(array(FEDORA_PURGE)); + $ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS), array(), TRUE, $user); + $this->assertFalse($ret, 'User access denied when permissions did not match.'); + // Test access with both permissions and content model. + $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS)); + $model = $object->models; + $model = reset($model); + $ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS), array($model), TRUE, $user); + $this->assertTrue($ret, 'User access granted for matching permission and model.'); + // Test access with matching permissions and non-matching content model. + $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS)); + $ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS), array('islandora:obviouslyNotACModel'), TRUE, $user); + $this->assertFalse($ret, 'User access denied for matching permission and non-matching model.'); + // Test access with all matching permissions and one matching model but + // access any is FALSE. + $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_PURGE)); + $model = $object->models; + $model = reset($model); + $ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS, FEDORA_PURGE), array($model, 'islandora:obviouslyNotACModel'), FALSE, $user); + $this->assertFalse($ret, 'User access denied for all matching permissions and one matching model but with access any set to FALSE.'); + $ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS, FEDORA_PURGE), array($model), FALSE, $user); + $this->assertTrue($ret, 'User access granted for all matching permissions and matching models with access any set to FALSE.'); + // Test passing in a Datastream. + $user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_PURGE)); + $ret = islandora_user_access($object['DC'], array(FEDORA_VIEW_OBJECTS), array(), TRUE, $user); + $this->assertTrue($ret, 'User access granted for matching permissions, with a datastream given instead of an object.'); + } +} diff --git a/tests/scripts/line_endings.sh b/tests/scripts/line_endings.sh new file mode 100755 index 00000000..b72dde22 --- /dev/null +++ b/tests/scripts/line_endings.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +RETURN=0 +FILES=`find -L $1 -name "*.info" -o -name "*.txt" -o -name "*.md"` +echo "Testing for files with DOS line endings..." +for FILE in $FILES +do + file $FILE | grep CRLF + if [ $? == 0 ] + then + RETURN=1 + fi +done +exit $RETURN diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh new file mode 100755 index 00000000..c3515198 --- /dev/null +++ b/tests/scripts/travis_setup.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +mysql -u root -e 'create database drupal;' +mysql -u root -e "create database fedora;" +mysql -u root -e "GRANT ALL PRIVILEGES ON fedora.* To 'fedora'@'localhost' IDENTIFIED BY 'fedora';" +mysql -u root -e "GRANT ALL PRIVILEGES ON drupal.* To 'drupal'@'localhost' IDENTIFIED BY 'drupal';" +cd $HOME +git clone git://github.com/Islandora/tuque.git +git clone -b $FEDORA_VERSION git://github.com/Islandora/islandora_tomcat.git +cd islandora_tomcat +export CATALINA_HOME='.' +./bin/startup.sh +cd $HOME +pyrus channel-discover pear.drush.org +pyrus channel-discover pear.phpqatools.org +pyrus channel-discover pear.netpirates.net +pyrus install drush/drush +pyrus install pear/PHP_CodeSniffer +pyrus install pear.phpunit.de/phpcpd +phpenv rehash +drush dl --yes drupal +cd drupal-* +drush si standard --db-url=mysql://drupal:drupal@localhost/drupal --yes +drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/dev/null & +ln -s $ISLANDORA_DIR sites/all/modules/islandora +mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/islandora/tests/test_config.ini +mkdir sites/all/libraries +ln -s $HOME/tuque sites/all/libraries/tuque +drush dl --yes coder +drush dl --yes potx +drush en --yes coder_review +drush en --yes simpletest +drush en --yes potx +drush en --user=1 --yes islandora +drush cc all +sleep 4 diff --git a/tests/travis.test_config.ini b/tests/travis.test_config.ini new file mode 100644 index 00000000..61d63713 --- /dev/null +++ b/tests/travis.test_config.ini @@ -0,0 +1,6 @@ +[fedora] +fedora_url = "http://localhost:8080/fedora" +use_drupal_filter = TRUE +drupal_filter_file = "/home/travis/islandora_tomcat/fedora/server/config/filter-drupal.xml" +admin_user = "fedoraAdmin" +admin_pass = "fedoraAdmin" diff --git a/tests/web_test_case.inc b/tests/web_test_case.inc index a513de43..8a5b9a00 100644 --- a/tests/web_test_case.inc +++ b/tests/web_test_case.inc @@ -18,6 +18,11 @@ class IslandoraWebTestCase extends DrupalWebTestCase { // Always enable islandora. $args[] = 'islandora'; parent::setUp($args); + + // Its possible test are running before class autoloading. + module_load_include('inc', 'islandora', 'includes/tuque'); + module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); + $this->configuration = $this->getTestConfiguration(); if ($this->configuration['use_drupal_filter']) { $this->backUpDrupalFilter(); diff --git a/theme/islandora-objects-grid.tpl.php b/theme/islandora-objects-grid.tpl.php new file mode 100644 index 00000000..e6d7e140 --- /dev/null +++ b/theme/islandora-objects-grid.tpl.php @@ -0,0 +1,17 @@ + +
+ +
+
+
+
+
+
+ +
diff --git a/theme/islandora-objects-list.tpl.php b/theme/islandora-objects-list.tpl.php new file mode 100644 index 00000000..67f5f02d --- /dev/null +++ b/theme/islandora-objects-list.tpl.php @@ -0,0 +1,29 @@ + +
+ + + +
+
+
+ +
+
+ + + +
+
+ +
+
+
+ + +
diff --git a/theme/islandora-objects.tpl.php b/theme/islandora-objects.tpl.php new file mode 100644 index 00000000..f1893ef6 --- /dev/null +++ b/theme/islandora-objects.tpl.php @@ -0,0 +1,21 @@ + +
+ + $display_links, + 'attributes' => array('class' => array('links', 'inline')), + ) + ); + ?> + + + + +
diff --git a/theme/theme.inc b/theme/theme.inc index 87e44fc1..7c43deef 100644 --- a/theme/theme.inc +++ b/theme/theme.inc @@ -157,3 +157,64 @@ function islandora_preprocess_islandora_object_print(array &$variables) { function theme_islandora_object_print(array &$variables) { return drupal_render($variables['content']); } + +/** + * Implements hook_preprocess_theme(). + */ +function islandora_preprocess_islandora_objects(array &$variables) { + module_load_include('inc', 'islandora_paged_content', 'includes/utilities'); + $display = (empty($_GET['display'])) ? 'grid' : $_GET['display']; + $grid_display = $display == 'grid'; + $list_display = !$grid_display; + $query_params = drupal_get_query_parameters($_GET); + $variables['display_links'] = array( + array( + 'title' => t('Grid view'), + 'href' => url($_GET['q'], array('absolute' => TRUE)), + 'attributes' => array( + 'class' => $grid_display ? 'active' : '', + ), + 'query' => array('display' => 'grid') + $query_params, + ), + array( + 'title' => t('List view'), + 'href' => url($_GET['q'], array('absolute' => TRUE)), + 'attributes' => array( + 'class' => $list_display ? 'active' : '', + ), + 'query' => array('display' => 'list') + $query_params, + ), + ); + // Pager. + $objects = $variables['objects']; + $limit = $variables['limit']; + $page_size = $variables['page_size']; + $page = pager_default_initialize(count($objects), $limit); + $objects = array_slice($objects, $page * $limit, $limit); + $variables['pager'] = theme('pager', array('quantity' => 10)); + // Content. + $map_objects = function($o) { + $o = islandora_object_load($o); + $url = "islandora/object/{$o->id}"; + $link_options = array('html' => TRUE, 'attributes' => array('title' => $o->label)); + $img = theme_image(array('path' => url("$url/datastream/TN/view"), 'attributes' => array())); + $description = NULL; + $dc = DublinCore::importFromXMLString($o['DC']->content); + if ($dc) { + $dc = $dc->asArray(); + $description = $dc['dc:description']['value']; + } + return array( + 'label' => $o->label, + 'class' => drupal_strtolower(preg_replace('/[^A-Za-z0-9]/', '-', $o->id)), + 'link' => l($o->label, $url, $link_options), + 'thumb' => l($img, $url, $link_options), + 'description' => $description, + ); + }; + $objects = array_map($map_objects, $objects); + $theme = $grid_display ? 'islandora_objects_grid' : 'islandora_objects_list'; + $variables['content'] = theme($theme, array('objects' => $objects)); + $module_path = drupal_get_path('module', 'islandora'); + drupal_add_css("$module_path/css/islandora.objects.css"); +}