Browse Source

Merge branch '7.x' of git://github.com/Islandora/islandora into 7.x

pull/457/head
Nelson Hart 11 years ago
parent
commit
c89553c04f
  1. 1
      .travis.yml
  2. 12
      README.md
  3. 23
      build.xml
  4. 13
      css/islandora.base.css
  5. 10
      css/islandora.print.css
  6. 20
      includes/derivatives.inc
  7. 8
      includes/dublin_core.inc
  8. 65
      includes/ingest.form.inc
  9. 60
      includes/metadata.inc
  10. 9
      includes/solution_packs.inc
  11. 1
      includes/tuque.inc
  12. 2
      includes/tuque_wrapper.inc
  13. 73
      includes/utilities.inc
  14. 17
      islandora.api.php
  15. 2
      islandora.info
  16. 172
      islandora.module
  17. 21
      js/spin/LICENSE.txt
  18. 11
      js/spin/README.md
  19. 1
      js/spin/spin.min.js
  20. 58
      js/spinner.js
  21. 473
      tests/datastream_validators.inc
  22. BIN
      tests/fixtures/test.jpg
  23. 161
      tests/islandora_web_test_case.inc
  24. 5
      tests/scripts/travis_setup.sh
  25. 20
      theme/islandora-dublin-core-description.tpl.php
  26. 24
      theme/islandora-dublin-core-display.tpl.php
  27. 18
      theme/islandora-object-print.tpl.php
  28. 24
      theme/theme.inc

1
.travis.yml

@ -8,6 +8,7 @@ branches:
env: env:
- FEDORA_VERSION="3.5" - FEDORA_VERSION="3.5"
- FEDORA_VERSION="3.6.2" - FEDORA_VERSION="3.6.2"
- FEDORA_VERSION="3.7.0"
before_install: before_install:
- export ISLANDORA_DIR=$TRAVIS_BUILD_DIR - export ISLANDORA_DIR=$TRAVIS_BUILD_DIR
- $TRAVIS_BUILD_DIR/tests/scripts/travis_setup.sh - $TRAVIS_BUILD_DIR/tests/scripts/travis_setup.sh

12
README.md

@ -29,6 +29,14 @@ It is expected to be in one of two paths:
- sites/all/libraries/tuque (libraries directory may need to be created) - sites/all/libraries/tuque (libraries directory may need to be created)
- islandora_folder/libraries/tuque - islandora_folder/libraries/tuque
OPTIONAL REQUIREMENTS
---------------------
If you want to support languages other than English download and enable
[String Translation](https://drupal.org/project/i18n), And follow our
[guide](wiki/Multilingual-Support) for setting up additional languges.
INSTALLATION INSTALLATION
------------ ------------
@ -36,6 +44,10 @@ Before installing Islandora the XACML policies located in the policies folder
should be copied into the Fedora global XACML policies folder. This will allow should be copied into the Fedora global XACML policies folder. This will allow
"authenticated users" in Drupal to access Fedora API-M functions. "authenticated users" in Drupal to access Fedora API-M functions.
You will also have to remove some default policies if you want full functionality as well.
Remove deny-purge-datastream-if-active-or-inactive.xml to allow for purging of datastream versions.
CONFIGURATION CONFIGURATION
------------- -------------

23
build.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project name="islandora" default="build"> <project name="islandora" default="build">
<target name="build" depends="clean,prepare,lint,phploc,code_sniff,phpcpd,pdepend,doxygen,phpcb,test" /> <target name="build" depends="clean,prepare,php-eval,lint,phploc,code_sniff,phpcpd,pdepend,doxygen,phpcb,test,cc-process,cc-export" />
<target name="clean" description="Cleanup build artifacts"> <target name="clean" description="Cleanup build artifacts">
<delete dir="${basedir}/build/test" /> <delete dir="${basedir}/build/test" />
@ -9,6 +9,7 @@
<delete dir="${basedir}/build/pdepend" /> <delete dir="${basedir}/build/pdepend" />
<delete dir="${basedir}/build/api" /> <delete dir="${basedir}/build/api" />
<delete dir="${basedir}/build/code-browser" /> <delete dir="${basedir}/build/code-browser" />
<delete dir="${basedir}/build/code_coverage" />
</target> </target>
<target name="prepare" description="Prepares workspace for artifacts" > <target name="prepare" description="Prepares workspace for artifacts" >
@ -17,6 +18,13 @@
<mkdir dir="${basedir}/build/pdepend" /> <mkdir dir="${basedir}/build/pdepend" />
<mkdir dir="${basedir}/build/api" /> <mkdir dir="${basedir}/build/api" />
<mkdir dir="${basedir}/build/code-browser" /> <mkdir dir="${basedir}/build/code-browser" />
<mkdir dir="${basedir}/build/code_coverage" />
</target>
<target name="php-eval" description="Use php-eval to set the code_coverage_filter_files variable">
<exec executable="drush">
<arg line="php-eval &quot;variable_set('code_coverage_files', array('sites/all/modules/islandora/islandora.module', 'sites/all/modules/islandora/islandora.install', 'sites/all/modules/islandora/islandora.api.php', 'sites/all/modules/islandora/islandora.drush.inc', 'sites/all/modules/islandora/islandora.rules.inc', 'sites/all/modules/islandora/includes/add_datastream.form.inc', 'sites/all/modules/islandora/includes/admin.form.inc', 'sites/all/modules/islandora/includes/authtokens.inc', 'sites/all/modules/islandora/includes/breadcrumb.inc', 'sites/all/modules/islandora/includes/content_model.autocomplete.inc', 'sites/all/modules/islandora/includes/datastream.inc', 'sites/all/modules/islandora/includes/delete_datastream.form.inc', 'sites/all/modules/islandora/includes/delete_object.form.inc', 'sites/all/modules/islandora/includes/derivatives.inc', 'sites/all/modules/islandora/includes/dublin_core.inc', 'sites/all/modules/islandora/includes/ingest.form.inc', 'sites/all/modules/islandora/includes/manage_deleted_objects.inc', 'sites/all/modules/islandora/includes/metadata.inc', 'sites/all/modules/islandora/includes/mime_detect.inc', 'sites/all/modules/islandora/includes/object.entity_controller.inc', 'sites/all/modules/islandora/includes/object_properties.form.inc', 'sites/all/modules/islandora/includes/solution_packs.inc', 'sites/all/modules/islandora/includes/tuque.inc', 'sites/all/modules/islandora/includes/tuque_wrapper.inc', 'sites/all/modules/islandora/includes/utilities.inc', 'sites/all/modules/islandora/tests/datastream_validators.inc', 'sites/all/modules/islandora/tests/islandora_web_test_case.inc', 'sites/all/modules/islandora/theme/islandora-dublin-core-description.tpl.php', 'sites/all/modules/islandora/theme/islandora-dublin-core-display.tpl.php', 'sites/all/modules/islandora/theme/islandora-object-edit.tpl.php', 'sites/all/modules/islandora/theme/islandora-object-img-print.tpl.php', 'sites/all/modules/islandora/theme/islandora-object.tpl.php', 'sites/all/modules/islandora/theme/islandora-objects-grid.tpl.php', 'sites/all/modules/islandora/theme/islandora-objects-list.tpl.php', 'sites/all/modules/islandora/theme/islandora-objects.tpl.php', 'sites/all/modules/islandora/theme/theme.inc')); variable_set('code_coverage_modules', array());&quot;"/>
</exec>
</target> </target>
<target name="lint" description="Perform syntax check of sourcecode files"> <target name="lint" description="Perform syntax check of sourcecode files">
@ -81,4 +89,17 @@
<arg line='-c "php ../../../../scripts/run-tests.sh --xml ${basedir}/build/test Islandora"' /> <arg line='-c "php ../../../../scripts/run-tests.sh --xml ${basedir}/build/test Islandora"' />
</exec> </exec>
</target> </target>
<target name="cc-process" description="Processes the code coverage output so it can be exported">
<exec executable="drush">
<arg line="cc-process all"/>
</exec>
</target>
<target name="cc-export" description="Generate a code coverage report for the simpletest run">
<exec executable="drush">
<arg line="cc-export latest --html --csv --tag-html --generate-index --path=${basedir}/build/code_coverage"/>
</exec>
</target>
</project> </project>

13
css/islandora.base.css

@ -43,6 +43,19 @@ dl.islandora-inline-metadata {
padding-left: 40px; padding-left: 40px;
} }
dl.islandora-metadata-fields {
width:100%;
}
.islandora-metadata dt,
.islandora-metadata dd {
border-top:1px solid #e5e5e5;
}
.islandora-metadata dt.first,
.islandora-metadata dd.first {
border-top:0;
}
/* /*
* In this rule, we reset the white-space (see hack above) * In this rule, we reset the white-space (see hack above)
*/ */

10
css/islandora.print.css

@ -4,15 +4,6 @@
* *
* We provide some sane print styling for Drupal, hiding most visuals. * We provide some sane print styling for Drupal, hiding most visuals.
*/ */
a:link,
a:visited { /* underline all links */
text-decoration: underline !important;
}
#site-name a:link,
#site-name a:visited { /* Don't underline header */
text-decoration: none !important;
}
#content a[href^="javascript:"]:after, #content a[href^="javascript:"]:after,
#content a[href^="#"]:after { /* Only display useful links. */ #content a[href^="#"]:after { /* Only display useful links. */
@ -66,6 +57,7 @@ body.sidebar-first {
.book-navigation, .book-navigation,
.forum-topic-navigation, .forum-topic-navigation,
.pager, .pager,
.contextual-links-region,
.feed-icons { /* Hide sidebars and nav elements */ .feed-icons { /* Hide sidebars and nav elements */
visibility: hidden !important; visibility: hidden !important;
display: none !important; display: none !important;

20
includes/derivatives.inc

@ -34,6 +34,7 @@
* watchdog if not defined. * watchdog if not defined.
*/ */
function islandora_do_derivatives(AbstractObject $object, array $options) { function islandora_do_derivatives(AbstractObject $object, array $options) {
module_load_include('inc', 'islandora', 'includes/utilities');
$options += array( $options += array(
'force' => FALSE, 'force' => FALSE,
); );
@ -60,11 +61,16 @@ function islandora_do_derivatives(AbstractObject $object, array $options) {
require_once $hook['file']; require_once $hook['file'];
} }
foreach ($hook['function'] as $function) { foreach ($hook['function'] as $function) {
if (function_exists($function)) {
$logging = call_user_func($function, $object, $options['force']); $logging = call_user_func($function, $object, $options['force']);
if (!empty($logging)) { if (!empty($logging)) {
$results[] = $logging; $results[] = $logging;
} }
} }
else {
watchdog('islandora', 'Unable to call derivative function @function as it was not found!', array('@function' => $function), WATCHDOG_ERROR);
}
}
} }
return $results; return $results;
} }
@ -91,7 +97,19 @@ function islandora_derivative_logging(array $logging_results) {
foreach ($logging_results as $result) { foreach ($logging_results as $result) {
foreach ($result['messages'] as $message) { foreach ($result['messages'] as $message) {
if ($message['type'] === 'dsm') { if ($message['type'] === 'dsm') {
drupal_set_message(filter_xss(format_string($message['message'], isset($message['message_sub']) ? $message['message_sub'] : array())), isset($message['severity']) ? $message['severity'] : 'status'); if (isset($message['severity']) && $message['severity'] != 'status') {
drupal_set_message(filter_xss(format_string($message['message'], isset($message['message_sub']) ? $message['message_sub'] : array())), $message['severity']);
}
else {
if (!isset($_SESSION['islandora_event_messages'])) {
$_SESSION['islandora_event_messages'] = array();
}
$_SESSION['islandora_event_messages'][] = array(
'message' => filter_xss(format_string($message['message'], isset($message['message_sub']) ? $message['message_sub'] : array())),
'severity' => 'status',
);
drupal_set_message(l(t('Derivatives successfully created.'), 'islandora/event-status'), 'status', FALSE);
}
} }
else { else {
// We know what we are doing here. Passing through the translated // We know what we are doing here. Passing through the translated

8
includes/dublin_core.inc

@ -130,8 +130,11 @@ class DublinCore {
} }
$dc_label = explode(':', $field); $dc_label = explode(':', $field);
$element_label = drupal_ucfirst($dc_label[1]); $element_label = drupal_ucfirst($dc_label[1]);
$dc_array[$field]['label'] = $element_label; $i18n_object_id = drupal_strtolower($element_label);
$dc_array[$field]['value'] = $value; $dc_array[$field]['label'] = function_exists('i18n_string') ?
i18n_string("islandora:dc:{$i18n_object_id}:label", $element_label) :
$element_label;
$dc_array[$field]['value'] = filter_xss($value);
$dc_array[$field]['class'] = drupal_strtolower(preg_replace('/[^A-Za-z0-9]/', '-', $field)); $dc_array[$field]['class'] = drupal_strtolower(preg_replace('/[^A-Za-z0-9]/', '-', $field));
$dc_array[$field]['dcterms'] = preg_replace('/^dc/', 'dcterms', $field); $dc_array[$field]['dcterms'] = preg_replace('/^dc/', 'dcterms', $field);
} }
@ -140,7 +143,6 @@ class DublinCore {
return $dc_array; return $dc_array;
} }
/** /**
* Creates a new instance of the class by parsing dc_xml. * Creates a new instance of the class by parsing dc_xml.
* *

65
includes/ingest.form.inc

@ -57,6 +57,12 @@ function islandora_ingest_form(array $form, array &$form_state, array $configura
return islandora_ingest_form_execute_step($form, $form_state); return islandora_ingest_form_execute_step($form, $form_state);
} }
catch (Exception $e) { catch (Exception $e) {
watchdog(
'islandora',
'Exception during ingest form processing with Message: "@exception", and Trace: @trace',
array('@exception' => $e->getMessage(), '@trace' => $e->getTraceAsString()),
WATCHDOG_ERROR
);
drupal_set_message($e->getMessage(), 'error'); drupal_set_message($e->getMessage(), 'error');
return array(array( return array(array(
'#markup' => l(t('Back'), 'javascript:window.history.back();', array('external' => TRUE)))); '#markup' => l(t('Back'), 'javascript:window.history.back();', array('external' => TRUE))));
@ -422,6 +428,9 @@ function islandora_ingest_form_execute_consecutive_callback_steps(array $form, a
function islandora_ingest_form_execute_callback_step(array $form, array &$form_state, array $step) { function islandora_ingest_form_execute_callback_step(array $form, array &$form_state, array $step) {
$args = array(&$form_state); $args = array(&$form_state);
$args = isset($step['do_function']['args']) ? array_merge($args, $step['do_function']['args']) : $args; $args = isset($step['do_function']['args']) ? array_merge($args, $step['do_function']['args']) : $args;
if (isset($step['do_function']['file'])) {
require_once drupal_get_path('module', $step['module']) . "/" . $step['do_function']['file'];
}
call_user_func_array($step['do_function']['function'], $args); call_user_func_array($step['do_function']['function'], $args);
} }
@ -460,6 +469,9 @@ function islandora_ingest_form_undo_consecutive_callback_steps(array $form, arra
function islandora_ingest_form_undo_callback_step(array $form, array &$form_state, array $step) { function islandora_ingest_form_undo_callback_step(array $form, array &$form_state, array $step) {
$args = array(&$form_state); $args = array(&$form_state);
$args = isset($step['undo_function']['args']) ? array_merge($args, $step['undo_function']['args']) : $args; $args = isset($step['undo_function']['args']) ? array_merge($args, $step['undo_function']['args']) : $args;
if (isset($step['undo_function']['file'])) {
require_once drupal_get_path('module', $step['module']) . "/" . $step['undo_function']['file'];
}
call_user_func_array($step['undo_function']['function'], $args); call_user_func_array($step['undo_function']['function'], $args);
} }
@ -697,11 +709,48 @@ function islandora_ingest_form_ingest_button(array &$form_state) {
'#type' => 'submit', '#type' => 'submit',
'#name' => 'ingest', '#name' => 'ingest',
'#value' => t('Ingest'), '#value' => t('Ingest'),
'#process' => array('islandora_ingest_form_ingest_button_process'),
'#validate' => $validate, '#validate' => $validate,
'#submit' => $submit, '#submit' => $submit,
); );
} }
/**
* Process hook for the ingest button, adds the please wait spinning icon.
*/
function islandora_ingest_form_ingest_button_process(array $element) {
$settings['spinner'][$element['#id']] = array(
'message' => t('Please be patient while the the page loads.'),
'options' => array(
'lines' => 10,
'length' => 20,
'width' => 10,
'radius' => 30,
'corners' => 1,
'rotate' => 0,
'direction' => 1,
'color' => '#000',
'speed' => 1,
'trail' => 60,
'shadow' => FALSE,
'hwaccel' => FALSE,
'className' => 'spinner',
'zIndex' => 2000000000,
'top' => 'auto',
'left' => 'auto',
),
);
drupal_add_js($settings, 'setting');
$islandora_path = drupal_get_path('module', 'islandora');
$element['#attached'] = array(
'js' => array(
"$islandora_path/js/spin/spin.min.js",
"$islandora_path/js/spinner.js",
),
);
return $element;
}
/** /**
* The submit handler for the ingest form. * The submit handler for the ingest form.
* *
@ -724,12 +773,24 @@ function islandora_ingest_form_submit(array $form, array &$form_state) {
try { try {
islandora_add_object($object); islandora_add_object($object);
$form_state['redirect'] = "islandora/object/{$object->id}"; $form_state['redirect'] = "islandora/object/{$object->id}";
drupal_set_message(
t('"@label" (ID: @pid) has been ingested.', array('@label' => $object->label, '@pid' => $object->id)),
'status');
} }
catch (Exception $e) { catch (Exception $e) {
// If post hooks throws it may already exist at this point but may be // If post hooks throws it may already exist at this point but may be
// invalid, so don't say failed. // invalid, so don't say failed.
watchdog('islandora', 'Exception Message: @exception.', array('@exception' => $e->getMessage()), WATCHDOG_ERROR); watchdog(
drupal_set_message(t('A problem occured while ingesting "@label" (ID: @pid), please notifiy the administrator.', array('@label' => $object->label, '@pid' => $object->id)), 'error'); 'islandora',
'Exception during ingest with Message: "@exception", and Trace: @trace',
array('@exception' => $e->getMessage(), '@trace' => $e->getTraceAsString()),
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'
);
} }
} }
// XXX: Foreaching with references can be weird... The reference exists in // XXX: Foreaching with references can be weird... The reference exists in

60
includes/metadata.inc

@ -9,20 +9,49 @@
* *
* @param AbstractObject $object * @param AbstractObject $object
* An AbstractObject representing an object within Fedora. * An AbstractObject representing an object within Fedora.
* @param bool $print
* Whether the object is being printed.
* *
* @return string * @return string
* Markup to be rendered for display on Islandora object pages. * Markup to be rendered for display on Islandora object pages.
*/ */
function islandora_retrieve_metadata_markup(AbstractObject $object) { function islandora_retrieve_metadata_markup(AbstractObject $object, $print = FALSE) {
$viewers = module_invoke_all('islandora_metadata_display_info'); $viewers = module_invoke_all('islandora_metadata_display_info');
$viewer = variable_get('islandora_metadata_display', 'dublin_core'); $viewer = variable_get('islandora_metadata_display', 'dublin_core');
$markup = '';
if (isset($viewers[$viewer]['metadata callback'])) {
$markup = call_user_func($viewers[$viewer]['metadata callback'], $object, $print);
// The callback doesn't have any markup provided for this particular object,
// default back to the dublin_core display.
if ($markup === FALSE) {
$markup = call_user_func($viewers['dublin_core']['metadata callback'], $object, $print);
}
}
return $markup;
}
if (isset($viewers[$viewer]['callback'])) { /**
return call_user_func($viewers[$viewer]['callback'], $object); * Retrieves the metadata display description for an Islandora object.
*
* @param AbstractObject $object
* An AbstractObject representing an object within Fedora.
*
* @return string
* Markup to be rendered for description on Islandora object pages.
*/
function islandora_retrieve_description_markup(AbstractObject $object) {
$viewers = module_invoke_all('islandora_metadata_display_info');
$viewer = variable_get('islandora_metadata_display', 'dublin_core');
$markup = '';
if (isset($viewers[$viewer]['description callback'])) {
$markup = call_user_func($viewers[$viewer]['description callback'], $object);
// The callback doesn't have any markup provided for this particular object,
// default back to the dublin_core display.
if ($markup === FALSE) {
$markup = call_user_func($viewers['dublin_core']['description callback'], $object);
} }
else {
return '';
} }
return $markup;
} }
/** /**
@ -111,13 +140,32 @@ function islandora_metadata_display_form_submit($form, $form_state) {
* *
* @param AbstractObject $object * @param AbstractObject $object
* An AbstractObject representing an object within Fedora. * An AbstractObject representing an object within Fedora.
* @param bool $print
* Whether the display is being printed or not.
* *
* @return string * @return string
* Markup representing the rendered metadata from Dublin Core. * Markup representing the rendered metadata from Dublin Core.
*/ */
function islandora_metadata_display_callback(AbstractObject $object) { function islandora_metadata_display_callback(AbstractObject $object, $print = FALSE) {
$elements = array( $elements = array(
'islandora_object' => $object, 'islandora_object' => $object,
'print' => $print,
); );
return theme('islandora_dublin_core_display', $elements); return theme('islandora_dublin_core_display', $elements);
} }
/**
* Metadata description callback for rendering Dublin Core description.
*
* @param AbstractObject $islandora_object
* An AbstractObject representing an object within Fedora.
*
* @return string
* Markup representing the rendered metadata from Dublin Core.
*/
function islandora_metadata_description_callback(AbstractObject $islandora_object) {
$elements = array(
'islandora_object' => $islandora_object,
);
return theme('islandora_dublin_core_description', $elements);
}

9
includes/solution_packs.inc

@ -28,8 +28,17 @@ function islandora_solution_packs_get_required_objects($module = NULL) {
if (!$required_objects) { if (!$required_objects) {
$connection = islandora_get_tuque_connection(); $connection = islandora_get_tuque_connection();
if (isset($module)) {
// The module may be disabled when this function runs, as modules must be
// disabled before they can be uninstalled. We must manually load the
// module file to use it's islandora_required_objects hook.
module_load_include('module', $module, $module);
$required_objects = module_invoke($module, 'islandora_required_objects', $connection);
}
else {
$required_objects = module_invoke_all('islandora_required_objects', $connection); $required_objects = module_invoke_all('islandora_required_objects', $connection);
} }
}
if ($module !== NULL) { if ($module !== NULL) {
if (isset($required_objects[$module])) { if (isset($required_objects[$module])) {

1
includes/tuque.inc

@ -85,6 +85,7 @@ class IslandoraTuque {
} }
if (self::exists()) { if (self::exists()) {
module_load_include('inc', 'islandora', 'includes/tuque_wrapper');
$this->connection = new IslandoraRepositoryConnection($url, $user_string, $pass_string); $this->connection = new IslandoraRepositoryConnection($url, $user_string, $pass_string);
$this->connection->reuseConnection = TRUE; $this->connection->reuseConnection = TRUE;
$this->api = new IslandoraFedoraApi($this->connection); $this->api = new IslandoraFedoraApi($this->connection);

2
includes/tuque_wrapper.inc

@ -207,6 +207,7 @@ class IslandoraFedoraApiM extends FedoraApiM {
'params' => $params, 'params' => $params,
); );
islandora_alter_datastream($object, $datastream, $context); islandora_alter_datastream($object, $datastream, $context);
$params = $context['params'];
if (isset($params['lastModifiedDate'])) { if (isset($params['lastModifiedDate'])) {
$params['lastModifiedDate'] = (string) $object[$dsid]->createdDate; $params['lastModifiedDate'] = (string) $object[$dsid]->createdDate;
} }
@ -244,6 +245,7 @@ class IslandoraFedoraApiM extends FedoraApiM {
'params' => $params, 'params' => $params,
); );
islandora_alter_object($object, $context); islandora_alter_object($object, $context);
$params = $context['params'];
try { try {
if ($context['block']) { if ($context['block']) {
throw new Exception('Modify Object was blocked.'); throw new Exception('Modify Object was blocked.');

73
includes/utilities.inc

@ -908,3 +908,76 @@ function islandora_as_renderable_array(&$markup_array) {
} }
unset($value); unset($value);
} }
/**
* Sanitizes an input string to be valid XML.
*
* @param string $input
* An input string.
* @param string $replacement
* What we are replacing invalid characters with, defaults to ''.
*
* @return string
* The sanitized string.
*/
function islandora_sanitize_input_for_valid_xml($input, $replacement = '') {
$input = preg_replace('/[^\x9\xA\xD\x20-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', $replacement, $input);
return $input;
}
/**
* Page callback for session status messages to be sent to drupal_set_message().
*
* @return array
* Render array containing markup.
*/
function islandora_event_status() {
$results = FALSE;
if (isset($_SESSION['islandora_event_messages'])) {
foreach ($_SESSION['islandora_event_messages'] as $message) {
drupal_set_message($message['message'], $message['severity']);
$results = TRUE;
}
unset($_SESSION['islandora_event_messages']);
}
$text = ($results) ? t('The status messages above will be deleted after viewing this page.') : t('No messages to display.');
return array('#markup' => $text);
}
/**
* Scales the given image.
*
* @param object $file
* The image file to scale.
* @param int $width
* The width to scale the derived image to.
* @param int $height
* The height to scale the derived image to.
*
* @return bool
* TRUE if successful, FALSE otherwise.
*/
function islandora_scale_thumbnail($file, $width, $height) {
$real_path = drupal_realpath($file->uri);
$image = image_load($real_path);
try {
if (!empty($image)) {
$scale = image_scale($image, $width, $height, TRUE);
if ($scale) {
return image_save($image);
}
}
}
catch (exception $e) {
drupal_set_message(t(
"Islandora failed to scale image with message: '@message'",
array("@message" => $e->getMessage())));
watchdog(
'islandora',
'Islandora failed to scale image.<br/> With stack: @trace',
array('@trace' => $e->getTraceAsString()),
WATCHDOG_ERROR
);
}
return FALSE;
}

17
islandora.api.php

@ -427,17 +427,21 @@ function hook_islandora_undeletable_datastreams(array $models) {
* - do_function: An associate array including: * - do_function: An associate array including:
* - 'function': The callback function to be called. * - 'function': The callback function to be called.
* - 'args': An array of arguments to pass to the callback function. * - 'args': An array of arguments to pass to the callback function.
* - 'file': A file to include (relative to the module's path, including
* the file's extension).
* - undo_function: An associate array including: * - undo_function: An associate array including:
* - 'function': The callback function to be called to reverse the * - 'function': The callback function to be called to reverse the
* executed action in the ingest steps. * executed action in the ingest steps.
* - 'args': An array of arguments to pass to the callback function. * - 'args': An array of arguments to pass to the callback function.
* - 'file': A file to include (relative to the module's path, including
* the file's extension).
* Shared parameters between both types: * Shared parameters between both types:
* - weight: The "weight" of this step--heavier(/"larger") values sink to the * - weight: The "weight" of this step--heavier(/"larger") values sink to the
* end of the process while smaller(/"lighter") values are executed first. * end of the process while smaller(/"lighter") values are executed first.
* Both types may optionally include: * Both types may optionally include:
* - module: A module from which we want to load an include. * - module: A module from which we want to load an include.
* "Form" type may optionally include: * "Form" type may optionally include:
* - file: A file to include (relative to the module's path, including the * - 'file': A file to include (relative to the module's path, including the
* file's extension). * file's extension).
*/ */
function hook_islandora_ingest_steps(array $form_state) { function hook_islandora_ingest_steps(array $form_state) {
@ -702,8 +706,12 @@ function hook_islandora_breadcrumbs_alter(&$breadcrumbs, $context) {
* An associative array where the values are the following: * An associative array where the values are the following:
* -label: Human readable display label for selection. * -label: Human readable display label for selection.
* -description: A description of what the metadata display viewer does. * -description: A description of what the metadata display viewer does.
* -callback: A callable function that provides the markup to be passed * -metadata callback: A callable function that provides the markup to be
* off to the template files. * passed off to the template files. Returns markup or FALSE if the viewer
* wishes to default back to the Dublin Core display for the current object.
* -description callback: A callable function that provides the markup to be
* passed for the description. Returns markup or FALSE if the viewer
* wishes to default back to the Dublin Core display for the current object.
* -configuration (Optional): A path to the administration page for the * -configuration (Optional): A path to the administration page for the
* metadata display. * metadata display.
@ -714,7 +722,8 @@ function hook_islandora_metadata_display_info() {
'hookable_displays_yay' => array( 'hookable_displays_yay' => array(
'label' => t('Hookable display yay!'), 'label' => t('Hookable display yay!'),
'description' => t('This is purely an example of how to implement this.'), 'description' => t('This is purely an example of how to implement this.'),
'callback' => 'hookable_displays_some_function_that_returns_markup', 'metadata callback' => 'hookable_displays_some_function_that_returns_metadata_markup',
'description callback' => 'hookable_displays_some_function_that_returns_description_markup',
'configuration' => 'admin/hookable_displays_yay/somepath', 'configuration' => 'admin/hookable_displays_yay/somepath',
), ),
); );

2
islandora.info

@ -1,6 +1,8 @@
name = Islandora name = Islandora
description = "View and manage Fedora objects" description = "View and manage Fedora objects"
package = Islandora package = Islandora
dependencies[] = image
dependencies[] = file
version = 7.x-dev version = 7.x-dev
core = 7.x core = 7.x
configure = admin/islandora/configure configure = admin/islandora/configure

172
islandora.module

@ -105,7 +105,7 @@ function islandora_menu() {
'page callback' => 'drupal_get_form', 'page callback' => 'drupal_get_form',
'page arguments' => array('islandora_metadata_display_form'), 'page arguments' => array('islandora_metadata_display_form'),
'file' => 'includes/metadata.inc', 'file' => 'includes/metadata.inc',
'access arguments' => array('adminisiter site configuration'), 'access arguments' => array('administer site configuration'),
); );
$items['admin/islandora/solution_packs'] = array( $items['admin/islandora/solution_packs'] = array(
'title' => 'Solution packs', 'title' => 'Solution packs',
@ -322,6 +322,13 @@ function islandora_menu() {
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 2), 'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 2),
'load arguments' => array(2), 'load arguments' => array(2),
); );
$items['islandora/event-status'] = array(
'title' => 'Event Status',
'page callback' => 'islandora_event_status',
'type' => MENU_CALLBACK,
'access callback' => TRUE,
'file' => 'includes/utilities.inc',
);
$items[ISLANDORA_CONTENT_MODELS_AUTOCOMPLETE] = array( $items[ISLANDORA_CONTENT_MODELS_AUTOCOMPLETE] = array(
'title' => 'Autocomplete callback', 'title' => 'Autocomplete callback',
'description' => 'Autocomplete a Fedora content model PID.', 'description' => 'Autocomplete a Fedora content model PID.',
@ -462,6 +469,20 @@ function islandora_theme() {
// An example template might be named: // An example template might be named:
// "islandora-dublin-core-display--islandora-27.tpl.php". // "islandora-dublin-core-display--islandora-27.tpl.php".
'pattern' => 'islandora_dublin_core_display__', 'pattern' => 'islandora_dublin_core_display__',
'variables' => array(
'islandora_object' => NULL,
'print' => NULL,
),
),
'islandora_dublin_core_description' => array(
'file' => 'theme/theme.inc',
'template' => 'theme/islandora-dublin-core-description',
// We can add PIDs to the end of this pattern in our preprocess function
// and templates will be able to have have a pid appended to the
// template name to overide a template on a per object basis.
// An example template might be named:
// "islandora-dublin-core-description--islandora-27.tpl.php".
'pattern' => 'islandora_dublin_core_description__',
'variables' => array('islandora_object' => NULL), 'variables' => array('islandora_object' => NULL),
), ),
); );
@ -511,6 +532,48 @@ function islandora_permission() {
); );
} }
/**
* Implements hook_i18n_string_info().
*/
function islandora_i18n_string_info() {
return array(
'islandora' => array(
'title' => t('Islandora'),
'description' => t('Translatable Metadata labels, etc.'),
'format' => FALSE,
'list' => TRUE,
),
);
}
/**
* Implements islandora_i18n_string_list().
*/
function islandora_i18n_string_list($group) {
if ($group == 'islandora') {
return array(
'islandora' => array(
'dc' => array(
'title' => array('label' => 'Title'),
'creator' => array('label' => 'Creator'),
'subject' => array('label' => 'Subject'),
'description' => array('label' => 'Description'),
'publisher' => array('label' => 'Publisher'),
'contributor' => array('label' => 'Contributor'),
'date' => array('label' => 'Date'),
'type' => array('label' => 'Type'),
'format' => array('label' => 'Format'),
'identifier' => array('label' => 'Identifier'),
'source' => array('label' => 'Source'),
'language' => array('label' => 'Language'),
'relation' => array('label' => 'Relation'),
'coverage' => array('label' => 'Coverage'),
),
),
);
}
}
/** /**
* Renders the print page for the given object. * Renders the print page for the given object.
* *
@ -897,7 +960,7 @@ function islandora_view_object(AbstractObject $object) {
), array( ), array(
'type' => 'setting')); 'type' => 'setting'));
drupal_add_js(array('islandora' => array('print_link' => 'islandora/object/' . $object->id . '/print')), array('type' => 'setting')); drupal_add_js(array('islandora' => array('print_link' => 'islandora/object/' . $object->id . '/print_object')), array('type' => 'setting'));
drupal_add_js($path . '/js/add_print.js'); drupal_add_js($path . '/js/add_print.js');
drupal_set_title($object->label); drupal_set_title($object->label);
@ -979,7 +1042,7 @@ function islandora_drupal_title(AbstractObject $object) {
module_load_include('inc', 'islandora', 'includes/breadcrumb'); module_load_include('inc', 'islandora', 'includes/breadcrumb');
drupal_set_breadcrumb(islandora_get_breadcrumbs($object)); drupal_set_breadcrumb(islandora_get_breadcrumbs($object));
return $object->label; return filter_xss($object->label);
} }
/** /**
@ -1010,6 +1073,7 @@ function islandora_default_islandora_view_object($object) {
function islandora_default_islandora_printer_object($object, $alter) { function islandora_default_islandora_printer_object($object, $alter) {
module_load_include('inc', 'islandora', 'includes/utilities'); module_load_include('inc', 'islandora', 'includes/utilities');
module_load_include('inc', 'islandora', 'includes/datastream'); module_load_include('inc', 'islandora', 'includes/datastream');
module_load_include('inc', 'islandora', 'includes/metadata');
$path = drupal_get_path('module', 'islandora'); $path = drupal_get_path('module', 'islandora');
drupal_add_css($path . '/css/islandora.print.css'); drupal_add_css($path . '/css/islandora.print.css');
@ -1024,11 +1088,12 @@ function islandora_default_islandora_printer_object($object, $alter) {
catch (Exception $e) { catch (Exception $e) {
drupal_set_message(t('Error retrieving object %s %t', array('%s' => $islandora_object->id, '%t' => $e->getMessage())), 'error', FALSE); drupal_set_message(t('Error retrieving object %s %t', array('%s' => $islandora_object->id, '%t' => $e->getMessage())), 'error', FALSE);
} }
$metadata = islandora_retrieve_metadata_markup($object, TRUE);
$variables = isset($dc_object) ? $dc_object->asArray() : array(); $variables = isset($dc_object) ? $dc_object->asArray() : array();
$output = theme('islandora_object_print_object', array( $output = theme('islandora_object_print_object', array(
'object' => $object, 'object' => $object,
'dc_array' => $variables, 'dc_array' => $variables,
'metadata' => $metadata,
'islandora_content' => $alter)); 'islandora_content' => $alter));
return array('Default output' => array('#markup' => $output)); return array('Default output' => array('#markup' => $output));
@ -1048,6 +1113,7 @@ function islandora_default_islandora_printer_object($object, $alter) {
* A IslandoraTuque instance * A IslandoraTuque instance
*/ */
function islandora_get_tuque_connection($user = NULL, $url = NULL) { function islandora_get_tuque_connection($user = NULL, $url = NULL) {
module_load_include('inc', 'islandora', 'includes/tuque');
$tuque = &drupal_static(__FUNCTION__); $tuque = &drupal_static(__FUNCTION__);
if (!$tuque) { if (!$tuque) {
if (IslandoraTuque::exists()) { if (IslandoraTuque::exists()) {
@ -1569,6 +1635,7 @@ function islandora_datastream_access($op, $datastream, $user = NULL) {
* Implements hook_islandora_basic_collection_get_query_filters(). * Implements hook_islandora_basic_collection_get_query_filters().
*/ */
function islandora_islandora_basic_collection_get_query_filters() { function islandora_islandora_basic_collection_get_query_filters() {
module_load_include('inc', 'islandora', 'includes/utilities');
$enforced = variable_get('islandora_namespace_restriction_enforced', FALSE); $enforced = variable_get('islandora_namespace_restriction_enforced', FALSE);
if ($enforced) { if ($enforced) {
$namespace_array = islandora_get_allowed_namespaces(); $namespace_array = islandora_get_allowed_namespaces();
@ -1623,6 +1690,100 @@ function islandora_islandora_datastream_modified(AbstractObject $object, Abstrac
} }
/** /**
* Implements hook_form_simpletest_test_form_alter().
*/
function islandora_form_simpletest_test_form_alter(array &$form) {
module_load_include('inc', 'simpletest', 'simpletest.pages');
$form['tests'] = array(
'#type' => 'fieldset',
'#title' => t('Tests'),
'#description' => t('Select the test(s) or test group(s) you would like to run, and click <em>Run tests</em>.<br/><br/>NOTE: Tests in groups prefixed with <em>Islandora</em> generally require a configuration file, found in the Islandora module "tests" folder, as well as the use of the Islandora Drupal filter. Before any tests are run, please ensure that your web server has the appropriate permissions to alter the drupal_filter.xml file in your Fedora config folder. Your original Islandora Drupal filter configuration will NOT be overwritten by tests; HOWEVER, if PHP exits abnormally before the filter is reset, please use the "Repair Drupal Filter" button below.'),
);
$form['tests']['table'] = array(
'#theme' => 'simpletest_test_table',
);
// Generate the list of tests arranged by group.
$groups = simpletest_test_get_all();
foreach ($groups as $group => $tests) {
$form['tests']['table'][$group] = array(
'#collapsed' => TRUE,
);
foreach ($tests as $class => $info) {
$form['tests']['table'][$group][$class] = array(
'#type' => 'checkbox',
'#title' => filter_xss($info['name']),
'#description' => filter_xss($info['description']),
);
}
}
// Operation buttons.
$form['tests']['op'] = array(
'#type' => 'submit',
'#value' => t('Run tests'),
);
$form['reset'] = array(
'#type' => 'fieldset',
'#collapsible' => FALSE,
'#collapsed' => FALSE,
'#title' => t('Repair Islandora Drupal Filter'),
'#description' => t('Attempts to repair the Islandora Drupal filter if a test that alters the Drupal filter has crashed. This is also intended for developers when creating tests built using the IslandoraWebTestCase class.'),
);
$form['reset']['op'] = array(
'#type' => 'submit',
'#value' => t('Repair Drupal Filter'),
'#submit' => array('islandora_repair_drupal_filter'),
);
}
/**
* Submit handler for islandora_form_simpletest_test_form_alter().
*/
function islandora_repair_drupal_filter() {
// Grab the config.
$path = drupal_get_path('module', 'islandora');
if (file_exists("$path/tests/test_config.ini")) {
$configuration = parse_ini_file("$path/tests/test_config.ini");
}
elseif (file_exists("$path/tests/default.test_config.ini")) {
$configuration = parse_ini_file("$path/tests/default.test_config.ini");
}
else {
drupal_set_message(t('Required default.test_config.ini/test_config.ini file not found'), 'error');
return FALSE;
}
// Xpath to the filter 'sql' elements.
$drupal_filter_dom = new DOMDocument();
$drupal_filter_dom->loadXML(file_get_contents($configuration['drupal_filter_file']));
$drupal_filter_xpath = new DOMXPath($drupal_filter_dom);
// Blow out the simpletest stuff.
$entries = 0;
foreach ($drupal_filter_xpath->query('//sql') as $sql) {
if (strpos($sql->nodeValue, 'simpletest') !== FALSE) {
$parent = $sql->parentNode;
$root = $parent->parentNode;
$parent->removeChild($sql);
$root->removeChild($parent);
$entries++;
}
}
file_put_contents($configuration['drupal_filter_file'], $drupal_filter_dom->saveXML());
if ($entries == 0) {
drupal_set_message(t("No simpletest entries were found in the Drupal filter."));
}
else {
drupal_set_message(format_plural($entries, "Removed 1 simpletest entry from the Drupal filter.", "Removed @count simpletest entries from the Drupal filter."));
}
}
/**
* Implements hook_islandora_metadata_display_info(). * Implements hook_islandora_metadata_display_info().
*/ */
function islandora_islandora_metadata_display_info() { function islandora_islandora_metadata_display_info() {
@ -1630,7 +1791,8 @@ function islandora_islandora_metadata_display_info() {
'dublin_core' => array( 'dublin_core' => array(
'label' => t('Dublin Core'), 'label' => t('Dublin Core'),
'description' => t('Dublin Core metadata'), 'description' => t('Dublin Core metadata'),
'callback' => 'islandora_metadata_display_callback', 'metadata callback' => 'islandora_metadata_display_callback',
'description callback' => 'islandora_metadata_description_callback',
), ),
); );
} }

21
js/spin/LICENSE.txt

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2011 Felix Gnass [fgnass at neteye dot de]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

11
js/spin/README.md

@ -0,0 +1,11 @@
CONTENTS OF THIS FILE
---------------------
* summary
SUMMARY
-------
This directory contains the spin.js library: http://fgnass.github.io/spin.js/
downloaded from http://fgnass.github.io/spin.js/dist/spin.min.js on Sept 27th
2013. This project is under version control: https://github.com/fgnass/spin.js.

1
js/spin/spin.min.js vendored

@ -0,0 +1 @@
(function(t,e){if(typeof exports=="object")module.exports=e();else if(typeof define=="function"&&define.amd)define(e);else t.Spinner=e()})(this,function(){"use strict";var t=["webkit","Moz","ms","O"],e={},i;function o(t,e){var i=document.createElement(t||"div"),o;for(o in e)i[o]=e[o];return i}function n(t){for(var e=1,i=arguments.length;e<i;e++)t.appendChild(arguments[e]);return t}var r=function(){var t=o("style",{type:"text/css"});n(document.getElementsByTagName("head")[0],t);return t.sheet||t.styleSheet}();function s(t,o,n,s){var a=["opacity",o,~~(t*100),n,s].join("-"),f=.01+n/s*100,l=Math.max(1-(1-t)/o*(100-f),t),u=i.substring(0,i.indexOf("Animation")).toLowerCase(),d=u&&"-"+u+"-"||"";if(!e[a]){r.insertRule("@"+d+"keyframes "+a+"{"+"0%{opacity:"+l+"}"+f+"%{opacity:"+t+"}"+(f+.01)+"%{opacity:1}"+(f+o)%100+"%{opacity:"+t+"}"+"100%{opacity:"+l+"}"+"}",r.cssRules.length);e[a]=1}return a}function a(e,i){var o=e.style,n,r;i=i.charAt(0).toUpperCase()+i.slice(1);for(r=0;r<t.length;r++){n=t[r]+i;if(o[n]!==undefined)return n}if(o[i]!==undefined)return i}function f(t,e){for(var i in e)t.style[a(t,i)||i]=e[i];return t}function l(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var o in i)if(t[o]===undefined)t[o]=i[o]}return t}function u(t){var e={x:t.offsetLeft,y:t.offsetTop};while(t=t.offsetParent)e.x+=t.offsetLeft,e.y+=t.offsetTop;return e}function d(t,e){return typeof t=="string"?t:t[e%t.length]}var p={lines:12,length:7,width:5,radius:10,rotate:0,corners:1,color:"#000",direction:1,speed:1,trail:100,opacity:1/4,fps:20,zIndex:2e9,className:"spinner",top:"auto",left:"auto",position:"relative"};function c(t){if(typeof this=="undefined")return new c(t);this.opts=l(t||{},c.defaults,p)}c.defaults={};l(c.prototype,{spin:function(t){this.stop();var e=this,n=e.opts,r=e.el=f(o(0,{className:n.className}),{position:n.position,width:0,zIndex:n.zIndex}),s=n.radius+n.length+n.width,a,l;if(t){t.insertBefore(r,t.firstChild||null);l=u(t);a=u(r);f(r,{left:(n.left=="auto"?l.x-a.x+(t.offsetWidth>>1):parseInt(n.left,10)+s)+"px",top:(n.top=="auto"?l.y-a.y+(t.offsetHeight>>1):parseInt(n.top,10)+s)+"px"})}r.setAttribute("role","progressbar");e.lines(r,e.opts);if(!i){var d=0,p=(n.lines-1)*(1-n.direction)/2,c,h=n.fps,m=h/n.speed,y=(1-n.opacity)/(m*n.trail/100),g=m/n.lines;(function v(){d++;for(var t=0;t<n.lines;t++){c=Math.max(1-(d+(n.lines-t)*g)%m*y,n.opacity);e.opacity(r,t*n.direction+p,c,n)}e.timeout=e.el&&setTimeout(v,~~(1e3/h))})()}return e},stop:function(){var t=this.el;if(t){clearTimeout(this.timeout);if(t.parentNode)t.parentNode.removeChild(t);this.el=undefined}return this},lines:function(t,e){var r=0,a=(e.lines-1)*(1-e.direction)/2,l;function u(t,i){return f(o(),{position:"absolute",width:e.length+e.width+"px",height:e.width+"px",background:t,boxShadow:i,transformOrigin:"left",transform:"rotate("+~~(360/e.lines*r+e.rotate)+"deg) translate("+e.radius+"px"+",0)",borderRadius:(e.corners*e.width>>1)+"px"})}for(;r<e.lines;r++){l=f(o(),{position:"absolute",top:1+~(e.width/2)+"px",transform:e.hwaccel?"translate3d(0,0,0)":"",opacity:e.opacity,animation:i&&s(e.opacity,e.trail,a+r*e.direction,e.lines)+" "+1/e.speed+"s linear infinite"});if(e.shadow)n(l,f(u("#000","0 0 4px "+"#000"),{top:2+"px"}));n(t,n(l,u(d(e.color,r),"0 0 1px rgba(0,0,0,.1)")))}return t},opacity:function(t,e,i){if(e<t.childNodes.length)t.childNodes[e].style.opacity=i}});function h(){function t(t,e){return o("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',e)}r.addRule(".spin-vml","behavior:url(#default#VML)");c.prototype.lines=function(e,i){var o=i.length+i.width,r=2*o;function s(){return f(t("group",{coordsize:r+" "+r,coordorigin:-o+" "+-o}),{width:r,height:r})}var a=-(i.width+i.length)*2+"px",l=f(s(),{position:"absolute",top:a,left:a}),u;function p(e,r,a){n(l,n(f(s(),{rotation:360/i.lines*e+"deg",left:~~r}),n(f(t("roundrect",{arcsize:i.corners}),{width:o,height:i.width,left:i.radius,top:-i.width>>1,filter:a}),t("fill",{color:d(i.color,e),opacity:i.opacity}),t("stroke",{opacity:0}))))}if(i.shadow)for(u=1;u<=i.lines;u++)p(u,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(u=1;u<=i.lines;u++)p(u);return n(e,l)};c.prototype.opacity=function(t,e,i,o){var n=t.firstChild;o=o.shadow&&o.lines||0;if(n&&e+o<n.childNodes.length){n=n.childNodes[e+o];n=n&&n.firstChild;n=n&&n.firstChild;if(n)n.opacity=i}}}var m=f(o("group"),{behavior:"url(#default#VML)"});if(!a(m,"transform")&&m.adj)h();else i=a(m,"animation");return c});

58
js/spinner.js

@ -0,0 +1,58 @@
/**
* @file
* Triggers the display of a spinning icon when the form is submitted.
*/
(function ($) {
Drupal.behaviors.spinner = {
attach: function(context, settings) {
// Store what triggered the submit.
$('form').once('submit-resolver', function() {
$(this).click(function(event) {
$(this).data('clicked', $(event.target));
});
$(this).keypress(function(event) {
// On enter the first submit button is assumed as is most often the
// case and this is part of the HTML 5 specification, although some
// Browsers may choose the button with the lowest tab-index.
if (event.which == 13) {
$(this).data('clicked', $(':submit', this).first());
}
});
});
for (var base in settings.spinner) {
var id = '#' + base;
$(id, context).once('spinner', function () {
var spinner = new Spinner(settings.spinner[base].opts);
$(id).parents('form').one('submit', function(event) {
if ($(this).data('clicked').is(id)) {
event.preventDefault();
// Add Message.
var message = $('<div/>').text(settings.spinner[base].message);
$(id).after(message);
// Make UI changes.
spinner.spin(this);
$('#edit-next').hide();
$('#edit-prev').hide();
// Submit the form after a set timeout, this handles problems with
// safari, in that safari submit's immediately..
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
$(':submit').attr('disabled', 'disabled');
}
setTimeout(function() {
// Allow for the button to be clicked, then click it then
// prevent the default behavoir.
$(id).removeAttr('disabled')
.click()
.click(function(event) {
event.preventDefault();
});
}, 500);
}
return true;
});
});
}
}
};
})(jQuery);

473
tests/datastream_validators.inc

@ -0,0 +1,473 @@
<?php
/**
* @file
* Assertions for various datastream types.
*
* For a datastream validator to work correctly with IslandoraWebTestCase::
* validateDatastreams(), it needs to return an array of results, each entry of
* which contains two values: first, TRUE or FALSE, depending on whether or not
* that particular result passed or failed, and second, a string containing a
* message to accompany the result.
*
* It also should contain three parameters, all of which may use any label, but
* must be organized in the following order:
* $object - an object that the datastream can be loaded from.
* $datastream - a DSID to pull from $object.
* $optional_params - a parameter for any data the function requires.
*
* When IslandoraWebTestCase::validateDatastreams() is called, it is passed an
* array of datastreams, each of which is itself an array containing the DSID of
* the datastream, the middle of the function name (image, pdf, tiff, etc.), and
* (optional) data to be passed to that third parameter.
*/
/**
* A function to pass assertions to and receive results from.
*
* @param bool $assertion
* The if/then statement to validate against.
* @param array $results
* An array of results to append the generated result to.
* @param string $pass
* A message to return if the assertion turns up true.
* @param string $fail
* An optional message to return if the assertion turns up false.
* If left empty, the $pass message will be returned.
*
* @return array
* A result that can be made useful in the validation functions below.
*/
function islandora_assert_valid($assertion, $results, $pass, $fail = NULL) {
if ($assertion) {
$result = array(TRUE, $pass);
}
else {
if (isset($fail)) {
$result = array(FALSE, $fail);
}
else {
$result = array(FALSE, $pass);
}
}
array_push($results, $result);
return $results;
}
/**
* Converts a hexidecimal string to an integer.
*
* This is useful for running checks on values that appear in the binary
* of a datastream. Returns FALSE if the hex value contains non-hex characters
* or if the string would not return a 16- or 32-bit formatted big-endian
* signed integer.
*
* @param string $hex
* The hexidecimal string.
*
* @return bool|int
* FALSE on failure, or the integer on success.
*/
function islandora_hex2int($hex) {
// A couple of quick string checks.
if (!ctype_xdigit($hex)) {
drupal_set_message(t('String passed to islandora_hex2int() contains non-hexidecimal characters.'), 'error');
return FALSE;
}
if (!strlen($hex) === 4 || !strlen($hex) === 8) {
drupal_set_message(t('String passed to islandora_hex2int() cannot create a 16- or 32-bit little-endian signed integer'), 'error');
return FALSE;
}
// The actual conversion.
try {
$reverse_hex = implode('', array_reverse(str_split($hex, 2)));
$int = hexdec($reverse_hex);
return $int;
}
catch (Exception $e) {
throw new Exception('An error occurred during the conversion of hexidecimal to integer.', 0, $e);
}
}
/**
* Asserts that an object's given datastreams are common-type image files.
*
* Uses PHPGD to run the assertion check. This means that only certain kinds
* of image files can be checked. Please check the documentation for the PHPGD
* imagecreatefromstring() function to determine what filetypes are valid.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID to check that corresponds to a PHPGD-valid image datastream.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_image_datastream($object, $datastream) {
$datastream_string = $object[$datastream]->content;
$results = array();
$pass = "Image datastream {$datastream} is valid.";
$fail = "Image datastream {$datastream} is either invalid or corrupt.";
$results = islandora_assert_valid(imagecreatefromstring($datastream_string), $results, $pass, $fail);
return $results;
}
/**
* Asserts the validity of any .tif/.tiff datastream.
*
* Does not use the islandora_assert_valid() function, as this is not a simple
* true/false.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID to check that corresponds to a .tif/.tiff datastream.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_tiff_datastream($object, $datastream) {
$datastream_string = $object[$datastream]->content;
$datastream_header_hex = substr(bin2hex($datastream_string), 0, 8);
$results = array();
if ($datastream_header_hex == "49492a00") {
// In this case, the ingested TIFF is designated as using the "Intel
// byte-order" (e.g. little-endian) by starting with the characters "II"
// (repeated so that byte order does not yet need to be significant).
// The number that follows is '42' in little-endian hex, a number of
// 'deep philosophical significance' to the TIFF format creators.
array_push($results, array(TRUE, "{$datastream} datastream asserts that it is a valid Intel-byte-orderded TIF/TIFF file."));
}
elseif ($datastream_header_hex == "4d4d002a") {
// In this case, the ingested TIFF is designated as using the "Motorola
// byte-order" (e.g. big-endian) by starting with the characters "MM"
// instead. 42 follows once again, this time in big-endian hex.
array_push($results, array(TRUE, "{$datastream} datastream asserts that it is a valid Motorola-byte-ordered TIF/TIFF file."));
}
else {
array_push($results, array(FALSE, "{$datastream} datastream does not assert that it is a valid TIF/TIFF file."));
}
return $results;
}
/**
* Asserts the validity of any .jp2 datastream.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID to check that corresponds to a .jp2 datastream.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_jp2_datastream($object, $datastream) {
$datastream_hex = bin2hex($object[$datastream]->content);
$results = array();
// JP2 files begin with an offset header at the second 32-bit integer,
// 0x6A502020. This header is in all .jp2s, and we check for it here.
$pass = "{$datastream} datastream begins correctly with the appropriate .jp2 header.";
$fail = "{$datastream} datastream does not begin with the appropriate .jp2 header.";
$results = islandora_assert_valid(substr($datastream_hex, 8, 8) == '6a502020', $results, $pass, $fail);
// JP2 files have their codestream capped with a marker, 0xFFD9. We're
// just checking for it here to see if the .jp2 encoder finished okay.
$pass = "{$datastream} datastream ends correctly with the appropriate .jp2 marker.";
$fail = "{$datastream} datastream does not end with a .jp2 marker; derivative generation was likely interrupted.";
$results = islandora_assert_valid(substr($datastream_hex, strlen($datastream_hex) - 4, 4) == 'ffd9', $results, $pass, $fail);
return $results;
}
/**
* Asserts the validity of any .pdf datastream.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID to check that corresponds to a .pdf datastream.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_pdf_datastream($object, $datastream) {
$pdf = $object[$datastream]->content;
$pdf_version = substr($pdf, 5, 3);
$results = array();
$pass = "{$datastream} datastream asserts that it is a valid PDF file using PDF version {$pdf_version}";
$fail = "{$datastream} datastream binary header appears to be corrupt and missing a valid PDF signature.";
$results = islandora_assert_valid(substr($pdf, 0, 5) == '%PDF-', $results, $pass, $fail);
$pdf_streams = substr_count(bin2hex($pdf), '0a73747265616d0a');
$pass = "{$datastream} datastream reports the existence of {$pdf_streams} PDF streams. Note that an extremely low number could still indicate corruption.";
$fail = "{$datastream} datastream contains zero PDF streams, and is likely not a PDF file.";
$results = islandora_assert_valid($pdf_streams, $results, $pass, $fail);
$pass = "{$datastream} datastream reports the existence of the closing 'EOF' tag required at the end of PDFs";
$fail = "{$datastream} datastream does not contain the closing 'EOF' tag. If this is the only PDF validation that failed, it is likely that derivative generation was interrupted.";
$results = islandora_assert_valid(strpos(bin2hex($pdf), '0a2525454f460a'), $results, $pass, $fail);
return $results;
}
/**
* Asserts that a string of text shows up inside a datastream.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID to check that corresponds to a datastream containing text.
* @param array $text
* An array of strings/the number of times it should appear in the datastream.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_text_datastream($object, $datastream, array $text) {
$results = array();
$content = $object[$datastream]->content;
$string_count = substr_count($content, $text[0]);
$pass = "{$datastream} datastream contains the word(s) '{$text[0]}' repeated {$string_count} time(s) (expected: {$text[1]}).";
$fail = "{$datastream} datastream contains the word(s) '{$text[0]}' repeated {$string_count} time(s) (expected: {$text[1]}).";
$results = islandora_assert_valid($string_count == $text[1], $results, $pass, $fail);
return $results;
}
/**
* Asserts the validity of any .wav datastraeam.
*
* WAV files contain a rigidly detailed header that contains all sorts of fun
* information we can use to validate things against other things. So, we check
* rigorously that the header contains properly constructed data by looking to
* see if certain values are at their expected byte offset. We also compare
* declared chunk sizes against actual sizes. If any of these are off, WAV
* players will fail to function.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID to check that corresponds to a datastream generated via OCR or HOCR.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_wav_datastream($object, $datastream) {
$results = array();
$wav = bin2hex($object['OBJ']->content);
$wav_subchunk2size = islandora_hex2int(substr($wav, 80, 8));
$wav_samplerate = islandora_hex2int(substr($wav, 48, 8));
$wav_numchannels = islandora_hex2int(substr($wav, 44, 4));
$wav_bytespersample = islandora_hex2int(substr($wav, 68, 4)) / 8;
$wav_numsamples = strlen(substr($wav, 88)) / $wav_numchannels / $wav_bytespersample / 2;
$magic_number = str_split(substr($wav, 0, 24), 8);
$pass = "Header of the {$datastream} datastream contains correct file signature";
$fail = "Header of the {$datastream} datastream contains corrupt file signature";
$results = islandora_assert_valid($magic_number[0] = '52494646' && $magic_number[2] = '57415645', $results, $pass, $fail);
$pass = "{$datastream} datastream chunksize in WAV header is correct";
$fail = "{$datastream} datastream chunksize in WAV header does not match actual chunksize.";
$results = islandora_assert_valid(islandora_hex2int(substr($wav, 8, 8)) === 36 + $wav_subchunk2size, $results, $pass, $fail);
$pass = "{$datastream} datastream contains a 'fmt' subchunk.";
$fail = "{$datastream} datastream is missing the required 'fmt' subchunk.";
$results = islandora_assert_valid(substr($wav, 24, 8) === '666d7420', $results, $pass, $fail);
$pass = "{$datastream} datastream byterate in the WAV header is correct.";
$fail = "{$datastream} datastream byterate in the WAV header does not match actual calculated byterate.";
$results = islandora_assert_valid(islandora_hex2int(substr($wav, 56, 8)) === $wav_samplerate * $wav_numchannels * $wav_bytespersample, $results, $pass, $fail);
$pass = "{$datastream} datastream block alignment is set correctly.";
$fail = "{$datastream} datastream block alignment is off.";
$results = islandora_assert_valid(islandora_hex2int(substr($wav, 64, 4)) === $wav_numchannels * $wav_bytespersample, $results, $pass, $fail);
$pass = "{$datastream} datastream contains 'data' subchunk.";
$fail = "{$datastream} datastream is missing the 'data' subchunk.";
$results = islandora_assert_valid(substr($wav, 72, 8) === '64617461', $results, $pass, $fail);
$pass = "{$datastream} datastream 'data' chunk is the correct size.";
$fail = "{$datastream} datastream 'data' chunk is sized incorrectly.";
$results = islandora_assert_valid($wav_subchunk2size === $wav_numsamples * $wav_numchannels * $wav_bytespersample, $results, $pass, $fail);
return $results;
}
/**
* Asserts the validity of any .mp3 datastream.
*
* Our default setup tries to create an MP3 using VBR, but we do some extra
* checks in case someone turns that off. If the header contains the characters
* 'Xing', it is flagged as VBR, and we can do an in-depth check on each of the
* VBR settings. Otherwise, we look for the basic MP3 signature 'fffa' or 'fffb'
* at the start of the binary.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID of a datastream corresponding to an mp3 file.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_mp3_datastream($object, $datastream) {
$results = array();
$mp3 = bin2hex($object[$datastream]->content);
$mp3_size = strlen($mp3) / 2;
// Looks to see if VBR was set properly by LAME. If so, MATH TIME!
if (strpos($mp3, '58696e67')) {
$mp3_vbrheader = substr($mp3, strpos($mp3, '58696e67'), 240);
// Check the field flags. VBR-formatted MP3 files contain a 32-bit
// integer (stored as $mp3_flag_value) that is a combination of four
// bits, each one indicating the on-off status of a VBR setting, via
// logical OR. Rather than disassembling this value into individual
// bits, we use the algorithm "if (binary_total+bit_value*2)/bit_value*2
// is greater than or equal to bit_value, that bit is turned on" to find
// the status of each bit, so we know whether to offset the rest.
$mp3_field_offset = array(0, 0, 0);
$mp3_flag_value = hexdec(substr($mp3_vbrheader, 8, 8));
// We can't use the first flag, but we still need to offset the rest.
if (($mp3_flag_value + 1) % 2 == 0) {
$mp3_field_offset[0] += 8;
$mp3_field_offset[1] += 8;
$mp3_field_offset[2] += 8;
}
// The second flag leads us to filesize data, which we can verify.
if (($mp3_flag_value + 4) % 4 > 1) {
$mp3_field_bytes = hexdec(substr($mp3_vbrheader, $mp3_field_offset[0] + 16, 8));
$pass = "{$datastream} datastream reported filesize of {$mp3_size} bytes matches size field value of {$mp3_field_bytes}";
$fail = "{$datastream} datastream reported filesize of {$mp3_size} bytes does not match size field value of {$mp3_field_bytes}";
$results = islandora_assert_valid($mp3_size == $mp3_field_bytes, $results, $pass, $fail);
$mp3_field_offset[1] += 8;
$mp3_field_offset[2] += 8;
}
// We can't use the third flag for anything either.
if (($mp3_flag_value + 8) % 8 > 3) {
$mp3_field_offset[2] += 200;
}
// The fourth flag leads us to VBR quality data, which we can validate.
if ($mp3_flag_value > 7) {
$mp3_field_quality = hexdec(substr($mp3_vbrheader, $mp3_field_offset[2] + 16, 8));
$pass = "{$datastream} datastream reports valid VBR quality of {$mp3_field_quality} (expected: between 0-100)";
$fail = "{$datastream} datastream reports invalid VBR quality of {$mp3_field_quality} (expected: between 0-100)";
$results = islandora_assert_valid($mp3_field_quality <= 100 && $mp3_field_quality >= 0, $results, $pass, $fail);
}
}
// Otherwise, just forget everything and check the file signature.
elseif (strpos($mp3, '58696e67') == FALSE && substr($mp3, 0, 4) == 'fffa') {
$results = array(array(TRUE, "{$datastream} datastream is encoded as a valid MPEG-1 Layer 3 file with CRC protection"));
}
elseif (strpos($mp3, '58696e67') == FALSE && substr($mp3, 0, 4) == 'fffb') {
$results = array(array(TRUE, "{$datastream} datastream is encoded as a valid unprotected MPEG-1 Layer 3 file"));
}
else {
$results = array(array(FALSE, "{$datastream} datastream is corrupt and does not identify as a valid MP3."));
}
return $results;
}
/**
* Attempts to validate an .mp4 datastream.
*
* MP4 files are a subset of the ISO file format specification, and as such need
* to contain a 64-bit declaration of type within the first eight eight bytes of
* the file. This declaration is comprised of the characters 'ftyp', followed by
* a four-character filetype code. Below, we look for 'ftyp', and then pass the
* filetype code to the test message.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID of a datastream corresponding to an mp4 file.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_mp4_datastream($object, $datastream) {
$results = array();
$mp4 = $object[$datastream]->content;
if (strpos($mp4, 'ftyp')) {
$mp4_ftyp = substr(strpos($mp4, 'ftyp'), 4, 4);
}
$pass = "{$datastream} datastream asserts that it is a valid ISO-formatted video file using ftyp {$mp4_ftyp}";
$fail = "{$datastream} datastream is not a valid ISO-formatted video";
$results = islandora_assert_valid(strpos($mp4, 'ftyp'), $results, $pass, $fail);
return $results;
}
/**
* Attempts to validate an .ogg/ogv datastream using Vorbis and Theora encoding.
*
* OGG files are made up of several 'pages' of OGG data, each prefaced with an
* OGG marker - the letters 'OggS'. The file header also contains information on
* what encoders were used to create the file. Here, we're looking for at least
* one OGG page, and confirming that the file asserts the Theora and Vorbis
* codecs were used to create the file.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID of a datastream corresponding to an ogg file.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_ogg_datastream($object, $datastream) {
$results = array();
$ogg = $object[$datastream]->content;
$ogg_pages = substr_count($ogg, 'OggS');
$pass = "{$datastream} datastream asserts that it contains {$ogg_pages} Ogg pages (even a very small file should contain several).";
$fail = "{$datastream} datastream contains no Ogg pages.";
$results = islandora_assert_valid(substr_count($ogg, 'OggS'), $results, $pass, $fail);
$pass = "{$datastream} datastream asserts that it contains Theora-encoded video data.";
$fail = "{$datastream} datastream contains no marker indicating the presence of Theora-encoded video data.";
$results = islandora_assert_valid(substr_count($ogg, 'theora'), $results, $pass, $fail);
$pass = "{$datastream} datastream asserts that it contains Vorbis-encoded audio data";
$fail = "{$datastream} datastream contains no marker indicating the presence of Vorbis-encoded audio data.";
$results = islandora_assert_valid(substr_count($ogg, 'vorbis'), $results, $pass, $fail);
return $results;
}
/**
* Attempts to validate an .mkv datastream.
*
* There's not much we can do to check an MKV file, since the format is really,
* really loose. We do know a couple of things though - first, since MKV is an
* EBML format, the first four characters will always be the same. Since they're
* non-standard characters, we're looking at their hex values instead. And
* second, we know that the file will contain the declaration 'matroska' soon
* after. We could look for this in the binary, but we already have the hex-
* translated version, so we just look for 'matroska' in hex.
*
* @param AbstractObject $object
* The PID of the object.
* @param string $datastream
* A DSID of a datastream corresponding to an MKV file.
*
* @return array
* A series of TRUE(pass)/FALSE(fail) results paired with result messages.
*/
function islandora_validate_mkv_datastream($object, $datastream) {
$results = array();
$mkv = bin2hex($object[$datastream]->content);
$pass = "{$datastream} datastream asserts that it is an EBML-formatted file";
$fail = "{$datastream} datastream is not an EBML-formatted file.";
$results = islandora_assert_valid(substr($mkv, 0, 8) == '1a45dfa3', $results, $pass, $fail);
$pass = "{$datastream} datastream asserts that its EBML DocType is Matroska";
$fail = "{$datastream} datastream does not contain a Matroska EBML DocType marker.";
$results = islandora_assert_valid(substr_count($mkv, '6d6174726f736b61') == 1, $results, $pass, $fail);
return $results;
}

BIN
tests/fixtures/test.jpg vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

161
tests/islandora_web_test_case.inc

@ -74,15 +74,22 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
$connection_info = Database::getConnectionInfo('default'); $connection_info = Database::getConnectionInfo('default');
$drupal_filter_dom = new DomDocument(); $drupal_filter_dom = new DomDocument();
$drupal_filter_dom->loadXML($this->originalDrupalFilterContent); $drupal_filter_dom->loadXML($this->originalDrupalFilterContent);
$drupal_filter_xpath = new DOMXPath($drupal_filter_dom);
$server = $connection_info['default']['host']; $server = $connection_info['default']['host'];
$dbname = $connection_info['default']['database']; $dbname = $connection_info['default']['database'];
$user = $connection_info['default']['username']; $user = $connection_info['default']['username'];
$password = $connection_info['default']['password']; $password = $connection_info['default']['password'];
$port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306';
$prefix = $connection_info['default']['prefix']['default']; $prefix = $connection_info['default']['prefix']['default'];
$results = $drupal_filter_xpath->query("/FilterDrupal_Connection/connection[@server='$server' and @dbname='$dbname' and @user='$user' and @password='$password' and @port='$port']/sql"); $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0);
$results->item(0)->nodeValue = "SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;"; $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0);
$connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node);
$connection_node->setAttributeNode(new DOMAttr('server', $server));
$connection_node->setAttributeNode(new DOMAttr('dbname', $dbname));
$connection_node->setAttributeNode(new DOMAttr('user', $user));
$connection_node->setAttributeNode(new DOMAttr('password', $password));
$connection_node->setAttributeNode(new DOMAttr('port', $port));
$sql_node = $connection_node->appendChild(new DOMElement('sql'));
$sql_node->appendChild($drupal_filter_dom->createTextNode("SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;"));
file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML());
} }
@ -181,29 +188,37 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
} }
/** /**
* Asserts that an object's given datastreams are common-type image files. * Attempts to validate an array of datastreams, generally via binary checks.
* *
* Uses PHPGD to run the assertion check. This means that only certain kinds * These functions exist in, and can be added to, datastream_validators.inc,
* of image files can be checked. Please check the documentation for the PHPGD * which is found in this folder.
* imagecreatefromstring() function to determine what filetypes are valid.
* *
* @param AbstractObject $object * $param AbstractObject $object
* The PID of the object. * The object to load datastreams from.
* @param array $datastreams * $param array $datastreams
* An array of datastreams to check. * An array of paired DSIDs, validate function names, and optional params.
*/ */
public function assertImageDatastreams($object, array $datastreams) { public function validateDatastreams($object, array $datastreams) {
if (!is_object($object)) { if (!is_object($object)) {
$this->fail("Failed. Object passed in is invalid.", 'Islandora'); $this->fail("Failed. Object passed in is invalid.", 'Islandora');
} }
else { module_load_include('inc', 'islandora', 'tests/datastream_validators');
foreach ($datastreams as $datastream) { foreach ($datastreams as $datastream) {
$datastream_string = $object[$datastream]->content; if (isset($object[$datastream[0]])) {
if (!imagecreatefromstring($datastream_string)) { $function = 'islandora_validate_' . $datastream[1] . '_datastream';
$this->fail("Image datastream {$datastream} is either invalid or corrupt.", 'Islandora'); if (function_exists($function)) {
if (isset($datastream[2])) {
$results = $function($object, $datastream[0], $datastream[2]);
}
else {
$results = $function($object, $datastream[0]);
}
foreach ($results as $result) {
$this->assertTrue($result[0], $result[1], 'Islandora');
}
} }
else { else {
$this->pass("Image datastream {$datastream} is valid.", 'Islandora'); $this->fail("No {$datastream[0]} validation function exists for the {$datastream[1]} datastream.", 'Islandora');
} }
} }
} }
@ -240,10 +255,16 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
* @param string $button * @param string $button
* The label of the first 'Delete' button * The label of the first 'Delete' button
*/ */
public function deleteObject($pid, $button = 'Delete') { public function deleteObject($pid, $button = NULL) {
$path = 'islandora/object/' . $pid . '/manage/properties'; $path = 'islandora/object/' . $pid . '/manage/properties';
$edit = array(); $edit = array();
if (isset($button)) {
$this->drupalPost($path, $edit, $button); $this->drupalPost($path, $edit, $button);
}
else {
$object = islandora_object_load($pid);
$this->drupalPost($path, $edit, "Permanently remove '{$object->label}' from repository");
}
$this->drupalPost($this->url, $edit, t('Delete')); $this->drupalPost($this->url, $edit, t('Delete'));
$object = islandora_object_load($pid); $object = islandora_object_load($pid);
@ -252,34 +273,98 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
} }
/** /**
* Reverses a hex string and converts it to a little-endian-formatted integer. * Constructs and ingests a Fedora object and datastream(s) via tuque.
* *
* This is useful for running checks on values that appear in the binary * All keys inside the parameter arrays for this function are optional. it
* of a datastream. Returns FALSE if the hex value contains non-hex characters * can be run simply by calling $this->ingestConstructedObject();.
* or if the string would not return a 16- or 32-bit formatted big-endian
* signed integer.
* *
* @param string $hex * @param array $properties
* The hex value being converted. * An array containing object information using these keys:
* 'label' - The object label; randomized if not set.
* 'pid' - 'namespace:pid', or just 'namespace' to generate the suffix.
* 'models' - An array that can contain multiple content model PIDs.
* 'owner' - The object's owner.
* 'parent' - The PID of the parent collection.
* @param array $datastreams
* An array containing zero or more datastream arrays that use the keys:
* 'dsid' - the datastream ID; randomized if not set.
* 'path' - The path to the file to use; defaults to fixtures/test.jpg.
* 'control_group' - The single-letter control group identifier.
* 'mimetype' - The datastream's mimetype.
* *
* @return bool|int * @return bool|array
* FALSE or the integer value that is converted. * FALSE if the object ingest failed, or the object array if successful.
*/ */
public function convertHexToInt($hex) { public function ingestConstructedObject(array $properties = array(), array $datastreams = array()) {
module_load_include('inc', 'islandora', 'includes/tuque');
$tuque = new IslandoraTuque();
$repository = $tuque->repository;
if (!isset($properties['pid'])) {
$properties['pid'] = "islandora";
}
$object = $repository->constructObject($properties['pid']);
// A couple of quick string checks. // Set the object properties before ingesting it.
if (!ctype_xdigit($hex)) { if (isset($properties['label'])) {
$this->fail('String passed to convertHexToInt() contains non-hexidecimal characters.', 'PHP'); $object->label = $properties['label'];
return FALSE;
} }
if (!strlen($hex) === 4 || !strlen($hex) === 8) { else {
$this->fail('String passed to convertHexToInt() cannot create a 16- or 32-bit little-endian signed integer', 'PHP'); $properties['label'] = $this->randomName(16);
$object->label = $properties['label'];
}
if (isset($properties['owner'])) {
$object->owner = $properties['owner'];
}
if (isset($properties['models']) && is_array($properties['models'])) {
foreach ($properties['models'] as $model) {
$object->relationships->add(FEDORA_MODEL_URI, 'hasModel', $model);
}
}
elseif (isset($properties['models']) && !is_array($properties['models'])) {
$this->fail(t("'models' key of properties variable is not an array. Content model(s) will not be set."), 'Islandora');
}
$repository->ingestObject($object);
if (!$object) {
$this->fail(t("Failed to ingest object."), 'Islandora');
return FALSE; return FALSE;
} }
else {
$this->pass(t("Ingested object %object", array('%object' => $object->id)), 'Islandora');
}
// The actual conversion. // Chuck in some datastreams.
$reverse_hex = implode('', array_reverse(str_split($hex, 2))); if (!empty($datastreams)) {
$int = hexdec($reverse_hex); foreach ($datastreams as $datastream) {
return $int; if (!isset($datastream['dsid'])) {
$datastream['dsid'] = $this->randomName(8);
}
if (!isset($datastream['path'])) {
$datastream['path'] = drupal_get_path('module', 'islandora') . '/tests/fixtures/test.jpg';
} }
if (!isset($datastream['control_group'])) {
$new_datastream = $object->constructDatastream($datastream['dsid']);
}
else {
$new_datastream = $object->constructDatastream($datastream['dsid'], $datastream['control_group']);
}
$new_datastream->label = $datastream['dsid'];
if (isset($datastream['mimetype'])) {
$new_datastream->mimetype = $datastream['mimetype'];
}
$new_datastream->setContentFromFile($datastream['path']);
$object->ingestDatastream($new_datastream);
}
}
// Add a parent relationship, if necessary.
if (isset($properties['parent'])) {
$object->relationships->add(FEDORA_RELS_EXT_URI, 'isMemberOfCollection', $properties['parent']);
}
return $object;
}
} }

5
tests/scripts/travis_setup.sh

@ -9,6 +9,7 @@ git clone git://github.com/Islandora/tuque.git
git clone -b $FEDORA_VERSION git://github.com/Islandora/islandora_tomcat.git git clone -b $FEDORA_VERSION git://github.com/Islandora/islandora_tomcat.git
cd islandora_tomcat cd islandora_tomcat
export CATALINA_HOME='.' export CATALINA_HOME='.'
export JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=512m -XX:+CMSClassUnloadingEnabled -Djavax.net.ssl.trustStore=$CATALINA_HOME/fedora/server/truststore -Djavax.net.ssl.trustStorePassword=tomcat"
./bin/startup.sh ./bin/startup.sh
cd $HOME cd $HOME
pear upgrade --force Console_Getopt pear upgrade --force Console_Getopt
@ -20,11 +21,11 @@ pear channel-discover pear.phpqatools.org
pear channel-discover pear.netpirates.net pear channel-discover pear.netpirates.net
pear install pear/PHP_CodeSniffer pear install pear/PHP_CodeSniffer
pear install pear.phpunit.de/phpcpd pear install pear.phpunit.de/phpcpd
pear install drush/drush pear install drush/drush-5.9.0
phpenv rehash phpenv rehash
drush dl --yes drupal drush dl --yes drupal
cd drupal-* cd drupal-*
drush si standard --db-url=mysql://drupal:drupal@localhost/drupal --yes drush si minimal --db-url=mysql://drupal:drupal@localhost/drupal --yes
drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/dev/null & drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/dev/null &
ln -s $ISLANDORA_DIR sites/all/modules/islandora 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 mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/islandora/tests/test_config.ini

20
theme/islandora-dublin-core-description.tpl.php

@ -0,0 +1,20 @@
<?php
/**
* @file
* This is the template file for the Dublin Core metadata description.
*
* Available variables:
* - $islandora_object: The Islandora object rendered in this template file
* $dc_array: The DC datastream object values as a sanitized array. This
* includes label, value and class name.
*
* @see template_preprocess_islandora_dublin_core_description()
* @see theme_islandora_dublin_core_description()
*/
?>
<div class="islandora-metadata-sidebar">
<?php if (!empty($dc_array['dc:description']['value'])): ?>
<h2><?php print $dc_array['dc:description']['label']; ?></h2>
<p property="description"><?php print $dc_array['dc:description']['value']; ?></p>
<?php endif; ?>
</div>

24
theme/islandora-dublin-core-display.tpl.php

@ -1,14 +1,30 @@
<fieldset class="collapsible collapsed"> <?php
/**
* @file
* This is the template file for the object page for large image
*
* Available variables:
* - $islandora_object: The Islandora object rendered in this template file
* - $islandora_dublin_core: The DC datastream object
* - $dc_array: The DC datastream object values as a sanitized array. This
* includes label, value and class name.
* - $islandora_object_label: The sanitized object label.
*
* @see template_preprocess_islandora_dublin_core_display()
* @see theme_islandora_dublin_core_display()
*/
?>
<fieldset <?php $print ? print('class="islandora islandora-metadata"') : print('class="islandora islandora-metadata collapsible collapsed"');?>>
<legend><span class="fieldset-legend"><?php print t('Details'); ?></span></legend> <legend><span class="fieldset-legend"><?php print t('Details'); ?></span></legend>
<div class="fieldset-wrapper"> <div class="fieldset-wrapper">
<dl xmlns:dcterms="http://purl.org/dc/terms/" class="islandora-inline-metadata islandora-metadata-fields"> <dl xmlns:dcterms="http://purl.org/dc/terms/" class="islandora-inline-metadata islandora-metadata-fields">
<?php $row_field = 0; ?> <?php $row_field = 0; ?>
<?php foreach($dc_array as $key => $value): ?> <?php foreach($dc_array as $key => $value): ?>
<dt property="<?php print $value['dcterms']; ?>" content="<?php print $value['value']; ?>" class="<?php print $value['class']; ?><?php print $row_field == 0 ? ' first' : ''; ?>"> <dt property="<?php print $value['dcterms']; ?>" content="<?php print filter_xss($value['value']); ?>" class="<?php print $value['class']; ?><?php print $row_field == 0 ? ' first' : ''; ?>">
<?php print $value['label']; ?> <?php print filter_xss($value['label']); ?>
</dt> </dt>
<dd class="<?php print $value['class']; ?><?php print $row_field == 0 ? ' first' : ''; ?>"> <dd class="<?php print $value['class']; ?><?php print $row_field == 0 ? ' first' : ''; ?>">
<?php print $value['value']; ?> <?php print filter_xss($value['value']); ?>
</dd> </dd>
<?php $row_field++; ?> <?php $row_field++; ?>
<?php endforeach; ?> <?php endforeach; ?>

18
theme/islandora-object-print.tpl.php

@ -10,21 +10,5 @@
<div> <div>
<?php print $islandora_content; ?> <?php print $islandora_content; ?>
</div> </div>
<fieldset class="islandora-basic-image-metadata islandora"> <?php print $metadata; ?>
<legend><span class="fieldset-legend"><?php print t('Details'); ?></span></legend>
<div class="fieldset-wrapper">
<dl class="islandora-inline-metadata islandora-basic-image-fields">
<?php $row_field = 0; ?>
<?php foreach( $dc_array as $key => $value): ?>
<dt class="<?php print $value['class']; ?><?php print $row_field == 0 ? ' first' : ''; ?>">
<?php print $value['label']; ?>
</dt>
<dd class="<?php print $value['class']; ?><?php print $row_field == 0 ? ' first' : ''; ?>">
<?php print $value['value']; ?>
</dd>
<?php $row_field++; ?>
<?php endforeach; ?>
</dl>
</div>
</fieldset>
</div> </div>

24
theme/theme.inc

@ -39,7 +39,7 @@ function islandora_preprocess_islandora_default_edit(array &$variables) {
); );
$row[] = array( $row[] = array(
'class' => 'datastream-label', 'class' => 'datastream-label',
'data' => $ds->label, 'data' => filter_xss($ds->label),
); );
$row[] = array( $row[] = array(
'class' => 'datastream-control', 'class' => 'datastream-control',
@ -47,7 +47,7 @@ function islandora_preprocess_islandora_default_edit(array &$variables) {
); );
$row[] = array( $row[] = array(
'class' => 'datastream-mime', 'class' => 'datastream-mime',
'data' => $ds->mimeType, 'data' => filter_xss($ds->mimeType),
); );
$row[] = array( $row[] = array(
'class' => 'datastream-size', 'class' => 'datastream-size',
@ -81,7 +81,7 @@ function islandora_preprocess_islandora_default_edit(array &$variables) {
); );
$rows[] = $row; $rows[] = $row;
} }
$caption = $islandora_object->label . ' - ' . $islandora_object->id; $caption = filter_xss($islandora_object->label) . ' - ' . $islandora_object->id;
$table = array( $table = array(
'colgroups' => NULL, 'colgroups' => NULL,
'sticky' => TRUE, 'sticky' => TRUE,
@ -438,6 +438,22 @@ function islandora_preprocess_islandora_dublin_core_display(array &$variables) {
drupal_set_message(t('Error retrieving object %s %t', array('%s' => $islandora_object->id, '%t' => $e->getMessage())), 'error', FALSE); drupal_set_message(t('Error retrieving object %s %t', array('%s' => $islandora_object->id, '%t' => $e->getMessage())), 'error', FALSE);
} }
} }
$variables['islandora_dublin_core'] = isset($dc_object) ? $dc_object : NULL; $variables['dc_array'] = isset($dc_object) ? $dc_object->asArray() : array();
}
/**
* Implements hook_preprocess().
*/
function islandora_preprocess_islandora_dublin_core_description(array &$variables) {
$islandora_object = $variables['islandora_object'];
if (islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $islandora_object['DC'])) {
try {
$dc = $islandora_object['DC']->content;
$dc_object = DublinCore::importFromXMLString($dc);
}
catch (Exception $e) {
drupal_set_message(t('Error retrieving object %s %t', array('%s' => $islandora_object->id, '%t' => $e->getMessage())), 'error', FALSE);
}
}
$variables['dc_array'] = isset($dc_object) ? $dc_object->asArray() : array(); $variables['dc_array'] = isset($dc_object) ? $dc_object->asArray() : array();
} }

Loading…
Cancel
Save