. */ // Common datastreams define('DS_COMP_STREAM', 'DS-COMPOSITE-MODEL'); // Permissions define('FEDORA_VIEW', 'view fedora repository'); 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_MODIFY_STATE', 'modify fedora state'); define('FEDORA_MANAGE', 'manage fedora items'); // Hooks define('ISLANDORA_VIEW_HOOK', 'islandora_view_object'); define('ISLANDORA_EDIT_HOOK', 'islandora_edit_object'); define('ISLANDORA_PRE_INGEST_HOOK', 'islandora_ingest_pre_ingest'); define('ISLANDORA_POST_INGEST_HOOK', 'islandora_ingest_post_ingest'); define('ISLANDORA_PRE_PURGE_OBJECT_HOOK', 'islandora_pre_purge_object'); define('ISLANDORA_POST_PURGE_OBJECT_HOOK', 'islandora_post_purge_object'); define('ISLANDORA_INGEST_STEP_HOOK', 'islandora_ingest_steps'); define('ISLANDORA_PRE_PURGE_DATASTREAM_HOOK', 'islandora_pre_purge_datastream'); define('ISLANDORA_POST_PURGE_DATASTREAM_HOOK', 'islandora_post_purge_datastream'); define('ISLANDORA_EDIT_DATASTREAM_HOOK', 'islandora_edit_datastream'); /** * 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' => 'admin/islandora.admin.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), ); $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, 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_edit_object', 'page arguments' => array(2), 'type' => MENU_LOCAL_TASK, 'access callback' => 'islandora_object_access_callback', 'access arguments' => array(FEDORA_MODIFY_STATE, 2), ); $items['islandora/object/%islandora_object/manage/datastreams'] = array( 'title' => 'Datastreams', 'type' => MENU_DEFAULT_LOCAL_TASK, '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_MODIFY_STATE, 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, 2, 4), 'load arguments' => array(2), ); $items['islandora/object/%islandora_object/datastream/%islandora_datastream/view'] = array( 'title' => 'View datastream', '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, 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/islandora.theme.inc', 'template' => 'theme/islandora-object', 'variables' => array('islandora_object' => NULL), ), // default edit page 'islandora_default_edit' => array( 'file' => 'theme/islandora.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 => array( 'title' => t('View repository objects and datastreams'), 'description' => t('View objects in the repository and their associated datastreams. 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_MODIFY_STATE => array( 'title' => t('Change repository object states'), 'description' => t('Change the state of objects in the repository (e.g. from Active to Inactive).') ), FEDORA_MANAGE => array( 'title' => t('View object management tabs'), 'description' => t('View tabs that provide object management functions.') ) ); } /** * 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 with the given * permission. * * Checks for object existance, accessiblitly, namespace permissions, * and user permissions * * @see islandora_object_load() To find potential solutions to enable * page not found errors. * * @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. * * @return boolean * 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'); return user_access($perm) && is_object($object) && islandora_namespace_accessible($object->id); } /** * Checks whether the user can access the given object and datastream with * the given permission. * * Checks for object existance, accessiblitly, namespace permissions, * and user permissions * * @see islandora_object_load() To find potential solutions to enable page * not found errors. * * @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. * * @return boolean * TRUE if the user is allowed to access this object, FALSE otherwise. */ function islandora_object_datastream_access_callback($perm, $object = NULL, $datastream = NULL) { module_load_include('inc', 'islandora', 'includes/utilities'); return user_access($perm) && is_object($object) && islandora_namespace_accessible($object->id) && is_object($datastream); } /** * 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/utilities'); $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_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, with some very * basic error logging. * * @return IslandoraTuque * A IslandoraTuque instance */ function islandora_get_tuque_connection() { module_load_include('inc', 'islandora', 'includes/IslandoraTuque'); $tuque = &drupal_static(__FUNCTION__); if (!$tuque) { if (IslandoraTuque::exists()) { try { $tuque = new IslandoraTuque(); } catch (Exception $e) { drupal_set_message(t('Unable to connect to the repository %e', array('%e' => $e)), 'error'); } } else { return NULL; } } return $tuque; } /** * A helper function to get a connection and return an object for objects * 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 { $object = $tuque->repository->getObject($object_id); drupal_alter('islandora_object', $object); return $object; } 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; } /** * 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 string $object_id * The object to load the datastream from. * * @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 = islandora_object_load($object_id); if (!$object) { return NULL; } return $object[$datastream_id]; } /** * 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() { $module_path = drupal_get_path('module', 'islandora'); return array( 'islandora' => array( 'title' => 'Islandora', 'objects' => array( array( '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", ), ), ), ), ), ); } /** * Implements islandora_undeleteable_datastreams(). */ function islandora_islandora_undeletable_datastreams(array $models) { return array('DC'); } /** * Ingest the given object into Fedora calling its pre/post hooks as well. * * @todo will be cleaned up in the future * * @param NewFedoraObject $object * An ingestable FedoraObject. * * @return FedoraObject * The ingested FedoraObject, after running the pre/post ingest hooks. * Returns FALSE if the ingest failed. */ function islandora_add_object(NewFedoraObject &$object) { islandora_pre_add_object($object); try { $object->repository->ingestObject($object); islandora_post_add_object($object); return $object; } catch (Exception $e) { watchdog('islandora', 'Failed to ingest object: @pid
code: @code
message: @msg', array( '@pid' => $object->id, '@code' => $e->getCode(), '@msg' => $e->getMessage()), WATCHDOG_ERROR); } return FALSE; } /** * Calls the ISLANDORA_PRE_INGEST_HOOK hooks. * * @param NewFedoraObject $object * An ingestable FedoraObject. */ function islandora_pre_add_object(NewFedoraObject $object) { module_load_include('inc', 'islandora', 'includes/utilities'); foreach (islandora_build_hook_list(ISLANDORA_PRE_INGEST_HOOK, $object->models) as $hook) { module_invoke_all($hook, $object); } } /** * Calls the ISLANDORA_POST_INGEST_HOOK hooks. * * @param FedoraObject $object * A recently ingestable FedoraObject. */ function islandora_post_add_object(FedoraObject $object) { foreach (islandora_build_hook_list(ISLANDORA_POST_INGEST_HOOK, $object->models) as $hook) { module_invoke_all($hook, $object); } } /** * Deletes the given object into Fedora calling its pre/post hooks as well. * * @param FedoraObject $object * An object to delete. * * @return FedoraObject * The ingested FedoraObject, after running the pre/post ingest hooks. */ function islandora_delete_object(FedoraObject &$object) { $object_id = $object->id; $models = $object->models; $action = islandora_pre_delete_object($object); switch ($action) { case 'blocked': // Do nothing. return FALSE; case 'delete': // Change the state to deleted. $object->delete(); islandora_post_delete_object($object_id, $models); return TRUE; default: // Purge $object->repository->purgeObject($object_id); islandora_post_delete_object($object_id, $models); $object = NULL; return TRUE; } } /** * Calls the ISLANDORA_PRE_PURGE_OBJECT_HOOK hooks. * * @param FedoraObject $object * The object that is about to be deleted. */ function islandora_pre_delete_object(FedoraObject $object) { module_load_include('inc', 'islandora', 'includes/utilities'); $results = array(); foreach (islandora_build_hook_list(ISLANDORA_PRE_PURGE_OBJECT_HOOK, $object->models) as $hook) { $results = array_merge_recursive($results, module_invoke_all($hook, $object)); } $action = (isset($results['block']) && $results['block']) ? 'block' : FALSE; $action = (!$action && isset($results['delete']) && $results['delete']) ? 'delete' : $action; $action = !$action ? 'purge' : $action; return $action; } /** * Calls the ISLANDORA_POST_PURGE_OBJECT_HOOK hooks. * * @param string $object_id * The object id of an the recently deleted object. * @param array $models * The list of content models the delete object subsribed to. */ function islandora_post_delete_object($object_id, array $models) { module_load_include('inc', 'islandora', 'includes/utilities'); foreach (islandora_build_hook_list(ISLANDORA_POST_PURGE_OBJECT_HOOK, $models) as $hook) { module_invoke_all($hook, $object_id, $models); } } /** * Delete's/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 boolean * TRUE is returned if the datastream was Deleted/Purged, FALSE if it was * blocked. */ function islandora_delete_datastream(FedoraDatastream &$datastream) { $datastream_id = $datastream->id; $object = $datastream->parent; $action = islandora_pre_delete_datastream($datastream); switch ($action) { case 'blocked': // Do nothing. return FALSE; case 'delete': // Change the state to deleted. $object[$datastream_id]->state = 'D'; // @todo Differentiate between delete/purge in the hooks. islandora_post_delete_datastream($object, $datastream_id); return TRUE; default: // Purge $object->purgeDatastream($datastream_id); islandora_post_delete_datastream($object, $datastream_id); $datastream = NULL; return TRUE; } } /** * The default behaviour is to 'purge' the datastream but this can be overridden * by modules that implement the 'islandora_pre_purge_datastream' hook. * * @todo make this an alter. * * The returned array can include a 'block' => TRUE * pair which will prevent the datastream from being deleted if it particularly * needed for a certain function. Returning 'delete' => TRUE will cause the * datastream to be put into a deleted state. * * @param FedoraDatastream $datastream * The datastream to delete. * * @return string * The action to take when deleting the given datastream, either 'purge', * 'delete', or 'block'. */ function islandora_pre_delete_datastream(FedoraDatastream $datastream) { module_load_include('inc', 'islandora', 'includes/utilities'); $results = array(); foreach (islandora_build_hook_list(ISLANDORA_PRE_PURGE_DATASTREAM_HOOK, $datastream->parent->models) as $hook) { // Not sure this will work the greatest, probably an alter would be better.. $results = array_merge_recursive($results, module_invoke_all($hook, $datastream)); } $action = (isset($results['block']) && $results['block']) ? 'block' : FALSE; $action = (!$action && isset($results['delete']) && $results['delete']) ? 'delete' : $action; $action = !$action ? 'purge' : $action; return $action; } /** * Calls the post purge datastream hooks. * * @todo Should differentiate between purging/deleting. * * @param FedoraObject $object * The parent object of the deleted datastream. * @param string $datastream_id * The datastream id of the deleted datastream. */ function islandora_post_delete_datastream(FedoraObject $object, $datastream_id) { module_load_include('inc', 'islandora', 'includes/utilities'); foreach (islandora_build_hook_list(ISLANDORA_POST_PURGE_DATASTREAM_HOOK, $object->models) as $hook) { module_invoke_all($hook, $object, $datastream_id); } }