From 03ca4ac116d546f3f4a6811315f355b5013f9cc8 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Wed, 8 Oct 2014 17:09:40 -0300 Subject: [PATCH 1/7] Basic implentation of other hooks and XPath Rules condition. --- includes/tuque_wrapper.inc | 4 ++ islandora.module | 8 ++++ islandora.rules.inc | 92 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 2f4ea1df..53942554 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -228,6 +228,10 @@ class IslandoraFedoraObject extends FedoraObject { if ($this->state == 'D') { islandora_invoke_object_hooks(ISLANDORA_OBJECT_PURGED_HOOK, $this->models, $this->id); } + // Fire of event if rules is enabled. + if (module_exists('rules')) { + rules_invoke_event('islandora_object_modified', $object); + } } catch (Exception $e) { watchdog('islandora', 'Failed to modify object: @pid
code: @code
message: @msg', array( diff --git a/islandora.module b/islandora.module index 603ab6a0..0b2a3ce8 100644 --- a/islandora.module +++ b/islandora.module @@ -1762,6 +1762,10 @@ function islandora_islandora_object_ingested(AbstractObject $object) { * equal to the current ingested datastream's id. */ function islandora_islandora_datastream_ingested(AbstractObject $object, AbstractDatastream $datastream) { + if (module_exists('rules')) { + rules_invoke_event('islandora_datastream_ingested', $object, $datastream->id); + } + module_load_include('inc', 'islandora', 'includes/derivatives'); // Defer derivatives if necessary. if (islandora_get_defer_derivatives_flag($object)) { @@ -1778,6 +1782,10 @@ function islandora_islandora_datastream_ingested(AbstractObject $object, Abstrac * existing derivatives will be updated to reflect the change in the source. */ function islandora_islandora_datastream_modified(AbstractObject $object, AbstractDatastream $datastream) { + if (module_exists('rules')) { + rules_invoke_event('islandora_datastream_modified', $object, $datastream->id); + } + module_load_include('inc', 'islandora', 'includes/derivatives'); $logging_results = islandora_do_derivatives($object, array( 'source_dsid' => $datastream->id, diff --git a/islandora.rules.inc b/islandora.rules.inc index 02f5c822..17fa8227 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -21,6 +21,49 @@ function islandora_rules_event_info() { ), ), ), + 'islandora_datastream_ingested' => array( + 'group' => t('Islandora'), + 'label' => t('Datastream ingested'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('The ingested object'), + 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), + ), + 'datastream' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('The ID of the ingested datastream.'), + ), + ), + ), + 'islandora_object_modified' => array( + 'group' => t('Islandora'), + 'label' => t('Object modified'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('The modified object'), + 'description' => t('A Tuque object for the modified Fedora object, as an entity.'), + ), + ), + ), + 'islandora_datastream_modified' => array( + 'group' => t('Islandora'), + 'label' => t('Datastream modified'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('The modified object'), + 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), + ), + 'datastream' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('The ID of the ingested datastream.'), + ), + ), + ), ); } @@ -60,6 +103,32 @@ function islandora_rules_relationship_parameter_array() { ); } +function islandora_rules_base_xpath_parameters() { + return array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The object in which to check the XPath.'), + ), + 'datastream' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('The ID of the XML datastream to check.'), + ), + 'xpath' => array( + 'type' => 'text', + 'label' => t('XPath'), + 'description' => t('An XPath to evaluate.'), + ), + 'xpath_namespaces' => array( + 'type' => 'taxonomy_vocabulary', + 'label' => t('XPath Namespace Taxonomy'), + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + ), + ); +} + + /** * Implements hook_rules_condition_info(). */ @@ -71,6 +140,11 @@ function islandora_rules_condition_info() { 'group' => t('Islandora'), 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_rules_datastream_has_xpath'] = array( + 'label' => t('Check for an XPath match in an XML datastream'), + 'group' => t('Islandora'), + 'parameter' => islandora_rules_base_xpath_parameters(), + ); return $cond; } @@ -92,6 +166,7 @@ function islandora_rules_action_info() { 'group' => t('Islandora'), 'parameter' => islandora_rules_relationship_parameter_array(), ); + return $cond; } /** @@ -133,3 +208,20 @@ function islandora_object_remove_relationship($sub, $pred_uri, $pred, $object, $ function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $type) { $sub->relationships->add($pred_uri, $pred, $object, $type); } + +function islandora_rules_datastream_has_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab) { + if (!isset($object[$datastream_id])) { + // Datastream does no exists... No match. + return FALSE; + } + + $datastream = $object[$datastream_id]; + $doc = new DOMDocument(); + $doc->loadXML($datastream->content); + $xpath = new DOMXPath($doc); + foreach (taxonomy_get_tree($xpath_vocab->vid, 0, 1, FALSE) as $term) { + $xpath->registerNamespace($term->name, $term->description); + } + $result = $xpath->query($search_xpath); + return $result->length > 0; +} From fd95c8dce72a8c6fbb7e36021b448781a3055909 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Fri, 10 Oct 2014 16:08:23 -0300 Subject: [PATCH 2/7] Somewhat further. Have a one-off and the ability to query, iterate and set values of multiple nodes in XML in one shot. Going to add some temporary "entity" implementation. --- includes/object.entity_controller.inc | 1 + includes/tuque_wrapper.inc | 8 - includes/utilities.inc | 10 +- islandora.info | 1 + islandora.module | 25 ++- islandora.rules.inc | 234 +++++++++++++++++++++++--- 6 files changed, 242 insertions(+), 37 deletions(-) diff --git a/includes/object.entity_controller.inc b/includes/object.entity_controller.inc index 9464d7b5..87b5e4de 100644 --- a/includes/object.entity_controller.inc +++ b/includes/object.entity_controller.inc @@ -30,6 +30,7 @@ class IslandoraObjectEntityController implements DrupalEntityControllerInterface */ public function load($ids = array(), $conditions = array()) { if (!empty($conditions)) { + // TODO: Allow loading by specifying IDs in the condition. throw new Exception('Conditions not implemented.'); } diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 53942554..d14a86b3 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -115,10 +115,6 @@ class IslandoraFedoraRepository extends FedoraRepository { foreach ($object as $dsid => $datastream) { islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_INGESTED_HOOK, $object->models, $dsid, $object, $datastream); } - // Fire of event if rules is enabled. - if (module_exists('rules')) { - rules_invoke_event('islandora_object_ingested', $object); - } return $ret; } catch (Exception $e) { @@ -228,10 +224,6 @@ class IslandoraFedoraObject extends FedoraObject { if ($this->state == 'D') { islandora_invoke_object_hooks(ISLANDORA_OBJECT_PURGED_HOOK, $this->models, $this->id); } - // Fire of event if rules is enabled. - if (module_exists('rules')) { - rules_invoke_event('islandora_object_modified', $object); - } } catch (Exception $e) { watchdog('islandora', 'Failed to modify object: @pid
code: @code
message: @msg', array( diff --git a/includes/utilities.inc b/includes/utilities.inc index 26633df5..7476948b 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -188,12 +188,18 @@ function islandora_describe_repository($url = NULL) { */ function islandora_invoke_hook_list($hook, array $refinements, array $args) { $return = array(); - foreach (islandora_build_hook_list($hook, $refinements) as $hook) { - array_unshift($args, $hook); + foreach (islandora_build_hook_list($hook, $refinements) as $refined_hook) { + array_unshift($args, $refined_hook); $result = call_user_func_array('module_invoke_all', $args); $return = array_merge_recursive($return, $result); array_shift($args); } + if (module_exists('rules') && $event = rules_get_cache("event_$hook")) { + $parameters = $event->parameterInfo(); + $rule_args = array_slice($args, 0, count($parameters)); + array_unshift($rule_args, $hook); + $result = call_user_func_array('rules_invoke_event', $rule_args); + } return $return; } diff --git a/islandora.info b/islandora.info index 55c50359..580a81ac 100644 --- a/islandora.info +++ b/islandora.info @@ -13,6 +13,7 @@ files[] = includes/dublin_core.inc files[] = includes/tuque.inc files[] = includes/tuque_wrapper.inc files[] = includes/object.entity_controller.inc +files[] = includes/datastream.entity_controller.inc files[] = tests/includes/datastream_validators.inc files[] = tests/includes/islandora_web_test_case.inc files[] = tests/includes/islandora_unit_test_case.inc diff --git a/islandora.module b/islandora.module index 0b2a3ce8..85bcd2bc 100644 --- a/islandora.module +++ b/islandora.module @@ -1543,21 +1543,25 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Object Label'), 'description' => t('The label of the object.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['owner'] = array( 'type' => 'text', 'label' => t('Object Owner'), 'description' => t('The name of the owner of the object.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['state'] = array( 'type' => 'text', 'label' => t('Object State'), 'description' => t('An initial representing the state of the object.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['models'] = array( 'type' => 'list', 'label' => t('Content Models'), 'description' => t('The list of content models which the object has.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['createdDate'] = array( 'type' => 'text', @@ -1574,11 +1578,24 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Datastream Label'), 'description' => t('The label of the datastream.'), + 'setter callback' => 'entity_property_verbatim_set', ); $datastream_properties['mimetype'] = array( 'type' => 'text', 'label' => t('MIME type'), 'description' => t('Content type of the datastream.'), + 'setter callback' => 'entity_property_verbatim_set', + ); + $datastream_properties['parent'] = array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The Tuque object on which this datastream exists.'), + ); + $datastream_properties['content'] = array( + 'type' => 'text', + 'label' => t('Datastream content'), + 'description' => t('The contents of the datastream; only useful when the content is textual.'), + 'setter callback' => 'entity_property_verbatim_set', ); return $info; @@ -1762,10 +1779,6 @@ function islandora_islandora_object_ingested(AbstractObject $object) { * equal to the current ingested datastream's id. */ function islandora_islandora_datastream_ingested(AbstractObject $object, AbstractDatastream $datastream) { - if (module_exists('rules')) { - rules_invoke_event('islandora_datastream_ingested', $object, $datastream->id); - } - module_load_include('inc', 'islandora', 'includes/derivatives'); // Defer derivatives if necessary. if (islandora_get_defer_derivatives_flag($object)) { @@ -1782,10 +1795,6 @@ function islandora_islandora_datastream_ingested(AbstractObject $object, Abstrac * existing derivatives will be updated to reflect the change in the source. */ function islandora_islandora_datastream_modified(AbstractObject $object, AbstractDatastream $datastream) { - if (module_exists('rules')) { - rules_invoke_event('islandora_datastream_modified', $object, $datastream->id); - } - module_load_include('inc', 'islandora', 'includes/derivatives'); $logging_results = islandora_do_derivatives($object, array( 'source_dsid' => $datastream->id, diff --git a/islandora.rules.inc b/islandora.rules.inc index 17fa8227..aca1d9dc 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -31,7 +31,7 @@ function islandora_rules_event_info() { 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), ), 'datastream' => array( - 'type' => 'text', + 'type' => 'islandora_datastream', 'label' => t('Datastream ID'), 'description' => t('The ID of the ingested datastream.'), ), @@ -57,10 +57,37 @@ function islandora_rules_event_info() { 'label' => t('The modified object'), 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), ), + 'datastream' => array( + 'type' => 'islandora_datastream', + 'label' => t('Datastream'), + 'description' => t('The modified datastream.'), + ), + ), + ), + 'islandora_object_purged' => array( + 'group' => t('Islandora'), + 'label' => t('Object purged'), + 'variables' => array( + 'object' => array( + 'type' => 'text', + 'label' => t('Object ID'), + 'description' => t('The ID of the purged object.'), + ), + ), + ), + 'islandora_datastream_purged' => array( + 'group' => t('Islandora'), + 'label' => t('Datastream purged'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('A Tuque object for the Fedora object from which the datastream exists, as an entity.'), + ), 'datastream' => array( 'type' => 'text', 'label' => t('Datastream ID'), - 'description' => t('The ID of the ingested datastream.'), + 'description' => t('The identifier of the purged datastream.'), ), ), ), @@ -105,15 +132,10 @@ function islandora_rules_relationship_parameter_array() { function islandora_rules_base_xpath_parameters() { return array( - 'object' => array( - 'type' => 'islandora_object', - 'label' => t('Object'), - 'description' => t('The object in which to check the XPath.'), - ), 'datastream' => array( - 'type' => 'text', - 'label' => t('Datastream ID'), - 'description' => t('The ID of the XML datastream to check.'), + 'type' => 'islandora_datastream', + 'label' => t('Datastream'), + 'description' => t('The XML datastream to check.'), ), 'xpath' => array( 'type' => 'text', @@ -167,8 +189,94 @@ function islandora_rules_action_info() { 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_rules_datastream_load_domxpath'] = array( + 'label' => t('Load a DOMXPath for a given XML.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'datastrea,' => array( + 'type' => 'text', + 'label' => t('XML'), + 'description' => t('A string containing the XML to load.'), + ), + ), + 'provides' => array( + 'islandora_domxpath' => array( + 'type' => 'islandora_domxpath', + 'label' => t('Loaded DOMXPath instance'), + ), + ), + ); + $cond['islandora_rules_datastream_load_xpath'] = array( + 'label' => t('Load a DOMXPath from a datastream.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'datastream' => array( + 'type' => 'islandora_datastream', + 'label' => t('Datastream'), + 'description' => t('A datastream containing the XML to load.'), + ), + ), + 'provides' => array( + 'islandora_domxpath' => array( + 'type' => 'islandora_domxpath', + 'label' => t('Loaded DOMXPath instance'), + ), + ), + ); + $cond['islandora_rules_datastream_load_namespace_vocab'] = array( + 'label' => t('Register namespaces on a DOMXPath instance.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'value' => array( + 'type' => 'islandora_domxpath', + 'label' => t('DOMXPath instance'), + 'description' => t('The DOMXPath instance on which to register the namespaces.'), + ), + 'xpath_namespaces' => array( + 'type' => 'taxonomy_vocabulary', + 'label' => t('XPath Namespace Taxonomy'), + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + ), + ), + ); + $cond['islandora_rules_datastream_query_xpath'] = array( + 'label' => t('Query nodes from DOMXPath instance.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'xpath' => array( + 'type' => 'islandora_domxpath', + 'label' => t('DOMXPath instance'), + 'description' => t('The DOMXPath instance on which to perform the query.'), + ), + 'query' => array( + 'type' => 'text', + 'label' => t('XPath query'), + 'description' => t('The XPath query to perform.') + ), + ), + 'provides' => array( + 'nodes' => array( + 'type' => 'list', + 'label' => t('Queried DOMNode elements'), + ), + ), + ); + + $cond['islandora_rules_datastream_set_xpath'] = array( + 'label' => t('Set value in elements matched by an XPath in an XML datastream'), + 'group' => t('Islandora'), + 'parameter' => islandora_rules_base_xpath_parameters() + array( + 'value' => array( + 'type' => 'text', + 'label' => t('Value'), + 'description' => t('The value to set in the XML on elements matched by the XPath.'), + ), + ), + ); + return $cond; } + /** * Checks that there is a relationship match on the given object. * @@ -209,19 +317,107 @@ function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $typ $sub->relationships->add($pred_uri, $pred, $object, $type); } -function islandora_rules_datastream_has_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab) { - if (!isset($object[$datastream_id])) { - // Datastream does no exists... No match. - return FALSE; - } - - $datastream = $object[$datastream_id]; +function islandora_rules_datastream_load_domxpath($string) { $doc = new DOMDocument(); - $doc->loadXML($datastream->content); + $doc->loadXML($string); $xpath = new DOMXPath($doc); + return array('islandora_domxpath' => $xpath); +} + +function islandora_rules_datastream_load_namespace_vocab($xpath, $xpath_vocab) { foreach (taxonomy_get_tree($xpath_vocab->vid, 0, 1, FALSE) as $term) { $xpath->registerNamespace($term->name, $term->description); } - $result = $xpath->query($search_xpath); +} + +/** + * Rules XPath helper; grab the datastream content and build a DOMXPath. + */ +function islandora_rules_datastream_load_xpath(AbstractDatastream $datastream, $xpath_vocab) { + $result = islandora_rules_datastream_load_domxpath($datastream->content, $xpath_vocab); + islandora_rules_datastream_load_namespace_vocab($result['islandora_domxpath'], $xpath_vocab); + return $result; +} + +function islandora_rules_datastream_has_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab) { + $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); + $result = $xpath['islandora_domxpath']->query($search_xpath); return $result->length > 0; } + +function islandora_rules_datastream_set_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab, $value) { + $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); + $result = $xpath['islandora_domxpath']->query($search_xpath); + foreach ($result as $node) { + $node->nodeValue = $value; + } + $datastream->content = $xpath['islandora_domxpath']->document->saveXML(); +} + +/** + * Implements hook_rules_data_info(). + */ +function islandora_rules_data_info() { + return array( + 'islandora_domxpath' => array( + 'label' => t('DOMXPath instance'), + 'group' => t('Islandora'), + 'property info' => array( + 'content' => array( + 'type' => 'text', + 'label' => t('XML Content'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_get_domxpath_document_content', + 'token type' => 'string', + ), + ), + 'wrap' => TRUE, + ), + 'islandora_domnode' => array( + 'label' => t('DOMNode instance'), + 'group' => t('Islandora'), + 'property info' => array( + 'node_value' => array( + 'type' => 'text', + 'label' => t('Node value'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_domnode_get', + 'setter callback' => 'islandora_rules_domnode_set', + 'property' => 'nodeValue', + 'token type' => 'string', + ), + 'text_content' => array( + 'type' => 'text', + 'label' => t('Text content'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_domnode_get', + 'property' => 'textContent', + 'token type' => 'string', + ), + ), + 'wrap' => TRUE, + ), + 'islandora_domelement' => array( + 'label' => t('DOMElement instance'), + 'group' => t('Islandora'), + 'parent' => 'islandora_domnode', + 'wrap' => TRUE, + ), + ); +} + +function islandora_rules_domnode_get($data, $options, $name, $type, $info) { + return $data->$info['property']; +} + +function islandora_rules_domnode_set(&$data, $name, $value, $langcode, $type, $info) { + $data->$info['property'] = $value; +} + +function islandora_rules_get_domxpath_document_content(DOMXPath $xpath) { + return $xpath->document->saveXML(); +} + +function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query) { + return array('nodes' => iterator_to_array($xpath->query($query))); +} From 2bc288d8b125bed581ca15280c0c55b75a4c1cd1 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 13:27:30 -0300 Subject: [PATCH 3/7] Fix setting of properties and flesh out comments a bit. --- islandora.module | 26 +++++-- islandora.rules.inc | 184 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 183 insertions(+), 27 deletions(-) diff --git a/islandora.module b/islandora.module index 85bcd2bc..b98b713a 100644 --- a/islandora.module +++ b/islandora.module @@ -1543,25 +1543,25 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Object Label'), 'description' => t('The label of the object.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['owner'] = array( 'type' => 'text', 'label' => t('Object Owner'), 'description' => t('The name of the owner of the object.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['state'] = array( 'type' => 'text', 'label' => t('Object State'), 'description' => t('An initial representing the state of the object.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['models'] = array( 'type' => 'list', 'label' => t('Content Models'), 'description' => t('The list of content models which the object has.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['createdDate'] = array( 'type' => 'text', @@ -1578,13 +1578,13 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Datastream Label'), 'description' => t('The label of the datastream.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $datastream_properties['mimetype'] = array( 'type' => 'text', 'label' => t('MIME type'), 'description' => t('Content type of the datastream.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $datastream_properties['parent'] = array( 'type' => 'islandora_object', @@ -1595,7 +1595,7 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Datastream content'), 'description' => t('The contents of the datastream; only useful when the content is textual.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); return $info; @@ -2018,3 +2018,15 @@ function islandora_islandora_object_alter(AbstractObject $object, array &$contex islandora_set_defer_derivatives_flag($object); } } + +/** + * Set property on an entity. + * + * Drupal's usual entity_property_verbatim_set() does not work, as tries to use + * ArrayAccess stuff instead of setting properties directly. + */ +function islandora_entity_set_property(&$data, $name, $value, $langcode) { + if (is_object($data)) { + $data->$name = $value; + } +} diff --git a/islandora.rules.inc b/islandora.rules.inc index aca1d9dc..96e1fa5c 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -130,12 +130,20 @@ function islandora_rules_relationship_parameter_array() { ); } +/** + * Helper function; get default parameters for the XPath condition and action. + */ function islandora_rules_base_xpath_parameters() { return array( - 'datastream' => array( - 'type' => 'islandora_datastream', + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The object containing the datastream to check.'), + ), + 'datastream_id' => array( + 'type' => 'text', 'label' => t('Datastream'), - 'description' => t('The XML datastream to check.'), + 'description' => t('The identifier of the XML datastream to check.'), ), 'xpath' => array( 'type' => 'text', @@ -145,7 +153,7 @@ function islandora_rules_base_xpath_parameters() { 'xpath_namespaces' => array( 'type' => 'taxonomy_vocabulary', 'label' => t('XPath Namespace Taxonomy'), - 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.'), ), ); } @@ -162,6 +170,22 @@ function islandora_rules_condition_info() { 'group' => t('Islandora'), 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_object_has_datastream'] = array( + 'label' => t('Check object for existence of a datastream'), + 'group' => t('Islandora'), + 'parameter' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The object containing the datastream to check.'), + ), + 'datastream_id' => array( + 'type' => 'text', + 'label' => t('Datastream'), + 'description' => t('The identifier of the datastream to check.'), + ), + ), + ); $cond['islandora_rules_datastream_has_xpath'] = array( 'label' => t('Check for an XPath match in an XML datastream'), 'group' => t('Islandora'), @@ -189,11 +213,33 @@ function islandora_rules_action_info() { 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_rules_datastream_load'] = array( + 'label' => t('Load a datastream from an object.'), + 'group' => t('Islandora'), + 'parameter' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('A Tuque object for the Fedora object from which to load the datastream, as an entity.'), + ), + 'datastream_id' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('A string containing the identity of the datastream to load from the object.'), + ), + ), + 'provides' => array( + 'datastream' => array( + 'type' => 'islandora_datastream', + 'label' => t('Loaded datastream instance'), + ), + ), + ); $cond['islandora_rules_datastream_load_domxpath'] = array( 'label' => t('Load a DOMXPath for a given XML.'), 'group' => t('Islandora DOMXPath'), 'parameter' => array( - 'datastrea,' => array( + 'datastream' => array( 'type' => 'text', 'label' => t('XML'), 'description' => t('A string containing the XML to load.'), @@ -235,7 +281,7 @@ function islandora_rules_action_info() { 'xpath_namespaces' => array( 'type' => 'taxonomy_vocabulary', 'label' => t('XPath Namespace Taxonomy'), - 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.'), ), ), ); @@ -251,7 +297,7 @@ function islandora_rules_action_info() { 'query' => array( 'type' => 'text', 'label' => t('XPath query'), - 'description' => t('The XPath query to perform.') + 'description' => t('The XPath query to perform.'), ), ), 'provides' => array( @@ -277,6 +323,13 @@ function islandora_rules_action_info() { return $cond; } +/** + * Rules action callback; grab a datastream from an object. + */ +function islandora_rules_datastream_load(AbstractObject $object, $datastream_id) { + return array('datastream' => $object[$datastream_id]); +} + /** * Checks that there is a relationship match on the given object. * @@ -317,6 +370,9 @@ function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $typ $sub->relationships->add($pred_uri, $pred, $object, $type); } +/** + * Rules Action callback; instantiate a DOMXPath with some XML. + */ function islandora_rules_datastream_load_domxpath($string) { $doc = new DOMDocument(); $doc->loadXML($string); @@ -324,6 +380,15 @@ function islandora_rules_datastream_load_domxpath($string) { return array('islandora_domxpath' => $xpath); } +/** + * Rules Action callback; load namespaces onto a DOMXPath instance. + * + * @param DOMXPath $xpath + * A DOMXPath instance. + * @param object $xpath_vocab + * A loaded Drupal taxonomy vocabulary object, in which terms are understood + * to be namespace prefixes and descriptions are the namespace URIs. + */ function islandora_rules_datastream_load_namespace_vocab($xpath, $xpath_vocab) { foreach (taxonomy_get_tree($xpath_vocab->vid, 0, 1, FALSE) as $term) { $xpath->registerNamespace($term->name, $term->description); @@ -339,13 +404,21 @@ function islandora_rules_datastream_load_xpath(AbstractDatastream $datastream, $ return $result; } -function islandora_rules_datastream_has_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab) { +/** + * Rules Condition callback; test that an XPath returns a non-empty result set. + */ +function islandora_rules_datastream_has_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab) { + $datastream = $object[$datastream_id]; $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); $result = $xpath['islandora_domxpath']->query($search_xpath); return $result->length > 0; } -function islandora_rules_datastream_set_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab, $value) { +/** + * Rules Action callback; set the value of all matched nodes. + */ +function islandora_rules_datastream_set_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab, $value) { + $datastream = $object[$datastream_id]; $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); $result = $xpath['islandora_domxpath']->query($search_xpath); foreach ($result as $node) { @@ -364,12 +437,12 @@ function islandora_rules_data_info() { 'group' => t('Islandora'), 'property info' => array( 'content' => array( - 'type' => 'text', - 'label' => t('XML Content'), - 'computed' => TRUE, - 'getter callback' => 'islandora_rules_get_domxpath_document_content', - 'token type' => 'string', - ), + 'type' => 'text', + 'label' => t('XML Content'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_get_domxpath_document_content', + 'token type' => 'string', + ), ), 'wrap' => TRUE, ), @@ -381,8 +454,8 @@ function islandora_rules_data_info() { 'type' => 'text', 'label' => t('Node value'), 'computed' => TRUE, - 'getter callback' => 'islandora_rules_domnode_get', - 'setter callback' => 'islandora_rules_domnode_set', + 'getter callback' => 'islandora_rules_property_get', + 'setter callback' => 'islandora_rules_property_set', 'property' => 'nodeValue', 'token type' => 'string', ), @@ -390,7 +463,7 @@ function islandora_rules_data_info() { 'type' => 'text', 'label' => t('Text content'), 'computed' => TRUE, - 'getter callback' => 'islandora_rules_domnode_get', + 'getter callback' => 'islandora_rules_property_get', 'property' => 'textContent', 'token type' => 'string', ), @@ -406,18 +479,89 @@ function islandora_rules_data_info() { ); } -function islandora_rules_domnode_get($data, $options, $name, $type, $info) { +/** + * Property setter helper; set a property on an object. + * + * In Rules, properties can contain lowercase and numeric characters. Since + * we want to refer to "nodeValue", we have to step around the Rules constraint. + * + * @param object $data + * The object on which to set the property, described by $info['property']. + * @param array $options + * An array of options... Not sure how it's used? Not touched by us, in any + * case. :P + * @param string $name + * The name of the property to set, as used by Rules. + * @param string $type + * The type of object on which the property is being set, as used by Rules. + * @param array $info + * An associative array describing this property. In addition to that + * required by Rules/the Entity API, should contain: + * - property: A string indicate the actual property on the $data we wish to + * set. + */ +function islandora_rules_property_get($data, array $options, $name, $type, $info) { return $data->$info['property']; } -function islandora_rules_domnode_set(&$data, $name, $value, $langcode, $type, $info) { +/** + * Property setter helper; set a property on an object. + * + * In Rules, properties can contain lowercase and numeric characters. Since + * we want to refer to "nodeValue", we have to step around the Rules constraint. + * + * @param object $data + * The object on which to set the property, described by $info['property']. + * @param string $name + * The name of the property to set, as used by Rules. + * @param mixed $value + * The value to set on the property. + * @param string $langcode + * A string indicating the language being set, or NULL. + * @param string $type + * The type of object on which the property is being set, as used by Rules. + * @param array $info + * An associative array describing this property. In addition to that + * required by Rules/the Entity API, should contain: + * - property: A string indicate the actual property on the $data we wish to + * set. + */ +function islandora_rules_property_set(&$data, $name, $value, $langcode, $type, $info) { $data->$info['property'] = $value; } +/** + * Rules property "get" callback; get XML contained in a DOMXPath instance. + * + * @param DOMXPath $xpath + * A DOMXPath instance. + * + * @return string + * The XML contained inside of the DOMXPath instance. + */ function islandora_rules_get_domxpath_document_content(DOMXPath $xpath) { return $xpath->document->saveXML(); } +/** + * Rules action callback; perform a query on a DOMXPath instance. + * + * @param DOMXPath $xpath + * A DOMXPath instance. + * @param string $query + * An XPath query. + * + * @return array + * An array containing: + * - nodes: An array containing the results of the query. + */ function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query) { return array('nodes' => iterator_to_array($xpath->query($query))); } + +/** + * Rules condition callback; check for the datastream on an object. + */ +function islandora_object_has_datastream(AbstractObject $object, $datastream_id) { + return isset($object[$datastream_id]); +} From f82be2ed9c39f933fa7540f699ad8995a00485bd Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 13:47:00 -0300 Subject: [PATCH 4/7] Adjust string. --- islandora.rules.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.rules.inc b/islandora.rules.inc index 96e1fa5c..be6ec855 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -82,7 +82,7 @@ function islandora_rules_event_info() { 'object' => array( 'type' => 'islandora_object', 'label' => t('Object'), - 'description' => t('A Tuque object for the Fedora object from which the datastream exists, as an entity.'), + 'description' => t('A Tuque object for the Fedora object on which the datastream existed, as an entity.'), ), 'datastream' => array( 'type' => 'text', From 3b9eeef6da1c6d31c069fce95024944e28127103 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 14:55:02 -0300 Subject: [PATCH 5/7] Add "state" property to datastreams. --- islandora.module | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/islandora.module b/islandora.module index b98b713a..ac63069c 100644 --- a/islandora.module +++ b/islandora.module @@ -1574,6 +1574,12 @@ function islandora_entity_property_info() { 'label' => t('ID'), 'description' => t('The identifier of the datastream.'), ); + $datastream_properties['state'] = array( + 'type' => 'text', + 'label' => t('Datastream State'), + 'description' => t('An initial representing the state of the datastream.'), + 'setter callback' => 'islandora_entity_set_property', + ); $datastream_properties['label'] = array( 'type' => 'text', 'label' => t('Datastream Label'), From 969ef483dc110debf69f908ca0d61667fe55b7db Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 14:55:34 -0300 Subject: [PATCH 6/7] Add ability to specify context nodes when XPathing. --- islandora.rules.inc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/islandora.rules.inc b/islandora.rules.inc index be6ec855..2562656a 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -299,6 +299,14 @@ function islandora_rules_action_info() { 'label' => t('XPath query'), 'description' => t('The XPath query to perform.'), ), + 'context_node' => array( + 'type' => 'islandora_domnode', + 'label' => t('Context Node'), + 'description' => t('If provided, the query will be performed relative to the provided node.'), + 'optional' => TRUE, + 'default value' => NULL, + 'allow null' => TRUE, + ), ), 'provides' => array( 'nodes' => array( @@ -550,13 +558,16 @@ function islandora_rules_get_domxpath_document_content(DOMXPath $xpath) { * A DOMXPath instance. * @param string $query * An XPath query. + * @param DOMNode $context_node + * An optional DOMNode. If provided, the query will be performed relative to + * the given node. * * @return array * An array containing: * - nodes: An array containing the results of the query. */ -function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query) { - return array('nodes' => iterator_to_array($xpath->query($query))); +function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query, DOMNode $context_node = NULL) { + return array('nodes' => iterator_to_array($xpath->query($query, $context_node))); } /** From c4922ff4b41b2e07553444d5572aa4366ec17214 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 14:56:16 -0300 Subject: [PATCH 7/7] Drop reference to deleted "datastream entity controller". --- islandora.info | 1 - 1 file changed, 1 deletion(-) diff --git a/islandora.info b/islandora.info index 580a81ac..55c50359 100644 --- a/islandora.info +++ b/islandora.info @@ -13,7 +13,6 @@ files[] = includes/dublin_core.inc files[] = includes/tuque.inc files[] = includes/tuque_wrapper.inc files[] = includes/object.entity_controller.inc -files[] = includes/datastream.entity_controller.inc files[] = tests/includes/datastream_validators.inc files[] = tests/includes/islandora_web_test_case.inc files[] = tests/includes/islandora_unit_test_case.inc