From 7ec10d2c11dc80fdd0f4c89cd83c6dea90d93ac8 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 23 Jan 2013 21:59:45 +0100 Subject: [PATCH] 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"