diff --git a/includes/datastream.inc~ b/includes/datastream.inc~
new file mode 100644
index 00000000..ac4989d6
--- /dev/null
+++ b/includes/datastream.inc~
@@ -0,0 +1,146 @@
+mimetype);
+ if ($datastream->controlGroup == 'M' || $datastream->controlGroup == 'X') {
+ header('Content-length: ' . $datastream->size);
+ }
+ if ($download) {
+ // Browsers will not append all extensions.
+ $mime_detect = new MimeDetect();
+ $extension = $mime_detect->getExtension($datastream->mimetype);
+ $filename = $datastream->label . '.' . $extension;
+ header("Content-Disposition: attachment; filename=\"$filename\"");
+ }
+ drupal_page_is_cacheable(FALSE);
+ // Try not to load the file into PHP memory!
+ ob_end_flush();
+ $datastream->getContent('php://output');
+ exit();
+}
+
+/**
+ * Get the human readable size of the given datastream.
+ *
+ * @param FedoraDatastream $datastream
+ * The datastream to check.
+ *
+ * @return string
+ * A human readable size of the given datastream, or '-' if the size could not
+ * be determined.
+ */
+function islandora_datastream_get_human_readable_size(FedoraDatastream $datastream) {
+ module_load_include('inc', 'islandora', 'includes/utilities');
+ $size_is_calculatable = $datastream->controlGroup == 'M' || $datastream->controlGroup == 'X';
+ return $size_is_calculatable ? islandora_convert_bytes_to_human_readable($datastream->size) : '-';
+}
+
+/**
+ * Get either the 'view' or 'download' url for the given datastream if possible.
+ *
+ * @param FedoraDatastream $datastream
+ * The datastream to generated the url to.
+ *
+ * @return string
+ * either the 'view' or 'download' url for the given datastream.
+ */
+function islandora_datastream_get_url(FedoraDatastream $datastream, $type = 'download') {
+ return $datastream->controlGroup == 'R' ? $datastream->url : "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/$type";
+}
+
+/**
+ * Gets the delete link.
+ *
+ * @param FedoraDatastream $datastream
+ * The datastream to generated the url to.
+ */
+function islandora_datastream_get_delete_link(FedoraDatastream $datastream) {
+ $datastreams = module_invoke_all('islandora_undeletable_datastreams', $datastream->parent->models);
+ $can_delete = !in_array($datastream->id, $datastreams);
+ return $can_delete ? l(t('delete'), "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/delete") : '';
+}
+
+/**
+ * Gets the edit link.
+ *
+ * @param FedoraDatastream $datastream
+ * The datastream to generated the url to.
+ */
+function islandora_datastream_edit_get_link(FedoraDatastream $datastream) {
+ $edit_registry = module_invoke_all('islandora_edit_datastream_registry', $datastream->parent, $datastream);
+ $can_edit = user_access(FEDORA_METADATA_EDIT);
+ return $can_edit ? l(t('edit'), "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/edit") : '';
+}
+
+/**
+ * Display the edit datastream page.
+ *
+ * @param FedoraDatastream $datastream
+ * The datastream to edit.
+ */
+function islandora_edit_datastream(FedoraDatastream $datastream) {
+ $edit_registry = module_invoke_all('islandora_edit_datastream_registry', $datastream->parent, $datastream);
+ $edit_count = count($edit_registry);
+ switch ($edit_count) {
+ case 0:
+ // No edit implementations.
+ drupal_set_message(t('There are no edit methods specified for this datastream.'));
+ drupal_goto("islandora/object/{$object->id}/manage/datastreams");
+ break;
+
+ case 1:
+ // One registry implementation, go there.
+ drupal_goto($edit_registry[0]['url']);
+ break;
+
+ default:
+ // Multiple edit routes registered.
+ return islandora_edit_datastream_registry_render($edit_registry);
+ }
+}
+
+/**
+ * Displays links to all the edit datastream registry items.
+ *
+ * @param array $edit_registry
+ * A list of 'islandora_edit_datastream_registry' values.
+ */
+function islandora_edit_datastream_registry_render(array $edit_registry) {
+ $markup = '';
+ foreach ($edit_registry as $edit_route) {
+ $markup .= l($edit_route['name'], $edit_route['url']) . '
';
+ }
+ return array(
+ '#type' => 'markup',
+ '#markup' => $markup,
+ );
+}
diff --git a/includes/object_properties.form.inc b/includes/object_properties.form.inc
index 543fa6dd..1edcc26f 100644
--- a/includes/object_properties.form.inc
+++ b/includes/object_properties.form.inc
@@ -19,7 +19,9 @@
* The drupal form definition.
*/
function islandora_object_properties_form(array $form, array &$form_state, AbstractObject $object) {
+ module_load_include('inc', 'islandora', 'includes/breadcrumb');
drupal_set_title($object->label);
+ drupal_set_breadcrumb(islandora_get_breadcrumbs($object));
$form_state['object'] = $object;
return array(
'pid' => array(
diff --git a/includes/solution_packs.inc~ b/includes/solution_packs.inc~
new file mode 100644
index 00000000..3b730136
--- /dev/null
+++ b/includes/solution_packs.inc~
@@ -0,0 +1,667 @@
+ $solution_pack_info) {
+ // @todo We should probably get the title of the solution pack from the
+ // systems table for consistency in the interface.
+ $solution_pack_name = $solution_pack_info['title'];
+ $objects = array_filter($solution_pack_info['objects']);
+ $form = drupal_get_form('islandora_solution_pack_form_' . $solution_pack_module, $solution_pack_module, $solution_pack_name, $objects);
+ $output .= drupal_render($form);
+ }
+ return $output;
+}
+
+/**
+ * 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(array $form, array &$form_state, $solution_pack_module, $solution_pack_name, $objects = array()) {
+ // The order is important in terms of severity of the status, where higher
+ // index indicates the status is more serious, this will be used to determine
+ // what messages get displayed to the user.
+ $ok_image = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array()));
+ $warning_image = theme_image(array('path' => 'misc/watchdog-warning.png', 'attributes' => array()));
+ $status_info = array(
+ 'up_to_date' => array(
+ 'solution_pack' => t('All required objects are installed and up-to-date.'),
+ 'image' => $ok_image,
+ 'button' => t("Force reinstall objects"),
+ ),
+ 'modified_datastream' => array(
+ 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'),
+ 'image' => $warning_image,
+ 'button' => t("Reinstall objects"),
+ ),
+ 'out_of_date' => array(
+ 'solution_pack' => t('Some objects must be reinstalled. See objects list for details.'),
+ 'image' => $warning_image,
+ 'button' => t("Reinstall objects"),
+ ),
+ '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"),
+ ),
+ );
+ $status_severities = array_keys($status_info);
+ $solution_pack_status_severity = array_search('up_to_date', $status_severities);
+ $table_rows = array();
+ foreach ($objects as $object) {
+ $object_status = islandora_check_object_status($object);
+ $object_status_info = $status_info[$object_status['status']];
+ $object_status_severity = array_search($object_status['status'], $status_severities);
+ // The solution pack status severity will be the highest severity of
+ // the objects.
+ $solution_pack_status_severity = max($solution_pack_status_severity, $object_status_severity);
+ $exists = $object_status['status'] != 'missing';
+ $label = $exists ? l($object->label, "islandora/object/{$object->id}") : $object->label;
+ $status_msg = "{$object_status_info['image']} {$object_status['status_friendly']}";
+ $table_rows[] = array($label, $object->id, $status_msg);
+ }
+ $solution_pack_status = $status_severities[$solution_pack_status_severity];
+ $solution_pack_status_info = $status_info[$solution_pack_status];
+ return array(
+ 'solution_pack' => array(
+ '#type' => 'fieldset',
+ '#collapsible' => FALSE,
+ '#collapsed' => FALSE,
+ '#attributes' => array('class' => array('islandora-solution-pack-fieldset')),
+ 'solution_pack_module' => array(
+ '#type' => 'value',
+ '#value' => $solution_pack_module,
+ ),
+ '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' => '
',
+ '#suffix' => '
',
+ ),
+ 'install_status' => array(
+ '#markup' => t('Object status: !image !status', array(
+ '!image' => $solution_pack_status_info['image'],
+ '!status' => $solution_pack_status_info['solution_pack'],
+ )),
+ '#prefix' => '',
+ '#suffix' => '
',
+ ),
+ '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')),
+ ),
+ ),
+ );
+}
+
+/**
+ * Submit handler for solution pack form.
+ *
+ * @param array $form
+ * The form submitted.
+ * @param array $form_state
+ * The state of the form submited.
+ */
+function islandora_solution_pack_form_submit(array $form, array &$form_state) {
+ $solution_pack_module = $form_state['values']['solution_pack_module'];
+ $objects = $form_state['values']['objects'];
+ $batch = array(
+ 'title' => t('Installing / Updating solution pack objects'),
+ 'file' => drupal_get_path('module', 'islandora') . '/includes/solution_packs.inc',
+ 'operations' => array(),
+ );
+ foreach ($objects as $object) {
+ $batch['operations'][] = array('islandora_solution_pack_batch_operation_reingest_object', array($object));
+ }
+ batch_set($batch);
+ // Hook to let solution pack objects be modified.
+ // 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);
+}
+
+/**
+ * Batch operation 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) {
+ $existing_object = islandora_object_load($object->id);
+ if ($existing_object) {
+ $deleted = islandora_delete_object($existing_object);
+ if (!$deleted) {
+ $object_link = l($existing_object->label, "islandora/object/{$existing_object->id}");
+ drupal_set_message(t('Failed to purge existing object !object_link.', array(
+ '!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);
+}
+
+/**
+ * Install the given solution pack.
+ *
+ * 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 string $module
+ * The name of the module that is calling this function in its
+ * 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_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('module', $module, $module);
+ $info_file = drupal_get_path('module', $module) . "/{$module}.info";
+ $info_array = drupal_parse_info_file($info_file);
+ $module_name = $info_array['name'];
+ $admin_link = l(t('Solution Pack admin'), 'admin/islandora/solution_packs');
+ $config_link = l(t('Islandora configuration'), 'admin/islandora/configure');
+ if (!islandora_describe_repository()) {
+ $msg = '@module: Did not install any objects. Could not connect to the ';
+ $msg .= 'repository. Please check the settings on the !config_link page ';
+ $msg .= 'and install the required objects manually on the !admin_link page.';
+ drupal_set_message(st($msg, array(
+ '@module' => $module_name,
+ '!config_link' => $config_link,
+ '@admin_link' => $admin_link,
+ )), 'error');
+ return;
+ }
+ $connection = islandora_get_tuque_connection();
+ $required_objects = module_invoke($module, 'islandora_required_objects', $connection);
+ dsm($required_objects);
+ $objects = $required_objects[$module]['objects'];
+ $status_messages = array(
+ 'up_to_date' => 'The object already exists and is up-to-date',
+ 'missing_datastream' => 'The object already exists but is missing a datastream. Please reinstall the object on the !admin_link page',
+ 'out_of_date' => 'The object already exists but is out-of-date. Please update the object on the !admin_link page',
+ 'modified_datastream' => 'The object already exists but datastreams are modified. Please reinstall the object on the !admin_link page',
+ );
+ foreach ($objects as $object) {
+ $query = $connection->api->a->findObjects('query', 'pid=' . $object->id);
+ $already_exists = !empty($query['results']);
+ $label = $object->label;
+ $object_link = l($label, "islandora/object/{$object->id}");
+ if ($already_exists) {
+ $object_status = islandora_check_object_status($object);
+ $status_msg = $status_messages[$object_status['status']];
+ drupal_set_message(st("@module: Did not install !object_link. $status_msg.", array(
+ '@module' => $module_name,
+ '!object_link' => $object_link,
+ '!admin_link' => $admin_link,
+ )), 'warning');
+ }
+ else {
+ $object = islandora_add_object($object);
+ if ($object) {
+ drupal_set_message(t('@module: Successfully installed. !object_link.', array(
+ '@module' => $module_name,
+ '!object_link' => $object_link,
+ )), 'status');
+ }
+ else {
+ drupal_set_message(t('@module: Failed to install. @label.', array(
+ '@module' => $module_name,
+ '@label' => $label,
+ )), 'warning');
+ }
+ }
+ }
+}
+
+/**
+ * Uninstalls the given solution pack.
+ *
+ * @param string $module
+ * The solution pack to uninstall.
+ *
+ * @todo Implement hook_modules_uninstalled instead of calling this function
+ * directly for each solution pack.
+ */
+function islandora_uninstall_solution_pack($module) {
+ module_load_include('module', 'islandora', 'islandora');
+ module_load_include('inc', 'islandora', 'includes/utilities');
+ module_load_include('module', $module, $module);
+ $config_link = l(t('Islandora configuration'), 'admin/islandora/configure');
+ $info_file = drupal_get_path('module', $module) . "/{$module}.info";
+ $info_array = drupal_parse_info_file($info_file);
+ $module_name = $info_array['name'];
+ if (!islandora_describe_repository()) {
+ $msg = '@module: Did not uninstall any objects. Could not connect to the ';
+ $msg .= 'repository. Please check the settings on the !config_link page ';
+ $msg .= 'and uninstall the required objects manually if necessary.';
+ drupal_set_message(st($msg, array(
+ '@module' => $module_name,
+ '!config_link' => $config_link,
+ )), 'error');
+ return;
+ }
+ $connection = islandora_get_tuque_connection();
+ $required_objects = module_invoke($module, 'islandora_required_objects', $connection);
+ $objects = $required_objects[$module]['objects'];
+ $filter = function($o) use($connection) {
+ $param = "pid={$o->id}";
+ $query = $connection->api->a->findObjects('query', $param);
+ return !empty($query['results']);
+ };
+ $existing_objects = array_filter($objects, $filter);
+ foreach ($existing_objects as $object) {
+ $msg = '@module: Did not remove !object_link. It may be used by other sites.';
+ $object_link = l($object->label, "islandora/object/{$object->id}");
+ drupal_set_message(st($msg, array(
+ '!object_link' => $object_link,
+ '@module' => $module_name,
+ )), 'warning');
+ }
+}
+
+/**
+ * Function to check the status of an object against an object model array.
+ *
+ * @param NewFedoraObject $object_definition
+ * A new fedora object that defines what the object should contain.
+ *
+ * @return string
+ * Returns one of the following values:
+ * up_to_date, missing, missing_datastream or out_of_date
+ * You can perform an appropriate action based on this value.
+ *
+ * @see islandora_solution_pack_form()
+ * @see islandora_install_solution_pack()
+ */
+function islandora_check_object_status(NewFedoraObject $object_definition) {
+ $existing_object = islandora_object_load($object_definition->id);
+ if (!$existing_object) {
+ return array('status' => 'missing', 'status_friendly' => t('Missing'));
+ }
+
+ $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';
+ };
+ $xml_datastreams = array_filter(iterator_to_array($object_definition), $is_xml_datastream);
+ $out_of_date_datastreams = array();
+ foreach ($xml_datastreams as $ds) {
+ $installed_version = islandora_get_islandora_datastream_version($existing_object, $ds->id);
+ $available_version = islandora_get_islandora_datastream_version($object_definition, $ds->id);
+ if ($available_version > $installed_version) {
+ $out_of_date_datastreams[] = $ds->id;
+ }
+ }
+
+ if (count($out_of_date_datastreams)) {
+ $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,
+ );
+ }
+
+ // This is a pretty heavy function, but I'm not sure a better way. If we have
+ // performance trouble, we should maybe remove this.
+ $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 {
+ $object_definition_hash = md5($ds->content);
+ $object_actual_hash = md5($existing_object[$ds->id]->content);
+ if ($object_definition_hash != $object_actual_hash) {
+ $modified_datastreams[] = $ds->id;;
+ }
+ }
+ }
+ if (count($modified_datastreams)) {
+ $status_friendly = format_plural(count($modified_datastreams), 'Modified Datastream: %dsids.', 'Modified Datastreams: %dsids.', array('%dsids' => implode(', ', $modified_datastreams)));
+ return array(
+ 'status' => 'modified_datastream',
+ 'data' => $modified_datastreams,
+ 'status_friendly' => $status_friendly,
+ );
+ }
+
+ // 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
+ * @{
+ * Helper functions to include viewers for solution packs.
+ */
+
+/**
+ * A form construct to create a viewer selection table.
+ *
+ * The list of selectable viewers is limited by the $mimetype and the $model
+ * parameters. When neither are given all defined viewers are listed. If only
+ * $mimetype is given only viewers that support that mimetype will be listed,
+ * the same goes for the $model parameter. If both are given, than any viewer
+ * that supports either the give $mimetype or $model will be listed.
+ *
+ * @param string $variable_id
+ * The ID of the Drupal variable to save the viewer settings in
+ * @param string $mimetype
+ * The table will be populated with viewers supporting this mimetype
+ * @param string $model
+ * The table will be populated with viewers supporting this content model
+ *
+ * @return array
+ * A form api array which defines a themed table to select a viewer.
+ */
+function islandora_viewers_form($variable_id = NULL, $mimetype = NULL, $model = NULL) {
+ $form = array();
+ $viewers = islandora_get_viewers($mimetype, $model);
+ if (!empty($viewers)) {
+ $no_viewer = array();
+ $no_viewer['none'] = array(
+ 'label' => t('None'),
+ 'description' => t("Don't use a viewer for this solution pack."),
+ );
+ $viewers = array_merge_recursive($no_viewer, $viewers);
+ }
+ $viewers_config = variable_get($variable_id, array());
+ $form['viewers'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Viewers'),
+ '#collapsible' => FALSE,
+ '#collapsed' => FALSE,
+ );
+
+ if (!empty($viewers)) {
+ $form['viewers'][$variable_id] = array(
+ '#type' => 'item',
+ '#title' => t('Select a viewer'),
+ '#description' => t('Preferred viewer for your solution pack. These may be provided by third-party modules.'),
+ '#tree' => TRUE,
+ '#theme' => 'islandora_viewers_table',
+ );
+
+ foreach ($viewers as $name => $profile) {
+ $options[$name] = '';
+ $form['viewers'][$variable_id]['name'][$name] = array(
+ '#type' => 'hidden',
+ '#value' => $name,
+ );
+ $form['viewers'][$variable_id]['label'][$name] = array(
+ '#type' => 'item',
+ '#markup' => $profile['label'],
+ );
+ $form['viewers'][$variable_id]['description'][$name] = array(
+ '#type' => 'item',
+ '#markup' => $profile['description'],
+ );
+ $form['viewers'][$variable_id]['configuration'][$name] = array(
+ '#type' => 'item',
+ '#markup' => (isset($profile['configuration']) AND $profile['configuration'] != '') ? l(t('configure'), $profile['configuration']) : '',
+ );
+ }
+ $form['viewers'][$variable_id]['default'] = array(
+ '#type' => 'radios',
+ '#options' => isset($options) ? $options : array(),
+ '#default_value' => !empty($viewers_config) ? $viewers_config['default'] : 'none',
+ );
+ }
+ else {
+ $form['viewers'][$variable_id . '_no_viewers'] = array(
+ '#markup' => t('No viewers detected.'),
+ );
+ variable_del($variable_id);
+ }
+ return $form;
+}
+
+/**
+ * Returns all defined viewers.
+ *
+ * The list of selectable viewers is limited by the $mimetype and the
+ * $content_model parameters. When neither are given all defined viewers are
+ * listed. If only $mimetype is given only viewers that support that mimetype
+ * will be listed, the same goes for the $content_model parameter. If both are
+ * given, than any viewer that supports either the give $mimetype or $model will
+ * be listed.
+ *
+ * @param string $mimetype
+ * Specify a mimetype to return only viewers that support this certain
+ * mimetype.
+ * @param string $content_model
+ * Specify a content model to return only viewers that support the content
+ * model.
+ *
+ * @return array
+ * Viewer definitions, or FALSE if none are found.
+ */
+function islandora_get_viewers($mimetype = NULL, $content_model = NULL) {
+ $viewers = array();
+ $defined_viewers = module_invoke_all('islandora_viewer_info');
+ // Filter viewers by MIME type.
+ foreach ($defined_viewers as $key => $value) {
+ $value['mimetype'] = isset($value['mimetype']) ? $value['mimetype'] : array();
+ $value['model'] = isset($value['model']) ? $value['model'] : array();
+ if (in_array($mimetype, $value['mimetype']) OR in_array($content_model, $value['model'])) {
+ $viewers[$key] = $value;
+ }
+ }
+ if (!empty($viewers)) {
+ return $viewers;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements theme_hook().
+ */
+function theme_islandora_viewers_table($variables) {
+ $form = $variables['form'];
+ $rows = array();
+ foreach ($form['name'] as $key => $element) {
+ if (is_array($element) && element_child($key)) {
+ $row = array();
+ $row[] = array('data' => drupal_render($form['default'][$key]));
+ $row[] = array('data' => drupal_render($form['label'][$key]));
+ $row[] = array('data' => drupal_render($form['description'][$key]));
+ $row[] = array('data' => drupal_render($form['configuration'][$key]));
+ $rows[] = array('data' => $row);
+ }
+ }
+ $header = array();
+ $header[] = array('data' => t('Default'));
+ $header[] = array('data' => t('Label'));
+ $header[] = array('data' => t('Description'));
+ $header[] = array('data' => t('Configuration'));
+
+ $output = '';
+ $output .= theme('table', array(
+ 'header' => $header,
+ 'rows' => $rows,
+ 'attributes' => array('id' => 'islandora-viewers-table'),
+ ));
+ $output .= drupal_render_children($form);
+ return $output;
+}
+
+/**
+ * Gather information and return a rendered viewer.
+ *
+ * @param array/string $params
+ * Array or string with data the module needs in order to render a full viewer
+ * @param string $variable_id
+ * The id of the Drupal variable the viewer settings are saved in
+ * @param FedoraObject $fedora_object
+ * The tuque object representing the fedora object being displayed
+ *
+ * @return string
+ * The callback to the viewer module. Returns a rendered viewer. Returns FALSE
+ * if no viewer is set.
+ */
+function islandora_get_viewer($params = NULL, $variable_id = NULL, $fedora_object = NULL) {
+ $settings = variable_get($variable_id, array());
+ if (!empty($settings) AND $settings['default'] !== 'none') {
+ $viewer_id = islandora_get_viewer_id($variable_id);
+ if ($viewer_id AND $params !== NULL) {
+ $callback = islandora_get_viewer_callback($viewer_id);
+ return $callback($params, $fedora_object);
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Get id of the enabled viewer.
+ *
+ * @param string $variable_id
+ * The ID of the Drupal variable the viewer settings are saved in
+ *
+ * @return string
+ * The enabled viewer id. Returns FALSE if no viewer config is set.
+ */
+function islandora_get_viewer_id($variable_id) {
+ $viewers_config = variable_get($variable_id, array());
+ if (!empty($viewers_config)) {
+ return $viewers_config['default'];
+ }
+ return FALSE;
+}
+
+/**
+ * Get callback function for a viewer.
+ *
+ * @param string $viewer_id
+ * The ID of a viewer.
+ *
+ * @return string
+ * The callback function as a string as defined by the viewer.
+ */
+function islandora_get_viewer_callback($viewer_id = NULL) {
+ if ($viewer_id !== NULL) {
+ $viewers = module_invoke_all('islandora_viewer_info');
+ if (isset($viewers[$viewer_id]['callback'])) {
+ return $viewers[$viewer_id]['callback'];
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * @} End of "defgroup viewer-functions".
+ */
diff --git a/islandora.module b/islandora.module
index 32410596..de535a8b 100644
--- a/islandora.module
+++ b/islandora.module
@@ -570,7 +570,10 @@ function islandora_object_manage_access_callback($perms, $object = NULL) {
* The HTML repersentation of the manage object page.
*/
function islandora_manage_overview_object(AbstractObject $object) {
+ module_load_include('inc', 'islandora', 'includes/breadcrumb');
module_load_include('inc', 'islandora', 'includes/utilities');
+ drupal_set_title($object->label);
+ drupal_set_breadcrumb(islandora_get_breadcrumbs($object));
$output = array();
foreach (islandora_build_hook_list(ISLANDORA_OVERVIEW_HOOK, $object->models) as $hook) {
$temp = module_invoke_all($hook, $object);
diff --git a/islandora.module~ b/islandora.module~
new file mode 100644
index 00000000..d5287286
--- /dev/null
+++ b/islandora.module~
@@ -0,0 +1,930 @@
+.
+ */
+
+// Common datastreams.
+define('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');
+
+// Hooks.
+define('ISLANDORA_VIEW_HOOK', 'islandora_view_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');
+
+// @todo Add Documentation.
+define('ISLANDORA_OBJECT_INGESTED_HOOK', 'islandora_object_ingested');
+define('ISLANDORA_OBJECT_MODIFIED_HOOK', 'islandora_object_modified');
+define('ISLANDORA_OBJECT_PURGED_HOOK', 'islandora_object_purged');
+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');
+
+/**
+ * Implements hook_menu().
+ *
+ * We need some standard entry points so we can have consistent urls for
+ * different Object actions
+ */
+function islandora_menu() {
+ $items = array();
+ $items['admin/islandora'] = array(
+ 'title' => 'Islandora',
+ 'description' => "Configure settings associated with Islandora.",
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_NORMAL_ITEM,
+ );
+ $items['admin/islandora/configure'] = array(
+ 'title' => 'Configuration',
+ 'description' => 'Configure settings for Islandora.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('islandora_repository_admin'),
+ 'file' => 'includes/admin.form.inc',
+ 'access arguments' => array('administer site configuration'),
+ 'type' => MENU_NORMAL_ITEM,
+ 'weight' => -1,
+ );
+ $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),
+ 'file' => 'includes/solution_packs.inc',
+ 'type' => MENU_NORMAL_ITEM,
+ );
+ $items['islandora'] = array(
+ 'title' => 'Islandora Repository',
+ 'page callback' => 'islandora_view_default_object',
+ 'type' => MENU_NORMAL_ITEM,
+ 'access arguments' => array(FEDORA_VIEW_OBJECTS),
+ );
+ $items['islandora/object/%islandora_object'] = array(
+ 'title' => 'Repository',
+ 'page callback' => 'islandora_view_object',
+ 'page arguments' => array(2),
+ 'type' => MENU_NORMAL_ITEM,
+ 'access callback' => 'islandora_object_access_callback',
+ 'access arguments' => array(FEDORA_VIEW_OBJECTS, 2),
+ );
+ $items['islandora/object/%islandora_object/view'] = array(
+ 'title' => 'View',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -1,
+ );
+ $items['islandora/object/%islandora_object/view/default'] = array(
+ 'title' => 'View',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -1,
+ );
+ $items['islandora/object/%islandora_object/manage'] = array(
+ 'title' => 'Manage',
+ 'page callback' => 'islandora_manage_overview_object',
+ 'page arguments' => array(2),
+ 'type' => MENU_LOCAL_TASK,
+ 'access callback' => 'islandora_object_manage_access_callback',
+ 'access arguments' => array(
+ array(
+ FEDORA_MANAGE_PROPERTIES,
+ FEDORA_METADATA_EDIT,
+ FEDORA_ADD_DS,
+ FEDORA_PURGE,
+ ), 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,
+ 'page callback' => 'islandora_edit_object',
+ 'page arguments' => array(2),
+ 'access callback' => 'islandora_object_manage_access_callback',
+ 'access arguments' => array(
+ array(
+ FEDORA_METADATA_EDIT,
+ FEDORA_ADD_DS,
+ FEDORA_PURGE,
+ ), 2),
+ 'weight' => -10,
+ );
+
+ $items['islandora/object/%islandora_object/manage/properties'] = array(
+ 'title' => 'Properties',
+ 'page callback' => 'drupal_get_form',
+ 'file' => 'includes/object_properties.form.inc',
+ '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),
+ 'weight' => -5,
+ );
+ $items['islandora/object/%islandora_object/delete'] = array(
+ 'title' => 'Delete object',
+ 'file' => 'includes/delete_object.form.inc',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('islandora_delete_object_form', 2),
+ 'type' => MENU_CALLBACK,
+ 'access callback' => 'islandora_object_access_callback',
+ 'access arguments' => array(FEDORA_PURGE, 2),
+ );
+ $items['islandora/object/%islandora_object/manage/datastreams/add'] = array(
+ 'title' => 'Add a datastream',
+ 'file' => 'includes/add_datastream.form.inc',
+ 'page callback' => 'drupal_get_form',
+ '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),
+ );
+ $items['islandora/object/%islandora_object/manage/datastreams/add/autocomplete'] = array(
+ 'file' => 'includes/add_datastream.form.inc',
+ 'page callback' => 'islandora_add_datastream_form_autocomplete_callback',
+ 'page arguments' => array(2),
+ 'type' => MENU_CALLBACK,
+ 'access callback' => 'islandora_object_access_callback',
+ 'access arguments' => array(FEDORA_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),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'includes/datastream.inc',
+ 'access callback' => 'islandora_object_datastream_access_callback',
+ 'access arguments' => array(FEDORA_VIEW_OBJECTS, 2, 4),
+ 'load arguments' => array(2),
+ );
+ // 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',
+ 'load arguments' => array('%map'),
+ 'access callback' => 'islandora_object_datastream_tokened_access_callback',
+ 'access arguments' => array(FEDORA_VIEW_OBJECTS, 2, 4),
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+ $items['islandora/object/%islandora_object/datastream/%islandora_datastream/download'] = array(
+ 'title' => 'Download datastream',
+ 'page callback' => 'islandora_download_datastream',
+ 'page arguments' => array(4),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'includes/datastream.inc',
+ 'access callback' => 'islandora_object_datastream_access_callback',
+ 'access arguments' => array(FEDORA_VIEW_OBJECTS, 2, 4),
+ 'load arguments' => array(2),
+ );
+ $items['islandora/object/%islandora_object/datastream/%islandora_datastream/edit'] = array(
+ 'title' => 'Edit datastream',
+ 'page callback' => 'islandora_edit_datastream',
+ 'page arguments' => array(4),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'includes/datastream.inc',
+ 'access callback' => 'islandora_object_datastream_access_callback',
+ 'access arguments' => array(FEDORA_METADATA_EDIT, 2, 4),
+ 'load arguments' => array(2),
+ );
+ $items['islandora/object/%islandora_object/datastream/%islandora_datastream/delete'] = array(
+ 'title' => 'Delete data stream',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('islandora_delete_datastream_form', 4),
+ 'file' => 'includes/delete_datastream.form.inc',
+ 'type' => MENU_CALLBACK,
+ 'access callback' => 'islandora_object_datastream_access_callback',
+ 'access arguments' => array(FEDORA_PURGE, 2, 4),
+ 'load arguments' => array(2),
+ );
+ $items['islandora/ingest'] = array(
+ 'title' => 'Add an Object',
+ 'page callback' => 'islandora_ingest_callback',
+ 'file' => 'includes/ingest.menu.inc',
+ 'type' => MENU_SUGGESTED_ITEM,
+ 'access arguments' => array(FEDORA_INGEST),
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_admin_paths().
+ */
+function islandora_admin_paths() {
+ $paths = array();
+ $paths['islandora/object/*/manage*'] = TRUE;
+ $paths['islandora/object/*/delete'] = TRUE;
+ $paths['islandora/object/*/datastream/*/edit'] = TRUE;
+ return $paths;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function islandora_theme() {
+ return array(
+ // Default object template.
+ 'islandora_default' => array(
+ 'file' => 'theme/theme.inc',
+ 'template' => 'theme/islandora-object',
+ 'variables' => array('islandora_object' => NULL),
+ ),
+ // Default edit page.
+ 'islandora_default_edit' => array(
+ 'file' => 'theme/theme.inc',
+ 'template' => 'theme/islandora-object-edit',
+ 'variables' => array('islandora_object' => NULL),
+ ),
+ // Admin table for solution pack viewer selection.
+ 'islandora_viewers_table' => array(
+ 'file' => 'includes/solution_packs.inc',
+ 'render element' => 'form',
+ ),
+ );
+}
+
+/**
+ * Implements hook_permission().
+ */
+function islandora_permission() {
+ return array(
+ FEDORA_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(
+ '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(
+ 'title' => t('Edit metadata'),
+ 'description' => t('Edit metadata for objects in the repository.'),
+ ),
+ FEDORA_INGEST => array(
+ 'title' => t('Create new repository objects'),
+ 'description' => t('Create new objects in the repository.'),
+ ),
+ FEDORA_PURGE => array(
+ 'title' => t('Permanently remove objects from the repository'),
+ 'description' => t('Permanently remove objects from the repository.'),
+ ),
+ FEDORA_MANAGE_PROPERTIES => array(
+ 'title' => t('Manage object properties'),
+ 'description' => t('Modify object labels, owner IDs, and states.'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_forms().
+ */
+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',
+ );
+ }
+ return $forms;
+}
+
+/**
+ * Checks whether the user can access the given object.
+ *
+ * Checks for object existance, accessiblitly, namespace permissions,
+ * and user permissions
+ *
+ * @param string $perm
+ * User permission to test for.
+ * @param FedoraObject $object
+ * The object to test, if NULL given the object doesn't exist or is
+ * inaccessible.
+ *
+ * @return bool
+ * TRUE if the user is allowed to access this object, FALSE otherwise.
+ */
+function islandora_object_access_callback($perm, $object = NULL) {
+ 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);
+}
+
+/**
+ * Checks whether the user can access the given object and datastream.
+ *
+ * Checks for object existance, accessiblitly, namespace permissions,
+ * and user permissions
+ *
+ * @param string $perm
+ * The user permission to test for.
+ * @param FedoraObject $object
+ * The object to test, if NULL given the object doesn't exist or is
+ * inaccessible.
+ * @param FedoraDatastream $datastream
+ * The datastream to test, if NULL given the datastream doesn't exist
+ * or is inaccessible.
+ * @param StdObject $account
+ * The account to test permissions as or NULL for current user.
+ *
+ * @return bool
+ * TRUE if the user is allowed to access this object, FALSE otherwise.
+ */
+function islandora_object_datastream_access_callback($perm, $object = NULL, $datastream = NULL, $account = NULL) {
+ module_load_include('inc', 'islandora', 'includes/utilities');
+ return user_access($perm, $account) && is_object($object) && islandora_namespace_accessible($object->id) && is_object($datastream);
+}
+
+/**
+ * Checks whether the user can access the given object and datastream.
+ *
+ * This function will validate and use a token if present in the GET parameters.
+ *
+ * Checks for object existance, accessiblitly, namespace permissions,
+ * and user permissions
+ *
+ * @see islandora_object_datastream_tokened_access_callback()
+ */
+function islandora_object_datastream_tokened_access_callback($perm, $object = NULL, $datastream = NULL) {
+ module_load_include('inc', 'islandora', 'includes/utilities');
+
+ $token_account = NULL;
+ $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
+
+ if ($token) {
+ $user = islandora_validate_object_token($object->id, $datastream->id, $token);
+ if ($user) {
+ $token_account = user_load($user->uid);
+ }
+ }
+
+ return islandora_object_datastream_access_callback($perm, $object, $datastream, $token_account);
+}
+
+/**
+ * Checks whether the user can access the given object's manage tab.
+ *
+ * Checks for object existance, accessiblitly, namespace permissions,
+ * and user permissions
+ *
+ * @param array $perms
+ * Array of user permission to test for.
+ * @param FedoraObject $object
+ * The object to test, if NULL given the object doesn't exist or is
+ * inaccessible.
+ *
+ * @return bool
+ * TRUE if the user is allowed to access this object, FALSE otherwise.
+ */
+function islandora_object_manage_access_callback($perms, $object = NULL) {
+ module_load_include('inc', 'islandora', 'includes/utilities');
+
+ if (!$object && !islandora_describe_repository()) {
+ islandora_display_repository_inaccessible_message();
+ return FALSE;
+ }
+
+ $has_access = FALSE;
+ for ($i = 0; $i < count($perms) && !$has_access; $i++) {
+ $has_access = $has_access || user_access($perms[$i]);
+ }
+
+ return $has_access && is_object($object) && islandora_namespace_accessible($object->id);
+}
+
+/**
+ * Renders the given objects manage overview page.
+ *
+ * @param FedoraObject $object
+ * The object to manage.
+ *
+ * @return string
+ * The HTML repersentation of the manage object page.
+ */
+function islandora_manage_overview_object(FedoraObject $object) {
+ module_load_include('inc', 'islandora', 'includes/utilities');
+ $output = array();
+ foreach (islandora_build_hook_list(ISLANDORA_OVERVIEW_HOOK, $object->models) as $hook) {
+ $temp = module_invoke_all($hook, $object);
+ if (!empty($temp)) {
+ $output = array_merge_recursive($output, $temp);
+ }
+ }
+ if (empty($output)) {
+ // Add in the default, if we did not get any results.
+ $output = islandora_default_islandora_manage_overview_object($object);
+ }
+ arsort($output);
+ drupal_alter(ISLANDORA_OVERVIEW_HOOK, $object, $output);
+ return implode('', $output);
+}
+
+/**
+ * Renders the default manage object page for the given object.
+ *
+ * @param FedoraObject $object
+ * The object used to render the manage object page.
+ *
+ * @return array
+ * The default rendering of the object manage page, indexed at
+ * 'Default Edit output'.
+ */
+function islandora_default_islandora_manage_overview_object(FedoraObject $object) {
+ $output = theme('islandora_default_overview', array('islandora_object' => $object));
+ return array('Default overview output' => $output);
+}
+
+/**
+ * Renders the given objects manage page.
+ *
+ * Its possible to modify the output of this function if 'ISLANDORA_EDIT_HOOK'
+ * is implemented in other modules.
+ *
+ * If no modules implement 'ISLANDORA_EDIT_HOOK' then this function returns the
+ * default manage view.
+ *
+ * @param FedoraObject $object
+ * The object to manage.
+ *
+ * @return string
+ * The HTML repersentation of the manage object page.
+ */
+function islandora_edit_object(FedoraObject $object) {
+ module_load_include('inc', 'islandora', 'includes/breadcrumb');
+ module_load_include('inc', 'islandora', 'includes/utilities');
+ drupal_set_title($object->label);
+ drupal_set_breadcrumb(islandora_get_breadcrumbs($object));
+ $output = array();
+ foreach (islandora_build_hook_list(ISLANDORA_EDIT_HOOK, $object->models) as $hook) {
+ $temp = module_invoke_all($hook, $object);
+ if (!empty($temp)) {
+ $output = array_merge_recursive($output, $temp);
+ }
+ }
+ if (empty($output)) {
+ // Add in the default, if we did not get any results.
+ $output = islandora_default_islandora_edit_object($object);
+ }
+ arsort($output);
+ drupal_alter(ISLANDORA_EDIT_HOOK, $object, $output);
+ return implode('', $output);
+}
+
+/**
+ * Renders the default manage object page for the given object.
+ *
+ * @param FedoraObject $object
+ * The object used to render the manage object page.
+ *
+ * @return array
+ * The default rendering of the object manage page, indexed at
+ * 'Default Edit output'.
+ */
+function islandora_default_islandora_edit_object(FedoraObject $object) {
+ $output = theme('islandora_default_edit', array('islandora_object' => $object));
+ return array('Default Edit output' => $output);
+}
+
+/**
+ * Page callback for the path "islandora".
+ *
+ * Redirects to the view of the object indicated by the Drupal variable
+ * 'islandora_repository_pid'.
+ */
+function islandora_view_default_object() {
+ $pid = variable_get('islandora_repository_pid', 'islandora:root');
+ drupal_goto("islandora/object/$pid");
+}
+
+/**
+ * Renders the default view object page for the given object.
+ *
+ * Modules should implement ISLANDORA_VIEW_HOOK for the Fedora Content
+ * models that their modules want to provide a view for.
+ *
+ * If no modules implement the hook then the default view object page
+ * will be rendered.
+ *
+ * @param FedoraObject $object
+ * The object to view.
+ *
+ * @return string
+ * The html repersentation of this object.
+ */
+function islandora_view_object(FedoraObject $object) {
+ module_load_include('inc', 'islandora', 'includes/breadcrumb');
+ module_load_include('inc', 'islandora', 'includes/utilities');
+ 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'];
+ $output = array();
+ foreach (islandora_build_hook_list(ISLANDORA_VIEW_HOOK, $object->models) as $hook) {
+ // @todo Remove page number and size from this hook, implementers of the
+ // hook should use drupal page handling directly.
+ $temp = module_invoke_all($hook, $object, $page_number, $page_size);
+ if (!empty($temp)) {
+ $output = array_merge_recursive($output, $temp);
+ }
+ }
+ if (empty($output)) {
+ // No results, use the default view.
+ $output = islandora_default_islandora_view_object($object);
+ }
+ arsort($output);
+ drupal_alter(ISLANDORA_VIEW_HOOK, $object, $output);
+ return implode('', $output);
+}
+
+/**
+ * Renders the default view object page for the given object.
+ *
+ * @param FedoraObject $object
+ * The object used to render the view object page.
+ *
+ * @return array
+ * The default rendering of the object view page, indexed at 'Default output'.
+ */
+function islandora_default_islandora_view_object($object) {
+ $output = theme('islandora_default', array('islandora_object' => $object));
+ return array('Default output' => $output);
+}
+
+/**
+ * Just a wrapper around fetchings the IslandoraTuque object.
+ *
+ * Includes some very basic error logging.
+ *
+ * @param object $user
+ * The user to connect as.
+ * @param string $url
+ * The URL to connect to.
+ *
+ * @return IslandoraTuque
+ * A IslandoraTuque instance
+ */
+function islandora_get_tuque_connection($user = NULL, $url = NULL) {
+ $tuque = &drupal_static(__FUNCTION__);
+ if (!$tuque) {
+ if (IslandoraTuque::exists()) {
+ try {
+ $tuque = new IslandoraTuque($user, $url);
+ }
+ catch (Exception $e) {
+ drupal_set_message(t('Unable to connect to the repository %e', array('%e' => $e)), 'error');
+ }
+ }
+ else {
+ return NULL;
+ }
+ }
+ return $tuque;
+}
+
+/**
+ * Loads the object from the given ID if possible.
+ *
+ * Often used to get a connection and return an object for the one specified in
+ * the menu path as '%islandora_object'.
+ *
+ * @param string $object_id
+ * The pid of an object in the menu path identified by '%islandora_object'.
+ *
+ * @return FedoraObject
+ * If the given object id exists in the repository then this returns a
+ * FedoraObject.
+ * If no object was found it returns FALSE which triggers
+ * drupal_page_not_found().
+ * If the object was inaccessible then NULL is returned, and the
+ * access callback is expected to catch that case, triggering
+ * drupal_access_denied().
+ */
+function islandora_object_load($object_id) {
+ $tuque = islandora_get_tuque_connection();
+ if ($tuque) {
+ try {
+ return $tuque->repository->getObject(urldecode($object_id));
+ }
+ catch (Exception $e) {
+ if ($e->getCode() == '404') {
+ return FALSE;
+ }
+ else {
+ return NULL;
+ }
+ }
+ }
+ else {
+ IslandoraTuque::getError();
+ }
+ // Assuming access denied in all other cases for now.
+ return NULL;
+}
+
+/**
+ * Load the the object using a token if passed as a GET parameter.
+ *
+ * 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/authtokens');
+ $token_user = islandora_validate_object_token($object_id, $map[4], $token);
+ $token_user = $token_user ? user_load($token_user->uid) : NULL;
+ islandora_get_tuque_connection($token_user);
+ }
+ }
+ return islandora_object_load($object_id);
+}
+
+/**
+ * Fetches a datastream object.
+ *
+ * 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]);
+}
+
+/**
+ * Fetches a datastream object.
+ *
+ * A helper function to get an datastream specified as '%islandora_datastream'
+ * for the object specified in the menu path as '%islandora_object'.
+ *
+ * Its up to the access callbacks and menu callbacks to trigger
+ * drupal_access_denied() when appropriate.
+ *
+ * @param string $datastream_id
+ * The DSID of the datastream specified as '%islandora_datastream' to fetch
+ * from the given object in the menu path identified by '%islandora_object'.
+ *
+ * @param mixed $object_id
+ * 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
+ * If the given datastream ID exists then this returns a FedoraDatastream
+ * object, otherwise it returns NULL which triggers drupal_page_not_found().
+ */
+function islandora_datastream_load($datastream_id, $object_id) {
+ $object = is_object($object_id) ? $object_id : islandora_object_load($object_id);
+ if (!$object) {
+ return NULL;
+ }
+ return $object[$datastream_id];
+}
+
+/**
+ * Fetches the given datastream version from its datastream.
+ *
+ * Content model, collection view and collection policy datastreams may now
+ * optionally define a version number in their top-level XML element as an
+ * attribute, as in:
+ * content);
+ }
+ elseif (!empty($datastream_file)) {
+ $doc = simplexml_load_file($datastream_file);
+ }
+
+ if (!empty($doc) && $version = (int) $doc->attributes()->version) {
+ $return = $version;
+ }
+
+ return $return;
+}
+
+/**
+ * Implements hook_islandora_required_objects().
+ */
+function islandora_islandora_required_objects(IslandoraTuque $connection) {
+ $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(
+ 'islandora' => array(
+ 'title' => 'Islandora',
+ 'objects' => array(
+ $root_collection,
+ ),
+ ),
+ );
+}
+
+/**
+ * Implements islandora_undeleteable_datastreams().
+ */
+function islandora_islandora_undeletable_datastreams(array $models) {
+ return array('DC');
+}
+
+/**
+ * Ingest the given object.
+ *
+ * @param NewFedoraObject $object
+ * An ingestable FedoraObject.
+ *
+ * @return FedoraObject
+ * The ingested FedoraObject.
+ */
+function islandora_add_object(NewFedoraObject &$object) {
+ return $object->repository->ingestObject($object);
+}
+
+/**
+ * Delete's or purges the given object.
+ *
+ * @param FedoraObject $object
+ * An object to delete.
+ *
+ * @return bool
+ * The ingested FedoraObject, after running the pre/post ingest hooks.
+ */
+function islandora_delete_object(FedoraObject &$object) {
+ return $object->repository->purgeObject($object->id);
+}
+
+/**
+ * Delete's or purges the given datastream.
+ *
+ * @throws Exception
+ * Which types are undefined, but more than likely because of the hooks
+ * there will be several kinds.
+ *
+ * @param FedoraDatastream $datastream
+ * The datastream to delete.
+ *
+ * @return bool
+ * TRUE if successful, FALSE otherwise.
+ */
+function islandora_delete_datastream(FedoraDatastream &$datastream) {
+ $object = $datastream->parent;
+ return $object->purgeDatastream($datastream->id);
+}
+
+/**
+ * Implements hook_cron().
+ */
+function islandora_cron() {
+ module_load_include('inc', 'islandora', 'includes/authtokens');
+ islandora_remove_expired_tokens();
+}
+
+/**
+ * Implements hook_entity_info().
+ *
+ * Some boiler-plate for Tokens (the module, not our authentication work-
+ * around).
+ */
+function islandora_entity_info() {
+ $entities = array();
+
+ $entities['islandora_object'] = array(
+ 'label' => t('Islandora Object'),
+ 'controller class' => 'IslandoraObjectEntityController',
+ 'fieldable' => FALSE,
+ 'entity keys' => array(
+ 'id' => 'id',
+ 'label' => 'label',
+ ),
+ );
+
+ return $entities;
+}
+
+/**
+ * Implements hook_entity_property_info().
+ *
+ * Details the tokens which will be available on the given object.
+ */
+function islandora_entity_property_info() {
+ $info = array();
+
+ $p = &$info['islandora_object']['properties'];
+
+ $p['id'] = array(
+ 'type' => 'text',
+ 'label' => t('ID'),
+ 'description' => t('The identifier of the object.'),
+ );
+ $p['label'] = array(
+ 'type' => 'text',
+ 'label' => t('Object Label'),
+ 'description' => t('The label of the object.'),
+ );
+ $p['owner'] = array(
+ 'type' => 'text',
+ 'label' => t('Object Owner'),
+ 'description' => t('The name of the owner of the object.'),
+ );
+ $p['state'] = array(
+ 'type' => 'text',
+ 'label' => t('Object State'),
+ 'description' => t('An initial representing the state of the object.'),
+ );
+ $p['models'] = array(
+ 'type' => 'list',
+ 'label' => t('Content Models'),
+ 'description' => t('The list of content models which the object has.'),
+ );
+
+ return $info;
+}