Browse Source

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

pull/223/head
Adam Vessey 12 years ago
parent
commit
7a7b0fd1ba
  1. 8
      README
  2. 122
      includes/islandora_authtokens.inc
  3. 750
      includes/solution_packs.inc
  4. 14
      includes/utilities.inc
  5. 80
      islandora.install
  6. 126
      islandora.module

8
README

@ -38,6 +38,14 @@ INSTALLATION
CONFIGURATION CONFIGURATION
------------- -------------
The islandora_drupal_filter passes the username of 'anonymous' through to
Fedora for unauthenticated Drupal Users. A user with the name of 'anonymous'
may have XACML policies applied to them that are meant to be applied to Drupal
users that are not logged in or vice-versa. This is a potential security issue
that can be plugged by creating a user named 'anonymous' and restricting access
to the account.
Drupal's cron will can be ran to remove expired authentication tokens.
CUSTOMIZATION CUSTOMIZATION
------------- -------------

122
includes/islandora_authtokens.inc

@ -0,0 +1,122 @@
<?php
/**
* @file
* Library functions for handling authentication tokens.
* These are to be used when dealing with applications
* such as Djatoka that do not pass through credentials.
*/
// Token lifespan(seconds): after this duration the token expires.
// 5 minutes.
define('TOKEN_TIMEOUT', 300);
/**
* Request Islandora to construct an object/datastream authentication token.
* This token can later be turned in for access to the requested object or
* datastream.
*
* @param string $pid
* The Fedora PID to generate the token for.
* @param string $dsid
* The Fedora datastream ID to generate the token for.
* @param integer $uses
* Defaults to 1.
* The number of uses the token should be used for. There are
* times when this should be greater than 1: ie. Djatoka needs
* to make two calls.
*
* @return string
* The generated authentication token.
*/
function islandora_get_object_token($pid, $dsid, $uses = 1) {
global $user;
$time = time();
// The function mt_rand is not considered cryptographically secure
// and openssl_rando_pseudo_bytes() is only available in PHP > 5.3.
// We might be safe in this case because mt_rand should never be using
// the same seed, but this is still more secure.
$token = hash("sha256", mt_rand() . $time);
$id = db_insert("islandora_authtokens")->fields(
array(
'token' => $token,
'uid' => $user->uid,
'pid' => $pid,
'dsid' => $dsid,
'time' => $time,
'remaining_uses' => $uses,
))->execute();
return $token;
}
/**
* Submit a token to islandora for authentication. Supply islandora with the
* token and the object/datastream it is for and you will receive access if
* authentication passes. Tokens can only be redeemed in a short window after
* their creation.
*
* @param string $pid
* The PID of the object to retrieve.
* @param string $dsid
* The datastream id to retrieve.
* @param string $token
* The registered token that allows access to this object.
*
* @return mixed
* The user credentials for access if the token validation passes,
* FALSE otherwise
*/
function islandora_validate_object_token($pid, $dsid, $token) {
// Check for database token.
$time = time();
$query = db_select('islandora_authtokens', 'tokens');
$query->join('users', 'u', 'tokens.uid = u.uid');
// The results will look like user objects.
$result = $query
->fields('u', array('uid', 'name', 'pass'))
->fields('tokens', array('remaining_uses'))
->condition('token', $token, '=')
->condition('pid', $pid, '=')
->condition('dsid', $dsid, '=')
->condition('time', $time, '<=')
->condition('time', $time - TOKEN_TIMEOUT, '>')
->execute()
->fetchAll();
if ($result) {
$remaining_uses = $result[0]->remaining_uses;
$remaining_uses--;
// Remove the authentication token so it can't be used again.
if ($remaining_uses == 0) {
db_delete("islandora_authtokens")
->condition('token', $token, '=')
->condition('pid', $pid, '=')
->condition('dsid', $dsid, '=')
->execute();
}
// Decrement authentication token uses.
else {
db_update("islandora_authtokens")
->fields(array('remaining_uses' => $remaining_uses))
->condition('token', $token, '=')
->condition('pid', $pid, '=')
->condition('dsid', $dsid, '=')
->execute();
}
unset($result[0]->remaining_uses);
return $result[0];
}
else {
return FALSE;
}
}
/**
* Will remove any expired authentication tokens.
*/
function islandora_remove_expired_tokens() {
$time = time();
db_delete("islandora_authtokens")
->condition('time', $time - TOKEN_TIMEOUT, '<')
->execute();
}

750
includes/solution_packs.inc

@ -9,157 +9,143 @@
/** /**
* Solution pack admin page callback. * Solution pack admin page callback.
*
* @return string
* The html repersentation of all solution pack forms for required objects.
*/ */
function islandora_solution_packs_admin() { function islandora_solution_packs_admin() {
module_load_include('inc', 'islandora', 'includes/utilities'); module_load_include('inc', 'islandora', 'includes/utilities');
drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css');
if (!islandora_describe_repository()) { if (!islandora_describe_repository()) {
$message = t('Could not connect to the repository. Please check the settings on the ' . islandora_display_repository_inaccessible_message();
'<a href="@config_url" title="Islandora configuration page">Islandora configuration</a> page.', return '';
array('@config_url' => url('admin/islandora/configure')));
drupal_set_message($message, 'error');
} }
$enabled_solution_packs = module_invoke_all('islandora_required_objects');
$connection = islandora_get_tuque_connection();
drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css');
$output = ''; $output = '';
$enabled_solution_packs = module_invoke_all('islandora_required_objects', $connection);
foreach ($enabled_solution_packs as $solution_pack_module => $solution_pack_info) { foreach ($enabled_solution_packs as $solution_pack_module => $solution_pack_info) {
$objects = array(); // @todo We should probably get the title of the solution pack from the
foreach ($solution_pack_info as $field => $value) { // systems table for consistency in the interface.
switch ($field) { $solution_pack_name = $solution_pack_info['title'];
case 'title': $objects = array_filter($solution_pack_info['objects']);
$solution_pack_name = $value; $form = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects);
break; $output .= drupal_render($form);
case 'objects':
$objects = $value;
break;
}
}
$form_array = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects);
$output .= drupal_render($form_array);
} }
return $output; return $output;
} }
/** /**
* Solution pack admin page * A solution pack form for the given module, it lists all the given objects and
* their status, allowing the user to re-ingest them.
*
* @param array $form
* The Drupal form definition.
* @param array $form_state
* The Drupal form state.
* @param string $solution_pack_module
* The module which requires the given objects.
* @param string $solution_pack_name
* The name of the solution pack to display to the users.
* @param array $objects
* An array of NewFedoraObjects which describe the state in which objects
* should exist.
*
* @return array
* The Drupal form definition.
*/ */
function islandora_solution_pack_form($form, &$form_state, $solution_pack_module, $solution_pack_name, $objects = array()) { function islandora_solution_pack_form(array $form, array &$form_state, $solution_pack_module, $solution_pack_name, $objects = array()) {
global $base_url; // The order is important in terms of severity of the status, where higher
$needs_update = FALSE; // index indicates the status is more serious, this will be used to determine
$needs_install = FALSE; // what messages get displayed to the user.
$could_not_connect = FALSE; $ok_image = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array()));
$form = array(); $warning_image = theme_image(array('path' => 'misc/watchdog-warning.png', 'attributes' => array()));
$status_info = array(
$form['solution_pack'] = array( 'up_to_date' => array(
'#type' => 'fieldset', 'solution_pack' => t('All required objects are installed and up-to-date.'),
'#collapsible' => FALSE, 'image' => $ok_image,
'#collapsed' => FALSE, 'button' => t("Force reinstall objects"),
'#attributes' => array('class' => array('islandora-solution-pack-fieldset')), ),
); 'modified_datastream' => array(
'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'),
// adding values 'image' => $warning_image,
$form['solution_pack']['solution_pack_module'] = array( 'button' => t("Reinstall objects")
'#type' => 'value', ),
'#value' => $solution_pack_module, 'out_of_date' => array(
); 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'),
$form['solution_pack']['solution_pack_name'] = array( 'image' => $warning_image,
'#type' => 'value', 'button' => t("Reinstall objects")
'#value' => $solution_pack_name, ),
'missing_datastream' => array(
'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'),
'image' => $warning_image,
'button' => t("Reinstall objects")
),
'missing' => array(
'solution_pack' => t( 'Some objects are missing and must be installed. See objects list for details.'),
'image' => $warning_image,
'button' => t("Install objects")
),
); );
$form['solution_pack']['objects'] = array( $status_severities = array_keys($status_info);
'#type' => 'value', $solution_pack_status_severity = array_search('up_to_date', $status_severities);
'#value' => $objects,
);
$table_header = array(t('Label'), t('PID'), t('Status'));
$table_rows = array(); $table_rows = array();
foreach ($objects as $object) { foreach ($objects as $object) {
$datastreams = NULL; $object_status = islandora_check_object_status($object);
if (isset($object['pid'])) { $object_status_info = $status_info[$object_status['status']];
$pid = $object['pid']; $object_status_severity = array_search($object_status['status'], $status_severities);
$table_row = array(); // The solution pack status severity will be the highest severity of the objects.
$object_status = islandora_check_object_status($object); $solution_pack_status_severity = max($solution_pack_status_severity, $object_status_severity);
switch ($object_status) { $exists = $object_status['status'] != 'missing';
case 'up_to_date': $label = $exists ? l($object->label, "islandora/object/{$object->id}") : $object->label;
$object_status = t('Up-to-date'); $status_msg = "{$object_status_info['image']}&nbsp{$object_status['status_friendly']}";
break; $table_rows[] = array($label, $object->id, $status_msg);
case 'missing':
$object_status = t('Missing');
$needs_install = TRUE;
break;
case 'missing_datastream':
$object_status = t('Missing datastream');
$needs_update = TRUE;
break;
case 'out_of_date':
$object_status = t('Out-of-date');
$needs_update = TRUE;
break;
case 'could_not_connect':
$object_status = t('Could not connect');
$could_not_connect = TRUE;
break;
}
if ($needs_install OR $could_not_connect) {
$label = $object['label'] ? $object['label'] : '';
}
else {
$label = $object['label'] ? l($object['label'], $base_url . '/islandora/object/' . $pid) : '';
}
$table_row[] = $label;
$table_row[] = $pid;
$table_row[] = $object_status;
$table_rows[] = $table_row;
}
}
// title
if (!$form_state['submitted']) {
$form['solution_pack']['solution_pack_label'] = array(
'#markup' => filter_xss($solution_pack_name),
'#prefix' => '<h3>',
'#suffix' => '</h3>',
);
$form['solution_pack']['install_status'] = array(
'#markup' => '<strong>' . t('Object status:') . '&nbsp;</strong>',
'#prefix' => '<div class="islandora-solution-pack-install-status">',
'#suffix' => '</div>',
);
if (!$needs_install AND !$needs_update AND !$could_not_connect) {
$form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-ok.png')) . ' ' . t('All required objects are installed and up-to-date.');
$submit_button_text = t("Force reinstall objects");
}
elseif ($needs_install) {
$form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-warning.png')) . ' ' . t('Some objects are missing and must be installed. See objects list for details.');
$submit_button_text = t("Install objects");
}
elseif ($needs_update) {
$form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-warning.png')) . ' ' . t('Some objects must be reinstalled. See objects list for details.');
$submit_button_text = t("Reinstall objects");
}
elseif ($could_not_connect) {
$form['solution_pack']['install_status']['#markup'] .= ' ' . theme('image', array('path' => 'misc/watchdog-error.png')) . ' ' . t('Could not connect to the repository.');
$submit_button_text = '';
}
$form['solution_pack']['table'] = array(
'#type' => 'item',
'#markup' => theme('table', array('header' => $table_header, 'rows' => $table_rows)),
);
} }
$solution_pack_status = $status_severities[$solution_pack_status_severity];
if (!$could_not_connect) { $solution_pack_status_info = $status_info[$solution_pack_status];
$form['solution_pack']['submit'] = array( return array(
'#value' => $submit_button_text, 'solution_pack' => array(
'#type' => 'submit', '#type' => 'fieldset',
'#name' => $solution_pack_module, '#collapsible' => FALSE,
'#attributes' => array('class' => array('islandora-solution-pack-submit')), '#collapsed' => FALSE,
'#weight' => 40, '#attributes' => array('class' => array('islandora-solution-pack-fieldset')),
); 'solution_pack_module' => array(
$form['solution_pack']['#submit'] = array('islandora_solution_pack_form_submit'); '#type' => 'value',
} '#value' => $solution_pack_module,
return $form; ),
'solution_pack_name' => array(
'#type' => 'value',
'#value' => $solution_pack_name,
),
'objects' => array(
'#type' => 'value',
'#value' => $objects,
),
'solution_pack_label' => array(
'#markup' => $solution_pack_name,
'#prefix' => '<h3>',
'#suffix' => '</h3>',
),
'install_status' => array(
'#markup' => t('<strong>Object status:</strong> !image !status', array(
'!image' => $solution_pack_status_info['image'],
'!status' => $solution_pack_status_info['solution_pack']
)),
'#prefix' => '<div class="islandora-solution-pack-install-status">',
'#suffix' => '</div>',
),
'table' => array(
'#type' => 'item',
'#markup' => theme('table', array('header' => array(t('Label'), t('PID'), t('Status')), 'rows' => $table_rows))
),
'submit' => array(
'#type' => 'submit',
'#name' => $solution_pack_module,
'#value' => $solution_pack_status_info['button'],
'#attributes' => array('class' => array('islandora-solution-pack-submit')),
)
)
);
} }
/** /**
@ -167,349 +153,279 @@ function islandora_solution_pack_form($form, &$form_state, $solution_pack_modul
* *
* @param array $form * @param array $form
* The form submitted. * The form submitted.
* @param array_reference $form_state * @param array $form_state
* The state of the form submited. * The state of the form submited.
*/ */
function islandora_solution_pack_form_submit($form, &$form_state) { function islandora_solution_pack_form_submit(array $form, array &$form_state) {
$solution_pack_module = $form_state['values']['solution_pack_module']; $solution_pack_module = $form_state['values']['solution_pack_module'];
$solution_pack_name = $form_state['values']['solution_pack_name'];
$objects = $form_state['values']['objects']; $objects = $form_state['values']['objects'];
$batch = array( $batch = array(
'title' => t('Installing / updating solution pack objects'), 'title' => t('Installing / Updating solution pack objects'),
'file' => drupal_get_path('module', 'islandora') . '/includes/solution_packs.inc', 'file' => drupal_get_path('module', 'islandora') . '/includes/solution_packs.inc',
'operations' => array(), 'operations' => array(),
); );
foreach ($objects as $object) { foreach ($objects as $object) {
// Add this object to the batch job queue. $batch['operations'][] = array('islandora_solution_pack_batch_operation_reingest_object', array($object));
$batch['operations'][] = array('islandora_batch_reingest_object', array($object));
} }
batch_set($batch); batch_set($batch);
// Hook to let solution pack objects be modified. // Hook to let solution pack objects be modified.
// Not using module_invoke so solution packs can be expanded by other modules. // Not using module_invoke so solution packs can be expanded by other modules.
// @todo shouldn't we send the object list along as well?
module_invoke_all('islandora_postprocess_solution_pack', $solution_pack_module); module_invoke_all('islandora_postprocess_solution_pack', $solution_pack_module);
}
/**
* Batch operation used by the solution pack forms to ingest/reingest required
* object(s)
*
* @param NewFedoraObject $object
* The object to ingest/reingest.
* @param array $context
* The context of this batch operation.
*/
function islandora_solution_pack_batch_operation_reingest_object(NewFedoraObject $object, array &$context) {
$deleted = FALSE;
$existing_object = islandora_object_load($object->id);
if ($existing_object) {
$deleted = islandora_delete_object($existing_object);
$purged = $deleted && $existing_object == NULL;
if (!$purged) {
$object_link = l($existing_object->label, "islandora/object/{$existing_object->id}");
drupal_set_message(t('Failed to purge existing object !object_link.', array(
'!object_link' => $object_link,
)), 'error');
// Failed to purge don't attempt to ingest.
return;
}
}
// Object was deleted or did not exist.
$pid = $object->id;
$label = $object->label;
$action = $deleted ? 'reinstalled' : 'installed';
$object_link = l($label, "islandora/object/{$pid}");
$object = islandora_add_object($object);
$msg = $object ? "Successfully $action !object_link." : "Failed to $action @label identified by @pid.";
$status = $object ? 'status' : 'error';
drupal_set_message(t($msg, array(
'@pid' => $pid,
'@label' => $label,
'!object_link' => $object_link
)), $status);
} }
/** /**
* Batch reingest object(s) * This is to be called from the solution pack's hook_install()
* and hook_uninstall() functions. It provides a convient way to have a
* solution pack's required objects ingested at install time.
* *
* @param array $object * @param string $module_name
* @param type $context * The name of the module that is calling this function in its
* @return type * install/unistall hooks.
* @param string $op
* The operation to perform, either install or uninstall.
*
* @todo Implement hook_modules_installed/hook_modules_uninstalled instead of
* calling this function directly.
* @todo Remove the second parameter and have two seperate functions.
*/ */
function islandora_batch_reingest_object($object_model, &$context) { function islandora_install_solution_pack($module, $op = 'install') {
if ($op == 'uninstall') {
islandora_uninstall_solution_pack($module);
return;
}
module_load_include('module', 'islandora', 'islandora');
module_load_include('inc', 'islandora', 'includes/utilities'); module_load_include('inc', 'islandora', 'includes/utilities');
global $base_url; module_load_include('module', $module, $module);
$connection = islandora_get_tuque_connection(); $info_file = drupal_get_path('module', $module) . "/{$module}.info";
if (!$connection) { $info_array = drupal_parse_info_file($info_file);
$module_name = $info_array['name'];
$admin_link = l(t('Solution Pack admin'), 'admin/islandora/solution_packs');
$config_link = l(t('Islandora configuration'), 'admin/islandora/configure');
if (!islandora_describe_repository()) {
$msg = '@module: Did not install any objects. Could not connect to the ';
$msg .= 'repository. Please check the settings on the !config_link page ';
$msg .= 'and install the required objects manually on the !admin_link page.';
drupal_set_message(st($msg, array(
'@module' => $module_name,
'!config_link' => $config_link,
'@admin_link' => $admin_link
)), 'error');
return; return;
} }
if (!empty($object_model) && is_array($object_model)) { $connection = islandora_get_tuque_connection();
$pid = $object_model['pid']; $required_objects = module_invoke($module, 'islandora_required_objects', $connection);
if (!islandora_is_valid_pid($pid)) { $objects = $required_objects[$module]['objects'];
return NULL; $status_messages = array(
} 'up_to_date' => 'The object already exists and is up-to-date',
'missing_datastream' => 'The object already exists but is missing a datastream. Please reinstall the object on the !admin_link page',
// purge object 'out_of_date' => 'The object already exists but is out-of-date. Please update the object on the !admin_link page',
// check if object already exits 'modified_datastream' => 'The object already exists but datastreams are modified. Please reinstall the object on the !admin_link page',
$object_query = $connection->api->a->findObjects('query', 'pid=' . $pid); );
$reinstall = FALSE; foreach ($objects as $object) {
if (!empty($object_query['results'])) { $query = $connection->api->a->findObjects('query', 'pid=' . $object->id);
$object = islandora_object_load($pid); $already_exists = !empty($query['results']);
if (isset($object)) { $label = $object->label;
islandora_delete_object($object); $object_link = l($label, "islandora/object/{$object->id}");
} if ($already_exists) {
$reinstall = TRUE; $object_status = islandora_check_object_status($object);
$status_msg = $status_messages[$object_status['status']];
drupal_set_message(st("@module: Did not install !object_link. $status_msg.", array(
'@module' => $module_name,
'!object_link' => $object_link,
'!admin_link' => $admin_link,
)), 'warning');
} }
else {
// build and ingest new object $object = islandora_add_object($object);
try { if ($object) {
$object = islandora_solution_pack_add_object($object_model); drupal_set_message(t('@module: Successfully installed. !object_link.', array(
$object_name = $object->label; '@module' => $module_name,
if ($reinstall) { '!object_link' => $object_link,
drupal_set_message(t('Successfully reinstalled <em>@object_name</em>.', array('@object_name' => $object_name, '@pid' => $pid))); )), 'status');
} }
else { else {
drupal_set_message(t('Successfully installed <em>@object_name</em>.', array('@object_name' => $object_name, '@pid' => $pid))); drupal_set_message(t('@module: Failed to install. @label.', array(
'@module' => $module_name,
'@label' => $label,
)), 'warning');
} }
} }
catch (Exception $e) {
drupal_set_message(t('Installation of object @pid failed', array('@pid' => $pid)), 'error');
}
} }
} }
/** /**
* Callback function that can be called from the solution pack's hook_install() and hook_uninstall() functions. * Uninstalls the given solution pack.
*
* @param string $module
* The solution pack to uninstall.
* *
* @TODO: add documentation * @todo Implement hook_modules_uninstalled instead of calling this function
* directly for each solution pack.
*/ */
function islandora_install_solution_pack($module_name = NULL, $op = 'install') { function islandora_uninstall_solution_pack($module) {
// check if a module name is given. // @TODO: check module name for existance module_load_include('module', 'islandora', 'islandora');
if (!empty($module_name)) { module_load_include('inc', 'islandora', 'includes/utilities');
module_load_include('module', 'islandora', 'islandora'); module_load_include('module', $module, $module);
module_load_include('inc', 'islandora', 'includes/utilities'); $config_link = l(t('Islandora configuration'), 'admin/islandora/configure');
module_load_include('module', $module_name, $module_name); $info_file = drupal_get_path('module', $module) . "/{$module}.info";
$info_array = drupal_parse_info_file($info_file);
// set globals $module_name = $info_array['name'];
global $base_url; if (!islandora_describe_repository()) {
global $user; $msg = '@module: Did not uninstall any objects. Could not connect to the ';
$msg .= 'repository. Please check the settings on the !config_link page ';
// set variables $msg .= 'and uninstall the required objects manually if necessary.';
$sp_admin = url($base_url . '/admin/islandora/solution_packs'); drupal_set_message(st($msg, array(
$config_url = url('admin/islandora/configure'); '@module' => $module_name,
'!config_link' => $config_link
// get module info )), 'error');
$info_file = drupal_get_path('module', $module_name) . '/' . $module_name . '.info'; return;
$info_array = drupal_parse_info_file($info_file); }
$module_label = $info_array['name']; $connection = islandora_get_tuque_connection();
$required_objects = module_invoke($module, 'islandora_required_objects', $connection);
// check connection $objects = $required_objects[$module]['objects'];
$url = variable_get('islandora_base_url', 'http://localhost:8080/fedora'); $existing_objects = array_filter($objects, function($o) use($connection) {
$info = islandora_describe_repository($url); $param = "pid={$o->id}";
if (!$info) { $query = $connection->api->a->findObjects('query', $param);
// operation return !empty($query['results']);
switch ($op) { }
case 'install': );
drupal_set_message(st('@module_label: Did not install any objects. Could not connect to the repository. Please check the settings on the <a href="@config_url" title="Islandora configuration page">Islandora configuration</a> page and install the required objects manually on the <a href="@sp_url" title="Solution pack admin">solution pack admin</a> page.', array('@module_label' => $module_label, '@config_url' => $config_url, '@sp_url' => $sp_admin)), 'error'); foreach ($existing_objects as $object) {
break; $msg = '@module: Did not remove !object_link. It may be used by other sites.';
$object_link = l($object->label, "islandora/object/{$object->id}");
case 'uninstall': drupal_set_message(st($msg, array(
drupal_set_message(st('@module_label: Did not uninstall any objects. Could not connect to the repository. Please check the settings on the <a href="@config_url" title="Islandora configuration page">Islandora configuration</a> page and uninstall the required objects manually if necessary.', array('@module_label' => $module_label, '@config_url' => $config_url)), 'error'); '!object_link' => $object_link,
break; '@module' => $module_name
} )), 'warning');
return;
}
$connection = islandora_get_tuque_connection();
// get object models
$enabled_solution_packs = module_invoke_all('islandora_required_objects');
$islandora_required_objects = $module_name . '_islandora_required_objects';
$required_objects = $islandora_required_objects();
$objects = $required_objects[$module_name]['objects'];
// loop over object models
foreach ($objects as $object) {
// set variables
$pid = $object['pid'];
$label = isset($object['label']) ? $object['label'] : st('Object');
// check if object already exists
$query = $connection->api->a->findObjects('query', 'pid=' . $pid);
// object url
$object_url = url($base_url . '/islandora/object/' . $pid);
// operation: install or uninstall
switch ($op) {
case 'install':
// if object exists, don't re-ingest
if (!empty($query['results'])) {
// check object status
$object_status = islandora_check_object_status($object);
// set messages
switch ($object_status) {
case 'up_to_date':
drupal_set_message(st('@module_label: did not install <a href="@object_url" title="@pid">@label</a>. The object already exists and is up-to-date.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@object_url' => $object_url)));
break;
case 'missing_datastream':
drupal_set_message(st('@module_label: did not install <a href="@object_url" title="@pid">@label</a>. The object already exists but is missing a datastream. Please reinstall the object on the <a href="@sp_admin" title="Solution pack admin page">solution pack admin page</a>.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@objecturl' => $object_url, '@sp_admin' => $sp_admin)), 'warning');
break;
case 'out_of_date':
drupal_set_message(st('@module_label: did not install <a href="@object_url" title="@pid">@label</a>. The object already exists but is out-of-date. Please update the object on the <a href="@sp_admin" title="Solution pack admin page">solution pack admin page</a>.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@object_url' => $object_url, '@sp_admin' => $sp_admin)), 'warning');
break;
}
}
else {
islandora_solution_pack_add_object($object);
drupal_set_message(st('@module_label: installed <a href="@object_url" title="@pid">@label</a> object.', array('@module_label' => $module_label, '@label' => $label, '@pid' => $pid, '@object_url' => $object_url)));
}
break;
case 'uninstall':
// if object exists, set message
if (!empty($query['results'])) {
$object_url = url($base_url . '/islandora/object/' . $pid);
drupal_set_message(st('@module_label: did not remove <a href="@object_url" title="@pid">@label</a>. It may be used by other sites.', array('@pid' => $pid, '@object_url' => $object_url, '@label' => $label, '@module_label' => $module_label)), 'warning');
}
break;
}
}
} }
} }
/** /**
* Function to check the status of an object against an object model array. * Function to check the status of an object against an object model array.
* *
* @param array $object_model * @param NewFedoraObject $object_definition
* an array describing an object * A new fedora object that defines what the object should contain.
*
* @return string * @return string
* Returns one of the following values: * Returns one of the following values:
* up_to_date, missing, missing_datastream or out_of_date * up_to_date, missing, missing_datastream or out_of_date
* You can perform an appropriate action based on this value. * You can perform an appropriate action based on this value.
* Returns FALSE if the array is empty
* *
* @see islandora_solution_pack_form() * @see islandora_solution_pack_form()
* @see islandora_install_solution_pack() * @see islandora_install_solution_pack()
* @todo: Should this function live in islandora.module so it can be called
* easier without having to include the solution_packs.inc file?
*/ */
function islandora_check_object_status($object_model = array()) { function islandora_check_object_status(NewFedoraObject $object_definition) {
if (!empty($object_model)) { $existing_object = islandora_object_load($object_definition->id);
// set variables if (!$existing_object) {
$pid = $object_model['pid']; return array('status' => 'missing', 'status_friendly' => t('Missing'));
$object_status = 'up_to_date';
// table row
$table_row = array();
// check connection
module_load_include('inc', 'islandora', 'includes/utilities');
$url = variable_get('islandora_base_url', 'http://localhost:8080/fedora');
$info = islandora_describe_repository($url);
if (!$info) {
$object_status = 'could_not_connect';
}
else {
// load object
$object = islandora_object_load($pid);
// check if object exists
if (!$object) {
$object_status = 'missing';
}
else {
// object defined with single datastream file
// @TODO: should dsversion be mandatory for the check to valid?
if (isset($object_model['dsid']) && isset($object_model['datastream_file']) && isset($object_model['dsversion'])) {
$datastreams = array(
array(
'dsid' => $object_model['dsid'],
'datastream_file' => $object_model['datastream_file'],
'dsversion' => $object_model['dsversion'],
),
);
}
// object defined with multiple datastreams (using an array)
elseif (!empty($object_model['datastreams'])) {
$datastreams = $object_model['datastreams'];
}
if (!empty($datastreams) && is_array($datastreams)) {
// loop over defined datastreams
foreach ($datastreams as $ds) {
$ds_id = $ds['dsid'];
// check if defined datastream exists in the object
if (!$object[$ds_id]) {
$object_status = 'missing_datastream';
break;
}
elseif (isset($ds['dsversion'])) {
// Check if the datastream is versioned and needs updating.
$installed_version = islandora_get_islandora_datastream_version($object, $ds['dsid']);
$available_version = islandora_get_islandora_datastream_version(NULL, NULL, $ds['datastream_file']);
if ($available_version > $installed_version) {
$object_status = 'out_of_date';
break;
}
}
}
}
}
}
return $object_status;
} }
else {
return FALSE; $existing_datastreams = array_keys(iterator_to_array($existing_object));
$expected_datastreams = array_keys(iterator_to_array($object_definition));
$datastream_diff = array_diff($expected_datastreams, $existing_datastreams);
if (!empty($datastream_diff)) {
$status_friendly = format_plural(count($datastream_diff), 'Missing Datastream: %dsids.', 'Missing Datastreams: %dsids.', array('%dsids' => implode(', ', $datastream_diff)));
return array('status' => 'missing_datastream', 'status_friendly' => $status_friendly, 'data' => $datastream_diff);
} }
}
/** $is_xml_datastream = function($ds) { return $ds->mimetype == 'text/xml'; };
* Converts the given definition into an object and add's it to the repository. $xml_datastreams = array_filter(iterator_to_array($object_definition), $is_xml_datastream);
* $out_of_date_datastreams = array();
* @param array $object_definition foreach ($xml_datastreams as $ds) {
* An associative array containing the necessary parameters to create the $installed_version = islandora_get_islandora_datastream_version($existing_object, $ds->id);
* desired object: $available_version = islandora_get_islandora_datastream_version($object_definition, $ds->id);
* - pid: The PID with which the object will be created. if ($available_version > $installed_version) {
* - label: An optional label to apply to the object. $out_of_date_datastreams[] = $ds->id;
* - datastreams: Same as the "datastreams" array accepted by }
* islandora_prepare_new_object(). }
* - cmodel: Either an array of content models as accepted by
* islandora_preprare_new_object(), or a single content model PID to add
* to the object.
* - parent: Either an array of parents, or a single parent PID to which to
* relate to; uses isMemberOfCollection by default.
* - relationships: An array of relationships as accepted by
* islandora_prepare_new_object().
*
* @return FedoraObject
* The newly created object.
*/
function islandora_solution_pack_add_object(array $object_definition) {
$object = islandora_solution_pack_prepare_new_object($object_definition);
return islandora_add_object($object);
}
/** if (count($out_of_date_datastreams)) {
* Prepares a new object based on the solution pack style of declaring them as arrays. $status_friendly = format_plural(count($out_of_date_datastreams), 'Datastream out of date: %dsids.', 'Datastreams out of date: %dsids.', array('%dsids' => implode(', ', $out_of_date_datastreams)));
* return array('status' => 'out_of_date', 'status_friendly' => $status_friendly, 'data' => $out_of_date_datastreams);
* @param array $object_definition
* An associative array containing the necessary parameters to create the
* desired object:
* - pid: The PID with which the object will be created.
* - label: An optional label to apply to the object.
* - datastreams: Same as the "datastreams" array accepted by
* islandora_prepare_new_object().
* - cmodel: Either an array of content models as accepted by
* islandora_prepare_new_object(), or a single content model PID to add
* to the object.
* - parent: Either an array of parents, or a single parent PID to which to
* relate to; uses isMemberOfCollection by default.
* - relationships: An array of relationships as accepted by
* islandora_prepare_new_object().
*
* @return NewFedoraObject
* An NewFedoraObject which has been initalized with the given properties.
*/
function islandora_solution_pack_prepare_new_object(array $object_definition) {
module_load_include('inc', 'islandora', 'includes/utilities');
$namespace = $object_definition['pid'];
$label = !empty($object_definition['label']) ? $object_definition['label'] : NULL;
$datastreams = array();
if (!empty($object_definition['datastreams']) AND is_array($object_definition['datastreams'])) {
$datastreams = $object_definition['datastreams'];
} }
$content_models = array();
if (!empty($object_definition['cmodel'])) { // This is a pretty heavy function, but I'm not sure a better way. If we have
if (is_array($object_definition['cmodel'])) { // performance trouble, we should maybe remove this.
$content_models = $object_definition['cmodel']; $modified_datastreams = array();
foreach ($object_definition as $ds) {
if ($ds->mimetype == 'text/xml'
|| $ds->mimetype == 'application/rdf+xml'
|| $ds->mimetype == 'application/xml') {
// If the datastream is XML we use the domdocument C14N cannonicalization
// function to test if they are equal, because the strings likely won't
// be equal as Fedora does some XML mangling. In order for C14N to work
// we need to replace the info:fedora namespace, as C14N hates it.
// C14N also doesn't normalize whitespace at the end of lines and Fedora
// may add some whitespace on some lines.
$object_definition_dom = new DOMDocument();
$object_definition_dom->preserveWhiteSpace = FALSE;
$object_definition_dom->loadXML(str_replace('info:', 'http://', $ds->content));
$object_actual_dom = new DOMDocument();
$object_actual_dom->preserveWhiteSpace = FALSE;
$object_actual_dom->loadXML(str_replace('info:', 'http://', $existing_object[$ds->id]->content));
// Fedora changes the xml structure so we need to cannonize it.
if ($object_actual_dom->C14N() != $object_definition_dom->C14N()) {
$modified_datastreams[] = $ds->id;
}
} }
else { else {
$content_models[] = $object_definition['cmodel']; $object_definition_hash = md5($ds->content);
} $object_actual_hash = md5($existing_object[$ds->id]->content);
} if ($object_definition_hash != $object_actual_hash) {
$relationships = array(); $modified_datastreams[] = $ds->id;;
if (!empty($object_definition['parent']) AND !is_array($object_definition['parent'])) { }
$relationships[] = array('relationship' => 'isMemberOfCollection', 'pid' => $object_definition['parent']);
}
if (!empty($object_definition['parents']) AND is_array($object_definition['parents'])) {
foreach ($object_definition['parents'] as $parent) {
$relationships[] = array('relationship' => 'isMemberOfCollection', 'pid' => $parent);
} }
} }
if (!empty($object_definition['relationships']) AND is_array($object_definition['relationships'])) { if (count($modified_datastreams)) {
foreach ($object_definition['relationships'] as $relationship) { $status_friendly = format_plural(count($modified_datastreams), 'Modified Datastream: %dsids.', 'Modified Datastreams: %dsids.', array('%dsids' => implode(', ', $modified_datastreams)));
$relationships[] = array('relationship' => $relationship['relationship'], 'pid' => $relationship['pid']); return array('status' => 'modified_datastream', 'data' => $modified_datastreams, 'status_friendly' => $status_friendly);
}
} }
return islandora_prepare_new_object($namespace, $label, $datastreams, $content_models, $relationships);
}
// If not anything else we can assume its up to date.
return array('status' => 'up_to_date', 'status_friendly' => t('Up-to-date'));
}
/** /**
* @defgroup viewer-functions * @defgroup viewer-functions
* @{ * @{

14
includes/utilities.inc

@ -413,3 +413,17 @@ function islandora_prepare_new_object($namespace = NULL, $label = NULL, $datastr
} }
return $object; return $object;
} }
/**
* Displays the repository is inaccessible message
*
* Use anywhere we want to ensure a consitent error message when the repository
* is not accessible.
*/
function islandora_display_repository_inaccessible_message() {
$text = t('Islandora configuration');
$link = l($text, 'admin/islandora/configure', array('attributes' => array('title' => $text)));
$message = t('Could not connect to the repository. Please check the settings on the !link page.',
array('!link' => $link));
drupal_set_message($message, 'error', FALSE);
}

80
islandora.install

@ -1,15 +1,13 @@
<?php <?php
/** /**
* @file * @file
* * This file contains all install related hooks.
* This file contains all install related hooks.
*/ */
/** /**
* Implements hook_install(). * Implements hook_install().
* *
* @see islandora_islandora_required_objects(). * @see islandora_islandora_required_objects()
*/ */
function islandora_install() { function islandora_install() {
module_load_include('inc', 'islandora', 'includes/solution_packs'); module_load_include('inc', 'islandora', 'includes/solution_packs');
@ -19,9 +17,81 @@ function islandora_install() {
/** /**
* Implements hook_uninstall(). * Implements hook_uninstall().
* *
* @see islandora_islandora_required_objects(). * @see islandora_islandora_required_objects()
*/ */
function islandora_uninstall() { function islandora_uninstall() {
module_load_include('inc', 'islandora', 'includes/solution_packs'); module_load_include('inc', 'islandora', 'includes/solution_packs');
islandora_install_solution_pack('islandora', 'uninstall'); islandora_install_solution_pack('islandora', 'uninstall');
} }
/**
* Implements hook_schema().
*
* @return array
* The database schema for the module.
*/
function islandora_schema() {
$schema['islandora_authtokens'] = array(
'description' => 'The hub for all islandora authentication tokens',
'fields' => array(
'id' => array(
'description' => 'key',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'token' => array(
'description' => 'a unique identifier for this token',
'type' => 'varchar',
'length' => 64,
),
'remaining_uses' => array(
'description' => 'How many uses until this should be removed',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(
'description' => 'the user id that requested this token',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'time' => array(
'description' => 'when this token was created',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'pid' => array(
'description' => 'the pid of the object this token unlocks',
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
),
'dsid' => array(
'description' => 'the datasteram id of the object this token unlocks',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
),
),
'unique keys' => array(
'id' => array('id'),
),
'primary key' => array('id'),
);
return $schema;
}
/**
* Implements hook_update_N().
*
* Add the required table for handling authentication tokens.
* This is the first instance that has this table.
*/
function islandora_update_7001(&$sandbox) {
drupal_install_schema('islandora');
$t = get_t();
return $t("Islandora database updates complete");
}

126
islandora.module

@ -161,8 +161,10 @@ function islandora_menu() {
'access arguments' => array(FEDORA_VIEW, 2, 4), 'access arguments' => array(FEDORA_VIEW, 2, 4),
'load arguments' => array(2), 'load arguments' => array(2),
); );
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/view'] = array( // This menu item uses token authentication in islandora_tokened_object.
$items['islandora/object/%islandora_tokened_object/datastream/%islandora_tokened_datastream/view'] = array(
'title' => 'View datastream', 'title' => 'View datastream',
'load arguments' => array('%map'),
'type' => MENU_DEFAULT_LOCAL_TASK, 'type' => MENU_DEFAULT_LOCAL_TASK,
); );
$items['islandora/object/%islandora_object/datastream/%islandora_datastream/download'] = array( $items['islandora/object/%islandora_object/datastream/%islandora_datastream/download'] = array(
@ -311,6 +313,12 @@ function islandora_forms($form_id) {
*/ */
function islandora_object_access_callback($perm, $object = NULL) { function islandora_object_access_callback($perm, $object = NULL) {
module_load_include('inc', 'islandora', 'includes/utilities'); module_load_include('inc', 'islandora', 'includes/utilities');
if (!$object && !islandora_describe_repository()) {
islandora_display_repository_inaccessible_message();
return FALSE;
}
return user_access($perm) && is_object($object) && islandora_namespace_accessible($object->id); return user_access($perm) && is_object($object) && islandora_namespace_accessible($object->id);
} }
@ -518,6 +526,55 @@ function islandora_object_load($object_id) {
return NULL; return NULL;
} }
/**
* A helper function to get a connection and return an object using a token
* for authentication.
*
* @param string $object_id
* The PID of an object in the menu path identified by
* '%islandora_tokened_object'.
* @param array $map
* Used to extract the Fedora object's DSID at $map[4].
*
* @return FedoraObject
* A token authenticated object. @see islandora_object_load
*/
function islandora_tokened_object_load($object_id, $map) {
if (array_key_exists('token', $_GET)) {
$token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
if ($token) {
module_load_include('inc', 'islandora', 'includes/islandora_authtokens');
$token_user = islandora_validate_object_token($object_id, $map[4], $token);
islandora_get_tuque_connection($user = $token_user);
}
}
return islandora_object_load($object_id);
}
/**
* This datastream load must take in arguments in a different
* order than the usual islandora_datastream_load. This is because
* the function islandora_tokened_object_load needs DSID. It uses
* the path %map to avoid duplicate parameters. The menu system
* passes 'load arguments' to both islandora_tokened_object_load
* and this function and the first parameter is positional with the token.
* An alternative:
* islandora_tokened_object_load(PID, DSID, PID)
* islandora_tokened_datastream_load(DSID, DSID, PID)
*
* @param mixed $datastream_id
* %islandora_tokened_datastream @see islandora_datastream_load
* @param array $map
* Used to extract the Fedora object's PID at $map[2].
*
* @return FedoraDatastream
* A datastream from Fedora.
* @see islandora_datastream_load
*/
function islandora_tokened_datastream_load($datastream_id, $map) {
return islandora_datastream_load($datastream_id, $map[2]);
}
/** /**
* A helper function to get an datastream specified as '%islandora_datastream' * A helper function to get an datastream specified as '%islandora_datastream'
* for the object specified in the menu path as '%islandora_object'. * for the object specified in the menu path as '%islandora_object'.
@ -526,23 +583,23 @@ function islandora_object_load($object_id) {
* drupal_access_denied() when appropriate. * drupal_access_denied() when appropriate.
* *
* @param string $datastream_id * @param string $datastream_id
* The dsid of the datastream specified as '%islandora_datastream' to fetch * The DSID of the datastream specified as '%islandora_datastream' to fetch
* from the given object in the menu path identified by '%islandora_object'. * from the given object in the menu path identified by '%islandora_object'.
* *
* $param string $object_id * @param mixed $object_id
* The object to load the datastream from. * The object to load the datastream from. This can be a Fedora PID or
* an instantiated IslandoraFedoraObject as it implements __toString()
* returning the PID.
* *
* @return FedoraDatastream * @return FedoraDatastream
* If the given datastream ID exists then this returns a FedoraDatastream * If the given datastream ID exists then this returns a FedoraDatastream
* object, otherwise it returns NULL which triggers drupal_page_not_found(). * object, otherwise it returns NULL which triggers drupal_page_not_found().
*/ */
function islandora_datastream_load($datastream_id, $object_id) { function islandora_datastream_load($datastream_id, $object_id) {
$object = islandora_object_load($object_id); $object = is_object($object_id) ? $object_id : islandora_object_load($object_id);
if (!$object) { if (!$object) {
return NULL; return NULL;
} }
return $object[$datastream_id]; return $object[$datastream_id];
} }
@ -578,35 +635,32 @@ function islandora_get_islandora_datastream_version($item = NULL, $dsid = NULL,
/** /**
* Implements hook_islandora_required_objects(). * Implements hook_islandora_required_objects().
*/ */
function islandora_islandora_required_objects() { function islandora_islandora_required_objects(IslandoraTuque $connection) {
$module_path = drupal_get_path('module', 'islandora'); $module_path = drupal_get_path('module', 'islandora');
// Root Collection
$root_collection = $connection->repository->constructObject('islandora:root');
$root_collection->owner = 'fedoraAdmin';
$root_collection->label = 'Top-level Collection';
$root_collection->models = 'islandora:collectionCModel';
// Collection Policy Datastream
$datastream = $root_collection->constructDatastream('COLLECTION_POLICY', 'X');
$datastream->label = 'Collection policy';
$datastream->mimetype = 'text/xml';
$datastream->setContentFromFile("$module_path/xml/islandora_collection_policy.xml", FALSE);
$root_collection->ingestDatastream($datastream);
// TN Datastream
$datastream = $root_collection->constructDatastream('TN', 'M');
$datastream->label = 'Thumbnail';
$datastream->mimetype = 'image/png';
$datastream->setContentFromFile("$module_path/images/folder.png", FALSE);
$root_collection->ingestDatastream($datastream);
return array( return array(
'islandora' => array( 'islandora' => array(
'title' => 'Islandora', 'title' => 'Islandora',
'objects' => array( 'objects' => array(
array( $root_collection
'pid' => 'islandora:root', )
'label' => 'Top-level collection', )
'cmodel' => 'islandora:collectionCModel',
'datastreams' => array(
array(
'dsid' => 'COLLECTION_POLICY',
'label' => 'Collection policy',
'mimetype' => 'text/xml',
'control_group' => 'X',
'datastream_file' => "$module_path/xml/islandora_collection_policy.xml",
),
array(
'dsid' => 'TN',
'label' => 'Thumbnail',
'mimetype' => 'image/png',
'control_group' => 'M',
'datastream_file' => "$module_path/images/folder.png",
),
),
),
),
),
); );
} }
@ -818,3 +872,13 @@ function islandora_post_delete_datastream(FedoraObject $object, $datastream_id)
module_invoke_all($hook, $object, $datastream_id); module_invoke_all($hook, $object, $datastream_id);
} }
} }
/**
* Implements hook_cron()
*
* Removes expired authentication tokens.
*/
function islandora_cron() {
module_load_include('inc', 'islandora', 'includes/islandora_authtokens');
islandora_remove_expired_tokens();
}

Loading…
Cancel
Save