diff --git a/includes/add_datastream.form.inc b/includes/add_datastream.form.inc
index 62490c9a..3a1d964d 100644
--- a/includes/add_datastream.form.inc
+++ b/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,44 +40,44 @@ 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: @unused_dsids.", array('@unused_dsids' => $unused_datastreams)),
+ '#type' => 'textfield',
+ '#size' => 64,
+ '#maxlength' => 64,
+ '#required' => TRUE,
+ '#element_validate' => array(
+ 'islandora_add_datastream_form_field_is_not_an_existing_datastream_id',
+ 'islandora_add_datastream_form_field_starts_with_a_letter',
+ 'islandora_add_datastream_form_field_is_valid_dsid',
+ ),
+ '#autocomplete_path' => "islandora/object/{$object->id}/manage/datastreams/add/autocomplete",
),
- '#type' => 'textfield',
- '#size' => 64,
- '#maxlength' => 64,
- '#required' => TRUE,
- '#element_validate' => array(
- 'islandora_add_datastream_form_field_is_not_an_existing_datastream_id',
- 'islandora_add_datastream_form_field_starts_with_a_letter',
- 'islandora_add_datastream_form_field_is_valid_dsid',
+ 'label' => array(
+ '#title' => 'Datastream Label',
+ '#required' => TRUE,
+ '#size' => 64,
+ '#maxlength' => 64,
+ '#description' => t('A human-readable label'),
+ '#type' => 'textfield',
+ '#element_validate' => array('islandora_add_datastream_form_field_does_not_contain_a_forward_slash'),
),
- '#autocomplete_path' => "islandora/object/{$object->id}/manage/datastreams/add/autocomplete",
- ),
- 'label' => array(
- '#title' => 'Datastream Label',
- '#required' => TRUE,
- '#size' => 64,
- '#maxlength' => 64,
- '#description' => t('A human-readable label'),
- '#type' => 'textfield',
- '#element_validate' => array('islandora_add_datastream_form_field_does_not_contain_a_forward_slash'),
- ),
- 'file' => array(
- '#type' => 'managed_file',
- '#required' => TRUE,
- '#title' => t('Upload Document'),
- '#size' => 48,
- '#description' => t('Select a file to upload.
Files must be less than @size MB.', array('@size' => $upload_size)),
- '#default_value' => isset($form_state['values']['files']) ? $form_state['values']['files'] : NULL,
- '#upload_location' => 'temporary://',
- '#upload_validators' => array(
- 'file_validate_extensions' => array(NULL),
- // Assume its specified in MB.
- 'file_validate_size' => array($upload_size * 1024 * 1024),
+ 'file' => array(
+ '#type' => 'managed_file',
+ '#required' => TRUE,
+ '#title' => t('Upload Document'),
+ '#size' => 48,
+ '#description' => t('Select a file to upload.
Files must be less than @size MB.', array('@size' => $upload_size)),
+ '#default_value' => isset($form_state['values']['files']) ? $form_state['values']['files'] : NULL,
+ '#upload_location' => 'temporary://',
+ '#upload_validators' => array(
+ 'file_validate_extensions' => array(NULL),
+ // Assume its specified in MB.
+ 'file_validate_size' => array($upload_size * 1024 * 1024),
+ ),
+ ),
+ 'submit' => array(
+ '#type' => 'submit',
+ '#value' => t('Add Datastream'),
),
- ),
- 'submit' => array(
- '#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.
diff --git a/includes/datastream.inc b/includes/datastream.inc
index dc01bdfe..da55dcd5 100644
--- a/includes/datastream.inc
+++ b/includes/datastream.inc
@@ -26,8 +26,19 @@ 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) {
+function islandora_view_datastream(AbstractDatastream $datastream, $download = FALSE, $version = NULL) {
+ if ($version !== NULL) {
+ if (isset($datastream[$version])) {
+ $datastream = $datastream[$version];
+ }
+ else {
+ return drupal_not_found();
+ }
+ }
+
header_remove('Cache-Control');
header_remove('Expires');
header('Content-type: ' . $datastream->mimetype);
@@ -69,12 +80,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 +112,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 +130,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 +175,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 = '';
diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc
new file mode 100644
index 00000000..1e7c0830
--- /dev/null
+++ b/includes/datastream.version.inc
@@ -0,0 +1,127 @@
+ $datastream->id)));
+
+ $header = array();
+ $header[] = array('data' => t('Created Date'));
+ $header[] = array('data' => t('Size'));
+ $header[] = array('data' => t('Label'));
+ $header[] = array('data' => t('Mime type'));
+ $header[] = array('data' => t('Operations'));
+ $rows = array();
+
+ foreach ($datastream as $version => $datastream_version) {
+ $row = array();
+ $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-mime',
+ 'data' => $datastream_version->mimeType,
+ );
+ $row[] = array(
+ 'class' => 'datastream-delete',
+ 'data' => theme('islandora_datastream_delete_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";
+}
diff --git a/includes/ingest.form.inc b/includes/ingest.form.inc
index 6faa642c..7edd0f60 100644
--- a/includes/ingest.form.inc
+++ b/includes/ingest.form.inc
@@ -186,10 +186,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 +205,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 +329,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 +366,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 +396,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,6 +416,8 @@ 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);
@@ -425,6 +434,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,6 +454,8 @@ 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);
@@ -457,11 +470,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(
@@ -692,6 +707,8 @@ function islandora_ingest_form_ingest_button(array &$form_state) {
*
* Attempts to ingest every object built by the previous steps.
*
+ * @param array $form
+ * The Drupal form.
* @param array $form_state
* The Drupal form state.
*/
diff --git a/includes/object.entity_controller.inc b/includes/object.entity_controller.inc
index d3fd6986..9464d7b5 100644
--- a/includes/object.entity_controller.inc
+++ b/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)) {
diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc
index baaf10b0..96f9919c 100644
--- a/includes/tuque_wrapper.inc
+++ b/includes/tuque_wrapper.inc
@@ -207,6 +207,9 @@ class IslandoraFedoraApiM extends FedoraApiM {
'params' => $params,
);
islandora_alter_datastream($object, $datastream, $context);
+ if (isset($params['lastModifiedDate'])) {
+ $params['lastModifiedDate'] = (string) $object[$dsid]->createdDate;
+ }
try {
if ($context['block']) {
throw new Exception('Modify Datastream was blocked.');
diff --git a/includes/utilities.inc b/includes/utilities.inc
index 5ce2ca14..c8d1ae5e 100644
--- a/includes/utilities.inc
+++ b/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.
diff --git a/islandora.info b/islandora.info
index ce6ba947..fea3ead2 100644
--- a/islandora.info
+++ b/islandora.info
@@ -17,4 +17,5 @@ files[] = tests/hooks.test
files[] = tests/ingest.test
files[] = tests/hooked_access.test
files[] = tests/islandora_manage_permissions.test
+files[] = tests/datastream_versions.test
php = 5.3
diff --git a/islandora.module b/islandora.module
index 8495129e..97081e32 100644
--- a/islandora.module
+++ b/islandora.module
@@ -33,6 +33,7 @@ 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_DATASTREAM_HISTORY', 'view old datastream versions');
// Hooks.
define('ISLANDORA_VIEW_HOOK', 'islandora_view_object');
@@ -241,6 +242,48 @@ function islandora_menu() {
'access arguments' => array(FEDORA_PURGE, 4),
'load arguments' => array(2),
);
+<<<<<<< HEAD
+=======
+ $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_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(FEDORA_PURGE, 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/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(FEDORA_VIEW_OBJECTS, 2),
+ 'load arguments' => array(2),
+ );
+>>>>>>> 73e09a9d0d9f0f91f2cbca4d6a204a96c9695fa1
$items['islandora/object/%islandora_object/download_clip'] = array(
'page callback' => 'islandora_download_clip',
'page arguments' => array(2),
@@ -267,8 +310,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;
}
@@ -338,16 +382,24 @@ function islandora_theme() {
),
'islandora_datastream_delete_link' => array(
'file' => 'theme/theme.inc',
- 'variables' => array('datastream' => NULL),
+ '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),
+ ),
);
}
@@ -380,6 +432,10 @@ function islandora_permission() {
'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.'),
+ ),
);
}
diff --git a/tests/datastream_versions.test b/tests/datastream_versions.test
new file mode 100644
index 00000000..6b22bd95
--- /dev/null
+++ b/tests/datastream_versions.test
@@ -0,0 +1,198 @@
+ '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);
+ }
+}
diff --git a/tests/islandora_ingest_test.module b/tests/islandora_ingest_test.module
index d414c717..8a6771d9 100644
--- a/tests/islandora_ingest_test.module
+++ b/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();
}
/**
diff --git a/tests/islandora_web_test_case.inc b/tests/islandora_web_test_case.inc
index de414c17..baf7a8ee 100644
--- a/tests/islandora_web_test_case.inc
+++ b/tests/islandora_web_test_case.inc
@@ -157,27 +157,27 @@ 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
* @param array $datastreams
- * An array of strings containing datastream names
+ * An array of strings containing datastream names
*/
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');
}
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');
}
}
+
}
/**
@@ -199,7 +199,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
}
}
}
- $this->fail("Failed to parse path : $path.");
+ $this->fail("Failed to parse path: $path.");
return FALSE;
}
@@ -208,31 +208,49 @@ 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 = 'Delete') {
$path = 'islandora/object/' . $pid . '/manage/properties';
$edit = array();
- $this->drupalPost($path, $edit, t('Delete'));
+ $this->drupalPost($path, $edit, $button);
$this->drupalPost($this->url, $edit, t('Delete'));
$object = islandora_object_load($pid);
$this->drupalGet("islandora/object/$pid");
$this->assertResponse(404, "Object $pid successfully deleted.");
+ }
+
+ /**
+ * Reverses a hex string and converts it to a little-endian-formatted 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 hex value being converted.
+ *
+ * @return bool|int
+ * FALSE or the integer value that is converted.
+ */
+ public function convertHexToInt($hex) {
- if ($current_user) {
- $this->drupalLogin($current_user);
+ // A couple of quick string checks.
+ if (!ctype_xdigit($hex)) {
+ $this->fail('String passed to convertHexToInt() contains non-hexidecimal characters.', 'PHP');
+ return FALSE;
}
- else {
- $this->drupalLogout();
+ if (!strlen($hex) === 4 || !strlen($hex) === 8) {
+ $this->fail('String passed to convertHexToInt() cannot create a 16- or 32-bit little-endian signed integer', 'PHP');
+ return FALSE;
}
+
+ // The actual conversion.
+ $reverse_hex = implode('', array_reverse(str_split($hex, 2)));
+ $int = hexdec($reverse_hex);
+ return $int;
}
}
diff --git a/theme/theme.inc b/theme/theme.inc
index f07f95d4..d988ee77 100644
--- a/theme/theme.inc
+++ b/theme/theme.inc
@@ -15,59 +15,71 @@ 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(
- 'class' => 'datastream-id',
- 'data' => theme('islandora_datastream_view_link', array(
- 'datastream' => $ds,
- )),
- ),
- array(
- 'class' => 'datastream-label',
- 'data' => $ds->label,
- ),
- array(
- 'class' => 'datastream-control',
- 'data' => islandora_control_group_to_human_readable($ds->controlGroup),
- ),
- array(
- 'class' => 'datastream-mime',
- 'data' => $ds->mimeType,
- ),
- array(
- 'class' => 'datastream-size',
- 'data' => islandora_datastream_get_human_readable_size($ds),
- ),
- array(
- 'class' => 'datastream-download',
- 'data' => theme('islandora_datastream_download_link', array(
- 'datastream' => $ds,
- )),
- ),
- array(
- 'class' => 'datstream-edit',
- 'data' => theme('islandora_datastream_edit_link', array(
- 'datastream' => $ds,
- )),
- ),
- array(
- 'class' => 'datastream-delete',
- 'data' => theme('islandora_datastream_delete_link', array(
+ $row = array();
+ $row[] = array(
+ 'class' => 'datastream-id',
+ 'data' => theme('islandora_datastream_view_link', array(
+ 'datastream' => $ds,
+ )),
+ );
+ $row[] = array(
+ 'class' => 'datastream-label',
+ 'data' => $ds->label,
+ );
+ $row[] = array(
+ 'class' => 'datastream-control',
+ 'data' => islandora_control_group_to_human_readable($ds->controlGroup),
+ );
+ $row[] = array(
+ 'class' => 'datastream-mime',
+ 'data' => $ds->mimeType,
+ );
+ $row[] = array(
+ 'class' => 'datastream-size',
+ 'data' => islandora_datastream_get_human_readable_size($ds),
+ );
+ 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,
+ )),
);
+ $row[] = array(
+ 'class' => 'datstream-edit',
+ 'data' => theme('islandora_datastream_edit_link', array(
+ 'datastream' => $ds,
+ )),
+ );
+ $row[] = array(
+ 'class' => 'datastream-delete',
+ 'data' => theme('islandora_datastream_delete_link', array(
+ 'datastream' => $ds,
+ )),
+ );
+ $rows[] = $row;
}
$caption = $islandora_object->label . ' - ' . $islandora_object->id;
$table = array(
@@ -239,6 +251,9 @@ 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'];
@@ -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');
- $label = check_plain($datastream->id);
- return islandora_datastream_access(FEDORA_VIEW_OBJECTS, $datastream) ?
- l($label, islandora_datastream_get_url($datastream, 'view')) :
- $label;
+ if ($vars['label'] === NULL) {
+ $label = check_plain($datastream->id);
+ }
+ else {
+ $label = check_plain($vars['label']);
+ }
+
+ if ($vars['version'] === NULL) {
+ $perm = FEDORA_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,6 +309,10 @@ 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'];
@@ -281,9 +321,22 @@ function theme_islandora_datastream_delete_link(array $vars) {
$can_delete = !in_array($datastream->id, $datastreams) && islandora_datastream_access(FEDORA_PURGE, $datastream);
- return $can_delete ?
- l(t('delete'), "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/delete") :
+ 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";
+ }
+
+ if ($can_delete) {
+ return l(t('delete'), $link);
+ }
+ else {
'';
+ }
}
/**
@@ -292,6 +345,9 @@ 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'];
@@ -304,3 +360,32 @@ function theme_islandora_datastream_edit_link(array $vars) {
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 '';
+ }
+}