From 7ec10d2c11dc80fdd0f4c89cd83c6dea90d93ac8 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 23 Jan 2013 21:59:45 +0100 Subject: [PATCH 01/15] Created hooks and implemented them as part of the tuque wrapper classes. hook_islandora_object_alter(AbstractFedoraObject $object, array &$context) hook_CMODEL_PID_islandora_object_alter(AbstractFedoraObject $object, array &$context) hook_islandora_datastream_alter(AbstractFedoraObject $object, AbstractFedoraDatastream $datastream, array &$context) hook_CMODEL_PID_DSID_islandora_datastream_altezr(AbstractFedoraObject $object, AbstractFedoraDatastream $datastream, array &$context) hook_islandora_object_ingested(FedoraObject $object) hook_CMODEL_PID_islandora_object_ingested(FedoraObject $object) hook_islandora_object_modified(FedoraObject $object) hook_CMODEL_PID_islandora_object_modified(FedoraObject $object) hook_islandora_object_purged($pid) hook_CMODEL_PID_islandora_object_purged($pid) hook_islandora_datastream_ingested(FedoraObject $object, FedoraDatastream $datastream) hook_CMODEL_PID_DSID_islandora_datastream_ingested(FedoraObject $object, FedoraDatastream $datastream) hook_islandora_datastream_modified(FedoraObject $object, FedoraDatastream $datastream) hook_CMODEL_PID_islandora_datastream_modified(FedoraObject $object, FedoraDatastream $datastream) hook_islandora_datastream_purged(FedoraObject $object, $dsid) hook_CMODEL_PID_islandora_datastream_purged(FedoraObject $object, $dsid) Also updated the testing scripts to support different configurations, as well wrote tests for all the implemented hooks. This requires the latest version of Tuque so be sure to update. --- .gitignore | 1 + includes/IslandoraTuqueWrapper.inc | 78 ---- ...IslandoraTuque.inc => islandora_tuque.inc} | 2 +- includes/islandora_tuque_wrapper.inc | 360 ++++++++++++++++++ includes/utilities.inc | 65 +++- islandora.api.php | 336 +++++++++++----- islandora.info | 5 +- islandora.module | 198 +--------- tests/README | 3 + tests/default.test_config.ini | 6 + tests/islandora_authtokens.test | 50 ++- tests/islandora_hooks.test | 213 +++++++++++ tests/islandora_hooks_test.info | 7 + tests/islandora_hooks_test.module | 152 ++++++++ tests/islandora_web_test_case.inc | 110 +++++- tests/test_config.ini | 4 +- 16 files changed, 1201 insertions(+), 389 deletions(-) delete mode 100644 includes/IslandoraTuqueWrapper.inc rename includes/{IslandoraTuque.inc => islandora_tuque.inc} (97%) create mode 100644 includes/islandora_tuque_wrapper.inc create mode 100644 tests/README create mode 100644 tests/default.test_config.ini create mode 100644 tests/islandora_hooks.test create mode 100644 tests/islandora_hooks_test.info create mode 100644 tests/islandora_hooks_test.module diff --git a/.gitignore b/.gitignore index 1133add7..6ef50997 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ cache.properties .DS_Store +tests/test_config.ini \ No newline at end of file diff --git a/includes/IslandoraTuqueWrapper.inc b/includes/IslandoraTuqueWrapper.inc deleted file mode 100644 index 2692c24c..00000000 --- a/includes/IslandoraTuqueWrapper.inc +++ /dev/null @@ -1,78 +0,0 @@ -connection = new IslandoraRepositoryConnection($url, $user_string, $pass_string); $this->connection->reuseConnection = TRUE; $this->api = new IslandoraFedoraApi($this->connection); diff --git a/includes/islandora_tuque_wrapper.inc b/includes/islandora_tuque_wrapper.inc new file mode 100644 index 00000000..8bd3c3e6 --- /dev/null +++ b/includes/islandora_tuque_wrapper.inc @@ -0,0 +1,360 @@ +models as $model) { + $types[] = "{$model}_islandora_object"; + } + drupal_alter($types, $object, $context); +} + +/** + * Allow modules to alter a datastream before a mutable event occurs. + */ +function islandora_alter_datastream(AbstractFedoraObject $object, AbstractDatastream $datastream, array &$context) { + $types = array('islandora_datastream'); + foreach ($object->models as $model) { + $types[] = "{$model}_{$datastream->id}_islandora_datastream"; + } + drupal_alter($types, $object, $datastream, $context); +} + +/** + * Constructs a list of hooks from the given paramenters and invokes them. + */ +function islandora_invoke_object_hooks($hook, array $models) { + module_load_include('inc', 'islandora', 'includes/utilities'); + return islandora_invoke_hook_list($hook, $models, array_slice(func_get_args(), 2)); +} + +/** + * Constructs a list of hooks from the given paramenters and invokes them. + */ +function islandora_invoke_datastream_hooks($hook, array $models, $dsid) { + module_load_include('inc', 'islandora', 'includes/utilities'); + $refinements = array(); + foreach ($models as $model) { + $refinements[] = "{$model}_{$dsid}"; + } + return islandora_invoke_hook_list($hook, $refinements, array_slice(func_get_args(), 3)); +} + +class IslandoraFedoraRepository extends FedoraRepository { + protected $queryClass = 'IslandoraRepositoryQuery'; + protected $newObjectClass = 'IslandoraNewFedoraObject'; + protected $objectClass = 'IslandoraFedoraObject'; + + /** + * Ingest the given object. + * + * @see FedoraRepository::ingestObject() + */ + public function ingestObject(NewFedoraObject &$object) { + $context = array( + 'action' => 'ingest', + 'block' => FALSE, + ); + islandora_alter_object($object, $context); + try { + if ($context['block']) { + throw new Exception('Ingest Object was blocked.'); + } + $ret = parent::ingestObject($object); + islandora_invoke_object_hooks(ISLANDORA_OBJECT_INGESTED_HOOK, $object->models, $object); + // Call the ingested datastream hooks for NewFedoraObject's after the + // object had been ingested. + foreach ($object as $dsid => $datastream) { + islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_INGESTED_HOOK, $object->models, $dsid, $object, $datastream); + } + return $ret; + } + 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); + throw $e; + } + } +} + +class IslandoraRepositoryQuery extends RepositoryQuery {} + +class IslandoraNewFedoraObject extends NewFedoraObject { + protected $newFedoraDatastreamClass = 'IslandoraNewFedoraDatastream'; + protected $fedoraDatastreamClass = 'IslandoraFedoraDatastream'; + protected $fedoraRelsExtClass = 'IslandoraFedoraRelsExt'; +} + +class IslandoraFedoraObject extends FedoraObject { + protected $newFedoraDatastreamClass = 'IslandoraNewFedoraDatastream'; + protected $fedoraDatastreamClass = 'IslandoraFedoraDatastream'; + protected $fedoraRelsExtClass = 'IslandoraFedoraRelsExt'; + + /** + * Ingest the given datastream. + * + * @see FedoraObject::ingestDatastream() + */ + public function ingestDatastream(&$datastream) { + $object = $datastream->parent; + $context = array( + 'action' => 'ingest', + 'block' => FALSE, + ); + islandora_alter_datastream($object, $datastream, $context); + try { + if ($context['block']) { + throw new Exception('Ingest Datastream was blocked.'); + } + $ret = parent::ingestDatastream($datastream); + islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_INGESTED_HOOK, $object->models, $datastream->id, $object, $datastream); + return $ret; + } + catch (Exception $e) { + watchdog('islandora', 'Failed to ingest object: @pid
code: @code
message: @msg', array( + '@pid' => $object->id, + '@dsid' => $datastream->id, + '@code' => $e->getCode(), + '@msg' => $e->getMessage()), WATCHDOG_ERROR); + throw $e; + } + } +} + +class IslandoraRepositoryConnection extends RepositoryConnection {} + +class IslandoraFedoraApi extends FedoraApi { + + /** + * Instantiate a IslandoraFedoraApi object. + * + * @see FedoraApi::__construct() + */ + public function __construct(IslandoraRepositoryConnection $connection, FedoraApiSerializer $serializer = NULL) { + if (!$serializer) { + $serializer = new FedoraApiSerializer(); + } + $this->a = new FedoraApiA($connection, $serializer); + $this->m = new IslandoraFedoraApiM($connection, $serializer); + $this->connection = $connection; + } +} + +class IslandoraFedoraApiM extends FedoraApiM { + + /** + * Update a datastream. + * + * Either changing its metadata, updaing the datastream contents or both. + * + * @throws Exception + * If the modify datastream request was block by some module. + * + * @see FedoraApiM::modifyDatastream + */ + public function modifyDatastream($pid, $dsid, $params = array()) { + $object = islandora_object_load($pid); + $datastream = $object[$dsid]; + $context = array( + 'action' => 'modify', + 'block' => FALSE, + 'params' => $params, + ); + islandora_alter_datastream($object, $datastream, $context); + try { + if ($context['block']) { + throw new Exception('Modify Datastream was blocked.'); + } + $ret = parent::modifyDatastream($pid, $dsid, $params); + islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_MODIFIED_HOOK, $object->models, $dsid, $object, $datastream); + if (isset($params['dsState']) && $params['dsState'] == 'D') { + islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_PURGED_HOOK, $object->models, $dsid, $object, $dsid); + } + return $ret; + } + catch(Exception $e) { + watchdog('islandora', 'Failed to modify datastream @dsid from @pid
code: @code
message: @msg', array( + '@pid' => $pid, + '@dsid' => $dsid, + '@code' => $e->getCode(), + '@msg' => $e->getMessage()), WATCHDOG_ERROR); + throw $e; + } + } + + /** + * Update Fedora Object parameters. + * + * @see FedoraApiM::modifyObject + */ + public function modifyObject($pid, $params = NULL) { + $object = islandora_object_load($pid); + $context = array( + 'action' => 'modify', + 'block' => FALSE, + 'params' => $params, + ); + islandora_alter_object($object, $context); + try { + if ($context['block']) { + throw new Exception('Modify Object was blocked.'); + } + $ret = parent::modifyObject($pid, $params); + islandora_invoke_object_hooks(ISLANDORA_OBJECT_MODIFIED_HOOK, $object->models, $object); + if (isset($params['state']) && $params['state'] == 'D') { + islandora_invoke_object_hooks(ISLANDORA_OBJECT_PURGED_HOOK, $object->models, $object->id); + } + return $ret; + } + catch(Exception $e) { + watchdog('islandora', 'Failed to modify object: @pid
code: @code
message: @msg', array( + '@pid' => $pid, + '@code' => $e->getCode(), + '@msg' => $e->getMessage()), WATCHDOG_ERROR); + throw $e; + } + } + + /** + * Purge a datastream from from Fedora. + * + * @see FedoraApiM::purgeDatastream + */ + public function purgeDatastream($pid, $dsid, $params = array()) { + $object = islandora_object_load($pid); + $context = array( + 'action' => 'purge', + 'purge' => TRUE, + 'delete' => FALSE, + 'block' => FALSE, + ); + islandora_alter_datastream($object, $object[$dsid], $context); + try { + $action = $context['block'] ? 'block' : FALSE; + $action = (!$action && $context['delete']) ? 'delete' : $action; + $action = !$action ? 'purge' : $action; + switch ($action) { + case 'block': + throw new Exception('Purge Datastream was blocked.'); + break; + + case 'delete': + $object[$dsid]->state = 'D'; + return array(); + + default: + $ret = parent::purgeDatastream($pid, $dsid, $params); + islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_PURGED_HOOK, $object->models, $dsid, $object, $dsid); + return $ret; + } + } + catch(Exception $e) { + watchdog('islandora', 'Failed to purge datastream @dsid from @pid
code: @code
message: @msg', array( + '@pid' => $pid, + '@dsid' => $dsid, + '@code' => $e->getCode(), + '@msg' => $e->getMessage()), WATCHDOG_ERROR); + throw $e; + } + } + + /** + * Purge an object. + * + * @see FedoraApiM::purgeObject + */ + public function purgeObject($pid, $log_message = NULL) { + $object = islandora_object_load($pid); + $context = array( + 'action' => 'purge', + 'purge' => TRUE, + 'delete' => FALSE, + 'block' => FALSE, + ); + islandora_alter_object($object, $context); + try { + $action = $context['block'] ? 'block' : FALSE; + $action = (!$action && $context['delete']) ? 'delete' : $action; + $action = !$action ? 'purge' : $action; + $models = $object->models; + switch ($action) { + case 'block': + throw new Exception('Purge object was blocked.'); + break; + + case 'delete': + $object->state = 'D'; + return ''; + + default: + $ret = parent::purgeObject($pid, $log_message); + islandora_invoke_object_hooks(ISLANDORA_OBJECT_PURGED_HOOK, $models, $pid); + return $ret; + } + } + catch(Exception $e) { + watchdog('islandora', 'Failed to purge object @pid
code: @code
message: @msg', array( + '@pid' => $pid, + '@code' => $e->getCode(), + '@msg' => $e->getMessage()), WATCHDOG_ERROR); + throw $e; + } + } + +} + +class IslandoraSimpleCache extends SimpleCache {} + +class IslandoraNewFedoraDatastream extends NewFedoraDatastream { + protected $fedoraRelsIntClass = 'IslandoraFedoraRelsInt'; + protected $fedoraDatastreamVersionClass = 'IslandoraFedoraDatastreamVersion'; +} + +class IslandoraFedoraDatastream extends FedoraDatastream { + protected $fedoraRelsIntClass = 'IslandoraFedoraRelsInt'; + protected $fedoraDatastreamVersionClass = 'IslandoraFedoraDatastreamVersion'; +} + +class IslandoraFedoraDatastreamVersion extends FedoraDatastreamVersion { + protected $fedoraRelsIntClass = 'IslandoraFedoraRelsInt'; + protected $fedoraDatastreamVersionClass = 'IslandoraFedoraDatastreamVersion'; +} + +class IslandoraFedoraRelsExt extends FedoraRelsExt {} + +class IslandoraFedoraRelsInt extends FedoraRelsInt {} diff --git a/includes/utilities.inc b/includes/utilities.inc index b2006722..ac63f35c 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -112,29 +112,66 @@ function islandora_describe_repository($url = NULL) { } /** - * Build a list of all the hooks to call. + * Build and invoke a list of hooks by combining the given hook and refinements. + * + * The given hook will be called as MODULE_HOOK, and for each hook refinement + * as MODULE_REFINEMENT_HOOK. Any additional arguments passed to this function + * will be passed as arguments to module_invoke_all(). * - * Concatenates the each pid (escaped) to the hook name, for calling in - * module_invoke_all(). + * @see islandora_build_hook_list() + * To see how the hook list is generated. * * @param string $hook * A hook to call. - * @param array $pids - * An array of PIDs (probably content models). + * @param array $refinements + * An array of strings, that will be escaped and concatinated with the given + * hook. This will most likely be PIDs/DSIDs/Labels etc. We often refine our + * hooks using an objects model. + * @param array $args + * Any arguments to pass onto module_invoke_all(). * * @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. + * The merged results from all the hooks. */ -function islandora_build_hook_list($hook, $pids = array()) { - $hooks = array(); - - $pids = array_unique($pids); - foreach ($pids as $pid) { - $hooks[] = islandora_escape_pid_for_function($pid) . '_' . $hook; +function islandora_invoke_hook_list($hook, array $refinements, array $args) { + dsm(func_get_args()); + $return = array(); + foreach (islandora_build_hook_list($hook, $refinements) as $hook) { + array_unshift($args, $hook); + $result = call_user_func_array('module_invoke_all', $args); + $return = array_merge_recursive($return, $result); + array_shift($args); } - $hooks[] = $hook; + return $return; +} +/** + * Build a list of all the hooks to call. + * + * Concatenates each hook $refinement (escaped) to the hook name, for calling + * with module_invoke_all(). + * + * Any non-valid PHP function characters in the given refinements are + * converted to "_" characters. + * + * @param string $hook + * The base hook to concatenate. + * @param array $refinements + * An array of strings, that will be escaped and concatinated with the given + * hook. This will most likely be PIDs/DSIDs/Labels etc. We often refine our + * hooks using an objects model. + * + * @return array + * An array with each refinement escaped and concatenated with the base hook + * name, in addition to the base hook name. + */ +function islandora_build_hook_list($hook, $refinements = array()) { + $refinements = array_unique($refinements); + $hooks = array($hook); + foreach ($refinements as $refinement) { + $refinement = preg_replace('/[^a-zA-Z0-9_]/', '_', $refinement); + $hooks[] = "{$refinement}_{$hook}"; + } return $hooks; } diff --git a/islandora.api.php b/islandora.api.php index f017f937..4fe2ea4f 100644 --- a/islandora.api.php +++ b/islandora.api.php @@ -2,24 +2,26 @@ /** * @file - * This file lists and documents all available hook functions to manipulate data. + * This file documents all available hook functions to manipulate data. */ /** * Generate a repository objects view. * - * @param FedoraObject $fedora_object - * A Tuque FedoraObject being operated on. + * @param FedoraObject $object + * The object to display * @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. + * @param string $page_size + * The size of the page. * * @return array * An array whose values are markup. */ -function hook_islandora_view_object($fedora_object, $user, $page_number, $page_size) {} +function hook_islandora_view_object($object, $user, $page_number, $page_size) { +} /** * Generate an object's display for the given content model. @@ -27,35 +29,38 @@ function hook_islandora_view_object($fedora_object, $user, $page_number, $page_s * Content models PIDs have colons and hyphens changed to underscores, to * create the hook name. * - * @param type $fedora_object + * @param FedoraObject $object * A Tuque FedoraObject * * @return array * An array whose values are markup. */ -function hook_CMODEL_PID_islandora_view_object($fedora_object) {} +function hook_CMODEL_PID_islandora_view_object($object) { +} /** * Alter display output after it has been generated. * - * @param FedoraObject $fedora_object + * @param FedoraObject $object * A Tuque FedoraObject being operated on. - * @param array $arr + * @param array $rendered * An arr of rendered views. */ -function hook_islandora_view_object_alter(&$fedora_object, &$arr) {} +function hook_islandora_view_object_alter(&$object, &$rendered) { +} /** * Generate an object's management display. * - * @param type $fedora_object + * @param FedoraObject $object * A Tuque FedoraObject * * @return array * An array whose values are markup. */ -function hook_islandora_edit_object($fedora_object) {} +function hook_islandora_edit_object($object) { +} /** * Generate an object's management display for the given content model. @@ -63,148 +68,305 @@ function hook_islandora_edit_object($fedora_object) {} * Content models PIDs have colons and hyphens changed to underscores, to * create the hook name. * - * @param type $fedora_object + * @param FedoraObject $object * A Tuque FedoraObject * * @return array * An array whose values are markup. */ -function hook_CMODEL_PID_islandora_edit_object($fedora_object) {} +function hook_CMODEL_PID_islandora_edit_object($object) { +} /** * Allow management display output to be altered. * - * @param type $fedora_object + * @param FedoraObject $object * A Tuque FedoraObject - * @param type $arr + * @param array $rendered * an arr of rendered views */ -function hook_islandora_edit_object_alter(&$fedora_object, &$arr) {} +function hook_islandora_edit_object_alter(&$object, &$rendered) { +} + +/** + * Allows modules to alter the object or block/modify the given action. + * + * This alter hook will be called before any object is ingested, modified or + * purged. + * + * Changing object properties such as "label", or "state", are considered + * modifications, where as manipulating an object's datstreams are not. + * + * @param AbstractFedoraObject $object + * The object to alter. + * @param array $context + * The context for the alter action, this will always contain at the + * following properties. + * + * @code + * array( + * // Either 'ingest', 'purge', 'modify'. + * 'action' => 'ingest', + * // Either TRUE or FALSE, if TRUE the action won't take place. + * // Set by the implementing alter hook. + * 'block' => FALSE, + * ) + * @endcode + * + * When the action is "purge" two additional boolean properties are present + * 'delete' defaults to FALSE, and 'purge' defaults to TRUE. If only purge + * is set to TRUE the object will be 'purged' if delete is set to TRUE and + * block is not then the object state will be set to 'Deleted'. If 'block' + * is set to TRUE the object will not be deleted or purged. + */ +function hook_islandora_object_alter(AbstractFedoraObject $object, array &$context) { +} /** - * Allows modules to add to an objects ingest process. + * Allows modules to alter the object or block/modify the given action. * - * @param FedoraObject $fedora_object - * A Tuque FedoraObject. + * @see hook_islandora_object_alter() */ -function hook_islandora_ingest_post_ingest($fedora_object) {} +function hook_CMODEL_PID_islandora_object_alter(AbstractFedoraObject $object, array &$context) { +} /** - * Allow modules to add to the ingest process of a specific content model. + * Allows modules to alter the datastream or block/modify the given action. + * + * This alter hook will be called before any datastream is ingested, modified or + * purged. + * + * Adding datastreams to NewFedoraObject's will not trigger this alter hook + * immediately, instead it will be triggered for all datastreams at the time + * of the NewFedoraObject's ingest. + * + * Purging datastreams from a NewFedoraObject will not trigger this alter hook + * at all. + * + * Changing datastream's properties such as "label", or "state", are considered + * modifications, as well as changing the datastreams content. + * + * @param AbstractFedoraObject $object + * The object to the datastream belong to. + * @param AbstractFedoraDatastream $datastream + * The datastream to alter. + * @param array $context + * The context for the alter action, this will always contain at the + * following properties. + * + * @code + * array( + * // Either 'ingest', 'purge', 'modify'. + * 'action' => 'ingest', + * // Either TRUE or FALSE, if TRUE the action won't take place. + * // Set by the implementing alter hook. + * 'block' => FALSE, + * ) + * @endcode + * + * When the action is "purge" two additional boolean properties are present + * 'delete' (defaults to FALSE), and 'purge' (defaults to TRUE). If only purge + * is set to TRUE the datastream will be 'purged' if delete is set to TRUE and + * block is not then the datastream state will be set to 'Deleted'. If 'block' + * is set to TRUE the datastream will not be deleted or purged. + * + * When the action is "modify" there is an additional property "params" that + * contains the modifications about to take place. */ -function hook_CMODEL_PID_islandora_ingest_post_ingest($fedora_object) {} +function hook_islandora_datastream_alter(AbstractFedoraObject $object, AbstractFedoraDatastream $datastream, array &$context) { +} + +/** + * Allows modules to alter the datastream or block/modify the given action. + * + * @see hook_islandora_datastream_alter() + */ +function hook_CMODEL_PID_DSID_islandora_datastream_alter(AbstractFedoraObject $object, AbstractFedoraDatastream $datastream, array &$context) { +} /** - * Allows modules to add to a repository objects view/edit(/misc) process. + * Notify modules that the given object was ingested. * - * If you implement this hook you must also register your module with - * hook_islandora_hook_info(). + * This hook is called after an object has been successfully ingested via a + * FedoraRepository object. * - * @param FedoraObject $fedora_object - * A Tuque FedoraObject. + * @note + * If ingested directly via the FedoraApiM object this will not be called as we + * don't have access to the ingested object at that time. * - * @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. + * @param FedoraObject $object + * The object that was ingested. */ -function hook_islandora_pre_purge_object($fedora_object) {} +function hook_islandora_object_ingested(FedoraObject $object) { +} /** - * Allow modules to react to the purge process of a specific content model. + * Notify modules that the given object was ingested. * - * @see hook_islandora_pre_purge_object() + * @see hook_islandora_object_ingested() */ -function hook_CMODEL_PID_islandora_pre_purge_object($fedora_object) {} +function hook_CMODEL_PID_islandora_object_ingested(FedoraObject $object) { +} /** - * Register potential ingest routes. + * Notify modules that the given object was modified. * - * Implementations should return an array containing possible routes. + * This hook is called after an object has been successfully modified. + * + * Changing object properties such as "label", or "state", are considered + * modifications, where as manipulating an object's datstreams are not. + * + * @param FedoraObject $object + * The object that was ingested. + * + * @todo We should also include what changes were made in a additional + * parameter. */ -function hook_islandora_ingest_registry($collection_object) { - $reg = array( - array( - 'name' => t('Ingest route name'), - 'url' => 'ingest_route/url', - 'weight' => 0, - ), - ); - return $reg; +function hook_islandora_object_modified(FedoraObject $object) { } /** - * Register a datastream edit route/form. + * Notify modules that the given object was ingested. * - * @param $islandora_object - * @param $ds_id + * @see hook_islandora_object_modified() */ -function hook_islandora_edit_datastream_registry($islandora_object, $ds_id) {} +function hook_CMODEL_PID_islandora_object_modified(FedoraObject $object) { +} /** - * Alter an object before it gets used further down the stack. + * Notify modules that the given object was purged/deleted. * - * @param type $object - * A Tuque FedoraObject + * This hook is called after an object has been successfully purged, or + * when its state has been changed to "Deleted". + * + * @param string $pid + * The ID of the object that was purged/deleted. */ -function hook_islandora_object_alter($fedora_object) {} +function hook_islandora_object_purged($pid) { +} /** - * Allow modification of an object before ingesting. + * Notify modules that the given object was purged/deleted. * - * @param type $islandora_object - * A Tuque FedoraObject + * @see hook_islandora_object_purged() */ -function hook_islandora_ingest_pre_ingest($islandora_object) {} +function hook_CMODEL_PID_islandora_object_purged($pid) { +} /** - * Allow modification of objects of a certain content model before ingesting. + * Notify modules that the given datastream was ingested. + * + * This hook is called after the datastream has been successfully ingested. + * + * @note + * If ingested directly via the FedoraApiM object this will not be called as we + * don't have access to the ingested datastream at that time. * - * @see hook_islandora_ingest_pre_ingest() + * @param FedoraObject $object + * The object the datastream belongs to. + * @param FedoraDatastream $datastream + * The ingested datastream. */ -function hook_CMODEL_PID_islandora_ingest_pre_ingest($islandora_object) {} +function hook_islandora_datastream_ingested(FedoraObject $object, FedoraDatastream $datastream) { +} /** - * Allow modules to setup for the purge of a datastream. + * Notify modules that the given datastream was ingested. * - * @param object $datastream - * A Tuque FedoraDatastream object. + * @see hook_islandora_object_ingested() */ -function hook_islandora_pre_purge_datastream($datastream) {} +function hook_CMODEL_PID_DSID_islandora_datastream_ingested(FedoraObject $object, FedoraDatastream $datastream) { +} /** - * Allow modules to react after a datastream is purged. + * Notify modules that the given datastream was modified. * - * @param object $object - * A Tuque FedoraObject. + * This hook is called after an datastream has been successfully modified. + * + * Changing datastream properties such as "label", or "state", are considered + * modifications, as well as the datastreams content. + * + * @param FedoraObject $object + * The object the datastream belongs to. + * @param FedoraDatastream $datastream + * The datastream that was ingested. + * + * @todo We should also include what changes were made in a additional + * parameter. + */ +function hook_islandora_datastream_modified(FedoraObject $object, FedoraDatastream $datastream) { +} + +/** + * Notify modules that the given datastream was ingested. + * + * @see hook_islandora_datastream_modified() + */ +function hook_CMODEL_PID_islandora_datastream_modified(FedoraObject $object, FedoraDatastream $datastream) { +} + +/** + * Notify modules that the given datastream was purged/deleted. + * + * This hook is called after an datastream has been successfully purged, or + * when its state has been changed to "Deleted". + * + * @param FedoraObject $object + * The object the datastream belonged to. * @param string $dsid - * A id of the former datastream. + * The ID of the datastream that was purged/deleted. + */ +function hook_islandora_datastream_purged(FedoraObject $object, $dsid) { +} + +/** + * Notify modules that the given datastream was purged/deleted. + * + * @see hook_islandora_datastream_purged() + */ +function hook_CMODEL_PID_islandora_datastream_purged(FedoraObject $object, $dsid) { +} + +/** + * Register potential ingest routes. + * + * Implementations should return an array containing possible routes. */ -function hook_islandora_post_purge_datastream($object, $dsid) {} +function hook_islandora_ingest_registry($collection_object) { + $reg = array( + array( + 'name' => t('Ingest route name'), + 'url' => 'ingest_route/url', + 'weight' => 0, + ), + ); + return $reg; +} /** - * Allow modules to react post-purge. + * Register a datastream edit route/form. * - * @param string $object_id - * The former object's PID. - * @param array $content_models - * An array containing the models to which the former object. + * @param FedoraObject $object + * The object to check. + * @param string $dsid + * todo */ -function hook_islandora_post_purge_object($object_id, $content_models) {} +function hook_islandora_edit_datastream_registry($object, $dsid) { +} /** * Registry hook for required objects. * * Solution packs can include data to create certain objects that describe or - * help the objects it would create. This includes collection objects and content - * models. + * help the objects it would create. This includes collection objects and + * content models. * * @see islandora_solution_packs_admin() * @see islandora_install_solution_pack() * @example islandora_islandora_required_objects() */ -function hook_islandora_required_objects() {} +function hook_islandora_required_objects() { +} /** * Registry hook for viewers that can be implemented by solution packs. @@ -215,20 +377,21 @@ function hook_islandora_required_objects() {} * @see islandora_get_viewers() * @see islandora_get_viewer_callback() */ -function hook_islandora_viewer_info() {} - +function hook_islandora_viewer_info() { +} /** * Returns a list of datastreams that are determined to be undeletable. */ -function hook_islandora_undeletable_datastreams(array $models) {} +function hook_islandora_undeletable_datastreams(array $models) { +} /** * Define steps used in the islandora_ingest_form() ingest process. * * @param array $form_state * An array containing the form_state, on which infomation from step storage - * might be extracted. Note that the + * might be extracted. Note that the * * @return array * An associative array of associative arrays which define each step in the @@ -265,4 +428,5 @@ function hook_islandora_ingest_steps(array $form_state) { * * @see hook_islandora_ingest_steps() */ -function hook_CMODEL_PID_islandora_ingest_steps(array $form_state) {} +function hook_CMODEL_PID_islandora_ingest_steps(array $form_state) { +} diff --git a/islandora.info b/islandora.info index d883aa49..518a1434 100644 --- a/islandora.info +++ b/islandora.info @@ -8,8 +8,9 @@ stylesheets[all][] = css/islandora.base.css stylesheets[all][] = css/islandora.theme.css files[] = includes/MimeDetect.inc files[] = includes/DublinCore.inc -files[] = includes/IslandoraTuque.inc -files[] = includes/IslandoraTuqueWrapper.inc +files[] = includes/islandora_tuque.inc +files[] = includes/islandora_tuque_wrapper.inc files[] = tests/islandora_web_test_case.inc files[] = tests/islandora_authtokens.test +files[] = tests/islandora_hooks.test php = 5.3 diff --git a/islandora.module b/islandora.module index 05b55cc8..bccf7322 100644 --- a/islandora.module +++ b/islandora.module @@ -37,14 +37,14 @@ define('FEDORA_MANAGE_PROPERTIES', 'manage object properties'); // 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'); +// @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'); -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(). @@ -541,7 +541,7 @@ function islandora_default_islandora_view_object($object) { * A IslandoraTuque instance */ function islandora_get_tuque_connection($user = NULL, $url = NULL) { - module_load_include('inc', 'islandora', 'includes/IslandoraTuque'); + module_load_include('inc', 'islandora', 'includes/islandora_tuque'); $tuque = &drupal_static(__FUNCTION__); if (!$tuque) { if (IslandoraTuque::exists()) { @@ -578,9 +578,7 @@ function islandora_object_load($object_id) { $tuque = islandora_get_tuque_connection(); if ($tuque) { try { - $object = $tuque->repository->getObject(urldecode($object_id)); - drupal_alter('islandora_object', $object); - return $object; + return $tuque->repository->getObject(urldecode($object_id)); } catch (Exception $e) { if ($e->getCode() == '404') { return FALSE; @@ -744,124 +742,33 @@ function islandora_islandora_undeletable_datastreams(array $models) { } /** - * Ingest the given object into Fedora calling its pre/post hooks as well. - * - * @todo will be cleaned up in the future + * Ingest the given object. * * @param NewFedoraObject $object * An ingestable FedoraObject. * * @return FedoraObject - * The ingested FedoraObject, after running the pre/post ingest hooks. - * Returns FALSE if the ingest failed. + * The ingested FedoraObject. */ 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); - } + return $object->repository->ingestObject($object); } /** - * Deletes the given object into Fedora calling its pre/post hooks as well. + * Delete's or purges the given object. * * @param FedoraObject $object * An object to delete. * - * @return FedoraObject + * @return bool * 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; + return $object->repository->purgeObject($object->id); } /** - * 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. + * Delete's or purges the given datastream. * * @throws Exception * Which types are undefined, but more than likely because of the hooks @@ -870,79 +777,12 @@ function islandora_post_delete_object($object_id, array $models) { * @param FedoraDatastream $datastream * The datastream to delete. * - * @return boolean - * TRUE is returned if the datastream was Deleted/Purged, FALSE if it was - * blocked. + * @return bool + * TRUE if successful, FALSE otherwise. */ 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); - } + return $object->purgeDatastream($datastream->id); } /** diff --git a/tests/README b/tests/README new file mode 100644 index 00000000..797d788b --- /dev/null +++ b/tests/README @@ -0,0 +1,3 @@ +You can define your own configurations specific to your enviroment by copying +default.test_config.ini to test_config.ini, making your changes in the copied +file. \ No newline at end of file diff --git a/tests/default.test_config.ini b/tests/default.test_config.ini new file mode 100644 index 00000000..f07fb4c3 --- /dev/null +++ b/tests/default.test_config.ini @@ -0,0 +1,6 @@ +[fedora] +fedora_url = "http://localhost:8080/fedora" +use_drupal_filter = TRUE +drupal_filter_file = "/usr/local/fedora/server/config/filter-drupal.xml" +admin_user = "fedoraAdmin" +admin_pass = "fedoraAdmin" diff --git a/tests/islandora_authtokens.test b/tests/islandora_authtokens.test index 953964c4..326225f2 100644 --- a/tests/islandora_authtokens.test +++ b/tests/islandora_authtokens.test @@ -1,7 +1,15 @@ 'Islandora Authorization Tokens', @@ -10,45 +18,50 @@ class IslandoraAuthtokensTestCase extends IslandoraWebTestCase { ); } + /** + * Set up data for the tests. + */ public function setUp() { - parent::setUp(array('islandora')); + parent::setUp(); } + /** + * Test redeeming invalid tokens. + */ public function testRedeemInvalidToken() { module_load_include('inc', 'islandora', 'includes/islandora_authtokens'); - - // get a token $token = islandora_get_object_token('test:pid', 'woot', 1); $this->assertTrue($token, 'Token was generated correctly.', 'Unit Tests'); - - // redeem a token that doesn't exist with real pid and dsid + // Redeem a token that doesn't exist with real pid and dsid. $account = islandora_validate_object_token('test:pid', 'woot', 'foo'); $this->assertFalse($account, 'Redeeming an token that doesn\'t exist returns FALSE', 'Unit Tests'); } + /** + * Test redeeming valid tokens. + */ public function testRedeemValidToken() { module_load_include('inc', 'islandora', 'includes/islandora_authtokens'); - - // change the current user + // Change the current user. global $user; $user_backup = $user; $test_account = $this->drupalCreateUser(); $user = $test_account; - $token = islandora_get_object_token('test:pid', 'woot', 1); - // logout again + // Logout again. $user = $user_backup; - $token_account = islandora_validate_object_token('test:pid', 'woot', $token); - $this->assertEqual($token_account->uid, $test_account->uid, 'UID from token is correct', 'Unit Tests'); $this->assertEqual($token_account->pass, $test_account->pass, 'Pass from token is correct', 'Unit Tests'); $this->assertEqual($token_account->name, $test_account->name, 'Name from token is correct', 'Unit Tests'); } + /** + * Test tokened datastream view without XACML. + */ public function testTokenedViewDatastreamWithoutXacml() { - // ingest the fixture + // Ingest the fixture. $fixture_path = drupal_get_path('module', 'islandora') . '/tests/fixtures/bug.jp2'; $tuque = islandora_get_tuque_connection(); $newpid = "{$this->randomName()}:{$this->randomName()}"; @@ -67,8 +80,8 @@ class IslandoraAuthtokensTestCase extends IslandoraWebTestCase { $this->drupalGet("islandora/object/{$newpid}/datastream/JP2/view"); $this->assertResponse(200, 'Page loaded as the authorized user'); - // do some voodoo to get a token as the user we are connecting as - // to do this we need to change the user we are logged in as + // Do some voodoo to get a token as the user we are connecting as + // to do this we need to change the user we are logged in as. module_load_include('inc', 'islandora', 'includes/islandora_authtokens'); global $user; $backup = $user; @@ -84,11 +97,14 @@ class IslandoraAuthtokensTestCase extends IslandoraWebTestCase { $this->drupalGet("islandora/object/{$newpid}/datastream/JP2/view", array('query' => array('token' => $token))); $this->assertResponse(403, 'Token is unable to be reused'); - // delete fixture object + // Delete fixture object. $tuque->repository->purgeObject($newpid); } + /** + * This will test something someday. + */ public function testTokenedViewDatastreamWithXacml() { - // we need to add this test. + // We need to add this test. } -} \ No newline at end of file +} diff --git a/tests/islandora_hooks.test b/tests/islandora_hooks.test new file mode 100644 index 00000000..0a667fee --- /dev/null +++ b/tests/islandora_hooks.test @@ -0,0 +1,213 @@ + 'Islandora Hooks', + 'description' => 'Ensure that the hooks for ingestion/purge/modification are called at the appropriate times.', + 'group' => 'Islandora', + ); + } + + /** + * Creates an admin user and a connection to a fedora repository. + * + * @see IslandoraWebTestCase::setUp() + */ + public function setUp() { + parent::setUp('islandora_hooks_test', 'devel'); + $this->repository = $this->admin->repository; + $this->purgeTestObjects(); + } + + /** + * Free any objects/resources created for this test. + * + * @see IslandoraWebTestCase::tearDown() + */ + public function tearDown() { + $this->purgeTestObjects(); + unset($this->repository); + parent::tearDown(); + } + + /** + * Purge any objects created by the test's in this class. + */ + public function purgeTestObjects() { + $objects = array( + 'test:testIngestedObjectHook', + 'test:testBlockedIngestedObjectHook', + 'test:testModifiedObjectHook', + 'test:testPurgedObjectHook', + 'test:testIngestedDatastreamHook', + 'test:testModifiedDatastreamHook', + 'test:testPurgedDatastreamHook', + ); + foreach ($objects as $object) { + try { + $object = $this->repository->getObject($object); + $object->label = "Don't Block"; + $this->repository->purgeObject($object->id); + } + catch(Exception $e) { + // Meh... Either it didn't exist or the purge failed. + } + } + } + + /** + * Test ALL THE HOOKS!. + */ + public function testHooks() { + // Test ingesting with FedoraRepository::ingestObject(). + $object = $this->repository->constructObject('test:testIngestedObjectHook'); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK] = FALSE; + $this->repository->ingestObject($object); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called "hook_islandora_object_alter" when ingesting via FedoraRepository::ingestObject.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called ISLANDORA_OBJECT_INGESTED_HOOK when ingesting via FedoraRepository::ingestObject.'); + $this->repository->purgeObject($object->id); + // Test blocking the ingest. + $object = $this->repository->constructObject('test:testIngestedObjectHook'); + $object->label = 'block'; + try { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK] = FALSE; + $this->repository->ingestObject($object); + $this->fail('Blocked ingest should throw an Exception.'); + $this->repository->purgeObject($object->id); + } + catch(Exception $e) { + $this->pass('Ingest blocked and exception thrown.'); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Called "hook_islandora_object_alter" when blocking ingesting via FedoraRepository::ingestObject.'); + $this->assertFalse($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK], 'Did not called ISLANDORA_OBJECT_INGESTED_HOOK when blocking ingesting via FedoraRepository::ingestObject.'); + } + // Test modifying via set magic functions. + $object = $this->repository->constructObject('test:testModifiedObjectHook'); + $this->repository->ingestObject($object); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE; + $object->label = "New Label!"; + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called "hook_islandora_object_alter" when modifying via set magic functions.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called ISLANDORA_OBJECT_MODIFIED_HOOK when modifying via set magic functions.'); + // Test blocking the modification. + try { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK] = FALSE; + $object->label = 'block'; + $this->fail('Blocked modify should throw an Exception.'); + } + catch(Exception $e) { + $this->pass('Modify blocked and exception thrown.'); + $this->assertNotEqual($object->label, 'block', 'Modification did not stick.'); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called "hook_islandora_object_alter" when blocking modifying via set magic functions.'); + $this->assertFALSE($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK], 'Called ISLANDORA_OBJECT_MODIFIED_HOOK when blocking modifying via set magic functions.'); + } + $this->repository->purgeObject($object->id); + // Test purging with FedoraRepository::purgeObject(). + $object = $this->repository->constructObject('test:testPurgedObjectHook'); + $this->repository->ingestObject($object); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $this->repository->purgeObject($object->id); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when purging via FedoraRepository::purgeObject.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when purging via FedoraRepository::purgeObject.'); + // Test deleting. + $object = $this->repository->constructObject('test:testPurgedObjectHook'); + $this->repository->ingestObject($object); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $object->delete(); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when deleting via FedoraObject::delete.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when purging via FedoraObject::delete.'); + $this->repository->purgeObject($object->id); + // Test alter blocking. + $object = $this->repository->constructObject('test:testPurgedObjectHook'); + $this->repository->ingestObject($object); + try { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $object->label = 'block'; + $this->repository->purgeObject($object->id); + $this->fail('Blocked modify should throw an Exception.'); + } + catch(Exception $e) { + $this->pass('Modify blocked and exception thrown.'); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when blocking purge via FedoraRepository::purgeObject.'); + $this->assertFalse($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when blocking purge via FedoraRepository::purgeObject.'); + } + // Test alter delete. + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = FALSE; + $object->label = 'delete'; + $this->repository->purgeObject($object->id); + $this->assertEqual($object->state, 'D', '"hook_islandora_object_alter" prevented purge and deleted the object.'); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called "hook_islandora_object_alter" when preventing purge and deleting.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK], 'Called ISLANDORA_OBJECT_PURGED_HOOK when preventing purge and deleting.'); + $object->label = 'Something other than delete'; + $this->repository->purgeObject($object->id); + // Test ingesting with FedoraRepository::ingestObject(). + $object = $this->repository->constructObject('test:testIngestedDatastreamHook'); + $this->repository->ingestObject($object); + $ds = $object->constructDatastream('TEST'); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = FALSE; + $object->ingestDatastream($ds); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called "hook_islandora_datastream_alter" when ingesting via FedoraObject::ingestDatastream.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK], 'Called ISLANDORA_DATASTREAM_INGESTED_HOOK when ingesting via FedoraObject::ingestDatastream.'); + $this->repository->purgeObject($object->id); + // Test modifying a datastream. + $object = $this->repository->constructObject('test:testModifiedDatastreamHook'); + $this->repository->ingestObject($object); + $ds = $object->constructDatastream('TEST'); + $object->ingestDatastream($ds); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE; + $ds->label = "New Label!"; + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called "hook_islandora_datastream_alter" when modifying via set magic functions.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when modifying via set magic functions.'); + // Test blocking modifying. + try { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = FALSE; + $ds->label = 'block'; + $this->fail('Blocked modify should throw an Exception.'); + } + catch(Exception $e) { + $this->pass('Modify blocked and exception thrown.'); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called "hook_islandora_datastream_alter" when blocking modifying via set magic functions.'); + $this->assertFALSE($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK], 'Called ISLANDORA_DATASTREAM_MODIFIED_HOOK when blocking modifying via set magic functions.'); + } + $this->repository->purgeObject($object->id); + // Test purging with FedoraRepository::purgeObject(). + $object = $this->repository->constructObject('test:testPurgedDatastreamHook'); + $this->repository->ingestObject($object); + $ds = $object->constructDatastream('TEST'); + $object->ingestDatastream($ds); + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_PURGED_HOOK] = FALSE; + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_PURGED_HOOK] = FALSE; + $object->purgeDatastream($ds->id); + $this->assert($_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_PURGED_HOOK], 'Called "hook_islandora_datastream_alter" when purging via FedoraObject::purgeDatastream.'); + $this->assert($_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_PURGED_HOOK], 'Called ISLANDORA_DATASTREAM_PURGED_HOOK when purging via FedoraObject::purgeDatastream.'); + $this->repository->purgeObject($object->id); + } + +} diff --git a/tests/islandora_hooks_test.info b/tests/islandora_hooks_test.info new file mode 100644 index 00000000..9d211978 --- /dev/null +++ b/tests/islandora_hooks_test.info @@ -0,0 +1,7 @@ +name = Islandora Hook testing +description = Tests Hooks. Do not enable. +core = 7.x +package = Testing +hidden = TRUE +dependencies[] = islandora +files[] = islandora_hooks_test.module diff --git a/tests/islandora_hooks_test.module b/tests/islandora_hooks_test.module new file mode 100644 index 00000000..315b88ee --- /dev/null +++ b/tests/islandora_hooks_test.module @@ -0,0 +1,152 @@ +id == 'test:testIngestedObjectHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_INGESTED_HOOK] = TRUE; + if ($object->label == 'block') { + $context['block'] = TRUE; + } + } + break; + + case 'modify': + if ($object->id == 'test:testModifiedObjectHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_MODIFIED_HOOK] = TRUE; + if (isset($context['params']['label']) && $context['params']['label'] == 'block') { + $context['block'] = TRUE; + } + } + elseif ($object->id == 'test:testPurgedObjectHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = TRUE; + if (isset($context['params']['label']) && $context['params']['label'] == 'block') { + $context['block'] = TRUE; + } + elseif (isset($context['params']['label']) && $context['params']['label'] == 'delete') { + $context['delete'] = TRUE; + } + } + break; + + case 'purge': + if ($object->id == 'test:testPurgedObjectHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_OBJECT_PURGED_HOOK] = TRUE; + if ($object->label == 'block') { + $context['block'] = TRUE; + } + elseif ($object->label == 'delete') { + $context['delete'] = TRUE; + } + } + break; + } +} + +/** + * Implements hook_islandora_object_alter(). + */ +function islandora_hooks_test_islandora_datastream_alter(AbstractFedoraObject $object, AbstractFedoraDatastream $datastream, array &$context) { + switch ($context['action']) { + case 'ingest': + if ($object->id == 'test:testIngestedDatastreamHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = TRUE; + if ($datastream->label == 'block') { + $context['block'] = TRUE; + } + } + break; + + case 'modify': + if ($object->id == 'test:testModifiedDatastreamHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = TRUE; + if (isset($context['params']['dsLabel']) && $context['params']['dsLabel'] == 'block') { + $context['block'] = TRUE; + } + } + elseif ($object->id == 'test:testPurgedDatastreamHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_PURGED_HOOK] = TRUE; + if (isset($context['params']['dsLabel']) && $context['params']['dsLabel'] == 'block') { + $context['block'] = TRUE; + } + elseif (isset($context['params']['dsLabel']) && $context['params']['dsLabel'] == 'delete') { + $context['delete'] = TRUE; + } + } + break; + + case 'purge': + if ($object->id == 'test:testPurgedDatastreamHook') { + $_SESSION['islandora_hooks']['alter'][ISLANDORA_DATASTREAM_PURGED_HOOK] = TRUE; + if ($datastream->label == 'block') { + $context['block'] = TRUE; + } + elseif ($datastream->label == 'delete') { + $context['delete'] = TRUE; + } + } + break; + } +} + +/** + * Implements hook_islandora_object_ingested(). + */ +function islandora_hooks_test_islandora_object_ingested(FedoraObject $object) { + if ($object->id == 'test:testIngestedObjectHook') { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_INGESTED_HOOK] = TRUE; + } +} + +/** + * Implements hook_islandora_object_modified(). + */ +function islandora_hooks_test_islandora_object_modified(FedoraObject $object) { + if ($object->id == 'test:testModifiedObjectHook') { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_MODIFIED_HOOK] = TRUE; + } +} + +/** + * Implements hook_islandora_object_purged(). + */ +function islandora_hooks_test_islandora_object_purged($pid) { + if ($pid == 'test:testPurgedObjectHook') { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_OBJECT_PURGED_HOOK] = TRUE; + } +} + +/** + * Implements hook_islandora_datastream_ingested(). + */ +function islandora_hooks_test_islandora_datastream_ingested(FedoraObject $object, FedoraDatastream $datastream) { + if ($object->id == 'test:testIngestedDatastreamHook' && $datastream->id == "TEST") { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_INGESTED_HOOK] = TRUE; + } +} + +/** + * Implements hook_islandora_datastream_modified(). + */ +function islandora_hooks_test_islandora_datastream_modified(FedoraObject $object, FedoraDatastream $datastream) { + if ($object->id == 'test:testModifiedDatastreamHook' && $datastream->id == "TEST") { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_MODIFIED_HOOK] = TRUE; + } +} + +/** + * Implements hook_islandora_datastream_purged(). + */ +function islandora_hooks_test_islandora_datastream_purged(FedoraObject $object, $dsid) { + if ($object->id == 'test:testPurgedDatastreamHook' && $dsid == "TEST") { + $_SESSION['islandora_hooks']['hook'][ISLANDORA_DATASTREAM_PURGED_HOOK] = TRUE; + } +} diff --git a/tests/islandora_web_test_case.inc b/tests/islandora_web_test_case.inc index d47dc214..cc5b8c67 100644 --- a/tests/islandora_web_test_case.inc +++ b/tests/islandora_web_test_case.inc @@ -1,36 +1,124 @@ configuration = $this->getTestConfiguration(); + if ($this->configuration['use_drupal_filter']) { + $this->backUpDrupalFilter(); + $this->setUpDrupalFilter(); + } + $this->createAdminUser(); + } - $islandora_path = drupal_get_path('module', 'islandora'); - $this->config_info = parse_ini_file("$islandora_path/tests/test_config.ini"); - $connection_info = Database::getConnectionInfo('default'); + /** + * Parses and returns the settings from the test configuration file. + * + * If no install specific test_config.ini file is found, it will use the + * assumed default configs found in default.test_config.ini. + * + * @return array + * The test configuration. + * + * @see parse_ini_file() + */ + protected function getTestConfiguration() { + $path = drupal_get_path('module', 'islandora'); + if (file_exists("$path/tests/test_config.ini")) { + $this->pass('Using custom test configuration.'); + return parse_ini_file("$path/tests/test_config.ini"); + } + elseif (file_exists("$path/tests/default.test_config.ini")) { + $this->pass('Using default test configuration.'); + return parse_ini_file("$path/tests/default.test_config.ini"); + } + throw new Exception('Required default.test_config.ini/test_config.ini file not found'); + } - $this->original_drupal_fiter = file_get_contents($this->config_info['filter_drupal_file']); + /** + * Stores the content of the Drupal Filter for later restoration. + */ + protected function backUpDrupalFilter() { + if (file_exists($this->configuration['filter_drupal_file'])) { + $this->originalDrupalFilterContent = file_get_contents($this->configuration['filter_drupal_file']); + } + else { + throw new Exception('Failed to find the required Drupal Filter configuration file.'); + } + } + /** + * Sets up a drupal filter that can read for the tests users table. + */ + protected function setUpDrupalFilter() { + $connection_info = Database::getConnectionInfo('default'); $drupal_filter_dom = new DomDocument(); - $drupal_filter_dom->loadXML($this->original_drupal_fiter); + $drupal_filter_dom->loadXML($this->originalDrupalFilterContent); $drupal_filter_xpath = new DOMXPath($drupal_filter_dom); - $server = $connection_info['default']['host']; $dbname = $connection_info['default']['database']; $user = $connection_info['default']['username']; $password = $connection_info['default']['password']; $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; $prefix = $connection_info['default']['prefix']['default']; - $results = $drupal_filter_xpath->query("/FilterDrupal_Connection/connection[@server='$server' and @dbname='$dbname' and @user='$user' and @password='$password' and @port='$port']/sql"); $results->item(0)->nodeValue = "SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;"; + file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); + } - file_put_contents($this->config_info['filter_drupal_file'], $drupal_filter_dom->saveXML()); + /** + * Creates the a full fedora admin user with a repository connection. + */ + protected function createAdminUser() { + $this->admin = new stdClass(); + $this->admin->uid = 1; + $this->admin->name = $this->configuration['admin_user']; + $this->admin->pass = $this->configuration['admin_pass']; + $url = variable_get('islandora_base_url', $this->configuration['fedora_url']); + $connection = islandora_get_tuque_connection($this->admin, $url); + $this->admin->repository = $connection->repository; + return $this->admin; + } + /** + * Stores the content of the Drupal Filter for later restoration. + */ + protected function restoreDrupalFilter() { + $file = $this->configuration['filter_drupal_file']; + if (isset($this->originalDrupalFilterContent)) { + file_put_contents($file, $this->originalDrupalFilterContent); + } + elseif (file_exists($file)) { + // Remove if there was never an original. + drupal_unlink($file); + } } + /** + * Restores the original Drupal filter, frees any allocated resources. + * + * @see DrupalWebTestCase::tearDown() + */ public function tearDown() { - file_put_contents($this->config_info['filter_drupal_file'], $this->original_drupal_fiter); + if ($this->configuration['use_drupal_filter']) { + $this->restoreDrupalFilter(); + } + unset($this->admin); + unset($this->configuration); parent::tearDown(); } -} \ No newline at end of file +} diff --git a/tests/test_config.ini b/tests/test_config.ini index 1d6eb7d6..350dd60b 100644 --- a/tests/test_config.ini +++ b/tests/test_config.ini @@ -1,4 +1,6 @@ [fedora] -filter_drupal_file = "/usr/local/fedora/server/config/filter-drupal.xml" +fedora_url = "http://localhost:8080/fedora" +use_drupal_filter = FALSE +drupal_filter_file = "/home/nbanks/projects/default/repository/server/config/filter-drupal.xml" admin_user = "fedoraAdmin" admin_pass = "fedoraAdmin" From 23b9e30dc2f3bf97dcd3240f78f984ef2bc9ce65 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 23 Jan 2013 22:56:49 +0100 Subject: [PATCH 02/15] Removing debug code. --- includes/utilities.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/utilities.inc b/includes/utilities.inc index ac63f35c..52a201fe 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -134,7 +134,6 @@ function islandora_describe_repository($url = NULL) { * The merged results from all the hooks. */ function islandora_invoke_hook_list($hook, array $refinements, array $args) { - dsm(func_get_args()); $return = array(); foreach (islandora_build_hook_list($hook, $refinements) as $hook) { array_unshift($args, $hook); From 145f1ad000d6c5781e09e18a8841d09efb45a4cc Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 25 Jan 2013 18:06:03 +0100 Subject: [PATCH 03/15] islandora_tuque.inc gets autoloaded now. --- islandora.module | 1 - 1 file changed, 1 deletion(-) diff --git a/islandora.module b/islandora.module index bccf7322..b9f264ca 100644 --- a/islandora.module +++ b/islandora.module @@ -541,7 +541,6 @@ function islandora_default_islandora_view_object($object) { * A IslandoraTuque instance */ function islandora_get_tuque_connection($user = NULL, $url = NULL) { - module_load_include('inc', 'islandora', 'includes/islandora_tuque'); $tuque = &drupal_static(__FUNCTION__); if (!$tuque) { if (IslandoraTuque::exists()) { From 60e17fd1c10d7b0a96c163d5a2f37331c223aea0 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 25 Jan 2013 18:12:39 +0100 Subject: [PATCH 04/15] Gets autoloaded now. --- includes/islandora_tuque.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/islandora_tuque.inc b/includes/islandora_tuque.inc index 2f5ca03a..9da9da14 100644 --- a/includes/islandora_tuque.inc +++ b/includes/islandora_tuque.inc @@ -78,7 +78,6 @@ class IslandoraTuque { } if (self::exists()) { - module_load_include('inc', 'islandora', 'includes/islandora_tuque_wrapper'); $this->connection = new IslandoraRepositoryConnection($url, $user_string, $pass_string); $this->connection->reuseConnection = TRUE; $this->api = new IslandoraFedoraApi($this->connection); From 12cce15665a548a28bb51ecd3b4595a05b705cd3 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 25 Jan 2013 18:43:47 +0100 Subject: [PATCH 05/15] Updated documentation to use the list style instead of @code sections. --- islandora.api.php | 71 +++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/islandora.api.php b/islandora.api.php index 4fe2ea4f..e3fd17ca 100644 --- a/islandora.api.php +++ b/islandora.api.php @@ -100,24 +100,22 @@ function hook_islandora_edit_object_alter(&$object, &$rendered) { * @param AbstractFedoraObject $object * The object to alter. * @param array $context - * The context for the alter action, this will always contain at the - * following properties. - * - * @code - * array( - * // Either 'ingest', 'purge', 'modify'. - * 'action' => 'ingest', - * // Either TRUE or FALSE, if TRUE the action won't take place. - * // Set by the implementing alter hook. - * 'block' => FALSE, - * ) - * @endcode - * - * When the action is "purge" two additional boolean properties are present - * 'delete' defaults to FALSE, and 'purge' defaults to TRUE. If only purge - * is set to TRUE the object will be 'purged' if delete is set to TRUE and - * block is not then the object state will be set to 'Deleted'. If 'block' - * is set to TRUE the object will not be deleted or purged. + * An associative array containing: + * - action: A string either 'ingest', 'purge', 'modify'. + * - block: Either TRUE or FALSE, if TRUE the action won't take place. + * Defaults to FALSE. + * - purge: Either TRUE or FALSE, only present when the action is 'purge'. + * If 'delete' or 'block' is set to TRUE, they will take precedence. + * Defaults to TRUE. + * - delete: Either TRUE or FALSE, only present when the action is 'purge'. + * If TRUE it will cause the object's state to be set to 'D' instead. + * If 'block' is set to TRUE, it will take precedence. + * Defaults to FALSE, + * - params: An associative array, only present when the action is 'modify'. + * The key value pairs repersent what values will be changed. The params + * will match the same params as passed to FedoraApiM::modifyObject(). + * + * @see FedoraApiM::modifyObject() */ function hook_islandora_object_alter(AbstractFedoraObject $object, array &$context) { } @@ -151,27 +149,22 @@ function hook_CMODEL_PID_islandora_object_alter(AbstractFedoraObject $object, ar * @param AbstractFedoraDatastream $datastream * The datastream to alter. * @param array $context - * The context for the alter action, this will always contain at the - * following properties. - * - * @code - * array( - * // Either 'ingest', 'purge', 'modify'. - * 'action' => 'ingest', - * // Either TRUE or FALSE, if TRUE the action won't take place. - * // Set by the implementing alter hook. - * 'block' => FALSE, - * ) - * @endcode - * - * When the action is "purge" two additional boolean properties are present - * 'delete' (defaults to FALSE), and 'purge' (defaults to TRUE). If only purge - * is set to TRUE the datastream will be 'purged' if delete is set to TRUE and - * block is not then the datastream state will be set to 'Deleted'. If 'block' - * is set to TRUE the datastream will not be deleted or purged. - * - * When the action is "modify" there is an additional property "params" that - * contains the modifications about to take place. + * An associative array containing: + * - action: A string either 'ingest', 'purge', 'modify'. + * - block: Either TRUE or FALSE, if TRUE the action won't take place. + * Defaults to FALSE. + * - purge: Either TRUE or FALSE, only present when the action is 'purge'. + * If 'delete' or 'block' is set to TRUE, they will take precedence. + * Defaults to TRUE. + * - delete: Either TRUE or FALSE, only present when the action is 'purge'. + * If TRUE it will cause the object's state to be set to 'D' instead. + * If 'block' is set to TRUE, it will take precedence. + * Defaults to FALSE, + * - params: An associative array, only present when the action is 'modify'. + * The key value pairs repersent what values will be changed. The params + * will match the same params as passed to FedoraApiM::modifyDatastream(). + * + * @see FedoraApiM::modifyDatastream() */ function hook_islandora_datastream_alter(AbstractFedoraObject $object, AbstractFedoraDatastream $datastream, array &$context) { } From c2be7aede8a3f5a9c02b1396f260f8c205e2670b Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 25 Jan 2013 18:46:07 +0100 Subject: [PATCH 06/15] Removing Ignored file. --- tests/test_config.ini | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 tests/test_config.ini diff --git a/tests/test_config.ini b/tests/test_config.ini deleted file mode 100644 index 350dd60b..00000000 --- a/tests/test_config.ini +++ /dev/null @@ -1,6 +0,0 @@ -[fedora] -fedora_url = "http://localhost:8080/fedora" -use_drupal_filter = FALSE -drupal_filter_file = "/home/nbanks/projects/default/repository/server/config/filter-drupal.xml" -admin_user = "fedoraAdmin" -admin_pass = "fedoraAdmin" From 23f57b21f9f06639f377354aab40c61999f492ca Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 25 Jan 2013 18:47:45 +0100 Subject: [PATCH 07/15] Removing unused ingest registry hook. --- islandora.api.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/islandora.api.php b/islandora.api.php index e3fd17ca..5e976194 100644 --- a/islandora.api.php +++ b/islandora.api.php @@ -320,22 +320,6 @@ function hook_islandora_datastream_purged(FedoraObject $object, $dsid) { function hook_CMODEL_PID_islandora_datastream_purged(FedoraObject $object, $dsid) { } -/** - * Register potential ingest routes. - * - * Implementations should return an array containing possible routes. - */ -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. * From eba34eabbb0c3ca9cf20038d6fc5a05c0a7800a8 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Tue, 29 Jan 2013 10:15:19 +0100 Subject: [PATCH 08/15] Follow File Name Conventions File naming conventions now state that all PHP files (except templates) should be lower-case and use underscores to separate words in a filename. includes/IslandoraTuque.inc, and includes/IslandoraTuqueWrapper.inc are not included in this commit as there is another pull request that corrects their names in place, and we would like to avoid merge conflicts. --- includes/{DublinCore.inc => dublin_core.inc} | 0 includes/{MimeDetect.inc => mime_detect.inc} | 0 islandora.info | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename includes/{DublinCore.inc => dublin_core.inc} (100%) rename includes/{MimeDetect.inc => mime_detect.inc} (100%) diff --git a/includes/DublinCore.inc b/includes/dublin_core.inc similarity index 100% rename from includes/DublinCore.inc rename to includes/dublin_core.inc diff --git a/includes/MimeDetect.inc b/includes/mime_detect.inc similarity index 100% rename from includes/MimeDetect.inc rename to includes/mime_detect.inc diff --git a/islandora.info b/islandora.info index d883aa49..98247858 100644 --- a/islandora.info +++ b/islandora.info @@ -6,8 +6,8 @@ core = 7.x configure = admin/islandora/configure stylesheets[all][] = css/islandora.base.css stylesheets[all][] = css/islandora.theme.css -files[] = includes/MimeDetect.inc -files[] = includes/DublinCore.inc +files[] = includes/mime_detect.inc +files[] = includes/dublin_core.inc files[] = includes/IslandoraTuque.inc files[] = includes/IslandoraTuqueWrapper.inc files[] = tests/islandora_web_test_case.inc From 65772a1c41fe83fb9f1b43b75164bfdb20ff06d8 Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Tue, 29 Jan 2013 12:34:01 -0400 Subject: [PATCH 09/15] made viewer callback potentially aware of fedora object being displayed --- includes/solution_packs.inc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index bf50a1f5..fc696eb5 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -622,11 +622,14 @@ function theme_islandora_viewers_table($variables) { * 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 * 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) { + +function islandora_get_viewer($params = NULL, $variable_id = NULL, $fedora_object = null) { // get viewer from settings $settings = variable_get($variable_id, array()); // make sure a viewer is set @@ -636,10 +639,10 @@ function islandora_get_viewer($params = NULL, $variable_id = NULL) { if ($viewer_id AND $params !== NULL) { $callback = islandora_get_viewer_callback($viewer_id); // call callback function - return $callback($params); + return $callback($params, $fedora_object); } } - return NULL; + return FALSE; } /** From a9abf13eaf35ac60b3b6a76b3f81427210a7d581 Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Tue, 29 Jan 2013 13:03:36 -0400 Subject: [PATCH 10/15] Changed case ot conform to standard --- includes/solution_packs.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index fc696eb5..f3e0f0e8 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -629,7 +629,7 @@ function theme_islandora_viewers_table($variables) { * if no viewer is set. */ -function islandora_get_viewer($params = NULL, $variable_id = NULL, $fedora_object = null) { +function islandora_get_viewer($params = NULL, $variable_id = NULL, $fedora_object = NULL) { // get viewer from settings $settings = variable_get($variable_id, array()); // make sure a viewer is set From 3015117fbb09b8bbb4a7b985635af8d1e34e021a Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 30 Jan 2013 22:07:07 +0100 Subject: [PATCH 11/15] Added two more policies, not necessarily required for all users. They both allow access to API-M functions to everyone including anonymous users. --- .../permit-getDatastream-unrestricted.xml | 25 +++++++++++++++++++ ...rmit-getDatastreamHistory-unrestricted.xml | 25 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 policies/permit-getDatastream-unrestricted.xml create mode 100644 policies/permit-getDatastreamHistory-unrestricted.xml diff --git a/policies/permit-getDatastream-unrestricted.xml b/policies/permit-getDatastream-unrestricted.xml new file mode 100644 index 00000000..7483cbb3 --- /dev/null +++ b/policies/permit-getDatastream-unrestricted.xml @@ -0,0 +1,25 @@ + + + Note that other policies may provide exceptions to this broad policy. This policy assumes api-m users have to be authenticated + + + + + + + + + + + urn:fedora:names:fedora:2.1:action:id-getDatastream + + + + + + + diff --git a/policies/permit-getDatastreamHistory-unrestricted.xml b/policies/permit-getDatastreamHistory-unrestricted.xml new file mode 100644 index 00000000..f7662eb0 --- /dev/null +++ b/policies/permit-getDatastreamHistory-unrestricted.xml @@ -0,0 +1,25 @@ + + + Note that other policies may provide exceptions to this broad policy. This policy assumes api-m users have to be authenticated + + + + + + + + + + + urn:fedora:names:fedora:2.1:action:id-getDatastreamHistory + + + + + + + From d583e9f87047ba97c68f8c2911a503c12c28ea4f Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Wed, 30 Jan 2013 18:34:15 -0330 Subject: [PATCH 12/15] Basic Entity support. Expose a few fields, which can be substituted in for Tokens (in Rules). --- .../islandora_object.entity_controller.inc | 31 +++++ islandora.info | 1 + islandora.module | 62 ++++++++++ islandora.rules.inc | 115 ++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 includes/islandora_object.entity_controller.inc create mode 100644 islandora.rules.inc diff --git a/includes/islandora_object.entity_controller.inc b/includes/islandora_object.entity_controller.inc new file mode 100644 index 00000000..6f58e723 --- /dev/null +++ b/includes/islandora_object.entity_controller.inc @@ -0,0 +1,31 @@ + 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; +} + diff --git a/islandora.rules.inc b/islandora.rules.inc new file mode 100644 index 00000000..bda999b3 --- /dev/null +++ b/islandora.rules.inc @@ -0,0 +1,115 @@ + array( + 'type' => 'islandora_object', + 'label' => t('Subject'), + 'description' => t('An object of which we should check the ' . + 'relationships (The "subject" of the relationship).'), + ), + 'pred_uri' => array( + 'type' => 'text', + 'label' => t('Predicate URI'), + 'description' => t('The URI namespace to which the predicate belongs.'), + ), + 'pred' => array( + 'type' => 'text', + 'label' => t('Predicate'), + 'description' => t('The predicate of the relationship.'), + ), + 'object' => array( + 'type' => 'text', + 'label' => t('Object'), + 'description' => t('The object of the relationship.'), + 'allow null' => TRUE, + 'default value' => NULL, + ), + 'type' => array( + 'type' => 'integer', + 'label' => t('Object type in the relationship'), + 'description' => t('0=URI, 1=plain literal'), + 'default value' => 0, + ), + ); + + return $to_return; +} + +/** + * Implements hook_rules_condition_info(). + */ +function islandora_rules_condition_info() { + $cond = array(); + + $cond['islandora_object_has_relationship'] = array( + 'label' => t('Check object for a relationship'), + 'group' => t('Islandora'), + 'parameter' => islandora_rules_relationship_parameter_array(), + ); + + return $cond; +} + +/** + * Implements hook_rules_action_info(). + */ +function islandora_rules_action_info() { + $cond = array(); + + $cond['islandora_object_remove_relationship'] = array( + 'label' => t('Remove a relationship from an object'), + 'group' => t('Islandora'), + 'parameter' => islandora_rules_relationship_parameter_array(), + ); + + $cond['islandora_object_add_relationship'] = array( + 'label' => t('Add a relationship to an object'), + 'group' => t('Islandora'), + 'parameter' => islandora_rules_relationship_parameter_array(), + ); + return $cond; +} +/** + * Checks that there is a relationship match on the given object. + * + * Takes a subject (either a FedoraObject or a FedoraDatastream), as well as + * the parameters for FedoraRelsExt::get() or FedoraRelsInt::get(), to try to + * find a match. + * + * @see FedoraRelsExt::get() + */ +function islandora_object_has_relationship($sub, $pred_uri, $pred, $object, $type) { + $relationships = $sub->relationships->get($pred_uri, $pred, $object, $type); + return !empty($relationships); +} + +/** + * Remove a relationship from the given object. + * + * Takes a subject (either a FedoraObject or a FedoraDatastream), as well as + * the parameters for FedoraRelsExt::remove() or FedoraRelsInt::remove(), to + * try to find a match. + * + * @see FedoraRelsExt::get() + */ +function islandora_object_remove_relationship($sub, $pred_uri, $pred, $object, $type) { + $sub->relationships->remove($pred_uri, $pred, $object, $type); +} + +/** + * Add a relationship to the given object. + * + * Takes a subject (either a FedoraObject or a FedoraDatastream), as well as + * the parameters for FedoraRelsExt::add() or FedoraRelsInt::add(), and adds + * the represented relationship. + * + * @see FedoraRelsExt::get() + */ +function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $type) { + $sub->relationships->add($pred_uri, $pred, $object, $type); +} + From f4885ae86875ad866fad4ad4ec7afdae9d5f561d Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 31 Jan 2013 16:16:44 +0100 Subject: [PATCH 13/15] Added a helper function for displaying if the given executable is accessible. This can be used in admin setting forms and else where to indicate if the given executable is availible, and if its version requirements are met. --- includes/utilities.inc | 43 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/includes/utilities.inc b/includes/utilities.inc index c07831a5..1650e078 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -433,7 +433,7 @@ function islandora_prepare_new_object($namespace = NULL, $label = NULL, $datastr $mimetype = isset($ds['mimetype']) ? $ds['mimetype'] : 'text/xml'; // Default 'Managed' $control_group = (isset($ds['control_group']) && in_array($ds['control_group'], array('X', 'M', 'R', 'E'))) ? $ds['control_group'] : 'M'; - + $ds_uri = FALSE; if (file_valid_uri($ds['datastream_file'])) { $datastream_file = $ds['datastream_file']; @@ -441,7 +441,7 @@ function islandora_prepare_new_object($namespace = NULL, $label = NULL, $datastr } else { $datastream_file = url($ds['datastream_file'], array('absolute' => TRUE)); - } + } $datastream = $object->constructDatastream($dsid, $control_group); $datastream->label = $label; $datastream->mimetype = $mimetype; @@ -476,3 +476,42 @@ function islandora_display_repository_inaccessible_message() { array('!link' => $link)); drupal_set_message($message, 'error', FALSE); } + +/** + * Create a message stating if the given executable is available. + * + * If the both the version and required version are given then only if the + * version is equal to or greater than the required version the executable + * will be considered correct. + * + * @param string $path + * The absolute path to an executable to check for availability. + * @param string $version + * The version of exectuable. + * @param string $required_version + * The required version of exectuable. + * + * @return string + * A message in html detailing if the given executable is accessible. + */ +function islandora_executable_available_message($path, $version = NULL, $required_version = NULL) { + $available = is_executable($path); + if ($available) { + $image = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array())); + $message = t('Executable found at @path', array('@path' => $path)); + if ($version) { + $message .= t('
Version: @version', array('@version' => $version)); + } + if ($required_version) { + $message .= t('
Required Version: @version', array('@version' => $required_version)); + if (version_compare($version, $required_version) < 0) { + $image = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())); + } + } + } + else { + $image = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())); + $message = t('Unable to locate executable at @path', array('@path' => $path)); + } + return $image . $message; +} From 370532007a9240b1e446a5a65c2aa038da361ef5 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 1 Feb 2013 13:29:58 +0100 Subject: [PATCH 14/15] Added a helper function for displaying if the given directory exists. This can be used in admin setting forms and else where to indicate if the given directory exists. --- includes/utilities.inc | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/includes/utilities.inc b/includes/utilities.inc index 1650e078..b210d35c 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -515,3 +515,25 @@ function islandora_executable_available_message($path, $version = NULL, $require } return $image . $message; } + +/** + * Create a message stating if the given directory exists. + * + * @param string $path + * The absolute path to an executable to check for availability. + * + * @return string + * A message in HTML detailing if the given directory is exists. + */ +function islandora_directory_exists_message($path) { + $available = is_dir($path); + if ($available) { + $image = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array())); + $message = t('Directory found at @path', array('@path' => $path)); + } + else { + $image = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())); + $message = t('Unable to locate directory at @path', array('@path' => $path)); + } + return $image . $message; +} From f8a98eb31ea61489a1017286d8a6779f6c39053c Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 1 Feb 2013 15:26:05 +0100 Subject: [PATCH 15/15] Automatically set the title to the object label when viewing objects. --- islandora.module | 5 ++++- theme/islandora-object-edit.tpl.php | 3 --- theme/islandora-object.tpl.php | 4 ---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/islandora.module b/islandora.module index 6a9b3b63..bddb2967 100644 --- a/islandora.module +++ b/islandora.module @@ -430,7 +430,10 @@ function islandora_object_manage_access_callback($perms, $object = NULL) { * 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); @@ -491,6 +494,7 @@ function islandora_view_default_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']; @@ -854,4 +858,3 @@ function islandora_entity_property_info() { return $info; } - diff --git a/theme/islandora-object-edit.tpl.php b/theme/islandora-object-edit.tpl.php index 0772a899..266a1259 100644 --- a/theme/islandora-object-edit.tpl.php +++ b/theme/islandora-object-edit.tpl.php @@ -20,7 +20,4 @@ * */ ?> - -label); ?> - diff --git a/theme/islandora-object.tpl.php b/theme/islandora-object.tpl.php index 9062098c..753232c5 100644 --- a/theme/islandora-object.tpl.php +++ b/theme/islandora-object.tpl.php @@ -58,10 +58,6 @@ * */ ?> - - - -