diff --git a/includes/islandora.ingest.inc b/includes/islandora.ingest.inc index a9869ed1..9aa4f4f7 100644 --- a/includes/islandora.ingest.inc +++ b/includes/islandora.ingest.inc @@ -2,7 +2,7 @@ /** * @file - * This file contains ingest callback functions + * This file contains ingest callback functions. */ /** @@ -16,23 +16,69 @@ function islandora_ingest_get_information(AbstractFedoraObject $collection_objec } /** - * @TODO: needs documentation + * Get an ingestable object. + * + * @deprecated + * Deprecated in favour of the more flexible + * islandora_ingest_new_object_prepare()--which this function has been made + * to call behind the scenes anyway. + * + * @param array $content_models + * An array of content models to which the new object should subscribe, where + * each content model is described by an associative array containing: + * - pid: The Fedora PID of the content model. + * @param string $collection_pid + * The collection to which the new object should belong. + * @param string $relationship + * The relationship this object will have to the collection. + * @param string $namespace + * The namespace in which the PID for the new object will be created. + * + * @return NewFedoraObject + * A NewFedoraObject which may be adjusted before ingesting. */ function islandora_ingest_get_object($content_models, $collection_pid, $relationship, $namespace) { - module_load_include('inc', 'islandora', 'includes/tuque'); - global $user; - $connection = new IslandoraTuque($user); - $object = $connection->repository->constructObject($namespace); - foreach ($content_models as $content_model) { - $object->relationships->add(FEDORA_MODEL_URI, 'hasModel', $content_model['pid']); + $models = array(); + foreach ($content_models as $relation) { + $models[] = $relation['pid']; } - $object->relationships->add(FEDORA_RELS_EXT_URI, $relationship, $collection_pid); - module_invoke_all('islandora_ingest_pre_ingest', $object, $content_models, $collection_pid); - return $object; + + return islandora_ingest_new_object_prepare($namespace, NULL, array(), $models, array( + array( + 'pid' => $collection_pid, + 'relationship' => $relationship, + ), + ), $collection_pid); } /** - * @TODO: needs documentation + * Prepare an ingestable object. + * + * @param string $namespace + * The namespace in which the PID for the new object will be created. + * @param string $label + * An optional label to apply to the object. + * @param array $datastreams + * A array of datastreams to add, where each datastream definition is an + * associative array containing: + * - dsid: The datastream ID. + * - label: An optional label for the datastream. + * - mimetype: A MIMEtype for the datastream; defaults to text/xml. + * - control_group: One of X, M, R and E; defaults to M. + * - datastream_file: A web-accessible path, for which we try to get an + * absolute path using url(). + * @param array $content_models + * An array of content model PIDs to which the new object should subscribe. + * @param array $relationships + * An array of relationships, where each relationship is an associative array + * containing: + * - relationship: The predicate for the relationship, from the Fedora + * RELS-EXT namespace. + * - pid: The object for the relationship, to which we are creating the + * relationhsip. + * + * @return NewFedoraObject + * An ingestable NewFedoraObject. */ function islandora_ingest_new_object_prepare($namespace = NULL, $label = NULL, $datastreams = array(), $content_models = array(), $relationships = array(), $collection_pid = NULL) { // include Tuque library @@ -86,20 +132,56 @@ function islandora_ingest_new_object_prepare($namespace = NULL, $label = NULL, $ $object->ingestDatastream($datastream); } - module_invoke_all('islandora_ingest_pre_ingest', $object, $content_models, $collection_pid); + module_load_include('inc', 'islandora', 'includes/utilities'); + foreach (islandora_build_hook_list('islandora_ingest_pre_ingest', $content_models) as $hook) { + module_invoke_all($hook, $object, $content_models, $collection_pid); + } + return $object; } /** - * @TODO: needs documentation + * Ingest the given object into Fedora. + * + * @param NewFedoraObject $object + * An ingestable FedoraObject. + * + * @return FedoraObject + * The ingested FedoraObject, after running the post ingest hooks. */ function islandora_ingest_add_object(&$object) { $object->repository->ingestObject($object); - module_invoke_all('islandora_ingest_post_ingest', $object); + + module_load_include('inc', 'islandora', 'includes/utilities'); + + foreach (islandora_build_hook_list(ISLANDORA_POST_INGEST_HOOK, $object->models) as $hook) { + module_invoke_all($hook, $object); + } + return $object; } - +/** + * Ingest an object. + * + * @param array $object_model + * 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_ingest_new_object_prepare(). + * - cmodel: Either an array of content models as accepted by + * islandora_ingest_new_object_prepare(), 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_ingest_new_object_prepare(). + * + * @return FedoraObject + * An FedoraObject which has been ingested into Fedora. + */ function islandora_ingest_new_object($object_model) { // prepare variables // namespace diff --git a/includes/utilities.inc b/includes/utilities.inc index a8dd68b3..820e7660 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -8,6 +8,8 @@ /** * Convert bytes to human readable format * + * XXX: Shouldn't Drupal's format_size() be preferred? + * * @param integer bytes Size in bytes to convert * @return string */ @@ -103,4 +105,50 @@ function islandora_describe_repository($url) { catch (RepositoryException $e) { return FALSE; } -} \ No newline at end of file +} + +/** + * Build a list of all the hooks to call. + * + * Concatenates the each pid (escaped) to the hook name, for calling in + * module_invoke_all(). + * + * @param string $hook + * A hook to call. + * @param array $pids + * An array of PIDs (probably content models). + * + * @return array + * An array with each PID escaped and concatenated with the base hook name, + * in addition to the base hook name at the end. + */ +function islandora_build_hook_list($hook, $pids = array()) { + $hooks = array(); + + foreach ($pids as $model) { + $hooks[] = islandora_escape_pid_for_function($model) . '_' . $hook; + } + $hooks[] = $hook; + + return $hooks; +} + +/** + * Escape a Fedora PID to be valid inside of a PHP function name. + * + * Originally intended to allow inclusion of a PID in a module_invoke_all() + * call. + */ +function islandora_escape_pid_for_function($pid) { + // Apparently, case doesn't matter for function calls in PHP, so let's not + // really worry about changing the case. + return str_replace( + // Any PID characters which are not valid in the name of a PHP function. + array( + ':', + '-', + ), + '_', + $pid + ); +} diff --git a/islandora.api.php b/islandora.api.php index 49be904d..dff4c217 100644 --- a/islandora.api.php +++ b/islandora.api.php @@ -6,120 +6,192 @@ */ /** - * remove a datastream from a repository object - * @param object $fedora_object - * tuque FedoraObject - * @param string $datastream_id + * Generate a repository objects view. + * + * @param FedoraObject $fedora_object + * A Tuque FedoraObject being operated on. + * @param object $user + * The user accessing the object. + * @param string $page_number + * The page in the content. + * @param string $page_size: The size of the page. + * + * @return array + * An array whose values are markup. */ -function hook_islandora_purge_datastream($fedora_object, $datastream_id) {} +function hook_islandora_view_object($fedora_object, $user, $page_number, $page_size) {} /** + * Generate an object's display for the given content model. * - * @param type $object - * tuque FedoraObject + * Content models PIDs have colons and hyphens changed to underscores, to + * create the hook name. + * + * @param type $fedora_object + * A Tuque FedoraObject + * + * @return array + * An array whose values are markup. */ -function hook_islandora_purge_object($islandora_object) {} +function hook_CMODEL_PID_islandora_view_object($fedora_object) {} + /** - * allows modules to add to a repository objects display. If you implement this - * hook you should also register your module for view with the get types hook. - * - * islandora gets all displays back in an array and iterates over them. the order - * they are displayed is based on the order of the key of the array that each - * module returns. - * - * your module may also want to register a varible that says whether or not it - * should be part of the default display. Modules can also add secondary tabs as - * a way to add there output to an islandora display. the basic image module has - * samples of both (the secondary tabs examples are commented out) + * Alter display output after it has been generated. * - * @param type $islandora_object - * tuque FedoraObject + * @param FedoraObject $fedora_object + * A Tuque FedoraObject being operated on. + * @param array $arr + * An arr of rendered views. */ -function hook_islandora_view_object($islandora_object) {} +function hook_islandora_view_object_alter(&$fedora_object, &$arr) {} /** - * returns an array listing object types provided by sub modules - * Ex. array($types['islandora:collectionCModel'][ISLANDORA_VIEW_HOOK] = variable_get('islandora_basic_collection_use_for_default_tab', TRUE); - * $types['islandora:collectionCModel'][ISLANDORA_EDIT_HOOK] = FALSE; + * Generate an object's management display. + * + * @param type $fedora_object + * A Tuque FedoraObject * * @return array + * An array whose values are markup. */ -function hook_islandora_get_types() {} +function hook_islandora_edit_object($fedora_object) {} /** - * allows modules to define an object edit page by cmodel + * Generate an object's management display for the given content model. * - * your module should return true for ISLANDORA_EDIT_HOOK in its get_types function + * Content models PIDs have colons and hyphens changed to underscores, to + * create the hook name. * - * islandora provides a default implementation that should work for most use cases - * @param string $islandora_object - * @return string + * @param type $fedora_object + * A Tuque FedoraObject * + * @return array + * An array whose values are markup. */ -function hook_islandora_edit_object($islandora_object) {} +function hook_CMODEL_PID_islandora_edit_object($fedora_object) {} /** - * allows modules to alter the fedora object before it is pass through the edit - * hooks - * @param type $islandora_object - * a tugue FedoraObject + * Allow management display output to be altered. + * + * @param type $fedora_object + * A Tuque FedoraObject + * @param type $arr + * an arr of rendered views */ -function hook_islandora_edit_object_alter($islandora_object) {} +function hook_islandora_edit_object_alter(&$fedora_object, &$arr) {} +/** + * Allows modules to add to an objects ingest process. + * + * @param FedoraObject $fedora_object + * A Tuque FedoraObject. + */ +function hook_islandora_ingest_post_ingest($fedora_object) {} /** - * creates and populates a php Fedora object. + * Allow modules to add to the ingest process of a specific content model. */ -function hook_islandora_preingest_alter() {} +function hook_CMODEL_PID_islandora_ingest_post_ingest($fedora_object) {} /** - * modules can implement this hook to add or remove datastreams after an - * object has been ingested. + * Allows modules to add to a repository objects view/edit(/misc) process. * - * Each module should check for the newly ingested repository objects content - * model to make sure it is a type of object they want to act on. + * If you implement this hook you must also register your module with + * hook_islandora_hook_info(). * - * @param type $islandora_object - * tugue FeodoraObject + * @param FedoraObject $fedora_object + * A Tuque FedoraObject. + * + * @return array|null + * An associative array with 'deleted' mapped to TRUE--indicating that the + * object should just be marked as deleted, instead of actually being + * purged--or NULL/no return if we just need to do something before the + * is purged. + */ +function hook_islandora_pre_purge_object($fedora_object) {} + +/** + * Allow modules to react to the purge process of a specific content model. + * + * @see hook_islandora_pre_purge_object() */ -function hook_islandora_postingest($islandora_object) {} +function hook_CMODEL_PID_islandora_pre_purge_object($fedora_object) {} /** - * Register potential ingest routes. Implementations should return an array containing possible routes. - * Ex. array( - * array('name' => t('Ingest Route Name'), 'url' => 'ingest_route/url', 'weight' => 0), - * ); + * Register potential ingest routes. + * + * Implementations should return an array containing possible routes. */ -function hook_islandora_ingest_registry($collection_object) {} +function hook_islandora_ingest_registry($collection_object) { + $reg = array( + array( + 'name' => t('Ingest Route Name'), + 'url' => 'ingest_route/url', + 'weight' => 0, + ), + ); + return $reg +} /** * Register a datastream edit route/form. + * * @param $islandora_object * @param $ds_id */ function hook_islandora_edit_datastream_registry($islandora_object, $ds_id) {} /** - * alter an object before it gets used further down the stack + * Alter an object before it gets used further down the stack. + * * @param type $object - * a tuque FedoraObject + * A Tuque FedoraObject */ function hook_islandora_object_alter($fedora_object) {} /** - * insert or remove rendered elements by implementing this function - * in your module - * @param type $arr - * an arr of rendered views - */ -function hook_islandora_display_alter($arr) {} - -/** + * Allow modification of an object before ingesting. * * @param type $islandora_object - * a tuque FedoraObject + * A Tuque FedoraObject * @param array $content_models * @param string $collection_pid */ function hook_islandora_ingest_pre_ingest($islandora_object, $content_models, $collection_pid) {} + +/** + * Allow modification of objects of a certain content model before ingesting. + * + * @see hook_islandora_ingest_pre_ingest() + */ +function hook_CMODEL_PID_islandora_ingest_pre_ingest($islandora_object, $content_models, $collection_pid) {} + +/** + * Allow modules to setup for the purge of a datastream. + * + * @param object $datastream + * A Tuque FedoraDatastream object. + */ +function hook_islandora_pre_purge_datastream($datastream) {} + +/** + * Allow modules to react after a datastream is purged. + * + * @param object $object + * A Tuque FedoraObject. + * @param string $dsid + * A id of the former datastream. + */ +function hook_islandora_post_purge_datastream($object, $dsid) {} + +/** + * Allow modules to react post-purge. + * + * @param string $object_id + * The former object's PID. + * @param array $content_models + * An array containing the models to which the former object. + */ +function hook_islandora_post_purge_object($object_id, $content_models) {} + diff --git a/islandora.module b/islandora.module index 5dd921eb..d1df4d48 100644 --- a/islandora.module +++ b/islandora.module @@ -31,9 +31,11 @@ define('FEDORA_PURGE', 'delete fedora objects and datastreams'); define('FEDORA_MODIFY_STATE', 'modify fedora state'); define('FEDORA_MANAGE', 'manage fedora items'); -// hooks +// Hooks define('ISLANDORA_VIEW_HOOK', 'islandora_view_object'); define('ISLANDORA_EDIT_HOOK', 'islandora_edit_object'); +define('ISLANDORA_POST_INGEST_HOOK', 'islandora_ingest_post_ingest'); +define('ISLANDORA_PRE_PURGE_OBJECT_HOOK', 'islandora_pre_purge_object'); /** * Implements hook_menu(). @@ -249,14 +251,6 @@ function islandora_access_callback($object = NULL, $perm = NULL) { return ($namespace_access && user_access($perm)); } -/** - * returns an array listing object types provided by sub modules - * @return array - */ -function islandora_get_types() { - return module_invoke_all('islandora_get_types'); -} - function islandora_init() { if (path_is_admin(current_path())) { drupal_add_css(drupal_get_path('module', 'islandora') . '/css/islandora.admin.css'); @@ -287,14 +281,30 @@ function islandora_edit_object($object) { if (!$object) { return drupal_not_found(); } - drupal_alter('islandora_edit_object', $object); - $arr = module_invoke_all('islandora_edit_object', $object); - $output = ""; - foreach ($arr as $key => $value) { - $output .= $value; //if we have multiple modules handle one cmodel we need to iterate over multiple + + module_load_include('inc', 'islandora', 'includes/utilities'); + + // Accumulate the output. + $output = array(); + + // Call cmodel oriented variants first. + 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); + } } - //we could do another module invoke all here to build the edit tab with a default implemented in this module? - return $output; + + // Add in the default, if we did not get any results. + if (empty($output)) { + $output = islandora_default_islandora_edit_object($object); + } + + arsort($output); + + drupal_alter(ISLANDORA_EDIT_HOOK, $object, $output); + + return implode('', $output); } /** @@ -314,23 +324,25 @@ function islandora_edit_properties($object_id) { } /** - * builds a default page for the edit tab + * Builds a default page for the edit tab. * * @param object $fedora_object * A tuque Fedora Object */ -function islandora_islandora_edit_object($fedora_object) { - $supported_models = islandora_get_types(); - $output = ""; - foreach ($fedora_object->models as $model) { - if (isset($supported_models[$model][ISLANDORA_EDIT_HOOK]) && $supported_models[$model][ISLANDORA_EDIT_HOOK] == TRUE) {//another module is handling the view - return; - } - } - $output = theme('islandora_default_edit', array('islandora_object' => $fedora_object)); - return array('Default edit output' => $output); +function islandora_default_islandora_edit_object($fedora_object) { + $output = theme('islandora_default_edit', array( + 'islandora_object' => $fedora_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"); @@ -346,6 +358,8 @@ function islandora_view_default_object() { */ function islandora_view_object($fedora_object = NULL) { module_load_include('inc', 'islandora', 'includes/breadcrumb'); + module_load_include('inc', 'islandora', 'includes/utilities'); + global $user; if (!$fedora_object) { @@ -358,37 +372,38 @@ function islandora_view_object($fedora_object = NULL) { $page_number = (empty($_GET['page'])) ? '1' : $_GET['page']; $page_size = (empty($_GET['pagesize'])) ? '10' : $_GET['pagesize']; - drupal_alter('islandora_object', $fedora_object); //modify object if required before it is passed along - $arr = module_invoke_all('islandora_view_object', $fedora_object, $user, $page_number, $page_size); //allow submodules to decide how to handle content base on object type - if (empty($arr)) { - //TODO: make sure we iterate over the array as they will be more then one cmodel per object - drupal_set_message(t('there was an error loading the view for Islandora object "%s"', array('%s' => $fedora_object->label)), 'error'); - return ""; + // Accumulate the output. + $output = array(); + + // Call cmodel oriented variants first. + foreach (islandora_build_hook_list(ISLANDORA_VIEW_HOOK, $fedora_object->models) as $hook) { + $temp = module_invoke_all($hook, $fedora_object, $user, $page_number, $page_size); + if (!empty($temp)) { + $output = array_merge_recursive($output, $temp); + } } - arsort($arr); - drupal_alter('islandora_display', $arr); - $output = ""; - foreach ($arr as $key => $value) { - $output .= $value; //if we have multiple modules handle one cmodel we need to iterate over multiple + + // Add in the default, if we did not get any results. + if (empty($output)) { + $output = islandora_default_islandora_view_object($fedora_object); } - return $output; + + arsort($output); + + drupal_alter(ISLANDORA_VIEW_HOOK, $fedora_object, $output); + + return implode('', $output); } /** - * The default view hook. If there are no modules registered to handle this objects cmodels or there are + * If there are no modules registered to handle this objects cmodels or there are * not any cmodels specified this will create a default display. * - * @param string $object_id - * @return string + * @param FedoraObject $object + * The object whose display we are to render. + * @return array */ -function islandora_islandora_view_object($object) { - $supported_models = islandora_get_types(); - $output = ""; - foreach ($object->models as $model) { - if (isset($supported_models[$model][ISLANDORA_VIEW_HOOK]) && (boolean) $supported_models[$model][ISLANDORA_VIEW_HOOK] === TRUE) { //another module is handling the view - return; - } - } +function islandora_default_islandora_view_object($object) { $output = theme('islandora_default', array('islandora_object' => $object)); return array('Default output' => $output); } @@ -460,7 +475,7 @@ function islandora_permission() { */ function islandora_object_load($object_id) { module_load_include('inc', 'islandora', 'includes/tuque'); - static $islandora_tuque = NULL; + $islandora_tuque = &drupal_static(__FUNCTION__); if (!$islandora_tuque) { $islandora_tuque = new IslandoraTuque(); @@ -472,6 +487,8 @@ function islandora_object_load($object_id) { } catch (Exception $e) { return NULL; } + + drupal_alter('islandora_object', $fedora_object); return $fedora_object; } else { @@ -500,8 +517,19 @@ function islandora_object_purge($object_id) { drupal_set_message(t('Could not remove object, object not found')); return; } + + module_load_include('inc', 'islandora', 'includes/utilities'); + $content_models = $object->models; - $arr = module_invoke_all('islandora_pre_purge_object', $object); // notify modules of pending deletion + + $arr = array(); + foreach (islandora_build_hook_list(ISLANDORA_PRE_PURGE_OBJECT_HOOK, $object->models) as $hook) { + $temp = module_invoke_all($hook, $object); + if (!empty($temp)) { + $arr = array_merge_recursive($arr, $temp); + } + } + if (isset($arr['delete']) && $arr['delete']) { try { $object->delete(); @@ -518,6 +546,7 @@ function islandora_object_purge($object_id) { return ""; } } + module_invoke_all('islandora_post_purge_object', $object_id, $content_models); // notify modules post deletion }