Browse Source

Merge pull request #2 from Islandora/7.x

Test
pull/436/head
Jonathan Green 11 years ago
parent
commit
fad277239f
  1. 1
      .travis.yml
  2. 12
      README.md
  3. 23
      build.xml
  4. 13
      css/islandora.base.css
  5. 64
      css/islandora.print.css
  6. 14
      css/islandora.theme.css
  7. BIN
      images/print-icon.png
  8. 11
      includes/add_datastream.form.inc
  9. 6
      includes/admin.form.inc
  10. 6
      includes/authtokens.inc
  11. 2
      includes/breadcrumb.inc
  12. 207
      includes/datastream.inc
  13. 268
      includes/datastream.version.inc
  14. 123
      includes/derivatives.inc
  15. 8
      includes/dublin_core.inc
  16. 98
      includes/ingest.form.inc
  17. 340
      includes/manage_deleted_objects.inc
  18. 171
      includes/metadata.inc
  19. 16
      includes/mime_detect.inc
  20. 3
      includes/object.entity_controller.inc
  21. 85
      includes/object_properties.form.inc
  22. 11
      includes/solution_packs.inc
  23. 1
      includes/tuque.inc
  24. 8
      includes/tuque_wrapper.inc
  25. 87
      includes/utilities.inc
  26. 227
      islandora.api.php
  27. 14
      islandora.drush.inc
  28. 5
      islandora.info
  29. 615
      islandora.module
  30. 18
      js/add_print.js
  31. 21
      js/spin/LICENSE.txt
  32. 11
      js/spin/README.md
  33. 1
      js/spin/spin.min.js
  34. 58
      js/spinner.js
  35. 2
      tests/authtokens.test
  36. 160
      tests/datastream_cache.test
  37. 473
      tests/datastream_validators.inc
  38. 198
      tests/datastream_versions.test
  39. 296
      tests/derivatives.test
  40. BIN
      tests/fixtures/test.jpg
  41. 6
      tests/hooked_access.test
  42. 7
      tests/islandora_derivatives_test.info
  43. 202
      tests/islandora_derivatives_test.module
  44. 5
      tests/islandora_hooked_access_test.module
  45. 12
      tests/islandora_ingest_test.module
  46. 50
      tests/islandora_manage_permissions.test
  47. 180
      tests/islandora_web_test_case.inc
  48. 7
      tests/scripts/travis_setup.sh
  49. 20
      theme/islandora-dublin-core-description.tpl.php
  50. 33
      theme/islandora-dublin-core-display.tpl.php
  51. 18
      theme/islandora-object-img-print.tpl.php
  52. 14
      theme/islandora-object-print.tpl.php
  53. 1
      theme/islandora-object.tpl.php
  54. 235
      theme/theme.inc

1
.travis.yml

@ -8,6 +8,7 @@ branches:
env:
- FEDORA_VERSION="3.5"
- FEDORA_VERSION="3.6.2"
- FEDORA_VERSION="3.7.0"
before_install:
- export ISLANDORA_DIR=$TRAVIS_BUILD_DIR
- $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)
- 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
------------
@ -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
"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
-------------

23
build.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<delete dir="${basedir}/build/test" />
@ -9,6 +9,7 @@
<delete dir="${basedir}/build/pdepend" />
<delete dir="${basedir}/build/api" />
<delete dir="${basedir}/build/code-browser" />
<delete dir="${basedir}/build/code_coverage" />
</target>
<target name="prepare" description="Prepares workspace for artifacts" >
@ -17,6 +18,13 @@
<mkdir dir="${basedir}/build/pdepend" />
<mkdir dir="${basedir}/build/api" />
<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 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"' />
</exec>
</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>

13
css/islandora.base.css

@ -43,6 +43,19 @@ dl.islandora-inline-metadata {
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)
*/

64
css/islandora.print.css

@ -0,0 +1,64 @@
/**
* @file
* Print styling
*
* We provide some sane print styling for Drupal, hiding most visuals.
*/
#content a[href^="javascript:"]:after,
#content a[href^="#"]:after { /* Only display useful links. */
/* content: ""; */
}
#content abbr[title]:after { /* Add visible title after abbreviations. */
/* content: " (" attr(title) ")"; */
}
#content {
left: 0 !important;
width: 100% !important;
}
uncomment when ready to test printing
#header {
display: none !important;
}
#content { /* Un-float the content */
float: none !important;
width: 100% !important;
margin: 0 !important;
padding: 0 !important;
}
body,
#page,
#main,
#content { /* Turn off any background colors or images */
color: #000;
background-color: transparent !important;
background-image: none !important;
}
body.sidebar-first {
left: 0 !important;
width: 100% !important;
}
#skip-link,
#toolbar,
#navigation,
/* .region-sidebar-first, */
/* .region-sidebar-second, */
#header,
#footer,
.breadcrumb,
.tabs,
.action-links,
.links,
.book-navigation,
.forum-topic-navigation,
.pager,
.contextual-links-region,
.feed-icons { /* Hide sidebars and nav elements */
visibility: hidden !important;
display: none !important;
}

14
css/islandora.theme.css

@ -11,16 +11,16 @@
dl.islandora-object-tn {
float: left;
width: 20.8333%;
padding: 0 10px 0 0;
margin: 1.5em 0;
padding: 0 10px 0 0;
width: 20.8333%;
}
dl.islandora-object-fields {
border-top: 0px solid #ddd;
float: right;
width:79.1666%;
border-top:0px solid #ddd;
margin: 1.5em 0;
width: 79.1666%;
}
.islandora-object-fields dt {
@ -28,13 +28,13 @@ dl.islandora-object-fields {
}
.islandora-object-fields dt.first {
border-top:0;
border-top: 0;
}
.islandora-object-fields dt,
.islandora-object-fields dd {
padding:6px 2% 4px;
border-top:1px solid #ddd;
border-top: 1px solid #ddd;
padding: 6px 2% 4px;
}
.islandora-object-fields dt.first,

BIN
images/print-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

11
includes/add_datastream.form.inc

@ -32,7 +32,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract
'#attributes' => array(
'enctype' => 'multipart/form-data',
),
'dsid' => array(
'dsid_fieldset' => array(
'#type' => 'fieldset',
'#title' => 'Add a datastream',
'#collapsible' => FALSE,
@ -40,7 +40,6 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract
'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, dashes and underscores. The following datastreams are defined by this content model but don't currently exist: <b>@unused_dsids</b>.", array('@unused_dsids' => $unused_datastreams)),
),
'#type' => 'textfield',
'#size' => 64,
'#maxlength' => 64,
@ -79,6 +78,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract
'#type' => 'submit',
'#value' => t('Add Datastream'),
),
),
);
}
@ -148,7 +148,7 @@ function islandora_add_datastream_form_field_does_not_contain_a_forward_slash(ar
}
/**
* Checks if the given datastream requires the upload to be a certian MIME type.
* Checks if the given datastream requires the upload to be a certain MIME type.
*
* @param array $form
* The Drupal form.
@ -161,7 +161,10 @@ function islandora_add_datastream_form_validate(array $form, array &$form_state)
if (isset($form_state['datastream_requirements'][$dsid]) && $file) {
$allowed_types = $form_state['datastream_requirements'][$dsid]['mime'];
$mime_detect = new MimeDetect();
$allowed_extensions = array_map(array($mime_detect, 'getExtension'), $allowed_types);
$allowed_extensions = array();
foreach ($allowed_types as $mime) {
$allowed_extensions = array_merge($allowed_extensions, $mime_detect->getValidExtensions($mime));
}
$errors = file_validate_extensions($file, implode(' ', $allowed_extensions));
if (count($errors) > 0) {
form_set_error('file', $errors[0]);

6
includes/admin.form.inc

@ -55,6 +55,12 @@ function islandora_repository_admin(array $form, array &$form_state) {
'#description' => t('The PID of the Root Collection Object'),
'#required' => TRUE,
),
'islandora_use_datastream_cache_headers' => array(
'#type' => 'checkbox',
'#title' => t('Generate/parse datastream HTTP cache headers'),
'#description' => t('HTTP caching can reduce network traffic, by allowing clients to used cached copies.'),
'#default_value' => variable_get('islandora_use_datastream_cache_headers', TRUE),
),
),
'islandora_namespace' => array(
'#type' => 'fieldset',

6
includes/authtokens.inc

@ -10,7 +10,7 @@
// Token lifespan(seconds): after this duration the token expires.
// 5 minutes.
define('TOKEN_TIMEOUT', 300);
define('ISLANDORA_AUTHTOKEN_TOKEN_TIMEOUT', 300);
/**
* Request Islandora to construct an object/datastream authentication token.
@ -92,7 +92,7 @@ function islandora_validate_object_token($pid, $dsid, $token) {
->condition('pid', $pid, '=')
->condition('dsid', $dsid, '=')
->condition('time', $time, '<=')
->condition('time', $time - TOKEN_TIMEOUT, '>')
->condition('time', $time - ISLANDORA_AUTHTOKEN_TOKEN_TIMEOUT, '>')
->execute()
->fetchAll();
if ($result) {
@ -131,6 +131,6 @@ function islandora_validate_object_token($pid, $dsid, $token) {
function islandora_remove_expired_tokens() {
$time = time();
db_delete("islandora_authtokens")
->condition('time', $time - TOKEN_TIMEOUT, '<')
->condition('time', $time - ISLANDORA_AUTHTOKEN_TOKEN_TIMEOUT, '<')
->execute();
}

2
includes/breadcrumb.inc

@ -27,6 +27,8 @@
function islandora_get_breadcrumbs($object) {
$breadcrumbs = islandora_get_breadcrumbs_recursive($object->id, $object->repository);
array_pop($breadcrumbs);
$context = 'islandora';
drupal_alter('islandora_breadcrumbs', $breadcrumbs, $context);
return $breadcrumbs;
}

207
includes/datastream.inc

@ -26,10 +26,26 @@ function islandora_download_datastream(AbstractDatastream $datastream) {
* @param bool $download
* If TRUE the file is download to the user computer for viewing otherwise it
* will attempt to display in the browser natively.
* @param int $version
* The version of the datastream to display
*/
function islandora_view_datastream(AbstractDatastream $datastream, $download = FALSE) {
header_remove('Cache-Control');
header_remove('Expires');
function islandora_view_datastream(AbstractDatastream $datastream, $download = FALSE, $version = NULL) {
// XXX: Certain features of the Devel module rely on the use of "shutdown
// handlers", such as query logging... The problem is that they might blindly
// add additional output which will break things if what is actually being
// output is anything but a webpage... like an image or video or audio or
// whatever the datastream is here.
$GLOBALS['devel_shutdown'] = FALSE;
if ($version !== NULL) {
if (isset($datastream[$version])) {
$datastream = $datastream[$version];
}
else {
return drupal_not_found();
}
}
header('Content-type: ' . $datastream->mimetype);
if ($datastream->controlGroup == 'M' || $datastream->controlGroup == 'X') {
header('Content-length: ' . $datastream->size);
@ -41,13 +57,164 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F
$filename = $datastream->label . '.' . $extension;
header("Content-Disposition: attachment; filename=\"$filename\"");
}
$cache_check = islandora_view_datastream_cache_check($datastream);
if ($cache_check !== 200) {
if ($cache_check === 304) {
header('HTTP/1.1 304 Not Modified');
}
elseif ($cache_check === 412) {
header('HTTP/1.0 412 Precondition Failed');
}
}
islandora_view_datastream_set_cache_headers($datastream);
drupal_page_is_cacheable(FALSE);
// Try not to load the file into PHP memory!
ob_end_flush();
// Close and flush ALL the output buffers!
while (@ob_end_flush()) {
};
// New content needed.
if ($cache_check === 200) {
$datastream->getContent('php://output');
}
exit();
}
/**
* Parse "etags" from HTTP If-Match or If-None-Match headers.
*
* Parses from the CSV-like struture supported by HTTP headers into an array,
* so `"asdf", "fdsa", W/"2132"` should become an array containing the strings:
* - asdf
* - fdsa
* - 2132
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
*
* @param string $header_value
* The value from the headers.
*
* @return array
* An array containing all the etags present.
*/
function islandora_parse_http_match_headers($header_value) {
$matches = array();
// Match the CSV-like structure supported by the HTTP headers.
$count = preg_match_all('/(((W\/)?("?)(\*|.+?)\4)(, +)?)/', $header_value, $matches);
// The fifth sub-expression/group is which will contain the etags.
return $matches[5];
}
/**
* Validate cache headers.
*
* @param AbstractDatastream $datastream
* The datastream for which to check the request headers against.
*
* @return int
* An integer representing the HTTP response code. One of:
* - 200: Proceed as normal. (Full download).
* - 304: Resource hasn't changed; pass cache validation.
* - 412: Resource has changed; fail cache validation.
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
*/
function islandora_view_datastream_cache_check(AbstractDatastream $datastream) {
if (!variable_get('islandora_use_datastream_cache_headers', TRUE)) {
return 200;
}
// Let's assume that if we get here, we'll be able to complete the request.
$return = 200;
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$modified_since = DateTime::createFromFormat('D, d M Y H:i:s e', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
if ($datastream->createdDate->getTimestamp() - $modified_since->getTimestamp() > 0) {
// Changed!
return $return;
}
else {
$return = 304;
}
}
if ($return === 200 && isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) {
$unmodified_since = DateTime::createFromFormat('D, d M Y H:i:s e', $_SERVER['HTTP_IF_UNMODIFIED_SINCE']);
if ($datastream->createdDate->getTimestamp() !== $unmodified_since->getTimestamp()) {
// Changed!
$return = 412;
}
else {
return $return;
}
}
// Only consider Etags we have provided.
if (isset($datastream->checksum)) {
$tags = array();
foreach ($datastream as $offset => $version) {
if (isset($version->checksum)) {
$tags[$offset] = $version->checksum;
}
}
if ($return === 200 && isset($_SERVER['HTTP_IF_MATCH'])) {
$request_tags = islandora_parse_http_match_headers($_SERVER['HTTP_IF_MATCH']);
if (in_array('*', $request_tags) || count(array_intersect($tags, $request_tags)) > 0) {
// There's a match... Let things go ahead.
return $return;
}
else {
$return = 412;
}
}
if (in_array($return, array(200, 304), TRUE) && isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$request_tags = islandora_parse_http_match_headers($_SERVER['HTTP_IF_NONE_MATCH']);
if (in_array('*', $request_tags) || count(array_intersect($tags, $request_tags)) > 0) {
$return = 304;
}
else {
$return = 200;
}
}
}
return $return;
}
/**
* Set various HTTP headers for caching.
*
* @param AbstractDatastream $datastream
* The datastream being viewed/downloaded.
*/
function islandora_view_datastream_set_cache_headers(AbstractDatastream $datastream) {
if (variable_get('islandora_use_datastream_cache_headers', TRUE)) {
// Force cache revalidation.
header('Expires: Sun, 19 Nov 1978 05:00:00 GMT');
$cache_control = array();
if ($datastream->parent->repository->api->connection->username == 'anonymous') {
$cache_control[] = 'public';
}
else {
$cache_control[] = 'private';
}
$cache_control[] = 'must-revalidate';
$cache_control[] = 'max-age=0';
header('Cache-Control: ' . implode(', ', $cache_control));
header('Last-Modified: ' . $datastream->createdDate->format('D, d M Y H:i:s \G\M\T'));
if (isset($datastream->checksum)) {
header("Etag: \"{$datastream->checksum}\"");
}
}
else {
header_remove('Cache-Control');
header_remove('Expires');
}
}
/**
* Get the human readable size of the given datastream.
*
@ -69,12 +236,31 @@ function islandora_datastream_get_human_readable_size(AbstractDatastream $datast
*
* @param AbstractDatastream $datastream
* The datastream to generated the url to.
* @param string $type
* One of:
* - download
* - view
* @param int $version
* (Optional) The version of the datastream to get a URL for.
*
* @return string
* either the 'view' or 'download' url for the given datastream.
*/
function islandora_datastream_get_url(AbstractDatastream $datastream, $type = 'download') {
return $datastream->controlGroup == 'R' ? $datastream->url : "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/$type";
function islandora_datastream_get_url(AbstractDatastream $datastream, $type = 'download', $version = NULL) {
if ($version === NULL) {
$link = "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/$type";
}
else {
$link = "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/version/$version/$type";
$datastream = $datastream[$version];
}
if ($datastream->controlGroup == 'R') {
return $datastream->url;
}
else {
return $link;
}
}
/**
@ -82,6 +268,9 @@ function islandora_datastream_get_url(AbstractDatastream $datastream, $type = 'd
*
* @param AbstractDatastream $datastream
* The datastream to generated the url to.
*
* @return string
* Markup containing the link to the confirm form to delete the datastream.
*/
function islandora_datastream_get_delete_link(AbstractDatastream $datastream) {
$message = islandora_deprecated('7.x-1.2', 'Use the "islandora_datastream_delete_link" theme implementation.');
@ -97,6 +286,9 @@ function islandora_datastream_get_delete_link(AbstractDatastream $datastream) {
*
* @param AbstractDatastream $datastream
* The datastream to generated the url to.
*
* @return string
* Markup containing the link to edit the datastream.
*/
function islandora_datastream_edit_get_link(AbstractDatastream $datastream) {
$message = islandora_deprecated('7.x-1.2', 'Use the "islandora_datastream_edit_link" theme implementation.');
@ -139,6 +331,9 @@ function islandora_edit_datastream(AbstractDatastream $datastream) {
*
* @param array $edit_registry
* A list of 'islandora_edit_datastream_registry' values.
*
* @return array
* A Drupal renderable array containing the "edit" markup.
*/
function islandora_edit_datastream_registry_render(array $edit_registry) {
$markup = '';

268
includes/datastream.version.inc

@ -0,0 +1,268 @@
<?php
/**
* @file
* The form displayed that shows the datastream version history.
*/
/**
* Function that returns markup for the datastream versions page.
*/
function islandora_datastream_version_table($datastream) {
module_load_include('inc', 'islandora', 'includes/datastream');
module_load_include('inc', 'islandora', 'includes/utilities');
$parent = $datastream->parent;
drupal_set_title(t("@dsid Previous Versions", array('@dsid' => $datastream->id)));
$audit_values = islandora_get_audit_trail($parent->id, $datastream->id);
$header = array();
$header[] = array('data' => t('Created Date'));
$header[] = array('data' => t('Size'));
$header[] = array('data' => t('Label'));
$header[] = array('data' => t('Responsibility'));
$header[] = array('data' => t('Mime type'));
$header[] = array('data' => t('Operations'), 'colspan' => '2');
$rows = array();
foreach ($datastream as $version => $datastream_version) {
$row = array();
$reponsibility = $parent->owner;
foreach ($audit_values as $audit_value) {
$internal = $datastream_version->createdDate;
if ($audit_value['date'] == $datastream_version->createdDate) {
$reponsibility = $audit_value['responsibility'];
}
}
$user = user_load_by_name($reponsibility);
if ($user) {
$user_id = $user->uid;
$user_val = l($reponsibility, "user/$user_id");
}
else {
$user_val = $reponsibility;
}
$row[] = array(
'class' => 'datastream-date',
'data' => theme('islandora_datastream_view_link', array(
'datastream' => $datastream,
'label' => $datastream_version->createdDate->format(DATE_RFC850),
'version' => $version,
)),
);
$row[] = array(
'class' => 'datastream-size',
'data' => islandora_datastream_get_human_readable_size($datastream_version),
);
$row[] = array(
'class' => 'datastream-label',
'data' => $datastream_version->label,
);
$row[] = array(
'class' => 'datastream-responsibility',
'data' => $user_val,
);
$row[] = array(
'class' => 'datastream-mime',
'data' => $datastream_version->mimeType,
);
$row[] = array(
'class' => 'datastream-delete',
'data' => theme('islandora_datastream_delete_link', array(
'datastream' => $datastream,
'version' => $version,
)),
);
$row[] = array(
'class' => 'datastream-revert',
'data' => theme('islandora_datastream_revert_link', array(
'datastream' => $datastream,
'version' => $version,
)),
);
$rows[] = $row;
}
return theme('table', array('header' => $header, 'rows' => $rows));
}
/**
* The admin delete datastream form.
*
* @param array $form
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param AbstractDatastream $datastream
* The datastream to be deleted.
* @param string $version
* The version number of the datastream we are trying to delete.
*
* @return array
* The drupal form definition.
*/
function islandora_delete_datastream_version_form(array $form, array &$form_state, AbstractDatastream $datastream, $version) {
if (!isset($datastream[$version]) || count($datastream) < 2) {
return drupal_not_found();
}
$form_state['datastream'] = $datastream;
$form_state['version'] = $version;
return confirm_form($form,
t('Are you sure you want to delete version @version of the @dsid datastream?', array('@dsid' => $datastream->id, '@version' => $version)),
"islandora/object/{$datastream->parent->id}",
t('This action cannot be undone.'),
t('Delete'),
t('Cancel')
);
}
/**
* Submit handler for the delete datastream form.
*
* Purges/Delete's the given AbstractDatastream if possible.
*
* The ISLANDORA_PRE_PURGE_DATASTREAM_HOOK will query other modules as to
* whether the given FedoraDatastream
* should be: blocked from purging; state set to 'Deleted'; or purged.
*
* @param array $form
* The Drupal form.
* @param array $form_state
* The Drupal form state.
*/
function islandora_delete_datastream_version_form_submit(array $form, array &$form_state) {
$datastream = $form_state['datastream'];
$version = $form_state['version'];
$datastream_id = $datastream->id;
$object = $datastream->parent;
try {
unset($datastream[$version]);
}
catch (Exception $e) {
drupal_set_message(t('Error deleting version %v of %s datastream from object %o %e', array(
'%v' => $version,
'%s' => $datastream_id,
'%o' => $object->label,
'%e' => $e->getMessage())), 'error');
}
drupal_set_message(t('%d datastream version successfully deleted from Islandora object %o', array(
'%d' => $datastream_id,
'%o' => $object->label)));
$form_state['redirect'] = "islandora/object/{$object->id}/datastream/{$datastream->id}/version";
}
/**
* The admin revert datastream form.
*
* @param array $form
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param AbstractDatastream $datastream
* The datastream to be deleted.
* @param string $version
* The version number of the datastream we are trying to revert to.
*
* @return array
* The drupal form definition.
*/
function islandora_revert_datastream_version_form(array $form, array &$form_state, AbstractDatastream $datastream, $version) {
if (!isset($datastream[$version]) || count($datastream) < 2) {
return drupal_not_found();
}
$form_state['dsid'] = $datastream->id;
$form_state['object_id'] = $datastream->parent->id;
$form_state['version'] = $version;
return confirm_form($form,
t('Are you sure you want to revert to version @version of the @dsid datastream?', array('@dsid' => $datastream->id, '@version' => $version)),
"islandora/object/{$datastream->parent->id}",
"",
t('Revert'),
t('Cancel')
);
}
/**
* Submit handler for the revert datastream form.
*
* Reverts the given AbstractDatastream if possible.
*
* @param array $form
* The Drupal form.
* @param array $form_state
* The Drupal form state.
*/
function islandora_revert_datastream_version_form_submit(array $form, array &$form_state) {
$islandora_object = islandora_object_load($form_state['object_id']);
$datastream_to_revert = $islandora_object[$form_state['dsid']];
$version = $form_state['version'];
// Create file holding specified datastream version, and set datastream to it.
$datastream_to_revert_to = $datastream_to_revert[$version];
if (in_array($datastream_to_revert->controlGroup, array('R', 'E'))) {
$datastream_to_revert->url = $datastream_to_revert_to->url;
}
else {
$filename = file_create_filename('datastream_temp_file', 'temporary://');
$datastream_to_revert_to->getContent($filename);
$datastream_to_revert->setContentFromFile($filename);
file_unmanaged_delete($filename);
}
if ($datastream_to_revert->mimeType != $datastream_to_revert_to->mimeType) {
$datastream_to_revert->mimeType = $datastream_to_revert_to->mimeType;
}
if ($datastream_to_revert->label != $datastream_to_revert_to->label) {
$datastream_to_revert->label = $datastream_to_revert_to->label;
}
drupal_set_message(t('%d datastream successfully reverted to version %v for Islandora object %o', array(
'%d' => $datastream_to_revert->id,
'%v' => $version,
'%o' => $islandora_object->label)));
$form_state['redirect'] = "islandora/object/{$islandora_object->id}/datastream/{$datastream_to_revert->id}/version";
}
/**
* Gets Audit datastream values from foxml.
*
* @param String $pid
* PID of parent object
*
* @return array
* Array of audit values
*/
function islandora_get_audit_trail($pid, $dsid) {
$url = variable_get('islandora_base_url', 'http://localhost:8080/fedora');
$connection = islandora_get_tuque_connection(NULL, $url);
$xml = $connection->api->m->getObjectXml($pid);
$simple_xml = simplexml_load_string($xml);
$fox_ns = "info:fedora/fedora-system:def/foxml#";
$audit_ns = 'info:fedora/fedora-system:def/audit#';
$foxml_nodes = $simple_xml->children($fox_ns);
foreach ($foxml_nodes as $node) {
if ($node->attributes()->ID == "AUDIT") {
$content = $node->datastreamVersion->xmlContent;
$audit_nodes = $content->children($audit_ns);
}
}
$audit_values = array();
if (isset($audit_nodes)) {
foreach ($audit_nodes->auditTrail->record as $record) {
if ($dsid == $record->componentID) {
$values['responsibility'] = $record->responsibility;
$values['date'] = $record->date;
$audit_values[] = $values;
}
}
}
return $audit_values;
}

123
includes/derivatives.inc

@ -0,0 +1,123 @@
<?php
/**
* @file
* Defines functions used when constructing derivatives.
*/
/**
* Kicks off derivative functions based upon hooks and conditions.
*
* @param AbstractObject $object
* An AbstractObject representing a FedoraObject.
* @param array $options
* An array of parameters containing:
* - force: Bool denoting whether we are forcing the generation of
* derivatives.
* - source_dsid: (Optional) String of the datastream id we are generating
* from or NULL if it's the object itself.
* - destination_dsid: (Optional) String of the datastream id that is being
* created. To be used in the UI.
*
* @return array
* An array of messages describing the outcome of the derivative events.
* Each individual message array has the following structure:
* - success: Bool denoting whether the operation was successful.
* - messages: An array structure containing:
* - message: A string passed through t() describing the
* outcome of the operation.
* - message_sub: (Optional) Substitutions to be passed along to t() or
* watchdog.
* - type: A string denoting whether the output is to be
* drupal_set_messaged (dsm) or watchdogged (watchdog).
* - severity: (Optional) A severity level / status to be used when
* logging messages. Uses the defaults of drupal_set_message and
* watchdog if not defined.
*/
function islandora_do_derivatives(AbstractObject $object, array $options) {
module_load_include('inc', 'islandora', 'includes/utilities');
$options += array(
'force' => FALSE,
);
$hooks = islandora_invoke_hook_list(ISLANDORA_DERVIATIVE_CREATION_HOOK, $object->models, array($object));
uasort($hooks, 'drupal_sort_weight');
$results = array();
if (array_key_exists('source_dsid', $options)) {
$hooks = array_filter($hooks, function($filter_hook) use($options) {
return array_key_exists('source_dsid', $filter_hook) &&
$filter_hook['source_dsid'] == $options['source_dsid'];
});
}
if (array_key_exists('destination_dsid', $options)) {
$hooks = array_filter($hooks, function($filter_hook) use($options) {
return array_key_exists('destination_dsid', $filter_hook) &&
$filter_hook['destination_dsid'] == $options['destination_dsid'];
});
}
foreach ($hooks as $hook) {
if (isset($hook['file'])) {
require_once $hook['file'];
}
foreach ($hook['function'] as $function) {
if (function_exists($function)) {
$logging = call_user_func($function, $object, $options['force']);
if (!empty($logging)) {
$results[] = $logging;
}
}
else {
watchdog('islandora', 'Unable to call derivative function @function as it was not found!', array('@function' => $function), WATCHDOG_ERROR);
}
}
}
return $results;
}
/**
* Handles the logging of derivative messages.
*
* @param array $logging_results
* An array of messages describing the outcome of the derivative events.
* Each individual message array has the following structure:
* - success: Bool denoting whether the operation was successful.
* - messages: An array structure containing:
* - message: A string passed through t() describing the
* outcome of the operation.
* - message_sub: (Optional) Substitutions to be passed along to t() or
* watchdog.
* - type: A string denoting whether the output is to be
* drupal_set_messaged (dsm) or watchdogged (watchdog).
* - severity: (Optional) A severity level / status to be used when
* logging messages. Uses the defaults of drupal_set_message and
* watchdog if not defined.
*/
function islandora_derivative_logging(array $logging_results) {
foreach ($logging_results as $result) {
foreach ($result['messages'] as $message) {
if ($message['type'] === 'dsm') {
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 {
// We know what we are doing here. Passing through the translated
// message and the substitutions needed. We are using
// call_user_func until such time as the @ignore changes
// are merged into the standard release for Coder.
call_user_func('watchdog', 'islandora_derivatives', $message['message'], isset($message['message_sub']) ? $message['message_sub'] : array(), isset($message['severity']) ? $message['severity'] : WATCHDOG_NOTICE);
}
}
}
}

8
includes/dublin_core.inc

@ -130,8 +130,11 @@ class DublinCore {
}
$dc_label = explode(':', $field);
$element_label = drupal_ucfirst($dc_label[1]);
$dc_array[$field]['label'] = $element_label;
$dc_array[$field]['value'] = $value;
$i18n_object_id = drupal_strtolower($element_label);
$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]['dcterms'] = preg_replace('/^dc/', 'dcterms', $field);
}
@ -140,7 +143,6 @@ class DublinCore {
return $dc_array;
}
/**
* Creates a new instance of the class by parsing dc_xml.
*

98
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);
}
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');
return array(array(
'#markup' => l(t('Back'), 'javascript:window.history.back();', array('external' => TRUE))));
@ -186,10 +192,10 @@ function islandora_ingest_form_get_step(array &$form_state, $step_id = NULL) {
*
* @param array $form_state
* The Drupal form state.
* @param string $step
* @param array $step
* The step relative to the result, if not provided the current step is used.
*
* @return string
* @return array|null
* The next step if found, NULL otherwise.
*/
function islandora_ingest_form_get_next_step(array &$form_state, array $step = NULL) {
@ -205,10 +211,10 @@ function islandora_ingest_form_get_next_step(array &$form_state, array $step = N
*
* @param array $form_state
* The Drupal form state.
* @param string $step
* @param array $step
* The step relative to the result, if not provided the current step is used.
*
* @return string
* @return array|null
* The next step if found, NULL otherwise.
*/
function islandora_ingest_form_get_previous_step(array &$form_state, array $step = NULL) {
@ -329,6 +335,9 @@ function islandora_ingest_form_decrement_step(array &$form_state) {
* The Drupal form.
* @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 array
* The form definition of the current step.
@ -363,6 +372,8 @@ function islandora_ingest_form_execute_step(array $form, array &$form_state, $st
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param array $step
* The step we are executing.
*
* @return array
* The form definition of the given step.
@ -391,6 +402,8 @@ function islandora_ingest_form_execute_form_step(array $form, array &$form_state
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param array $step
* The step that execution begins from.
*/
function islandora_ingest_form_execute_consecutive_callback_steps(array $form, array &$form_state, array $step) {
do {
@ -409,10 +422,15 @@ function islandora_ingest_form_execute_consecutive_callback_steps(array $form, a
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param array $step
* The step currently being executed.
*/
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;
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);
}
@ -425,6 +443,8 @@ function islandora_ingest_form_execute_callback_step(array $form, array &$form_s
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param array $step
* The step that execution begins from.
*/
function islandora_ingest_form_undo_consecutive_callback_steps(array $form, array &$form_state, array $step) {
do {
@ -443,10 +463,15 @@ function islandora_ingest_form_undo_consecutive_callback_steps(array $form, arra
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param array $step
* The step which the undo callback is being called on.
*/
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;
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);
}
@ -457,11 +482,13 @@ function islandora_ingest_form_undo_callback_step(array $form, array &$form_stat
* The Drupal form.
* @param array $form_state
* The Drupal form state.
* @param array $step
* An array defining a ingest step.
*
* @return array
* The stepified Drupal form definition for the given step.
*/
function islandora_ingest_form_stepify(array $form, array &$form_state, $step) {
function islandora_ingest_form_stepify(array $form, array &$form_state, array $step) {
$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['form_step_id'] = array(
@ -682,16 +709,55 @@ function islandora_ingest_form_ingest_button(array &$form_state) {
'#type' => 'submit',
'#name' => 'ingest',
'#value' => t('Ingest'),
'#process' => array('islandora_ingest_form_ingest_button_process'),
'#validate' => $validate,
'#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.
*
* Attempts to ingest every object built by the previous steps.
*
* @param array $form
* The Drupal form.
* @param array $form_state
* The Drupal form state.
*/
@ -703,16 +769,34 @@ function islandora_ingest_form_submit(array $form, array &$form_state) {
islandora_ingest_form_execute_consecutive_callback_steps($form, $form_state, $step);
}
// Ingest the objects.
$set_redirect = TRUE;
foreach ($form_state['islandora']['objects'] as &$object) {
try {
islandora_add_object($object);
// We want to redirect to the first object as it's considered to be the
// primary object.
if ($set_redirect) {
$form_state['redirect'] = "islandora/object/{$object->id}";
$set_redirect = FALSE;
}
drupal_set_message(
t('"@label" (ID: @pid) has been ingested.', array('@label' => $object->label, '@pid' => $object->id)),
'status');
}
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', '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');
watchdog(
'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

340
includes/manage_deleted_objects.inc

@ -0,0 +1,340 @@
<?php
/**
* @file
* Handles the management of deleted objects.
*/
/**
* The deletion management prep form.
*
* @param array $form
* The Drupal form definition.
* @param array $form_state
* The Drupal form state.
*
* @return array
* The Drupal form definition.
*/
function islandora_deleted_objects_prep_form($form, $form_state, $serialized_chosen = NULL) {
module_load_include('inc', 'islandora', 'includes/utilities');
$chosen_contentmodels = array();
if ($serialized_chosen) {
$chosen_contentmodels = unserialize($serialized_chosen);
}
$contentmodels_with_deleted_members = islandora_get_contentmodels_with_deleted_members();
$elegible_contentmodels = array_keys($contentmodels_with_deleted_members);
if (empty($contentmodels_with_deleted_members)) {
$form['message'] = array(
'#type' => 'markup',
'#markup' => t("There are no deleted objects in this repository."),
);
return $form;
}
$form['message'] = array(
'#type' => 'markup',
'#markup' => t("Select content models of deleted objects."),
);
$form['mapped_contentmodels'] = array(
'#type' => 'hidden',
'#value' => $contentmodels_with_deleted_members,
);
$table_element = islandora_content_model_select_table_form_element(NULL);
foreach ($table_element['#options'] as $option) {
if (!in_array($option['pid'], $elegible_contentmodels)) {
unset($table_element['#options'][$option['pid']]);
}
if (array_key_exists($option['pid'], $chosen_contentmodels)) {
$table_element['#default_value'][$option['pid']] = TRUE;
}
}
$form['contentmodels'] = $table_element;
$form['next'] = array(
'#type' => 'submit',
'#value' => t('Next'),
);
return $form;
}
/**
* Submit handler for deletion management prep form.
*
* @param array $form
* The form.
* @param array $form_state
* The form state.
*/
function islandora_deleted_objects_prep_form_submit($form, $form_state) {
$content_models = $form_state['values']['contentmodels'];
$chosen = function($element) {
return $element;
};
$serialized_contentmodels = serialize(array_filter($content_models, $chosen));
drupal_goto("admin/islandora/restore/manage/$serialized_contentmodels");
}
/**
* The deletion management prep form.
*
* @param array $form
* The Drupal form definition.
* @param array $form_state
* The Drupal form state.
*
* @return array
* The Drupal form definition.
*/
function islandora_deleted_objects_manage_form($form, $form_state, $serialized_chosen = NULL) {
$form['previous'] = array(
'#type' => 'submit',
'#value' => t('Previous'),
'#attributes' => array('source' => 'previous'),
);
$chosen_contentmodels = unserialize($serialized_chosen);
$content_models_with_deleted = islandora_get_contentmodels_with_deleted_members();
foreach ($chosen_contentmodels as $contentmodel) {
if (!array_key_exists($contentmodel, $content_models_with_deleted)) {
unset($chosen_contentmodels[$contentmodel]);
}
}
if (empty($chosen_contentmodels)) {
$form['message'] = array(
'#type' => 'markup',
'#markup' => t("There are no deleted objects with the selected content models in this repository."),
);
return $form;
}
if (is_array($chosen_contentmodels)) {
foreach ($chosen_contentmodels as $key => $value) {
if (in_array($key, $content_models_with_deleted)) {
$chosen_contentmodels[$key] = $content_models_with_deleted[$key];
}
}
}
$tuque = islandora_get_tuque_connection();
$repository = $tuque->repository;
// Query brings back fedora-system:FedoraObject-3.0, doubling the results.
$total = $repository->ri->countQuery(islandora_get_deleted_query($chosen_contentmodels), 'sparql') / 2;
$limit = 25;
if ($total < 28) {
$limit = $total;
}
$current_page = pager_default_initialize($total, $limit);
$query_limit = $limit * 2;
$offset = $current_page * $query_limit;
$options = islandora_get_deleted_objects($chosen_contentmodels, $query_limit, $offset);
foreach ($options as &$option) {
$option['content_model'] = $content_models_with_deleted[$option['content_model']];
}
$form['serialized_chosen'] = array(
'#type' => 'hidden',
'#value' => $serialized_chosen,
);
$form['pager'] = array(
'#type' => 'markup',
'#markup' => theme('pager', array('quantity', count($options))),
);
$form['propogate'] = array(
'#title' => t('Apply changes to related objects?'),
'#default_value' => TRUE,
'#description' => t("Objects associated with selected objects will also be purged/restored. ie page objects associated with a book object."),
'#type' => 'checkbox',
);
$form['chosen'] = array(
'#type' => 'hidden',
'#value' => $chosen_contentmodels,
);
$form['objects_to_process'] = array(
'#type' => 'tableselect',
'#header' => array(
'title' => t('Name'),
'pid' => t('PID'),
'content_model' => t('Content Model'),
),
'#multiple' => TRUE,
'#options' => $options,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Restore selected objects'),
'#attributes' => array('source' => 'restore'),
);
if (user_access(ISLANDORA_PURGE)) {
$form['purge'] = array(
'#type' => 'submit',
'#value' => t('Irrevocably purge selected objects'),
'#attributes' => array('source' => 'purge'),
);
}
return $form;
}
/**
* Submit handler for deletion management form.
*
* @param array $form
* The form.
* @param array $form_state
* The form state.
*/
function islandora_deleted_objects_manage_form_submit($form, $form_state) {
module_load_include('inc', 'islandora', 'includes/utilities');
$serialized_chosen = isset($form_state['values']['serialized_chosen']) ? $form_state['values']['serialized_chosen'] : NULL;
if (isset($form_state['clicked_button']['#attributes']['source']) && $form_state['clicked_button']['#attributes']['source'] == 'previous') {
drupal_goto("admin/islandora/restore/prep/$serialized_chosen");
}
if ($form_state['clicked_button']['#attributes']['source'] == 'restore') {
$descriptor = "Restoring";
$action = 'islandora_restore_object_by_pid';
}
if ($form_state['clicked_button']['#attributes']['source'] == 'purge') {
$descriptor = "Purging";
$action = 'islandora_purge_object_by_pid';
}
$objects_to_process = array_filter($form_state['values']['objects_to_process']);
$pids_to_restore = $objects_to_process;
if ($form_state['values']['propogate']) {
foreach ($objects_to_process as $pid) {
$fedora_object = islandora_object_load($pid);
$temp = islandora_invoke_hook_list(ISLANDORA_UPDATE_RELATED_OBJECTS_PROPERTIES_HOOK, $fedora_object->models, array($fedora_object));
if (!empty($temp)) {
$pids_to_restore = array_merge_recursive($pids_to_restore, $temp);
}
}
}
$batch = array(
'title' => t('@descriptor selected objects', array('@descriptor' => $descriptor)),
'file' => drupal_get_path('module', 'islandora') . '/includes/manage_deleted_objects.inc',
'operations' => array(),
);
foreach ($pids_to_restore as $pid) {
$batch['operations'][] = array(
$action,
array($pid),
);
}
batch_set($batch);
batch_process("admin/islandora/restore/manage/$serialized_chosen");
}
/**
* Gets PIDS of all deleted objects.
*
* @return array
* PIDS of deleted objects
*/
function islandora_get_deleted_objects($content_models, $limit, $offset) {
$tuque = islandora_get_tuque_connection();
$repository = $tuque->repository;
$query = islandora_get_deleted_query($content_models, $offset);
$objects = $repository->ri->sparqlQuery($query, $limit);
$deleted_objects = array();
foreach ($objects as $object) {
if ($object['object']['value'] != "fedora-system:FedoraObject-3.0") {
$pid = $object['subject']['value'];
$cm_pid = $object['object']['value'];
$title = $object['label']['value'];
$deleted_objects[$pid] = array(
'title' => $title, 'pid' => $pid,
'content_model' => $content_models[$cm_pid],
);
}
}
return $deleted_objects;
}
/**
* Gets PIDS of all content models associated with deleted objects.
*
* @return array
* array of content model pids
*/
function islandora_get_contentmodels_with_deleted_members() {
$tuque = new IslandoraTuque();
$repository = $tuque->repository;
$query = "PREFIX fm: <info:fedora/fedora-system:def/model#>
SELECT DISTINCT ?object ?label FROM <#ri>
WHERE {
{?subject fm:state fm:Deleted;
fm:hasModel ?object;
}
OPTIONAL{
?object fm:label ?label
}
}";
$objects = $repository->ri->sparqlQuery($query, -1);
$content_models = array();
foreach ($objects as $object) {
if ($object['object']['value'] != "fedora-system:FedoraObject-3.0") {
$content_models[$object['object']['value']] = $object['label']['value'];
}
}
return $content_models;
}
/**
* Restores deleted object.
*
* @param String $pid
* PID of object to be restored
*/
function islandora_restore_object_by_pid($pid) {
$fedora_object = islandora_object_load($pid);
$fedora_object->state = 'A';
}
/**
* Purges deleted object.
*
* @param String $pid
* PID of object to be restored
*/
function islandora_purge_object_by_pid($pid) {
$fedora_object = islandora_object_load($pid);
$fedora_object->repository->purgeObject($pid);
}
/**
* Get query to find all deleted objects by content type.
*
* @param array $content_models
* Content models to restrict search
* @param int $offset
* offset to be added to search
*
* @return String
* Sparql query
*/
function islandora_get_deleted_query($content_models, $offset = 0) {
$candidates = array_keys($content_models);
$first_contentmodel = array_shift($candidates);
$prefix = "PREFIX fm: <" . FEDORA_MODEL_URI . "> ";
$select = "SELECT DISTINCT ?subject ?label ?object FROM <#ri> WHERE { ";
$where_clause = "{?subject fm:hasModel <info:fedora/$first_contentmodel>;
fm:state fm:Deleted;
fm:hasModel ?object;
}";
$suffix = "} ORDER BY ?subject OFFSET $offset";
$unions = '';
foreach ($candidates as $contentmodel) {
$unions .= "UNION {?subject fm:hasModel <info:fedora/$contentmodel>;
fm:state fm:Deleted;
fm:hasModel ?object;
}
";
}
$optional = "OPTIONAL{?subject fm:label ?label}";
return "$prefix $select $where_clause $unions $optional $suffix";
}

171
includes/metadata.inc

@ -0,0 +1,171 @@
<?php
/**
* @file
* Defines functions used when viewing metadata displays on Islandora objects.
*/
/**
* Retrieves the metadata display markup for an Islandora object.
*
* @param AbstractObject $object
* An AbstractObject representing an object within Fedora.
* @param bool $print
* Whether the object is being printed.
*
* @return string
* Markup to be rendered for display on Islandora object pages.
*/
function islandora_retrieve_metadata_markup(AbstractObject $object, $print = FALSE) {
$viewers = module_invoke_all('islandora_metadata_display_info');
$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;
}
/**
* 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);
}
}
return $markup;
}
/**
* Form used for choosing which default metadata display to use for viewing.
*
* @param array $form
* An array representing a Drupal form.
* @param array $form_state
* An array containing the Drupal form state.
*
* @return array
* An array representing the metadata display viewer form.
*/
function islandora_metadata_display_form($form, $form_state) {
module_load_include('inc', 'islandora', 'includes/solution_packs.inc');
$form = array();
$defined_displays = module_invoke_all('islandora_metadata_display_info');
if (!empty($defined_displays)) {
$no_viewer = array();
$no_viewer['none'] = array(
'label' => t('None'),
'description' => t("Don't show any metadata for displaying"),
);
$viewers = array_merge_recursive($no_viewer, $defined_displays);
$form['viewers'] = array(
'#type' => 'item',
'#title' => t('Select a viewer'),
'#description' => t('Preferred metadata display for Islandora. These may be provided by third-party modules.'),
'#tree' => TRUE,
'#theme' => 'islandora_viewers_table',
);
foreach ($viewers as $name => $profile) {
$options[$name] = '';
$form['viewers']['name'][$name] = array(
'#type' => 'hidden',
'#value' => $name,
);
$form['viewers']['label'][$name] = array(
'#type' => 'item',
'#markup' => $profile['label'],
);
$form['viewers']['description'][$name] = array(
'#type' => 'item',
'#markup' => $profile['description'],
);
$form['viewers']['configuration'][$name] = array(
'#type' => 'item',
'#markup' => (isset($profile['configuration']) AND $profile['configuration'] != '') ? l(t('configure'), $profile['configuration']) : '',
);
}
$form['viewers']['default'] = array(
'#type' => 'radios',
'#options' => isset($options) ? $options : array(),
'#default_value' => variable_get('islandora_metadata_display', 'dublin_core'),
);
}
else {
$form['viewers']['no_viewers'] = array(
'#markup' => t('No viewers detected.'),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save configuration'),
);
return $form;
}
/**
* Submit handler for the metadata display form which sets the default viewer.
*
* @param array $form
* An array representing a Drupal form.
* @param array $form_state
* An array containing the Drupal form state.
*/
function islandora_metadata_display_form_submit($form, $form_state) {
variable_set('islandora_metadata_display', $form_state['values']['viewers']['default']);
drupal_set_message(t('The configuration options have been saved.'));
}
/**
* Metadata display callback for rendering Dublin Core metadata.
*
* @param AbstractObject $object
* An AbstractObject representing an object within Fedora.
* @param bool $print
* Whether the display is being printed or not.
*
* @return string
* Markup representing the rendered metadata from Dublin Core.
*/
function islandora_metadata_display_callback(AbstractObject $object, $print = FALSE) {
$elements = array(
'islandora_object' => $object,
'print' => $print,
);
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);
}

16
includes/mime_detect.inc

@ -162,6 +162,7 @@ class MimeDetect {
"xpm" => "image/x-xpixmap",
"xwd" => "image/x-windowdump",
// videos:
"mkv" => "video/x-matroska",
"mpeg" => "video/mpeg",
"mpe" => "video/mpeg",
"mpg" => "video/mpeg",
@ -382,4 +383,19 @@ class MimeDetect {
return $this->protectedMimeTypes;
}
/**
* Get all valid extensions for this MIME type.
*
* @param string $mimetype
* The MIME type we are searching for.
*
* @return array
* An array of valid extensions for this MIME type.
*/
public function getValidExtensions($mimetype) {
$filter = function ($mime) use ($mimetype) {
return $mime == $mimetype;
};
return array_keys(array_filter($this->protectedMimeTypes, $filter));
}
}

3
includes/object.entity_controller.inc

@ -24,6 +24,9 @@ class IslandoraObjectEntityController implements DrupalEntityControllerInterface
* The ID's of the entities.
* @param array $conditions
* The conditions to apply.
*
* @return array
* An array of loaded objects.
*/
public function load($ids = array(), $conditions = array()) {
if (!empty($conditions)) {

85
includes/object_properties.form.inc

@ -21,6 +21,11 @@
function islandora_object_properties_form(array $form, array &$form_state, AbstractObject $object) {
drupal_set_title($object->label);
$form_state['object'] = $object;
$temp = islandora_invoke_hook_list(ISLANDORA_UPDATE_RELATED_OBJECTS_PROPERTIES_HOOK, $object->models, array($object));
$related_objects_pids = array();
if (!empty($temp)) {
$related_objects_pids = array_merge_recursive($related_objects_pids, $temp);
}
return array(
'pid' => array(
'#type' => 'hidden',
@ -54,14 +59,28 @@ function islandora_object_properties_form(array $form, array &$form_state, Abstr
'#type' => 'select',
'#options' => array('A' => 'Active', 'I' => 'Inactive', 'D' => 'Deleted'),
),
'propogate' => array(
'#title' => t('Apply changes to related objects?'),
'#default_value' => TRUE,
'#description' => t("Changes to owner and state will applied to associated objects. ie page objects associated with a book object."),
'#type' => 'checkbox',
'#access' => count($related_objects_pids),
),
'related_pids' => array(
'#value' => $related_objects_pids,
'#type' => 'hidden',
'#access' => count($related_objects_pids),
),
'submit' => array(
'#type' => 'submit',
'#value' => 'Update Properties',
),
'delete' => array(
'#type' => 'submit',
'#access' => islandora_object_access(FEDORA_PURGE, $object),
'#value' => t('Delete'),
'#access' => islandora_object_access(ISLANDORA_PURGE, $object),
'#value' => t("Permanently remove '@label' from repository", array(
'@label' => truncate_utf8($object->label, 32, TRUE, TRUE))
),
'#submit' => array('islandora_object_properties_form_delete'),
'#limit_validation_errors' => array(array('pid')),
),
@ -81,32 +100,54 @@ function islandora_object_properties_form_submit(array $form, array &$form_state
$owner = $form_state['values']['object_owner'];
$state = $form_state['values']['object_state'];
$label = $form_state['values']['object_label'];
$propogate = $form_state['values']['propogate'];
$update_owners = FALSE;
$update_states = FALSE;
if (isset($owner) && $owner != $object->owner) {
try {
$object->owner = $owner;
$update_owners = TRUE;
drupal_set_message(t('Successfully updated owner %s', array('%s' => $owner)));
}
catch (Exception $e) {
form_set_error('object_owner', t('Error updating owner %s', array('%s' => $e->getMessage())));
}
}
if (isset($label) && $label != $object->label) {
try {
$object->label = $label;
drupal_set_message(t('Successfully updated label %s', array('%s' => check_plain($label))));
}
catch (Exception $e) {
form_set_error(t('Error updating label %s', array('%s' => $e->getMessage())));
}
}
if (isset($state) && $state != $object->state) {
try {
$object->state = $state;
$update_states = TRUE;
drupal_set_message(t('Successfully updated state %s', array('%s' => $state)));
}
catch (Exception $e) {
form_set_error('object_state', t('Error updating state %s', array('%s' => $e->getMessage())));
}
}
if (isset($label) && $label != $object->label) {
try {
$object->label = $label;
drupal_set_message(t('Successfully updated label %s', array('%s' => check_plain($label))));
}
catch (Exception $e) {
form_set_error(t('Error updating label %s', array('%s' => $e->getMessage())));
if ($propogate && ($update_states || $update_owners)) {
$related_objects_pids = $form_state['values']['related_pids'];
$batch = array(
'title' => t('Updating related objects'),
'file' => drupal_get_path('module', 'islandora') . '/includes/object_properties.form.inc',
'operations' => array(),
);
foreach ($related_objects_pids as $pid) {
$batch['operations'][] = array(
'islandora_update_object_properties',
array($pid, $update_states, $state, $update_owners, $owner),
);
}
batch_set($batch);
}
}
@ -121,3 +162,29 @@ function islandora_object_properties_form_submit(array $form, array &$form_state
function islandora_object_properties_form_delete(array $form, array &$form_state) {
drupal_goto("islandora/object/{$form_state['values']['pid']}/delete");
}
/**
* Updates object state.
*
* @param string $pid
* PID of object to be updated
* @param bool $update_states
* If TRUE, update object state
* @param string $state
* Desired object state
* @param bool $update_owners
* If TRUE, update Owner
* @param string $owner
* New Owner
*/
function islandora_update_object_properties($pid, $update_states, $state, $update_owners, $owner) {
$fedora_object = islandora_object_load($pid);
if ($fedora_object) {
if ($update_states) {
$fedora_object->state = $state;
}
if ($update_owners) {
$fedora_object->owner = $owner;
}
}
}

11
includes/solution_packs.inc

@ -28,8 +28,17 @@ function islandora_solution_packs_get_required_objects($module = NULL) {
if (!$required_objects) {
$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);
}
}
if ($module !== NULL) {
if (isset($required_objects[$module])) {
@ -745,9 +754,11 @@ function islandora_get_viewer($params = NULL, $variable_id = NULL, $fedora_objec
$viewer_id = islandora_get_viewer_id($variable_id);
if ($viewer_id AND $params !== NULL) {
$callback = islandora_get_viewer_callback($viewer_id);
if (function_exists($callback)) {
return $callback($params, $fedora_object);
}
}
}
return FALSE;
}

1
includes/tuque.inc

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

8
includes/tuque_wrapper.inc

@ -8,7 +8,11 @@
* @todo Overload functions and apply pre/post hooks.
*/
$islandora_module_path = drupal_get_path('module', 'islandora');
// This function may not exist when a batch operation is running from a
// multistep form.
if (function_exists('drupal_get_path')) {
$islandora_module_path = drupal_get_path('module', 'islandora');
}
// @todo this until we expost these in a module or library
@include_once 'sites/all/libraries/tuque/Datastream.php';
@ -207,6 +211,7 @@ class IslandoraFedoraApiM extends FedoraApiM {
'params' => $params,
);
islandora_alter_datastream($object, $datastream, $context);
$params = $context['params'];
if (isset($params['lastModifiedDate'])) {
$params['lastModifiedDate'] = (string) $object[$dsid]->createdDate;
}
@ -244,6 +249,7 @@ class IslandoraFedoraApiM extends FedoraApiM {
'params' => $params,
);
islandora_alter_object($object, $context);
$params = $context['params'];
try {
if ($context['block']) {
throw new Exception('Modify Object was blocked.');

87
includes/utilities.inc

@ -14,6 +14,8 @@
*
* @param int $bytes
* Size in bytes to convert
* @param int $precision
* The amount of decimal precision to show.
*
* @return string
* Human readable size.
@ -389,10 +391,10 @@ function islandora_get_datastreams_requirements_from_models(array $models) {
* - "optional": A boolean indicating if the given stream is optional.
*/
function islandora_get_datastreams_requirements_from_content_model(AbstractObject $object) {
if (empty($object[DS_COMP_STREAM]) || !islandora_datastream_access(FEDORA_VIEW_OBJECTS, $object[DS_COMP_STREAM])) {
if (empty($object[ISLANDORA_DS_COMP_STREAM]) || !islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $object[ISLANDORA_DS_COMP_STREAM])) {
return array();
}
$xml = new SimpleXMLElement($object[DS_COMP_STREAM]->content);
$xml = new SimpleXMLElement($object[ISLANDORA_DS_COMP_STREAM]->content);
foreach ($xml->dsTypeModel as $ds) {
$dsid = (string) $ds['ID'];
$optional = strtolower((string) $ds['optional']);
@ -887,9 +889,7 @@ function islandora_as_renderable_array(&$markup_array) {
if (!is_array($value)) {
// Not a renderable array, just a string. Let's convert it to a
// renderable '#markup' element.
$value = array(
'#markup' => $value,
);
$value = array('#markup' => $value);
}
elseif (!isset($value['#type']) && !isset($value['#markup'])) {
// A simple array--possibly the result of a recursive merge? Let's
@ -900,9 +900,7 @@ function islandora_as_renderable_array(&$markup_array) {
// If it is an array at this level, we can assume that it is a
// renderable array. If it is not an array, convert to a renderable
// '#markup' element.
$inner = array(
'#markup' => $inner,
);
$inner = array('#markup' => $inner);
}
}
unset($inner);
@ -910,3 +908,76 @@ function islandora_as_renderable_array(&$markup_array) {
}
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;
}

227
islandora.api.php

@ -21,8 +21,30 @@
* An array whose values are markup.
*/
function hook_islandora_view_object($object, $user, $page_number, $page_size) {
}
$output = array();
if (in_array('islandora:sp_basic_image', $object->models)) {
$resource_url = url("islandora/object/{$object->id}/datastream/OBJ/view");
$params = array(
'title' => $object->label,
'path' => $resource_url,
);
// Theme the image seperatly.
$variables['islandora_img'] = theme('image', $params);
$output = theme('islandora_default_print', array(
'islandora_content' => $variables['islandora_img']));
}
return $output;
}
/**
* Generate a print friendly page for the given object.
*
* @param object $object
* The object form to print.
*/
function hook_islandora_view_print_object($object) {
}
/**
* Generate an object's display for the given content model.
*
@ -64,7 +86,7 @@ function hook_CMODEL_PID_islandora_view_object_alter(&$object, &$rendered) {
}
/**
* Generate an object's management display.
* Generate an object's datastreams management display.
*
* @param AbstractObject $object
* A Tuque FedoraObject
@ -76,7 +98,7 @@ function hook_islandora_edit_object($object) {
}
/**
* Generate an object's management display for the given content model.
* Generate an object's datastreams management display based on content model.
*
* Content models PIDs have colons and hyphens changed to underscores, to
* create the hook name.
@ -91,7 +113,7 @@ function hook_CMODEL_PID_islandora_edit_object($object) {
}
/**
* Allow management display output to be altered.
* Allow datastreams management display output to be altered.
*
* @param AbstractObject $object
* A Tuque FedoraObject
@ -372,8 +394,17 @@ function hook_islandora_viewer_info() {
/**
* Returns a list of datastreams that are determined to be undeletable.
*
* The list is used to prevent delete links from being shown.
*
* @param array $models
* An array of content models for the current object.
*
* @return array
* An array of DSIDs that shouldn't be deleted.
*/
function hook_islandora_undeletable_datastreams(array $models) {
return array('DC', 'MODS');
}
/**
@ -396,17 +427,21 @@ function hook_islandora_undeletable_datastreams(array $models) {
* - do_function: An associate array including:
* - 'function': The callback function to be called.
* - '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:
* - '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.
* - 'file': A file to include (relative to the module's path, including
* the file's extension).
* 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': A file to include (relative to the module's path, including the
* file's extension).
*/
function hook_islandora_ingest_steps(array $form_state) {
@ -455,10 +490,11 @@ function hook_CMODEL_PID_islandora_ingest_steps(array $form_state) {
* @param object $user
* A loaded user object, as the global $user variable might contain.
*
* @return bool|NULL
* @return bool|NULL|array
* Either boolean TRUE or FALSE to explicitly allow or deny the operation on
* the given object, or NULL to indicate that we are making no assertion
* about the outcome.
* about the outcome. Can also be an array containing multiple
* TRUE/FALSE/NULLs, due to how hooks work.
*/
function hook_islandora_object_access($op, $object, $user) {
switch ($op) {
@ -493,10 +529,11 @@ function hook_CMODEL_PID_islandora_object_access($op, $object, $user) {
* @param object $user
* A loaded user object, as the global $user variable might contain.
*
* @return bool|NULL
* @return bool|NULL|array
* Either boolean TRUE or FALSE to explicitly allow or deny the operation on
* the given object, or NULL to indicate that we are making no assertion
* about the outcome.
* about the outcome. Can also be an array containing multiple
* TRUE/FALSE/NULLs, due to how hooks work.
*/
function hook_islandora_datastream_access($op, $object, $user) {
switch ($op) {
@ -519,3 +556,175 @@ function hook_islandora_datastream_access($op, $object, $user) {
*/
function hook_CMODEL_PID_islandora_datastream_access($op, $object, $user) {
}
/**
* Lets one add to the overview tab in object management.
*/
function hook_islandora_overview_object(AbstractObject $object) {
return drupal_render(drupal_get_form('some_form', $object));
}
/**
* Lets one add to the overview tab in object management.
*
* Content model specific.
*/
function hook_CMODEL_PID_islandora_overview_object(AbstractObject $object) {
return drupal_render(drupal_get_form('some_form', $object));
}
/**
* Lets one alter the overview tab in object management.
*/
function hook_islandora_overview_object_alter(AbstractObject &$object, &$output) {
$output = $output . drupal_render(drupal_get_form('some_form', $object));
}
/**
* Lets one alter the overview tab in object management.
*
* Content model specific.
*/
function hook_CMODEL_PID_islandora_overview_object_alter(AbstractObject &$object, &$output) {
$output = $output . drupal_render(drupal_get_form('some_form', $object));
}
/*
* Defines derivative functions to be executed based on certain conditions.
*
* @param AbstractObject $object
* Object to which derivatives will be added
* This hook fires when an object/datastream is ingested or a datastream is
* modified.
*
* @return array
* An array containing an entry for each derivative to be created. Each entry
* is an array of parameters containing:
* - force: Bool denoting whether we are forcing the generation of
* derivatives.
* - source_dsid: (Optional) String of the datastream id we are generating
* from or NULL if it's the object itself.
* - destination_dsid: (Optional) String of the datastream id that is being
* created. To be used in the UI.
* - weight: A string denoting the weight of the function. This value is
* sorted upon to run functions in order.
* - function: An array of function(s) to be ran when constructing
* derivatives. Functions that are defined to be called for derivation
* creation must have the following structure:
* module_name_derivative_creation_function($object, $force = FALSE)
* These functions must return an array in the structure of:
* - success: Bool denoting whether the operation was successful.
* - messages: An array structure containing:
* - message: A string passed through t() describing the
* outcome of the operation.
* - message_sub: (Optional) Substitutions to be passed along to t() or
* watchdog.
* - type: A string denoting whether the output is to be
* drupal_set_messaged (dsm) or watchdogged (watchdog).
* - severity: (Optional) A severity level / status to be used when
* logging messages. Uses the defaults of drupal_set_message and
* watchdog if not defined.
* - file: A string denoting the path to the file where the function
* is being called from.
*/
function hook_islandora_derivative(AbstractObject $object) {
$derivatives[] = array(
'source_dsid' => 'OBJ',
'destination_dsid' => 'DERIV',
'weight' => '0',
'function' => array(
'islandora_derivatives_test_create_deriv_datastream',
),
);
// Test object before adding this derivative.
if ($object['SOMEWEIRDDATASTREAM']->mimetype == "SOMETHING/ODD") {
$derivatives[] = array(
'source_dsid' => 'SOMEWEIRDDATASTREAM',
'destination_dsid' => 'STANLEY',
'weight' => '-1',
'function' => array(
'islandora_derivatives_test_create_some_weird_datastream',
),
);
}
$derivatives[] = array(
'source_dsid' => NULL,
'destination_dsid' => 'NOSOURCE',
'weight' => '-3',
'function' => array(
'islandora_derivatives_test_create_nosource_datastream',
),
);
return $derivatives;
}
/**
* Content model specific version of hook_islandora_derivative().
*
* @see hook_islandora_derivative()
*/
function hook_CMODEL_PID_islandora_derivative() {
}
/**
* Retrieves PIDS of related objects for property updating.
*
* @param AbstractObject $object
* AbstractObject representing deleted object
*/
function hook_islandora_update_related_objects_properties(AbstractObject $object) {
$related_objects = get_all_children_siblings_and_friends($object);
$pids_to_return = array();
foreach($related_objects as $related_object) {
$pids_to_return[] = $related_object->id;
}
return $pids_to_return;
}
/**
* Alters breadcrumbs used on Solr search results and within Islandora views.
*
* @param array $breadcrumbs
* Breadcrumbs array to be altered by reference. Each element is markup.
* @param string $context
* Where the alter is originating from for distinguishing.
*/
function hook_islandora_breadcrumbs_alter(&$breadcrumbs, $context) {
}
/**
* Registry hook for metadata display viewers.
*
* Modules can use this hook to override the default Dublin Core display.
* This hook lets Islandora know which viewers there are available.
*
* @return array
* An associative array where the values are the following:
* -label: Human readable display label for selection.
* -description: A description of what the metadata display viewer does.
* -metadata callback: A callable function that provides the markup to be
* 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
* metadata display.
* @see islandora_retrieve_metadata_markup()
*/
function hook_islandora_metadata_display_info() {
return array(
'hookable_displays_yay' => array(
'label' => t('Hookable display yay!'),
'description' => t('This is purely an example of how to implement this.'),
'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',
),
);
}

14
islandora.drush.inc

@ -26,6 +26,10 @@ function islandora_drush_command() {
'drupal dependencies' => array(
'islandora',
),
'examples' => array(
'drush -u 1 ispiro --module=islandora' => dt('Install missing solution pack objects for the "islandora" module.'),
'drush -u 1 ispiro --module=islandora --force' => dt('Install all solution pack objects for the "islandora" module, purging any which currently exist.'),
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN,
);
$commands['islandora-solution-pack-uninstall-required-objects'] = array(
@ -36,13 +40,17 @@ function islandora_drush_command() {
'required' => TRUE,
),
'force' => array(
'description' => dt('Force reinstallation of the objects.'),
'description' => dt('Force uninstallation of the objects.'),
),
),
'aliases' => array('ispuro'),
'drupal dependencies' => array(
'islandora',
),
'examples' => array(
'drush -u 1 ispuro --module=islandora' => dt('Uninstall solution pack objects for the "islandora" module.'),
'drush -u 1 ispuro --module=islandora --force' => dt('Force uninstallation of all solution pack objects for the "islandora" module.'),
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN,
);
$commands['islandora-solution-pack-required-objects-status'] = array(
@ -56,6 +64,10 @@ function islandora_drush_command() {
'drupal dependencies' => array(
'islandora',
),
'examples' => array(
'drush -u 1 ispros' => dt('Get the status of all solution pack objects.'),
'drush -u 1 ispros --module=islandora' => dt('Get the status of solution pack objects for the "islandora" module.'),
),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN,
);

5
islandora.info

@ -1,6 +1,8 @@
name = Islandora
description = "View and manage Fedora objects"
package = Islandora
dependencies[] = image
dependencies[] = file
version = 7.x-dev
core = 7.x
configure = admin/islandora/configure
@ -17,4 +19,7 @@ files[] = tests/hooks.test
files[] = tests/ingest.test
files[] = tests/hooked_access.test
files[] = tests/islandora_manage_permissions.test
files[] = tests/datastream_versions.test
files[] = tests/datastream_cache.test
files[] = tests/derivatives.test
php = 5.3

615
islandora.module

@ -24,24 +24,30 @@
*/
// Common datastreams.
define('DS_COMP_STREAM', 'DS-COMPOSITE-MODEL');
define('ISLANDORA_DS_COMP_STREAM', 'DS-COMPOSITE-MODEL');
// Permissions.
define('FEDORA_VIEW_OBJECTS', 'view fedora repository objects');
define('FEDORA_METADATA_EDIT', 'edit fedora metadata');
define('FEDORA_ADD_DS', 'add fedora datastreams');
define('FEDORA_INGEST', 'ingest fedora objects');
define('FEDORA_PURGE', 'delete fedora objects and datastreams');
define('FEDORA_MANAGE_PROPERTIES', 'manage object properties');
define('ISLANDORA_VIEW_OBJECTS', 'view fedora repository objects');
define('ISLANDORA_METADATA_EDIT', 'edit fedora metadata');
define('ISLANDORA_ADD_DS', 'add fedora datastreams');
define('ISLANDORA_INGEST', 'ingest fedora objects');
define('ISLANDORA_PURGE', 'delete fedora objects and datastreams');
define('ISLANDORA_MANAGE_PROPERTIES', 'manage object properties');
define('ISLANDORA_VIEW_DATASTREAM_HISTORY', 'view old datastream versions');
define('ISLANDORA_MANAGE_DELETED_OBJECTS', 'manage deleted objects');
define('ISLANDORA_REVERT_DATASTREAM', 'revert to old datastream');
// Hooks.
define('ISLANDORA_VIEW_HOOK', 'islandora_view_object');
define('ISLANDORA_PRINT_HOOK', 'islandora_view_print_object');
define('ISLANDORA_EDIT_HOOK', 'islandora_edit_object');
define('ISLANDORA_OVERVIEW_HOOK', 'islandora_overview_object');
define('ISLANDORA_PRE_INGEST_HOOK', 'islandora_ingest_pre_ingest');
define('ISLANDORA_POST_INGEST_HOOK', 'islandora_ingest_post_ingest');
define('ISLANDORA_PRE_PURGE_OBJECT_HOOK', 'islandora_pre_purge_object');
define('ISLANDORA_POST_PURGE_OBJECT_HOOK', 'islandora_post_purge_object');
define('ISLANDORA_UPDATE_RELATED_OBJECTS_PROPERTIES_HOOK', 'islandora_update_related_objects_properties');
// @todo Add Documentation.
define('ISLANDORA_OBJECT_INGESTED_HOOK', 'islandora_object_ingested');
@ -51,10 +57,24 @@ define('ISLANDORA_DATASTREAM_INGESTED_HOOK', 'islandora_datastream_ingested');
define('ISLANDORA_DATASTREAM_MODIFIED_HOOK', 'islandora_datastream_modified');
define('ISLANDORA_DATASTREAM_PURGED_HOOK', 'islandora_datastream_purged');
define('ISLANDORA_INGEST_STEP_HOOK', 'islandora_ingest_steps');
define('ISLANDORA_DERVIATIVE_CREATION_HOOK', 'islandora_derivative');
// Autocomplete paths.
define('ISLANDORA_CONTENT_MODELS_AUTOCOMPLETE', 'islandora/autocomplete/content-models');
/**
* @deprecated Constants.
*/
// @codingStandardsIgnoreStart
define('DS_COMP_STREAM', ISLANDORA_DS_COMP_STREAM);
define('FEDORA_VIEW_OBJECTS', ISLANDORA_VIEW_OBJECTS);
define('FEDORA_METADATA_EDIT', ISLANDORA_METADATA_EDIT);
define('FEDORA_ADD_DS', ISLANDORA_ADD_DS);
define('FEDORA_INGEST', ISLANDORA_INGEST);
define('FEDORA_PURGE', ISLANDORA_PURGE);
define('FEDORA_MANAGE_PROPERTIES', ISLANDORA_MANAGE_PROPERTIES);
// @codingStandardsIgnoreEnd
/**
* Implements hook_menu().
*
@ -79,11 +99,19 @@ function islandora_menu() {
'type' => MENU_NORMAL_ITEM,
'weight' => -1,
);
$items['admin/islandora/metadata'] = array(
'title' => 'Metadata Display',
'description' => 'Configure settings for metadata display on Islandora objects',
'page callback' => 'drupal_get_form',
'page arguments' => array('islandora_metadata_display_form'),
'file' => 'includes/metadata.inc',
'access arguments' => array('administer site configuration'),
);
$items['admin/islandora/solution_packs'] = array(
'title' => 'Solution packs',
'description' => 'Install content models and collections required by installed solution packs.',
'page callback' => 'islandora_solution_packs_admin',
'access arguments' => array(FEDORA_ADD_DS),
'access arguments' => array(ISLANDORA_ADD_DS),
'file' => 'includes/solution_packs.inc',
'type' => MENU_NORMAL_ITEM,
);
@ -91,7 +119,7 @@ function islandora_menu() {
'title' => 'Islandora Repository',
'page callback' => 'islandora_view_default_object',
'type' => MENU_NORMAL_ITEM,
'access arguments' => array(FEDORA_VIEW_OBJECTS),
'access arguments' => array(ISLANDORA_VIEW_OBJECTS),
);
$items['islandora/object/%islandora_object'] = array(
'title callback' => 'islandora_drupal_title',
@ -100,7 +128,23 @@ function islandora_menu() {
'page arguments' => array(2),
'type' => MENU_NORMAL_ITEM,
'access callback' => 'islandora_object_access_callback',
'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 2),
);
$items['islandora/object/%islandora_object/print_object'] = array(
'page callback' => 'islandora_printer_object',
'page arguments' => array(2),
'type' => MENU_NORMAL_ITEM,
'access callback' => 'islandora_object_access',
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 2),
);
$items['islandora/object/%islandora_object/print'] = array(
'title' => 'Print Object',
'page callback' => 'islandora_print_object',
'page arguments' => array(2),
'type' => MENU_CALLBACK,
'access callback' => 'islandora_object_access',
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 2),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/view'] = array(
'title' => 'View',
@ -120,20 +164,18 @@ function islandora_menu() {
'access callback' => 'islandora_object_manage_access_callback',
'access arguments' => array(
array(
FEDORA_MANAGE_PROPERTIES,
FEDORA_METADATA_EDIT,
FEDORA_ADD_DS,
FEDORA_PURGE,
FEDORA_INGEST,
ISLANDORA_MANAGE_PROPERTIES,
ISLANDORA_METADATA_EDIT,
ISLANDORA_ADD_DS,
ISLANDORA_PURGE,
ISLANDORA_INGEST,
), 2),
);
$items['islandora/object/%islandora_object/manage/overview'] = array(
'title' => 'Overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -20,
);
$items['islandora/object/%islandora_object/manage/datastreams'] = array(
'title' => 'Datastreams',
'type' => MENU_LOCAL_TASK,
@ -142,13 +184,12 @@ function islandora_menu() {
'access callback' => 'islandora_object_manage_access_callback',
'access arguments' => array(
array(
FEDORA_METADATA_EDIT,
FEDORA_ADD_DS,
FEDORA_PURGE,
ISLANDORA_METADATA_EDIT,
ISLANDORA_ADD_DS,
ISLANDORA_PURGE,
), 2),
'weight' => -10,
);
$items['islandora/object/%islandora_object/manage/properties'] = array(
'title' => 'Properties',
'page callback' => 'drupal_get_form',
@ -156,7 +197,7 @@ function islandora_menu() {
'page arguments' => array('islandora_object_properties_form', 2),
'type' => MENU_LOCAL_TASK,
'access callback' => 'islandora_object_access_callback',
'access arguments' => array(FEDORA_MANAGE_PROPERTIES, 2),
'access arguments' => array(ISLANDORA_MANAGE_PROPERTIES, 2),
'weight' => -5,
);
$items['islandora/object/%islandora_object/delete'] = array(
@ -166,7 +207,7 @@ function islandora_menu() {
'page arguments' => array('islandora_delete_object_form', 2),
'type' => MENU_CALLBACK,
'access callback' => 'islandora_object_access_callback',
'access arguments' => array(FEDORA_PURGE, 2),
'access arguments' => array(ISLANDORA_PURGE, 2),
);
$items['islandora/object/%islandora_object/manage/datastreams/add'] = array(
'title' => 'Add a datastream',
@ -175,7 +216,7 @@ function islandora_menu() {
'page arguments' => array('islandora_add_datastream_form', 2),
'type' => MENU_LOCAL_ACTION,
'access callback' => 'islandora_object_access_callback',
'access arguments' => array(FEDORA_ADD_DS, 2),
'access arguments' => array(ISLANDORA_ADD_DS, 2),
);
$items['islandora/object/%islandora_object/manage/datastreams/add/autocomplete'] = array(
'file' => 'includes/add_datastream.form.inc',
@ -183,16 +224,16 @@ function islandora_menu() {
'page arguments' => array(2),
'type' => MENU_CALLBACK,
'access callback' => 'islandora_object_access_callback',
'access arguments' => array(FEDORA_ADD_DS, 2),
'access arguments' => array(ISLANDORA_ADD_DS, 2),
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream'] = array(
'title' => 'View datastream',
'page callback' => 'islandora_view_datastream',
'page arguments' => array(4, FALSE),
'page arguments' => array(4, FALSE, NULL),
'type' => MENU_CALLBACK,
'file' => 'includes/datastream.inc',
'access callback' => 'islandora_datastream_access',
'access arguments' => array(FEDORA_VIEW_OBJECTS, 4),
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 4),
'load arguments' => array(2),
);
// This menu item uses token authentication in islandora_tokened_object.
@ -200,7 +241,7 @@ function islandora_menu() {
'title' => 'View datastream',
'load arguments' => array('%map'),
'access callback' => 'islandora_object_datastream_tokened_access_callback',
'access arguments' => array(FEDORA_VIEW_OBJECTS, 2, 4),
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 2, 4),
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/download'] = array(
@ -210,7 +251,7 @@ function islandora_menu() {
'type' => MENU_CALLBACK,
'file' => 'includes/datastream.inc',
'access callback' => 'islandora_datastream_access',
'access arguments' => array(FEDORA_VIEW_OBJECTS, 4),
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/edit'] = array(
@ -220,7 +261,7 @@ function islandora_menu() {
'type' => MENU_CALLBACK,
'file' => 'includes/datastream.inc',
'access callback' => 'islandora_datastream_access',
'access arguments' => array(FEDORA_METADATA_EDIT, 4),
'access arguments' => array(ISLANDORA_METADATA_EDIT, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/delete'] = array(
@ -230,16 +271,47 @@ function islandora_menu() {
'file' => 'includes/delete_datastream.form.inc',
'type' => MENU_CALLBACK,
'access callback' => 'islandora_datastream_access',
'access arguments' => array(FEDORA_PURGE, 4),
'access arguments' => array(ISLANDORA_PURGE, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/print'] = array(
'title' => 'Print Object',
'page callback' => 'islandora_print_object',
'page arguments' => array(2),
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/version'] = array(
'title' => 'Datastream Versions',
'page arguments' => array(4),
'page callback' => 'islandora_datastream_version_table',
'file' => 'includes/datastream.version.inc',
'type' => MENU_CALLBACK,
'access callback' => 'islandora_object_access',
'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
'access callback' => 'islandora_datastream_access',
'access arguments' => array(ISLANDORA_VIEW_DATASTREAM_HISTORY, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/version/%/delete'] = array(
'title' => 'Delete datastream version',
'page arguments' => array('islandora_delete_datastream_version_form', 4, 6),
'page callback' => 'drupal_get_form',
'file' => 'includes/datastream.version.inc',
'type' => MENU_CALLBACK,
'access callback' => 'islandora_datastream_access',
'access arguments' => array(ISLANDORA_PURGE, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/version/%/revert'] = array(
'title' => 'Revert to datastream version',
'page arguments' => array('islandora_revert_datastream_version_form', 4, 6),
'page callback' => 'drupal_get_form',
'file' => 'includes/datastream.version.inc',
'type' => MENU_CALLBACK,
'access callback' => 'islandora_datastream_access',
'access arguments' => array(ISLANDORA_REVERT_DATASTREAM, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/version/%/view'] = array(
'title' => 'View datastream version',
'page callback' => 'islandora_view_datastream',
'page arguments' => array(4, FALSE, 6),
'type' => MENU_CALLBACK,
'file' => 'includes/datastream.inc',
'access callback' => 'islandora_datastream_access',
'access arguments' => array(ISLANDORA_VIEW_DATASTREAM_HISTORY, 4),
'load arguments' => array(2),
);
$items['islandora/object/%islandora_object/download_clip'] = array(
@ -247,9 +319,16 @@ function islandora_menu() {
'page arguments' => array(2),
'type' => MENU_CALLBACK,
'access callback' => 'islandora_object_access',
'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 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(
'title' => 'Autocomplete callback',
'description' => 'Autocomplete a Fedora content model PID.',
@ -259,6 +338,26 @@ function islandora_menu() {
'access arguments' => array('administer site configuration'),
'type' => MENU_CALLBACK,
);
$items['admin/islandora/restore/prep'] = array(
'description' => 'Restore or permanantly remove objects with Deleted status',
'title' => 'Manage Deleted Objects',
'file' => 'includes/manage_deleted_objects.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array('islandora_deleted_objects_prep_form'),
'type' => MENU_NORMAL_ITEM,
'access arguments' => array(ISLANDORA_MANAGE_DELETED_OBJECTS),
);
$items['admin/islandora/restore/manage'] = array(
'description' => 'Restore or permanantly remove objects with Deleted status',
'title' => 'Manage Deleted Objects',
'file' => 'includes/manage_deleted_objects.inc',
'page callback' => 'drupal_get_form',
'page arguments' => array('islandora_deleted_objects_manage_form'),
'type' => MENU_SUGGESTED_ITEM,
'access arguments' => array(ISLANDORA_MANAGE_DELETED_OBJECTS),
);
return $items;
}
@ -268,8 +367,9 @@ function islandora_menu() {
function islandora_admin_paths() {
$paths = array();
$paths['islandora/object/*/manage*'] = TRUE;
$paths['islandora/object/*/delete'] = TRUE;
$paths['islandora/object/*/delete*'] = TRUE;
$paths['islandora/object/*/datastream/*/edit'] = TRUE;
$paths['islandora/object/*/datastream/*/versions'] = TRUE;
return $paths;
}
@ -295,11 +395,20 @@ function islandora_theme() {
'file' => 'includes/solution_packs.inc',
'render element' => 'form',
),
// Print object view.
// Print used by the clipper.
'islandora_object_print' => array(
'file' => 'theme/theme.inc',
'variables' => array('object' => NULL, 'content' => array()),
),
// Print object view, prints islandora objects.
'islandora_object_print_object' => array(
'file' => 'theme/theme.inc',
'template' => 'theme/islandora-object-print',
'variables' => array(
'object' => NULL,
'content' => NULL,
'islandora_content' => NULL),
),
// Render a bunch of objects as either a grid or a list.
'islandora_objects' => array(
'file' => 'theme/theme.inc',
@ -329,16 +438,53 @@ function islandora_theme() {
),
'islandora_datastream_delete_link' => array(
'file' => 'theme/theme.inc',
'variables' => array('datastream' => NULL),
'variables' => array('datastream' => NULL, 'version' => NULL),
),
'islandora_datastream_revert_link' => array(
'file' => 'theme/theme.inc',
'variables' => array('datastream' => NULL, 'version' => NULL),
),
'islandora_datastream_view_link' => array(
'file' => 'theme/theme.inc',
'variables' => array('datastream' => NULL),
'variables' => array(
'datastream' => NULL,
'version' => NULL,
'label' => NULL,
),
),
'islandora_datastream_download_link' => array(
'file' => 'theme/theme.inc',
'variables' => array('datastream' => NULL),
),
'islandora_datastream_version_link' => array(
'file' => 'theme/theme.inc',
'variables' => array('datastream' => NULL),
),
'islandora_dublin_core_display' => array(
'file' => 'theme/theme.inc',
'template' => 'theme/islandora-dublin-core-display',
// 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-display--islandora-27.tpl.php".
'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),
),
);
}
@ -347,31 +493,102 @@ function islandora_theme() {
*/
function islandora_permission() {
return array(
FEDORA_VIEW_OBJECTS => array(
ISLANDORA_VIEW_OBJECTS => array(
'title' => t('View repository objects'),
'description' => t('View objects in the repository. Note: Fedora XACML security policies may override this permission.'),
),
FEDORA_ADD_DS => array(
ISLANDORA_ADD_DS => array(
'title' => t('Add datastreams to repository objects'),
'description' => t('Add datastreams to objects in the repository. Note: Fedora XACML security policies may override this position.'),
),
FEDORA_METADATA_EDIT => array(
ISLANDORA_METADATA_EDIT => array(
'title' => t('Edit metadata'),
'description' => t('Edit metadata for objects in the repository.'),
),
FEDORA_INGEST => array(
ISLANDORA_INGEST => array(
'title' => t('Create new repository objects'),
'description' => t('Create new objects in the repository.'),
),
FEDORA_PURGE => array(
ISLANDORA_PURGE => array(
'title' => t('Permanently remove objects from the repository'),
'description' => t('Permanently remove objects from the repository.'),
),
FEDORA_MANAGE_PROPERTIES => array(
ISLANDORA_MANAGE_PROPERTIES => array(
'title' => t('Manage object properties'),
'description' => t('Modify object labels, owner IDs, and states.'),
),
ISLANDORA_VIEW_DATASTREAM_HISTORY => array(
'title' => t('View datastream history'),
'description' => t('View all previous versions of a datastream.'),
),
ISLANDORA_REVERT_DATASTREAM => array(
'title' => t('Revert datastream history'),
'description' => t('Revert to a previous version of a datastream.'),
),
ISLANDORA_MANAGE_DELETED_OBJECTS => array(
'title' => t('Manage deleted objects'),
'description' => t('Purge or revert deleted objects.'),
),
);
}
/**
* 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.
*
* Modules can either implement preprocess functions to append content onto the
* 'content' variable, or override the display by providing a theme suggestion.
*
* @param AbstractObject $object
* The object.
*
* @return array
* A renderable array.
*/
function islandora_print_object(AbstractObject $object) {
drupal_set_title($object->label);
return theme('islandora_object_print', array('object' => $object));
}
/**
@ -380,9 +597,7 @@ function islandora_permission() {
function islandora_forms($form_id) {
$forms = array();
if (strpos($form_id, 'islandora_solution_pack_form_') !== FALSE) {
$forms[$form_id] = array(
'callback' => 'islandora_solution_pack_form',
);
$forms[$form_id] = array('callback' => 'islandora_solution_pack_form');
}
return $forms;
}
@ -651,11 +866,7 @@ function islandora_manage_overview_object(AbstractObject $object) {
*/
function islandora_default_islandora_manage_overview_object(AbstractObject $object) {
$output = theme('islandora_default_overview', array('islandora_object' => $object));
return array(
'Default overview output' => array(
'#markup' => $output,
),
);
return array('Default overview output' => array('#markup' => $output));
}
/**
@ -708,11 +919,7 @@ function islandora_edit_object(AbstractObject $object) {
*/
function islandora_default_islandora_edit_object(AbstractObject $object) {
$output = theme('islandora_default_edit', array('islandora_object' => $object));
return array(
'Default Edit output' => array(
'#markup' => $output,
),
);
return array('Default Edit output' => array('#markup' => $output));
}
/**
@ -744,8 +951,21 @@ function islandora_view_default_object() {
function islandora_view_object(AbstractObject $object) {
module_load_include('inc', 'islandora', 'includes/breadcrumb');
module_load_include('inc', 'islandora', 'includes/utilities');
// Add the print button via JavaScript.
$path = drupal_get_path('module', 'islandora');
drupal_add_js(array(
'islandora' => array(
'print_img' => $path . '/images/print-icon.png'),
), 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_set_title($object->label);
drupal_set_breadcrumb(islandora_get_breadcrumbs($object));
// Optional pager parameters.
$page_number = (empty($_GET['page'])) ? '1' : $_GET['page'];
$page_size = (empty($_GET['pagesize'])) ? '10' : $_GET['pagesize'];
@ -764,12 +984,47 @@ function islandora_view_object(AbstractObject $object) {
// No results, use the default view.
$output = islandora_default_islandora_view_object($object);
}
arsort($output);
drupal_alter($hooks, $object, $output);
islandora_as_renderable_array($output);
return $output;
}
/**
* This will prepare an object to be printed.
*
* By default, all fedora objects can print DC record data,
* however, Solution packs that wish to modify the form
* to be printed must implement hook_islandora_view_print_object_alter,
* create a theme.tpl.php file, and return its markup by calling
* theme(themename.tpl.php).
*
* @param AbstractObject $object
* The object to print.
*
* @return string
* An HTML representation of this object.
*/
function islandora_printer_object(AbstractObject $object) {
$output = array();
$temp_arr = array();
// Dispatch print hook.
foreach (islandora_build_hook_list(ISLANDORA_PRINT_HOOK, $object->models) as $hook) {
$temp = module_invoke_all($hook, $object);
islandora_as_renderable_array($temp);
if (!empty($temp)) {
$temp_arr = array_merge_recursive($temp_arr, $temp);
}
}
$output = islandora_default_islandora_printer_object($object, drupal_render($temp_arr));
arsort($output);
islandora_as_renderable_array($output);
// Prompt to print.
drupal_add_js('jQuery(document).ready(function () { window.print(); });', 'inline');
return $output;
}
/**
* Title callback for drupal title.
@ -787,8 +1042,9 @@ function islandora_drupal_title(AbstractObject $object) {
module_load_include('inc', 'islandora', 'includes/breadcrumb');
drupal_set_breadcrumb(islandora_get_breadcrumbs($object));
return $object->label;
return filter_xss($object->label);
}
/**
* Renders the default view object page for the given object.
*
@ -800,11 +1056,47 @@ function islandora_drupal_title(AbstractObject $object) {
*/
function islandora_default_islandora_view_object($object) {
$output = theme('islandora_default', array('islandora_object' => $object));
return array(
'Default output' => array(
'#markup' => $output,
),
);
return array('Default output' => array('#markup' => $output));
}
/**
* Append the image alter to the printable form.
*
* @param AbstractObject $object
* The fedora object to print.
* @param unknown $alter
* The string representation of the themed viewable object.
*
* @return array
* A renderable array
*/
function islandora_default_islandora_printer_object($object, $alter) {
module_load_include('inc', 'islandora', 'includes/utilities');
module_load_include('inc', 'islandora', 'includes/datastream');
module_load_include('inc', 'islandora', 'includes/metadata');
$path = drupal_get_path('module', 'islandora');
drupal_add_css($path . '/css/islandora.print.css');
$islandora_object = islandora_object_load($object->id);
$repository = $islandora_object->repository;
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);
}
$metadata = islandora_retrieve_metadata_markup($object, TRUE);
$variables = isset($dc_object) ? $dc_object->asArray() : array();
$output = theme('islandora_object_print_object', array(
'object' => $object,
'dc_array' => $variables,
'metadata' => $metadata,
'islandora_content' => $alter));
return array('Default output' => array('#markup' => $output));
}
/**
@ -821,6 +1113,7 @@ function islandora_default_islandora_view_object($object) {
* A IslandoraTuque instance
*/
function islandora_get_tuque_connection($user = NULL, $url = NULL) {
module_load_include('inc', 'islandora', 'includes/tuque');
$tuque = &drupal_static(__FUNCTION__);
if (!$tuque) {
if (IslandoraTuque::exists()) {
@ -1023,18 +1316,16 @@ function islandora_islandora_required_objects(IslandoraTuque $connection) {
return array(
'islandora' => array(
'title' => 'Islandora',
'objects' => array(
$root_collection,
),
'objects' => array($root_collection),
),
);
}
/**
* Implements islandora_undeleteable_datastreams().
* Implements hook_islandora_undeleteable_datastreams().
*/
function islandora_islandora_undeletable_datastreams(array $models) {
return array('DC');
return array('DC', 'RELS-EXT');
}
/**
@ -1202,23 +1493,6 @@ function islandora_entity_property_info() {
return $info;
}
/**
* Renders the print page for the given object.
*
* Modules can either implement preprocess functions to append content onto the
* 'content' variable, or override the display by providing a theme suggestion.
*
* @param AbstractObject $object
* The object.
*
* @return array
* A renderable array.
*/
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.
*/
@ -1317,7 +1591,7 @@ function islandora_islandora_object_access($op, $object, $user) {
/**
* Hookable access callback for datastreams.
*
* Requires the equivalent permissions on the object.
* Positive permissions on object access suggests on the datastream.
*/
function islandora_datastream_access($op, $datastream, $user = NULL) {
$cache = &drupal_static(__FUNCTION__);
@ -1346,10 +1620,9 @@ function islandora_datastream_access($op, $datastream, $user = NULL) {
$user,
));
// Neither the object nor the datastream check returned FALSE, and one in
// the object or datastream checks returned TRUE.
// The datastream check returned FALSE, and one in the object or datastream
// checks returned TRUE.
$cache[$op][$datastream->parent->id][$datastream->id][$user->uid] = (
!in_array(FALSE, $object_results, TRUE) &&
!in_array(FALSE, $datastream_results, TRUE) &&
(in_array(TRUE, $object_results, TRUE) || in_array(TRUE, $datastream_results, TRUE))
);
@ -1362,12 +1635,164 @@ function islandora_datastream_access($op, $datastream, $user = NULL) {
* Implements hook_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);
if ($enforced) {
$namespace_array = islandora_get_allowed_namespaces();
$namespace_sparql = implode('|', $namespace_array);
return format_string('regex(str(?object), "info:fedora/(!namespaces):")', array(
'!namespaces' => $namespace_sparql,
return format_string('regex(str(?object), "info:fedora/(!namespaces):")', array('!namespaces' => $namespace_sparql));
}
}
/**
* Implements hook_islandora_object_ingested().
*
* On object ingestion we call the case of source_dsid being NULL only as
* the islandora_islandora_datastream_ingested hook will handle the cases
* where specific values of source_dsid can occur.
*/
function islandora_islandora_object_ingested(AbstractObject $object) {
module_load_include('inc', 'islandora', 'includes/derivatives');
$logging_results = islandora_do_derivatives($object, array(
'source_dsid' => NULL,
));
islandora_derivative_logging($logging_results);
}
/**
* Implements hook_islandora_datastream_ingested().
*
* When a datastream is ingested we filter the derivatives on source_dsid being
* equal to the current ingested datastream's id.
*/
function islandora_islandora_datastream_ingested(AbstractObject $object, AbstractDatastream $datastream) {
module_load_include('inc', 'islandora', 'includes/derivatives');
$logging_results = islandora_do_derivatives($object, array(
'source_dsid' => $datastream->id,
));
islandora_derivative_logging($logging_results);
}
/**
* Implements hook_islandora_datastream_modified().
*
* When a datastream is modified we filter the derivatives on source_dsid being
* equal to the current ingested datastream's id. Force is set to TRUE such that
* existing derivatives will be updated to reflect the change in the source.
*/
function islandora_islandora_datastream_modified(AbstractObject $object, AbstractDatastream $datastream) {
module_load_include('inc', 'islandora', 'includes/derivatives');
$logging_results = islandora_do_derivatives($object, array(
'source_dsid' => $datastream->id,
'force' => TRUE,
));
islandora_derivative_logging($logging_results);
}
/**
* 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().
*/
function islandora_islandora_metadata_display_info() {
return array(
'dublin_core' => array(
'label' => t('Dublin Core'),
'description' => t('Dublin Core metadata'),
'metadata callback' => 'islandora_metadata_display_callback',
'description callback' => 'islandora_metadata_description_callback',
),
);
}

18
js/add_print.js

@ -0,0 +1,18 @@
/**
* @file
* JavaScript file responsable for the print button behaviour.
*
* The print button is added automatically to every view, as metadata
* can be printed from every object.
*
*/
(function ($) {
$(document).ready(function() {
$('.tabs .primary').append('<img id="print_btn" title="Print" src="' + Drupal.settings.basePath + Drupal.settings.islandora.print_img + '"></img>');
$('#print_btn').css("cursor","pointer");
$('#print_btn').click(function() {
window.location=Drupal.settings.basePath + Drupal.settings.islandora.print_link;
});
});
})(jQuery);

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);

2
tests/authtokens.test

@ -74,7 +74,7 @@ class IslandoraAuthtokensTestCase extends IslandoraWebTestCase {
$this->drupalGet("islandora/object/{$newpid}/datastream/JP2/view");
$this->assertResponse(403, 'Page not found as anonymous');
$account = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS));
$account = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS));
$this->drupalLogin($account);
$this->drupalGet("islandora/object/{$newpid}/datastream/JP2/view");

160
tests/datastream_cache.test

@ -0,0 +1,160 @@
<?php
/**
* @file
* Tests to verify the cache headers we provide.
*/
class IslandoraDatastreamCacheTestCase extends IslandoraWebTestCase {
/**
* Gets info to display to describe this test.
*
* @see IslandoraWebTestCase::getInfo()
*/
public static function getInfo() {
return array(
'name' => 'Datastream Cache Headers',
'description' => 'Check our headers work as we expect them to.',
'group' => 'Islandora',
);
}
/**
* Creates an admin user and a connection to a fedora repository.
*
* @see IslandoraWebTestCase::setUp()
*/
public function setUp() {
parent::setUp();
$this->repository = $this->admin->repository;
$this->purgeTestObjects();
}
/**
* Free any objects/resources created for this test.
*
* @see IslandoraWebTestCase::tearDown()
*/
public function tearDown() {
$this->purgeTestObjects();
parent::tearDown();
}
/**
* Purge any objects created by the test's in this class.
*/
public function purgeTestObjects() {
$objects = array(
'test:test',
);
foreach ($objects as $object) {
try {
$object = $this->repository->getObject($object);
$this->repository->purgeObject($object->id);
}
catch (Exception $e) {
// Meh... Either it didn't exist or the purge failed.
}
}
}
/**
* Create our test object.
*/
protected function createTestObject() {
$object = $this->repository->constructObject('test:test');
$object->label = 'Test object';
$object->models = 'test:model';
$datastream = $object->constructDatastream('asdf', 'M');
$datastream->label = 'datastream of doom';
$datastream->mimetype = 'text/plain';
$datastream->content = 'And then things happened.';
$datastream->checksumType = 'SHA-1';
$object->ingestDatastream($datastream);
$this->repository->ingestObject($object);
return $object;
}
/**
* Test HTTP cache headers.
*/
public function testCacheHeaders() {
$object = $this->createTestObject();
$datastream = $object['asdf'];
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS));
$this->drupalLogin($user);
// Test If-Modified-Since.
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
'If-Modified-Since: ' . $datastream->createdDate->format('D, d M Y H:i:s \G\M\T'),
));
$this->assertResponse(304);
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
'If-Modified-Since: ' . $datastream->createdDate->sub(new DateInterval('P1M'))->format('D, d M Y H:i:s \G\M\T'),
));
$this->assertResponse(200);
// Test If-Unmodified-Since.
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
'If-Unmodified-Since: ' . $datastream->createdDate->format('D, d M Y H:i:s \G\M\T'),
));
$this->assertResponse(200);
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
'If-Unmodified-Since: ' . $datastream->createdDate->sub(new DateInterval('P1M'))->format('D, d M Y H:i:s \G\M\T'),
));
$this->assertResponse(412);
// Test If-Match.
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
format_string('If-Match: "!checksum"', array(
'!checksum' => $datastream->checksum,
)),
));
$this->assertResponse(200);
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
format_string('If-Match: "!checksum"', array(
'!checksum' => 'dont-match' . $datastream->checksum,
)),
));
$this->assertResponse(412);
// Test If-None-Match.
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
format_string('If-None-Match: "!checksum"', array(
'!checksum' => $datastream->checksum,
)),
));
$this->assertResponse(304);
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
format_string('If-None-Match: "!checksum"', array(
'!checksum' => 'dont-match' . $datastream->checksum,
)),
));
$this->assertResponse(200);
// Test combination of If-None-Match and If-Modified-Since
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
'If-Modified-Since: ' . $datastream->createdDate->format('D, d M Y H:i:s \G\M\T'),
format_string('If-None-Match: "!checksum"', array(
'!checksum' => $datastream->checksum,
)),
));
$this->assertResponse(304);
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
'If-Modified-Since: ' . $datastream->createdDate->format('D, d M Y H:i:s \G\M\T'),
format_string('If-None-Match: "!checksum"', array(
'!checksum' => 'dont-match' . $datastream->checksum,
)),
));
$this->assertResponse(200);
$result = $this->drupalGet("islandora/object/{$object->id}/datastream/{$datastream->id}/view", array(), array(
'If-Modified-Since: ' . $datastream->createdDate->sub(new DateInterval('P1M'))->format('D, d M Y H:i:s \G\M\T'),
format_string('If-None-Match: "!checksum"', array(
'!checksum' => $datastream->checksum,
)),
));
$this->assertResponse(200);
}
}

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;
}

198
tests/datastream_versions.test

@ -0,0 +1,198 @@
<?php
/**
* @file
* Tests that datastream versions code works as intended
*/
class IslandoraDatastreamVersionTestCase extends IslandoraWebTestCase {
/**
* Gets info to display to describe this test.
*
* @see IslandoraWebTestCase::getInfo()
*/
public static function getInfo() {
return array(
'name' => 'Islandora Datastream Versions',
'description' => 'Tests the functionality related to datastream versions in Islandora.',
'group' => 'Islandora',
);
}
/**
* Create an object with many datastram versions.
*
* @see IslandoraWebTestCase::setUp()
*/
public function setUp() {
parent::setUp();
$this->pid = $this->randomName() . ":" . $this->randomName();
$tuque = islandora_get_tuque_connection();
$object = $tuque->repository->constructObject($this->pid);
$object = $tuque->repository->ingestObject($object);
$this->dsid = $this->randomName();
$ds = $object->constructDatastream($this->dsid);
$ds->label = 'Test';
$ds->content = 'test';
$object->ingestDatastream($ds);
// Create three versions.
$ds->mimetype = 'application/pdf';
$ds->label = 'jaaaaa maaaan';
$ds->content = 'Tests... are the bests.';
}
/**
* Free any objects/resources created for this test.
*
* @see IslandoraWebTestCase::tearDown()
*/
public function tearDown() {
$tuque = islandora_get_tuque_connection();
$tuque->repository->purgeObject($this->pid);
parent::tearDown();
}
/**
* Check if the user can see datastream versions in the datastream table.
*/
public function testSeeDatastreamVersions() {
$user = $this->drupalCreateUser(array(
'view fedora repository objects',
'ingest fedora objects',
'view old datastream versions',
'add fedora datastreams',
));
$this->drupalLogin($user);
$this->drupalGet("islandora/object/{$this->pid}/manage/datastreams");
$this->assertLink($this->dsid);
$this->assertLink("4");
$encoded_pid = urlencode($this->pid);
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version");
}
/**
* Check that users without permission cannot see datastream versions.
*/
public function testNotSeeDatastreamVersions() {
$user = $this->drupalCreateUser(array(
'view fedora repository objects',
'ingest fedora objects',
'add fedora datastreams',
));
$this->drupalLogin($user);
$this->drupalGet("islandora/object/{$this->pid}/manage/datastreams");
$this->assertLink($this->dsid);
$this->assertNoLink("4");
$encoded_pid = urlencode($this->pid);
$this->assertNoLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version");
}
/**
* Check that users without permission cannot see datastream version pages.
*/
public function testDatastreamVersionPermissions() {
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version");
$this->assertResponse(403);
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/0/view");
$this->assertResponse(403);
}
/**
* Check that the proper infomration is displayed on the ds version page.
*/
public function testDatastreamVersionPage() {
$user = $this->drupalCreateUser(array(
'view old datastream versions',
));
$this->drupalLogin($user);
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version");
$this->assertNoLink("Delete");
$this->assertText("text/xml");
$this->assertText("application/pdf");
$this->assertText("jaaaaa maaaan");
$this->assertText("Test");
$encoded_pid = urlencode($this->pid);
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/0/view");
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/1/view");
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/2/view");
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/3/view");
}
/**
* Make sure the correct content is displayed for each datastream version.
*/
public function testDatastreamVersionContent() {
$user = $this->drupalCreateUser(array(
'view old datastream versions',
));
$this->drupalLogin($user);
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/3/view");
$content = $this->drupalGetContent();
if ($content != 'test') {
$this->fail("Incorrect datastream content");
}
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/2/view");
$content = $this->drupalGetContent();
if ($content != 'test') {
$this->fail("Incorrect datastream content");
}
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/1/view");
$content = $this->drupalGetContent();
if ($content != 'test') {
$this->fail("Incorrect datastream content");
}
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/0/view");
$content = $this->drupalGetContent();
if ($content != 'Tests... are the bests.') {
$this->fail("Incorrect datastream content");
}
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/5/view");
$this->assertResponse(404);
}
/**
* Make sure you can delete datastream versions.
*/
public function testDatastreamVersionDelete() {
$user = $this->drupalCreateUser(array(
'view old datastream versions',
'delete fedora objects and datastreams',
));
$this->drupalLogin($user);
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version");
$this->assertLink("delete");
$this->assertText('text/xml');
$encoded_pid = urlencode($this->pid);
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/0/delete");
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/1/delete");
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/2/delete");
$this->assertLinkByHref("islandora/object/$encoded_pid/datastream/{$this->dsid}/version/3/delete");
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/3/delete");
$this->drupalPost(NULL, array(), t('Delete'));
$this->assertNoText('text/xml');
}
/**
* Make sure you can't delete versions that don't exist/have only 1 version.
*/
public function testDatastreamVersionDeleteEdgeCase() {
$user = $this->drupalCreateUser(array(
'view old datastream versions',
'delete fedora objects and datastreams',
));
$this->drupalLogin($user);
$this->drupalGet("islandora/object/{$this->pid}/datastream/{$this->dsid}/version/6/delete");
$this->assertResponse(404);
$this->drupalGet("islandora/object/{$this->pid}/datastream/DC/version/0/delete");
$this->assertResponse(404);
}
}

296
tests/derivatives.test

@ -0,0 +1,296 @@
<?php
/**
* @file
* Tests to see if the hooks get called when appropriate.
*
* In the test module 'islandora_derivatives_test' there are implementations
* of hooks being tested. These implementations modifies the session, and
* that's how we test if the hook gets called.
*
* To make sense of these tests reference islandora_derivatives_test.module.
*/
class IslandoraDerivativesTestCase extends IslandoraWebTestCase {
/**
* Gets info to display to describe this test.
*
* @see IslandoraWebTestCase::getInfo()
*/
public static function getInfo() {
return array(
'name' => 'Islandora Derivative Generation',
'description' => 'Ensure that the derivative generation hooks return appropriate results.',
'group' => 'Islandora',
);
}
/**
* Creates an admin user and a connection to a fedora repository.
*
* @see IslandoraWebTestCase::setUp()
*/
public function setUp() {
parent::setUp(
array(
'islandora_derivatives_test',
)
);
$url = variable_get('islandora_base_url', 'http://localhost:8080/fedora');
$this->connection = new RepositoryConnection($url, $this->admin->name, $this->admin->pass);
$this->connection->reuseConnection = TRUE;
$this->api = new FedoraApi($this->connection);
$this->cache = new SimpleCache();
$this->repository = new FedoraRepository($this->api, $this->cache);
$this->pid = $this->randomName() . ":" . $this->randomName();
}
/**
* Free any objects/resources created for this test.
*
* @see IslandoraWebTestCase::tearDown()
*/
public function tearDown() {
$tuque = islandora_get_tuque_connection();
parent::tearDown();
}
/**
* Tests that the islandora_islandora_object_ingested hook gets fired.
*/
public function testDerivativeOnIngest() {
global $_islandora_derivative_test_ingest_method;
$_islandora_derivative_test_ingest_method = 'modifyDatastream';
$tuque = islandora_get_tuque_connection();
$object = $tuque->repository->constructObject($this->pid);
$object->models = array(
'some:cmodel',
);
$dsid = 'OBJ';
$ds = $object->constructDatastream($dsid);
$ds->label = 'Test';
$ds->content = 'test';
$object->ingestDatastream($ds);
$tuque->repository->ingestObject($object);
$this->assertDatastreams($object, array(
'RELS-EXT',
'DC',
'OBJ',
'DERIV',
'NOSOURCE',
));
$this->assertEqual('ingestDatastream', $_islandora_derivative_test_ingest_method, 'The expected ingest method is "ingestDatastream", got "' . $_islandora_derivative_test_ingest_method . '".');
$this->assertEqual('test some string', $object['DERIV']->content, 'The expected content of the DERIV datastream is "test some string", got "' . $object['DERIV']->content . '".');
$this->assertEqual('NOSOURCE', $object['NOSOURCE']->content, 'The expected content of the NOSOURCE datastream is "NOSOURCE", got "' . $object['NOSOURCE']->content . '".');
}
/**
* Tests the ingest method when when forcing on existing datastreams.
*/
public function testDerivativeOnForceExistingDatastream() {
global $_islandora_derivative_test_ingest_method;
$_islandora_derivative_test_ingest_method = 'ingestDatastream';
$object = $this->constructBaseObject();
$object = $this->constructDERIVDatastream($object);
$this->constructNOSOURCEDatastream($object);
$islandora_object = islandora_object_load($this->pid);
islandora_do_derivatives($islandora_object, array(
'force' => TRUE,
));
$this->assertEqual('modifyDatastream', $_islandora_derivative_test_ingest_method, 'The expected ingest method is "modifyDatastream", got "' . $_islandora_derivative_test_ingest_method . '".');
$this->assertEqual('FORCEFULLY APPENDING CONTENT TO test', $islandora_object['DERIV']->content, 'The expected content of the DERIV datastream is "FORCEFULLY APPENDING CONTENT TO test", got "' . $islandora_object['DERIV']->content . '".');
}
/**
* Tests the ingest method when forcing on non-existing datastreams.
*/
public function testDerivativeOnForceNonExistingDatastream() {
global $_islandora_derivative_test_ingest_method;
$_islandora_derivative_test_ingest_method = 'modifyDatastream';
$this->constructBaseObject();
$object = islandora_object_load($this->pid);
islandora_do_derivatives($object, array(
'force' => TRUE,
));
$this->assertEqual('ingestDatastream', $_islandora_derivative_test_ingest_method, 'The expected ingest method is "ingestDatastream", got "' . $_islandora_derivative_test_ingest_method . '".');
$this->assertEqual('test some string', $object['DERIV']->content, 'The expected content of the DERIV datastream is "test some string", got "' . $object['DERIV']->content . '".');
}
/**
* Tests the islandora_datastream_modified hook when there are existing DSes.
*/
public function testDerivativeOnModifyExistingDatastream() {
global $_islandora_derivative_test_ingest_method;
$_islandora_derivative_test_ingest_method = 'ingestDatastream';
$object = $this->constructBaseObject();
$this->constructDERIVDatastream($object);
// Need to do this as Tuque caches.
$connection = islandora_get_tuque_connection();
$connection->cache->resetCache();
$islandora_object = islandora_object_load($this->pid);
$changed_content = 'islandora beast';
$islandora_object['OBJ']->content = $changed_content;
$this->assertEqual('modifyDatastream', $_islandora_derivative_test_ingest_method, 'The expected ingest method is "modifyDatastream", got "' . $_islandora_derivative_test_ingest_method . '".');
$this->assertEqual('FORCEFULLY APPENDING CONTENT TO ' . $changed_content, $islandora_object['DERIV']->content, 'The expected content of the DERIV datastream is "FORCEFULLY APPENDING CONTENT TO islandora beast", got "' . $islandora_object['DERIV']->content . '".');
}
/**
* Tests islandora_datastream_modified hook when there are no existing DSes.
*/
public function testDerivativeOnModifyNonExistingDatastream() {
global $_islandora_derivative_test_ingest_method;
$_islandora_derivative_test_ingest_method = 'modifyDatastream';
$this->constructBaseObject();
// Need to do this as Tuque caches.
$connection = islandora_get_tuque_connection();
$connection->cache->resetCache();
$islandora_object = islandora_object_load($this->pid);
$changed_content = 'islandora beast';
$islandora_object['OBJ']->content = $changed_content;
$this->assertEqual('ingestDatastream', $_islandora_derivative_test_ingest_method, 'The expected ingest method is "ingestDatastream", got "' . $_islandora_derivative_test_ingest_method . '".');
$this->assertEqual($changed_content . ' some string', $islandora_object['DERIV']->content, 'The expected content of the DERIV datastream is "islandora beast string", got "' . $islandora_object['DERIV']->content . '".');
}
/**
* Tests derivative hook filtering based upon source_dsid.
*/
public function testDerivativeFilteringOnSourceDSID() {
global $_islandora_derivative_test_derivative_functions;
$_islandora_derivative_test_derivative_functions = array();
$this->constructBaseObject();
$object = islandora_object_load($this->pid);
islandora_do_derivatives($object, array(
'source_dsid' => 'OBJ',
));
$this->assertEqual(1, count($_islandora_derivative_test_derivative_functions), 'Expected 1 derivative function for the source_dsid of "OBJ", got ' . count($_islandora_derivative_test_derivative_functions) . '.');
$called_function = (string) reset($_islandora_derivative_test_derivative_functions);
$this->assertEqual('islandora_derivatives_test_create_deriv_datastream', $called_function, 'Expected derivative function is "islandora_derivatives_test_create_deriv_datastream", got "' . $called_function . '".');
// Reset the derivative functions array as we are going to use it again.
$_islandora_derivative_test_derivative_functions = array();
islandora_do_derivatives($object, array(
'source_dsid' => 'SOMEWEIRDDATASTREAM',
));
$this->assertEqual(1, count($_islandora_derivative_test_derivative_functions), 'Expected 1 derivative function for the source_dsid of "SOMEWEIRDDATASTREAM", got ' . count($_islandora_derivative_test_derivative_functions) . '.');
$called_function = (string) reset($_islandora_derivative_test_derivative_functions);
$this->assertEqual('islandora_derivatives_test_create_some_weird_datastream', $called_function, 'Expected derivative function is "islandora_derivatives_test_create_some_weird_datastream", got "' . $called_function . '".');
}
/**
* Tests that only functions were the source_dsid is NULL are fired.
*/
public function testNULLSourceDSID() {
global $_islandora_derivative_test_derivative_functions;
$_islandora_derivative_test_derivative_functions = array();
$this->constructBaseObject();
$object = islandora_object_load($this->pid);
islandora_do_derivatives($object, array(
'source_dsid' => NULL,
));
$this->assertDatastreams($object, array(
'DC',
'RELS-EXT',
'OBJ',
'NOSOURCE',
));
$this->assertEqual(1, count($_islandora_derivative_test_derivative_functions), 'Expected 1 derivative function for the source_dsid of "NULL", got ' . count($_islandora_derivative_test_derivative_functions) . '.');
$called_function = (string) reset($_islandora_derivative_test_derivative_functions);
$this->assertEqual('islandora_derivatives_test_create_nosource_datastream', $called_function, 'Expected derivative function is "islandora_derivatives_test_create_nosource_datastream", got "' . $called_function . '".');
$this->assertEqual('NOSOURCE', $object['NOSOURCE']->content, 'The expected content of the NOSOURCE datastream is "NOSOURCE", got "' . $object['NOSOURCE']->content . '".');
}
/**
* Tests that when no source_dsid all derivative functions are called.
*/
public function testNoSourceDSIDNoForce() {
global $_islandora_derivative_test_derivative_functions;
$_islandora_derivative_test_derivative_functions = array();
$this->constructBaseObject();
$object = islandora_object_load($this->pid);
islandora_do_derivatives($object, array());
$this->assertDatastreams($object, array(
'DC',
'RELS-EXT',
'OBJ',
'DERIV',
'NOSOURCE',
));
$this->assertEqual(3, count($_islandora_derivative_test_derivative_functions), 'Expected 3 derivative functions when there is no source_dsid, got ' . count($_islandora_derivative_test_derivative_functions) . '.');
}
/**
* Tests that when no source_dsid all derivative functions are called.
*/
public function testNoSourceDSIDForce() {
global $_islandora_derivative_test_derivative_functions;
$_islandora_derivative_test_derivative_functions = array();
$this->constructBaseObject();
$object = islandora_object_load($this->pid);
islandora_do_derivatives($object, array(
'force' => TRUE,
));
$this->assertDatastreams($object, array(
'DC',
'RELS-EXT',
'OBJ',
'DERIV',
'NOSOURCE',
));
$this->assertEqual(3, count($_islandora_derivative_test_derivative_functions), 'Expected 3 derivative functions when there is no source_dsid, got ' . count($_islandora_derivative_test_derivative_functions) . '.');
}
/**
* Helper function that will construct a base object.
*/
public function constructBaseObject() {
$object = $this->repository->constructObject($this->pid);
$object->models = array(
'some:cmodel',
);
$dsid = 'OBJ';
$ds = $object->constructDatastream($dsid);
$ds->label = 'Test';
$ds->content = 'test';
$object->ingestDatastream($ds);
$this->repository->ingestObject($object);
return $object;
}
/**
* Helper function to construct the DERIV datastream without firing hooks.
*
* @param AbstractObject $object
* An AbstractObject representing a FedoraObject.
*
* @return AbstractObject
* The modified AbstractObject.
*/
public function constructDERIVDatastream(AbstractObject $object) {
$dsid = 'DERIV';
$ds = $object->constructDatastream($dsid);
$ds->label = 'Test';
$ds->content = 'test some string';
$object->ingestDatastream($ds);
return $object;
}
/**
* Helper function to construct the NOSOURCE datastream without firing hooks.
*
* @param AbstractObject $object
* An AbstractObject representing a FedoraObject.
*
* @return AbstractObject
* The modified AbstractObject.
*/
public function constructNOSOURCEDatastream(AbstractObject $object) {
$dsid = 'NOSOURCE';
$ds = $object->constructDatastream($dsid);
$ds->label = 'Test';
$ds->content = 'NOSOURCE';
$object->ingestDatastream($ds);
return $object;
}
}

BIN
tests/fixtures/test.jpg vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

6
tests/hooked_access.test

@ -37,9 +37,9 @@ class IslandoraHookedAccessTestCase extends IslandoraWebTestCase {
$this->objects = array(
'test:testAccessHook',
);
$this->op = FEDORA_VIEW_OBJECTS;
$this->other_op = FEDORA_INGEST;
$this->denied_op = FEDORA_PURGE;
$this->op = ISLANDORA_VIEW_OBJECTS;
$this->other_op = ISLANDORA_INGEST;
$this->denied_op = ISLANDORA_PURGE;
$this->purgeTestObjects();
$this->dsid = 'asdf';
$this->createTestObjects();

7
tests/islandora_derivatives_test.info

@ -0,0 +1,7 @@
name = Islandora Derivatives Generation testing
description = Tests derivative generation hooks. Do not enable.
core = 7.x
package = Testing
hidden = TRUE
files[] = islandora_derivatives_test.module
dependencies[] = islandora

202
tests/islandora_derivatives_test.module

@ -0,0 +1,202 @@
<?php
/**
* @file
* Tests for derivative generation.
*/
/**
* Implements hook_islandora_CMODEL_PID_derivative().
*/
function islandora_derivatives_test_some_cmodel_islandora_derivative() {
return array(
array(
'source_dsid' => 'OBJ',
'destination_dsid' => 'DERIV',
'weight' => '0',
'function' => array(
'islandora_derivatives_test_create_deriv_datastream',
),
),
array(
'source_dsid' => 'SOMEWEIRDDATASTREAM',
'destination_dsid' => 'STANLEY',
'weight' => '-1',
'function' => array(
'islandora_derivatives_test_create_some_weird_datastream',
),
),
array(
'source_dsid' => NULL,
'destination_dsid' => 'NOSOURCE',
'weight' => '-3',
'function' => array(
'islandora_derivatives_test_create_nosource_datastream',
),
),
);
}
/**
* Creates the DERIV datastream for use in testing.
*
* @param AbstractObject $object
* An AbstractObject representing a Fedora object.
* @param bool $force
* Whether or not derivative generation is to be forced.
* @return array
* An array detailing the success of the operation.
*
* @see hook_islandora_derivative()
*/
function islandora_derivatives_test_create_deriv_datastream(AbstractObject $object, $force = FALSE) {
global $_islandora_derivative_test_derivative_functions;
$_islandora_derivative_test_derivative_functions[] = 'islandora_derivatives_test_create_deriv_datastream';
$return = '';
if (!isset($object['DERIV']) || (isset($object['DERIV']) && $force === TRUE)) {
if ($force !== TRUE || !isset($object['DERIV'])) {
$deriv_string = $object['OBJ']->content . ' some string';
}
else {
$deriv_string = "FORCEFULLY APPENDING CONTENT TO " . $object['OBJ']->content;
}
$added_successfully = islandora_derivatives_test_add_datastream($object, 'DERIV', $deriv_string);
if ($added_successfully !== TRUE) {
$return = islandora_derivatives_test_failed_adding($added_successfully);
}
else {
$return = array(
'success' => TRUE,
'messages' => array(
array(
'message' => t('The DERIV datastream was added successfully for @pid!'),
'message_sub' => array('@pid' => $object->id),
'type' => 'dsm',
),
),
);
}
return $return;
}
}
/**
* Stub function that used only for datastream filtering counts.
*
* @param AbstractObject $object
* An AbstractObject representing a Fedora object.
* @param bool $force
* Whether the derivatives are being forcefully generated or not.
*/
function islandora_derivatives_test_create_some_weird_datastream(AbstractObject $object, $force = FALSE) {
global $_islandora_derivative_test_derivative_functions;
// Add to the global that we got to this function.
$_islandora_derivative_test_derivative_functions[] = 'islandora_derivatives_test_create_some_weird_datastream';
}
/**
* Creates the NOSOURCE datastream for use in testing.
*
* @param AbstractObject $object
* An AbstractObject representing a Fedora object.
* @param bool $force
* Whether or not derivative generation is to be forced.
* @return array
* An array detailing the success of the operation.
*
* @see hook_islandora_derivative()
*/
function islandora_derivatives_test_create_nosource_datastream(AbstractObject $object, $force = FALSE) {
global $_islandora_derivative_test_derivative_functions;
$_islandora_derivative_test_derivative_functions[] = 'islandora_derivatives_test_create_nosource_datastream';
$return = '';
if (!isset($object['NOSOURCE']) || (isset($object['NOSOURCE']) && $force === TRUE)) {
if ($force !== TRUE || !isset($object['NOSOURCE'])) {
$deriv_string = 'NOSOURCE';
}
else {
$deriv_string = "FORCEFULLY APPENDING CONTENT TO " . $object['NOSOURCE']->content;
}
$added_successfully = islandora_derivatives_test_add_datastream($object, 'NOSOURCE', $deriv_string);
if ($added_successfully !== TRUE) {
$return = islandora_derivatives_test_failed_adding($added_successfully);
}
else {
$return = array(
'success' => TRUE,
'messages' => array(
array(
'message' => t('The DERIV datastream was added successfully for @pid!'),
'message_sub' => array('@pid' => $object->id),
'type' => 'dsm',
),
),
);
}
return $return;
}
}
/**
* Helper function that adds/modifies the datastream to the object in testing.
*
* @param AbstractObject $object
* An AbstractObject representing a Fedora object.
* @param string $dsid
* The datastream id for which we are adding/modifying.
* @param string $deriv_string
* The content of the datastream we are adding.
*
* @return bool|string
* A bool if the operation was successfully, the error message otherwise.
*/
function islandora_derivatives_test_add_datastream(AbstractObject $object, $dsid, $deriv_string) {
global $_islandora_derivative_test_ingest_method;
try {
$ingest = !isset($object[$dsid]);
if ($ingest) {
$ds = $object->constructDatastream($dsid, 'M');
$ds->label = $dsid;
}
else {
$ds = $object[$dsid];
}
$ds->content = $deriv_string;
if ($ingest) {
$_islandora_derivative_test_ingest_method = 'ingestDatastream';
$object->ingestDatastream($ds);
}
else {
$_islandora_derivative_test_ingest_method = 'modifyDatastream';
}
return TRUE;
}
catch (exception $e) {
$message = $e->getMessage();
return $message;
}
}
/**
* Returns a message if we failed to add a derivative.
*
* @see hook_islandora_derivative()
*
* @param string $message
* The error message to be returned back.
*
* @return array
* An array describing the outcome of our failure.
*/
function islandora_derivatives_test_failed_adding($message) {
return array(
'success' => FALSE,
'messages' => array(
array(
'message' => $message,
'type' => 'watchdog',
'severity' => WATCHDOG_ERROR,
),
),
);
}

5
tests/islandora_hooked_access_test.module

@ -9,7 +9,7 @@
* Implements hook_islandora_object_access().
*/
function islandora_hooked_access_test_islandora_object_access($op, $object, $user) {
if ($op == FEDORA_PURGE) {
if ($op == ISLANDORA_PURGE) {
return FALSE;
}
if (isset($_SESSION['islandora_hooked_access_test']) && $_SESSION['islandora_hooked_access_test'] === func_get_args()) {
@ -23,6 +23,9 @@ function islandora_hooked_access_test_islandora_object_access($op, $object, $use
* Implements hook_islandora_datastream_access().
*/
function islandora_hooked_access_test_islandora_datastream_access($op, $datastream, $user) {
if ($op == FEDORA_PURGE) {
return FALSE;
}
if (isset($_SESSION['islandora_hooked_access_test']) && $_SESSION['islandora_hooked_access_test'] === func_get_args()) {
return TRUE;
}

12
tests/islandora_ingest_test.module

@ -87,7 +87,11 @@ function islandora_ingest_test_test_testcmodel2_islandora_ingest_steps(array &$f
* The Drupal form definition.
*/
function islandora_ingest_test_set_label_form(array $form, array &$form_state) {
$models = array('test:nomorestepscmodel', 'test:testcmodel', 'test:testcmodel2');
$models = array(
'test:nomorestepscmodel',
'test:testcmodel',
'test:testcmodel2',
);
$model = isset($form_state['values']['model']) ? $form_state['values']['model'] : reset($models);
$shared_storage = &islandora_ingest_form_get_shared_storage($form_state);
$shared_storage['models'] = array($model);
@ -139,8 +143,7 @@ function islandora_ingest_test_set_label_form_submit(array $form, array &$form_s
* Test the First content model.
*/
function islandora_ingest_test_testcmodel_form(array $form, array &$form_state) {
return array(
);
return array();
}
/**
@ -154,8 +157,7 @@ function islandora_ingest_test_testcmodel_form_submit(array $form, array &$form_
* Test the second content model.
*/
function islandora_ingest_test_testcmodel2_form(array $form, array &$form_state) {
return array(
);
return array();
}
/**

50
tests/islandora_manage_permissions.test

@ -33,16 +33,16 @@ class IslandoraPermissionsTestCase extends IslandoraWebTestCase {
* Test manage permissions.
*/
public function testManagePermissions() {
// Test permission FEDORA_VIEW_OBJECTS.
// Test permission ISLANDORA_VIEW_OBJECTS.
// Create a user with permission.
$user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS));
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS));
// Log the user in.
$this->drupalLogin($user);
$this->clickLink(t('Islandora Repository'));
$this->assertNoLink('Manage', 'Manage tab is not on current page.');
// Test permission FEDORA_VIEW_OBJECTS, FEDORA_MANAGE_PROPERTIES.
$user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_MANAGE_PROPERTIES));
// Test permission ISLANDORA_VIEW_OBJECTS, ISLANDORA_MANAGE_PROPERTIES.
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_MANAGE_PROPERTIES));
$this->drupalLogin($user);
$this->clickLink(t('Islandora Repository'));
$this->assertLink('Manage', 0, 'Manage tab is on current page.');
@ -51,8 +51,8 @@ class IslandoraPermissionsTestCase extends IslandoraWebTestCase {
$this->assertNoLink('Datastreams', 'Datastreams tab is not on current page.');
$this->assertNoLink('Collection', 'Collection tab is not on current page.');
// Test permission FEDORA_VIEW_OBJECTS, FEDORA_ADD_DS.
$user = $this->drupalCreateUser(array(FEDORA_VIEW_OBJECTS, FEDORA_ADD_DS));
// Test permission ISLANDORA_VIEW_OBJECTS, ISLANDORA_ADD_DS.
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_ADD_DS));
$this->drupalLogin($user);
$this->clickLink(t('Islandora Repository'));
$this->assertLink('Manage', 0, 'Manage tab is on current page.');
@ -61,8 +61,8 @@ class IslandoraPermissionsTestCase extends IslandoraWebTestCase {
$this->assertNoLink('Properties', 'Properties tab is not on current page.');
$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));
// Test permission ISLANDORA_VIEW_OBJECTS, ISLANDORA_METADATA_EDIT.
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_METADATA_EDIT));
$this->drupalLogin($user);
$this->clickLink(t('Islandora Repository'));
$this->assertLink('Manage', 0, 'Manage tab is on current page.');
@ -71,8 +71,8 @@ class IslandoraPermissionsTestCase extends IslandoraWebTestCase {
$this->assertNoLink('Properties', 'Properties tab is not on current page.');
$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));
// Test permission ISLANDORA_VIEW_OBJECTS, ISLANDORA_PURGE.
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_PURGE));
$this->drupalLogin($user);
$this->clickLink(t('Islandora Repository'));
$this->assertLink('Manage', 0, 'Manage tab is on current page.');
@ -98,39 +98,39 @@ class IslandoraPermissionsTestCase extends IslandoraWebTestCase {
$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);
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS));
$ret = islandora_user_access($object, array(ISLANDORA_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);
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS));
$ret = islandora_user_access($object, array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_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);
$user = $this->drupalCreateUser(array(ISLANDORA_PURGE));
$ret = islandora_user_access($object, array(ISLANDORA_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));
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS));
$model = $object->models;
$model = reset($model);
$ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS), array($model), TRUE, $user);
$ret = islandora_user_access($object, array(ISLANDORA_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);
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS));
$ret = islandora_user_access($object, array(ISLANDORA_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));
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_PURGE));
$model = $object->models;
$model = reset($model);
$ret = islandora_user_access($object, array(FEDORA_VIEW_OBJECTS, FEDORA_PURGE), array($model, 'islandora:obviouslyNotACModel'), FALSE, $user);
$ret = islandora_user_access($object, array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_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);
$ret = islandora_user_access($object, array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_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);
$user = $this->drupalCreateUser(array(ISLANDORA_VIEW_OBJECTS, ISLANDORA_PURGE));
$ret = islandora_user_access($object['DC'], array(ISLANDORA_VIEW_OBJECTS), array(), TRUE, $user);
$this->assertTrue($ret, 'User access granted for matching permissions, with a datastream given instead of an object.');
}
}

180
tests/islandora_web_test_case.inc

@ -74,15 +74,22 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
$connection_info = Database::getConnectionInfo('default');
$drupal_filter_dom = new DomDocument();
$drupal_filter_dom->loadXML($this->originalDrupalFilterContent);
$drupal_filter_xpath = new DOMXPath($drupal_filter_dom);
$server = $connection_info['default']['host'];
$dbname = $connection_info['default']['database'];
$user = $connection_info['default']['username'];
$password = $connection_info['default']['password'];
$port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306';
$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");
$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=?;";
$filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0);
$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());
}
@ -157,7 +164,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
}
/**
* Asserts that the given datastreams exist on the object.
* Asserts that the given datastreams exist correctly on the object.
*
* @param AbstractObject $object
* The PID of the object
@ -166,16 +173,53 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
*/
public function assertDatastreams($object, array $datastreams) {
if (!is_object($object)) {
$this->fail("Failed. Object passed in is invalid.");
return;
$this->fail("Failed. Object passed in is invalid.", 'Islandora');
}
else {
foreach ($datastreams as $datastream) {
if (isset($object[$datastream])) {
$this->pass("Loaded datastream {$datastream} from PID {$object->id}");
$this->pass("Loaded datastream {$datastream} from PID {$object->id}.", 'Islandora');
}
else {
$this->fail("Failed to load datastream {$datastream} from PID {$object->id}");
$this->fail("Failed to load datastream {$datastream} from PID {$object->id}.", 'Islandora');
}
}
}
}
/**
* Attempts to validate an array of datastreams, generally via binary checks.
*
* These functions exist in, and can be added to, datastream_validators.inc,
* which is found in this folder.
*
* $param AbstractObject $object
* The object to load datastreams from.
* $param array $datastreams
* An array of paired DSIDs, validate function names, and optional params.
*/
public function validateDatastreams($object, array $datastreams) {
if (!is_object($object)) {
$this->fail("Failed. Object passed in is invalid.", 'Islandora');
}
module_load_include('inc', 'islandora', 'tests/datastream_validators');
foreach ($datastreams as $datastream) {
if (isset($object[$datastream[0]])) {
$function = 'islandora_validate_' . $datastream[1] . '_datastream';
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 {
$this->fail("No {$datastream[0]} validation function exists for the {$datastream[1]} datastream.", 'Islandora');
}
}
}
}
@ -199,7 +243,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
}
}
}
$this->fail("Failed to parse path : $path.");
$this->fail("Failed to parse path: $path.");
return FALSE;
}
@ -208,31 +252,119 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
*
* @param string $pid
* The PID of the collection to be deleted
* @param string $button
* The label of the first 'Delete' button
*/
public function deleteObject($pid) {
$current_user = $this->loggedInUser;
$user = $this->drupalCreateUser(array(
'manage object properties',
'delete fedora objects and datastreams',
'view fedora repository objects',
));
$this->drupalLogin($user);
public function deleteObject($pid, $button = NULL) {
$path = 'islandora/object/' . $pid . '/manage/properties';
$edit = array();
$this->drupalPost($path, $edit, t('Delete'));
if (isset($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'));
$object = islandora_object_load($pid);
$this->drupalGet("islandora/object/$pid");
$this->assertResponse(404, "Object $pid successfully deleted.");
}
/**
* Constructs and ingests a Fedora object and datastream(s) via tuque.
*
* All keys inside the parameter arrays for this function are optional. it
* can be run simply by calling $this->ingestConstructedObject();.
*
* @param array $properties
* 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|array
* FALSE if the object ingest failed, or the object array if successful.
*/
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']);
if ($current_user) {
$this->drupalLogin($current_user);
// Set the object properties before ingesting it.
if (isset($properties['label'])) {
$object->label = $properties['label'];
}
else {
$this->drupalLogout();
$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;
}
else {
$this->pass(t("Ingested object %object", array('%object' => $object->id)), 'Islandora');
}
// Chuck in some datastreams.
if (!empty($datastreams)) {
foreach ($datastreams as $datastream) {
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;
}
}

7
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
cd islandora_tomcat
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
cd $HOME
pear upgrade --force Console_Getopt
@ -18,13 +19,13 @@ pear channel-discover pear.drush.org
pear channel-discover pear.drush.org
pear channel-discover pear.phpqatools.org
pear channel-discover pear.netpirates.net
pear install pear/PHP_CodeSniffer
pear install pear/PHP_CodeSniffer-1.4.8
pear install pear.phpunit.de/phpcpd
pear install drush/drush
pear install drush/drush-5.9.0
phpenv rehash
drush dl --yes 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 &
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

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>

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

@ -0,0 +1,33 @@
<?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>
<div class="fieldset-wrapper">
<dl xmlns:dcterms="http://purl.org/dc/terms/" class="islandora-inline-metadata islandora-metadata-fields">
<?php $row_field = 0; ?>
<?php foreach($dc_array as $key => $value): ?>
<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 filter_xss($value['label']); ?>
</dt>
<dd class="<?php print $value['class']; ?><?php print $row_field == 0 ? ' first' : ''; ?>">
<?php print filter_xss($value['value']); ?>
</dd>
<?php $row_field++; ?>
<?php endforeach; ?>
</dl>
</div>
</fieldset>

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

@ -0,0 +1,18 @@
<?php
/**
* @file
* The default view to theme an image of an object.
*
* This view is passed into 'islandora-object-print' theme file
* and is rendred as an image. Allows for seperate theming of image
* and metadata.
*
*/
?>
<?php if (isset($islandora_content)): ?>
<div>
<?php print $islandora_content; ?>
</div>
<?php endif; ?>

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

@ -0,0 +1,14 @@
<?php
/**
* @file
* The default view to print objects.
*
*/
?>
<div>
<div>
<?php print $islandora_content; ?>
</div>
<?php print $metadata; ?>
</div>

1
theme/islandora-object.tpl.php

@ -60,6 +60,7 @@
?>
<div class="islandora-object islandora">
<h2><?php print t('Details'); ?></h2>
<dl class="islandora-object-tn">
<dt>
<?php if (isset($variables['islandora_thumbnail_url'])): ?>

235
theme/theme.inc

@ -15,61 +15,73 @@ function islandora_preprocess_islandora_default_edit(array &$variables) {
$variables['islandora_editmetadata_url'] = $base_url . '/islandora/edit_form/' . $islandora_object->id;
module_load_include('inc', 'islandora', 'includes/datastream');
module_load_include('inc', 'islandora', 'includes/utilities');
$header = array(
array('data' => t('ID')),
array('data' => t('Label')),
array('data' => t('Type')),
array('data' => t('Mime type')),
array('data' => t('Size')),
array('data' => t('Operations'), 'colspan' => '3'),
);
$header = array();
$header[] = array('data' => t('ID'));
$header[] = array('data' => t('Label'));
$header[] = array('data' => t('Type'));
$header[] = array('data' => t('Mime type'));
$header[] = array('data' => t('Size'));
if (user_access(ISLANDORA_VIEW_DATASTREAM_HISTORY)) {
$header[] = array('data' => t('Versions'));
}
$header[] = array('data' => t('Operations'), 'colspan' => '3');
$table_attributes = array('class' => array('manage-datastreams'));
$rows = array();
foreach ($islandora_object as $ds) {
$rows[] = array(
array(
$row = array();
$row[] = array(
'class' => 'datastream-id',
'data' => theme('islandora_datastream_view_link', array(
'datastream' => $ds,
)),
),
array(
);
$row[] = array(
'class' => 'datastream-label',
'data' => $ds->label,
),
array(
'data' => filter_xss($ds->label),
);
$row[] = array(
'class' => 'datastream-control',
'data' => islandora_control_group_to_human_readable($ds->controlGroup),
),
array(
);
$row[] = array(
'class' => 'datastream-mime',
'data' => $ds->mimeType,
),
array(
'data' => filter_xss($ds->mimeType),
);
$row[] = array(
'class' => 'datastream-size',
'data' => islandora_datastream_get_human_readable_size($ds),
),
array(
);
if (user_access(ISLANDORA_VIEW_DATASTREAM_HISTORY)) {
$row[] = array(
'class' => 'datastream-versions',
'data' => theme('islandora_datastream_version_link', array(
'datastream' => $ds,
)),
);
}
$row[] = array(
'class' => 'datastream-download',
'data' => theme('islandora_datastream_download_link', array(
'datastream' => $ds,
)),
),
array(
);
$row[] = array(
'class' => 'datstream-edit',
'data' => theme('islandora_datastream_edit_link', array(
'datastream' => $ds,
)),
),
array(
);
$row[] = array(
'class' => 'datastream-delete',
'data' => theme('islandora_datastream_delete_link', array(
'datastream' => $ds,
)),
),
);
$rows[] = $row;
}
$caption = $islandora_object->label . ' - ' . $islandora_object->id;
$caption = filter_xss($islandora_object->label) . ' - ' . $islandora_object->id;
$table = array(
'colgroups' => NULL,
'sticky' => TRUE,
@ -103,7 +115,7 @@ function islandora_preprocess_islandora_default(&$variables) {
$download_path = islandora_datastream_get_url($ds, 'download');
$datastreams[$id]['id'] = $id;
$datastreams[$id]['label'] = $label;
$datastreams[$id]['label_link'] = islandora_datastream_access(FEDORA_VIEW_OBJECTS, $ds) ?
$datastreams[$id]['label_link'] = islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $ds) ?
l($label, $download_path) :
$label;
$datastreams[$id]['download_url'] = $download_path;
@ -118,14 +130,14 @@ function islandora_preprocess_islandora_default(&$variables) {
}
$variables['datastreams'] = $datastreams;
// Objects in fcrepo4 don't always contain a DC datastream.
if (isset($islandora_object['DC']) && islandora_datastream_access(FEDORA_VIEW_OBJECTS, $islandora_object['DC'])) {
if (isset($islandora_object['DC']) && islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $islandora_object['DC'])) {
$dc_object = DublinCore::importFromXMLString($islandora_object['DC']->content);
$dc_array = $dc_object->asArray();
}
$variables['dc_array'] = isset($dc_array) ? $dc_array : array();
$variables['islandora_dublin_core'] = isset($dc_object) ? $dc_object : NULL;
$variables['islandora_object_label'] = $islandora_object->label;
if (isset($islandora_object['TN']) && islandora_datastream_access(FEDORA_VIEW_OBJECTS, $islandora_object['TN'])) {
if (isset($islandora_object['TN']) && islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $islandora_object['TN'])) {
$variables['islandora_thumbnail_url'] = url("islandora/object/{$islandora_object->id}/datastream/TN/view");
}
}
@ -207,11 +219,11 @@ function islandora_preprocess_islandora_objects(array &$variables) {
$o = islandora_object_load($o);
$url = "islandora/object/{$o->id}";
$link_options = array('html' => TRUE, 'attributes' => array('title' => $o->label));
$img = islandora_datastream_access(FEDORA_VIEW_OBJECTS, $o['TN']) ?
$img = islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $o['TN']) ?
theme('image', array('path' => url("$url/datastream/TN/view"), 'attributes' => array())) :
'';
$description = NULL;
if (isset($o['DC']) && islandora_datastream_access(FEDORA_VIEW_OBJECTS, $o['DC'])) {
if (isset($o['DC']) && islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $o['DC'])) {
$dc = DublinCore::importFromXMLString($o['DC']->content);
if ($dc) {
$dc = $dc->asArray();
@ -239,13 +251,16 @@ function islandora_preprocess_islandora_objects(array &$variables) {
* @param array $vars
* An array containing:
* - datastream: An AbstractDatastream for which to generate a download link.
*
* @return string
* Markup containing the download url if the user has access, empty otherwise.
*/
function theme_islandora_datastream_download_link(array $vars) {
$datastream = $vars['datastream'];
module_load_include('inc', 'islandora', 'includes/utilities');
$label = t('download');
return islandora_datastream_access(FEDORA_VIEW_OBJECTS, $datastream) ?
return islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $datastream) ?
l($label, islandora_datastream_get_url($datastream, 'download')) :
'';
}
@ -256,15 +271,36 @@ function theme_islandora_datastream_download_link(array $vars) {
* @param array $vars
* An array containing:
* - datastream: An AbstractDatastream for which to generate a view link.
* - label: (Optional) The label for the link.
* - version: (Optional) The version of the datstream to link to.
*
* @return string
* Markup containing the link to the datastream or the label if inaccessible.
*/
function theme_islandora_datastream_view_link(array $vars) {
$datastream = $vars['datastream'];
module_load_include('inc', 'islandora', 'includes/utilities');
if ($vars['label'] === NULL) {
$label = check_plain($datastream->id);
return islandora_datastream_access(FEDORA_VIEW_OBJECTS, $datastream) ?
l($label, islandora_datastream_get_url($datastream, 'view')) :
$label;
}
else {
$label = check_plain($vars['label']);
}
if ($vars['version'] === NULL) {
$perm = ISLANDORA_VIEW_OBJECTS;
}
else {
$perm = ISLANDORA_VIEW_DATASTREAM_HISTORY;
}
if (islandora_datastream_access($perm, $datastream)) {
return l($label, islandora_datastream_get_url($datastream, 'view', $vars['version']));
}
else {
return $label;
}
}
/**
@ -273,17 +309,68 @@ function theme_islandora_datastream_view_link(array $vars) {
* @param array $vars
* An array containing:
* - datastream: An AbstractDatastream for which to generate a delete link.
* - version: (optional) the version of the datastream to delete.
*
* @return string
* Markup containing the url to delete a datastream, or empty if inaccessible.
*/
function theme_islandora_datastream_delete_link(array $vars) {
$datastream = $vars['datastream'];
$datastreams = module_invoke_all('islandora_undeletable_datastreams', $datastream->parent->models);
$can_delete = !in_array($datastream->id, $datastreams) && islandora_datastream_access(FEDORA_PURGE, $datastream);
$can_delete = !in_array($datastream->id, $datastreams) && islandora_datastream_access(ISLANDORA_PURGE, $datastream);
if ($vars['version'] !== NULL) {
if (count($datastream) == 1) {
$can_delete = FALSE;
}
$link = "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/version/{$vars['version']}/delete";
}
else {
$link = "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/delete";
}
return $can_delete ?
l(t('delete'), "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/delete") :
if ($can_delete) {
return l(t('delete'), $link);
}
else {
'';
}
}
/**
* Renders a link to allow replacing of a datatream.
*
* @param array $vars
* An array containing:
* - datastream: An AbstractDatastream for which to generate a revert link.
* - version: (optional) the version of the datastream to revert.
*
* @return string
* Markup containing the url to delete a datastream, or empty if inaccessible.
*/
function theme_islandora_datastream_revert_link(array $vars) {
$datastream = $vars['datastream'];
$can_revert = islandora_datastream_access(ISLANDORA_REVERT_DATASTREAM, $datastream);
if ($vars['version'] !== NULL) {
if (count($datastream) == 1) {
$can_revert = FALSE;
}
$link = "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/version/{$vars['version']}/revert";
}
else {
$can_revert = FALSE;
}
if ($can_revert) {
return l(t('revert'), $link);
}
else {
return '';
}
}
/**
@ -292,15 +379,81 @@ function theme_islandora_datastream_delete_link(array $vars) {
* @param array $vars
* An array containing:
* - datastream: An AbstractDatastream for which to generate a edit link.
*
* @return string
* Markup containing the url to edit a datastream, or empty if inaccessible.
*/
function theme_islandora_datastream_edit_link(array $vars) {
$datastream = $vars['datastream'];
$edit_registry = module_invoke_all('islandora_edit_datastream_registry', $datastream->parent, $datastream);
$can_edit = count($edit_registry) > 0 && islandora_datastream_access(FEDORA_METADATA_EDIT, $datastream);
$can_edit = count($edit_registry) > 0 && islandora_datastream_access(ISLANDORA_METADATA_EDIT, $datastream);
return $can_edit ?
l(t('edit'), "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/edit") :
'';
}
/**
* Renders a link to take you to the datastream versions page.
*
* @param array $vars
* An array containing:
* - datastream: An AbstractDatastream to generate the version link from.
*
* @return string
* Markup.
*/
function theme_islandora_datastream_version_link(array $vars) {
$datastream = $vars['datastream'];
module_load_include('inc', 'islandora', 'includes/utilities');
$see_history = islandora_datastream_access(ISLANDORA_VIEW_DATASTREAM_HISTORY, $datastream);
if ($see_history) {
if ($datastream->versionable) {
return l(count($datastream), "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/version");
}
else {
return t('Not Versioned');
}
}
else {
return '';
}
}
/**
* Implements hook_preprocess().
*/
function islandora_preprocess_islandora_dublin_core_display(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();
}
/**
* 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();
}

Loading…
Cancel
Save