Browse Source

Merge pull request #9 from Islandora/7.x

update 7.x
pull/576/head
Daniel Aitken 10 years ago
parent
commit
fb77bc1473
  1. 62
      includes/add_datastream.form.inc
  2. 24
      includes/datastream.version.inc
  3. 10
      includes/ingest.form.inc
  4. 2
      includes/mime_detect.inc
  5. 28
      includes/mimetype.utils.inc
  6. 1
      includes/object.entity_controller.inc
  7. 2
      includes/object_properties.form.inc
  8. 4
      includes/tuque_wrapper.inc
  9. 10
      includes/utilities.inc
  10. 102
      islandora.module
  11. 443
      islandora.rules.inc
  12. 59
      js/spinner.js
  13. 87
      tests/includes/islandora_web_test_case.inc
  14. 6
      tests/scripts/travis_setup.sh

62
includes/add_datastream.form.inc

@ -27,8 +27,8 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract
// @deprecated Storing objects in $form_state is asking for a bad time... // @deprecated Storing objects in $form_state is asking for a bad time...
// Causes issues with derivative generation when we try to use it. // Causes issues with derivative generation when we try to use it.
$form_state['object'] = $object; $form_state['object'] = $object;
$form_state['datastream_requirements'] = islandora_get_missing_datastreams_requirements($object); $datastream_requirements = islandora_get_missing_datastreams_requirements($object);
$unused_datastreams = array_keys($form_state['datastream_requirements']); $unused_datastreams = array_keys($datastream_requirements);
$unused_datastreams = "'" . implode("', '", $unused_datastreams) . "'"; $unused_datastreams = "'" . implode("', '", $unused_datastreams) . "'";
$upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize'));
return array( return array(
@ -38,7 +38,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract
), ),
'dsid_fieldset' => array( 'dsid_fieldset' => array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => 'Add a datastream', '#title' => t('Add a Datastream'),
'#collapsible' => FALSE, '#collapsible' => FALSE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
'dsid' => array( 'dsid' => array(
@ -60,7 +60,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract
'#required' => TRUE, '#required' => TRUE,
'#size' => 64, '#size' => 64,
'#maxlength' => 64, '#maxlength' => 64,
'#description' => t('A human-readable label'), '#description' => t('A human-readable label.'),
'#type' => 'textfield', '#type' => 'textfield',
'#element_validate' => array('islandora_add_datastream_form_field_does_not_contain_a_forward_slash'), '#element_validate' => array('islandora_add_datastream_form_field_does_not_contain_a_forward_slash'),
), ),
@ -69,10 +69,11 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract
'#required' => TRUE, '#required' => TRUE,
'#title' => t('Upload Document'), '#title' => t('Upload Document'),
'#size' => 48, '#size' => 48,
'#description' => t('Select a file to upload.<br/>Files must be less than <b>@size MB.</b>', array('@size' => $upload_size)), '#description' => t('Select a file to upload.<br/>Files must be less than <strong>@size MB.</strong>', array('@size' => $upload_size)),
'#default_value' => isset($form_state['values']['files']) ? $form_state['values']['files'] : NULL, '#default_value' => isset($form_state['values']['files']) ? $form_state['values']['files'] : NULL,
'#upload_location' => 'temporary://', '#upload_location' => file_default_scheme() . '://',
'#upload_validators' => array( '#upload_validators' => array(
// Disable default file_validate_extensions; we need direct control.
'file_validate_extensions' => array(NULL), 'file_validate_extensions' => array(NULL),
// Assume its specified in MB. // Assume its specified in MB.
'file_validate_size' => array($upload_size * 1024 * 1024), 'file_validate_size' => array($upload_size * 1024 * 1024),
@ -136,6 +137,30 @@ function islandora_add_datastream_form_field_is_valid_dsid(array $element, array
} }
} }
/**
* Validation callback for islandora_add_datastream_form.
*
* Checks if the given datastream can accept the given MIME type.
*/
function islandora_add_datastream_form_validate(array $form, array &$form_state) {
module_load_include('inc', 'islandora', 'includes/mimetype.utils');
$extensions = islandora_get_extensions_for_datastream(
$form_state['object'],
$form_state['values']['dsid']
);
$file = file_load($form_state['values']['file']);
// Only validate extensions if mimes defined in ds-composite.
if ($file && $extensions) {
$errors = file_validate_extensions($file, implode(' ', $extensions));
if (count($errors) > 0) {
form_set_error(
'file',
t("!error (for the set DSID)", array('!error' => $errors[0]))
);
}
}
}
/** /**
* Checks if the given form field contains a "/" character. * Checks if the given form field contains a "/" character.
* *
@ -152,31 +177,6 @@ function islandora_add_datastream_form_field_does_not_contain_a_forward_slash(ar
} }
} }
/**
* Checks if the given datastream requires the upload to be a certain MIME type.
*
* @param array $form
* The Drupal form.
* @param array $form_state
* The Drupal form state.
*/
function islandora_add_datastream_form_validate(array $form, array &$form_state) {
$file = file_load($form_state['values']['file']);
$dsid = $form_state['values']['dsid'];
if (isset($form_state['datastream_requirements'][$dsid]) && $file) {
$allowed_types = $form_state['datastream_requirements'][$dsid]['mime'];
$mime_detect = new MimeDetect();
$allowed_extensions = array();
foreach ($allowed_types as $mime) {
$allowed_extensions = array_merge($allowed_extensions, $mime_detect->getValidExtensions($mime));
}
$errors = file_validate_extensions($file, implode(' ', $allowed_extensions));
if (count($errors) > 0) {
form_set_error('file', $errors[0]);
}
}
}
/** /**
* Adds the new datastream based on the submitted values. * Adds the new datastream based on the submitted values.
* *

24
includes/datastream.version.inc

@ -281,31 +281,23 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract
$form_state['object_id'] = $object->id; $form_state['object_id'] = $object->id;
$form_state['dsid'] = $datastream->id; $form_state['dsid'] = $datastream->id;
$form_state['object'] = $object; $form_state['object'] = $object;
$datastream_mime_map = islandora_get_object_extensions($object);
$current_mime = $datastream->mimetype; $extensions = islandora_get_extensions_for_datastream($object, $datastream->id);
$mimes = $datastream_mime_map[$datastream->id]['mime'];
if (!in_array($current_mime, $mimes)) {
$mimes[] = $current_mime;
}
$extensions = array();
foreach ($mimes as $mime) {
$extensions = array_merge($extensions, islandora_get_extensions_for_mimetype($mime));
}
$valid_extensions = implode(' ', $extensions); $valid_extensions = implode(' ', $extensions);
$upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize'));
return array( return array(
'dsid_fieldset' => array( 'dsid_fieldset' => array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t("Update datastream with latest version"), '#title' => t("Update Datastream"),
'#collapsible' => FALSE, '#collapsible' => FALSE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
'dsid' => array( 'dsid' => array(
'#type' => 'markup', '#type' => 'markup',
'#markup' => "<div>DSID: <b>$datastream->id</b></div>", '#markup' => t("<div>DSID: <strong>@dsid</strong></div>", array('@dsid' => $datastream->id)),
), ),
'label' => array( 'label' => array(
'#type' => 'markup', '#type' => 'markup',
'#markup' => "<div>Label: <b>$datastream->label</b></div>", '#markup' => t("<div>Label: <strong>@label</strong></div>", array('@label' => $datastream->label)),
), ),
'file' => array( 'file' => array(
'#type' => 'managed_file', '#type' => 'managed_file',
@ -313,7 +305,7 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract
'#title' => t('Upload Document'), '#title' => t('Upload Document'),
'#size' => 64, '#size' => 64,
'#description' => t('Select a file to upload.<br/>Files must be less than <strong>@size MB.</strong>', array('@size' => $upload_size)), '#description' => t('Select a file to upload.<br/>Files must be less than <strong>@size MB.</strong>', array('@size' => $upload_size)),
'#upload_location' => 'temporary://', '#upload_location' => file_default_scheme() . '://',
'#upload_validators' => array( '#upload_validators' => array(
'file_validate_extensions' => array($valid_extensions), 'file_validate_extensions' => array($valid_extensions),
// Assume its specified in MB. // Assume its specified in MB.
@ -344,7 +336,9 @@ function islandora_datastream_version_replace_form_submit($form, &$form_state) {
$file = file_load($form_state['values']['file']); $file = file_load($form_state['values']['file']);
try { try {
$ds = $object[$form_state['dsid']]; $ds = $object[$form_state['dsid']];
$ds->mimetype = $file->filemime; if ($ds->mimetype != $file->filemime) {
$ds->mimetype = $file->filemime;
}
$path = drupal_realpath($file->uri); $path = drupal_realpath($file->uri);
$ds->setContentFromFile($path); $ds->setContentFromFile($path);
file_delete($file); file_delete($file);

10
includes/ingest.form.inc

@ -499,6 +499,14 @@ function islandora_ingest_form_stepify(array $form, array &$form_state, array $s
); );
$form['prev'] = $first_form_step ? NULL : islandora_ingest_form_previous_button($form_state); $form['prev'] = $first_form_step ? NULL : islandora_ingest_form_previous_button($form_state);
$form['next'] = $last_form_step ? islandora_ingest_form_ingest_button($form_state) : islandora_ingest_form_next_button($form_state); $form['next'] = $last_form_step ? islandora_ingest_form_ingest_button($form_state) : islandora_ingest_form_next_button($form_state);
// Duplicate next button and hide it at the top of the form so that the form
// always triggers "next" if the user submits the form with the enter key.
if (!is_null($form['prev'])) {
$form['hidden_next'] = $form['next'];
$form['hidden_next']['#weight'] = -99;
$form['hidden_next']['#prefix'] = '<div style="display:none;">';
$form['hidden_next']['#suffix'] = '</div>';
}
// Allow for a hook_form_FORM_ID_alter(). // Allow for a hook_form_FORM_ID_alter().
drupal_alter(array('form_' . $step['form_id'], 'form'), $form, $form_state, $step['form_id']); drupal_alter(array('form_' . $step['form_id'], 'form'), $form, $form_state, $step['form_id']);
return $form; return $form;
@ -799,7 +807,7 @@ function islandora_ingest_form_submit(array $form, array &$form_state) {
WATCHDOG_ERROR WATCHDOG_ERROR
); );
drupal_set_message( drupal_set_message(
t('A problem occured while ingesting "@label" (ID: @pid), please notifiy the administrator.', t('A problem occured while ingesting "@label" (ID: @pid), please notify the administrator.',
array('@label' => $object->label, '@pid' => $object->id)), array('@label' => $object->label, '@pid' => $object->id)),
'error' 'error'
); );

2
includes/mime_detect.inc

@ -222,10 +222,12 @@ class MimeDetect {
"tar" => "application/x-tar", "tar" => "application/x-tar",
"gtar" => "application/x-gtar", "gtar" => "application/x-gtar",
"zip" => "application/x-zip", "zip" => "application/x-zip",
"dat" => "application/octet-stream",
// others: // others:
'bin' => 'application/octet-stream', 'bin' => 'application/octet-stream',
// Web Archives: // Web Archives:
"warc" => "application/warc", "warc" => "application/warc",
"json" => "application/json",
); );
protected $protectedFileExtensions; protected $protectedFileExtensions;
protected $extensionExceptions = array( protected $extensionExceptions = array(

28
includes/mimetype.utils.inc

@ -58,3 +58,31 @@ function islandora_get_extensions_for_mimetype($mimetype) {
} }
return $extensions; return $extensions;
} }
/**
* Extensions for the mimes accepted by a datastream.
*
* @param AbstractObject $object
* Object to check for extensions.
* @param string $dsid
* Datastream ID to check for extensions.
*
* @return string[]
* Extensions for the mimes accepted by the datastream ID on the object.
*/
function islandora_get_extensions_for_datastream(AbstractObject $object, $dsid) {
module_load_include('inc', 'islandora', 'includes/utilities');
$datastream_mime_map = islandora_get_datastreams_requirements_from_models($object->models);
$mimes = isset($datastream_mime_map[$dsid]) ? $datastream_mime_map[$dsid]['mime'] : array();
if (isset($object[$dsid])) {
$current_mime = $object[$dsid]->mimetype;
if (!in_array($current_mime, $mimes)) {
$mimes[] = $current_mime;
}
}
$extensions = array();
foreach ($mimes as $mime) {
$extensions = array_merge($extensions, islandora_get_extensions_for_mimetype($mime));
}
return array_unique($extensions);
}

1
includes/object.entity_controller.inc

@ -30,6 +30,7 @@ class IslandoraObjectEntityController implements DrupalEntityControllerInterface
*/ */
public function load($ids = array(), $conditions = array()) { public function load($ids = array(), $conditions = array()) {
if (!empty($conditions)) { if (!empty($conditions)) {
// TODO: Allow loading by specifying IDs in the condition.
throw new Exception('Conditions not implemented.'); throw new Exception('Conditions not implemented.');
} }

2
includes/object_properties.form.inc

@ -31,7 +31,7 @@ function islandora_object_properties_form(array $form, array &$form_state, Abstr
module_load_include('inc', 'islandora', 'includes/derivatives'); module_load_include('inc', 'islandora', 'includes/derivatives');
$hooks = islandora_invoke_hook_list(ISLANDORA_DERVIATIVE_CREATION_HOOK, $object->models, array($object)); $hooks = islandora_invoke_hook_list(ISLANDORA_DERVIATIVE_CREATION_HOOK, $object->models, array($object));
$hooks = islandora_filter_derivatives($hooks, array('force' => TRUE), $object); $hooks = islandora_filter_derivatives($hooks, array('force' => TRUE), $object);
if (count($hooks) > 1) { if (count($hooks) >= 1) {
$regenerate_derivatives_access = TRUE; $regenerate_derivatives_access = TRUE;
} }
} }

4
includes/tuque_wrapper.inc

@ -115,10 +115,6 @@ class IslandoraFedoraRepository extends FedoraRepository {
foreach ($object as $dsid => $datastream) { foreach ($object as $dsid => $datastream) {
islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_INGESTED_HOOK, $object->models, $dsid, $object, $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; return $ret;
} }
catch (Exception $e) { catch (Exception $e) {

10
includes/utilities.inc

@ -188,12 +188,18 @@ function islandora_describe_repository($url = NULL) {
*/ */
function islandora_invoke_hook_list($hook, array $refinements, array $args) { function islandora_invoke_hook_list($hook, array $refinements, array $args) {
$return = array(); $return = array();
foreach (islandora_build_hook_list($hook, $refinements) as $hook) { foreach (islandora_build_hook_list($hook, $refinements) as $refined_hook) {
array_unshift($args, $hook); array_unshift($args, $refined_hook);
$result = call_user_func_array('module_invoke_all', $args); $result = call_user_func_array('module_invoke_all', $args);
$return = array_merge_recursive($return, $result); $return = array_merge_recursive($return, $result);
array_shift($args); 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; return $return;
} }

102
islandora.module

@ -1511,6 +1511,14 @@ function islandora_entity_info() {
'label' => 'label', 'label' => 'label',
), ),
); );
$entities['islandora_datastream'] = array(
'label' => t('Islandora Datastream'),
'fieldable' => FALSE,
'entity keys' => array(
'id' => 'id',
'label' => 'label',
),
);
return $entities; return $entities;
} }
@ -1523,39 +1531,79 @@ function islandora_entity_info() {
function islandora_entity_property_info() { function islandora_entity_property_info() {
$info = array(); $info = array();
$p = &$info['islandora_object']['properties']; $object_properties = &$info['islandora_object']['properties'];
$datastream_properties = &$info['islandora_datastream']['properties'];
$p['id'] = array( $object_properties['id'] = array(
'type' => 'text', 'type' => 'text',
'label' => t('ID'), 'label' => t('ID'),
'description' => t('The identifier of the object.'), 'description' => t('The identifier of the object.'),
); );
$p['label'] = array( $object_properties['label'] = array(
'type' => 'text', 'type' => 'text',
'label' => t('Object Label'), 'label' => t('Object Label'),
'description' => t('The label of the object.'), 'description' => t('The label of the object.'),
'setter callback' => 'islandora_entity_set_property',
); );
$p['owner'] = array( $object_properties['owner'] = array(
'type' => 'text', 'type' => 'text',
'label' => t('Object Owner'), 'label' => t('Object Owner'),
'description' => t('The name of the owner of the object.'), 'description' => t('The name of the owner of the object.'),
'setter callback' => 'islandora_entity_set_property',
); );
$p['state'] = array( $object_properties['state'] = array(
'type' => 'text', 'type' => 'text',
'label' => t('Object State'), 'label' => t('Object State'),
'description' => t('An initial representing the state of the object.'), 'description' => t('An initial representing the state of the object.'),
'setter callback' => 'islandora_entity_set_property',
); );
$p['models'] = array( $object_properties['models'] = array(
'type' => 'list<text>', 'type' => 'list<text>',
'label' => t('Content Models'), 'label' => t('Content Models'),
'description' => t('The list of content models which the object has.'), 'description' => t('The list of content models which the object has.'),
'setter callback' => 'islandora_entity_set_property',
); );
$p['createdDate'] = array( $object_properties['createdDate'] = array(
'type' => 'text', 'type' => 'text',
'label' => t('Created Date'), 'label' => t('Created Date'),
'description' => t('When the object was created.'), 'description' => t('When the object was created.'),
); );
$datastream_properties['id'] = array(
'type' => 'text',
'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'),
'description' => t('The label of the datastream.'),
'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' => 'islandora_entity_set_property',
);
$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' => 'islandora_entity_set_property',
);
return $info; return $info;
} }
@ -1766,10 +1814,29 @@ function islandora_islandora_datastream_modified(AbstractObject $object, Abstrac
*/ */
function islandora_form_simpletest_test_form_alter(array &$form) { function islandora_form_simpletest_test_form_alter(array &$form) {
module_load_include('inc', 'simpletest', 'simpletest.pages'); module_load_include('inc', 'simpletest', 'simpletest.pages');
module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction');
$configuration = IslandoraTestUtilityClass::getTestConfiguration();
$filter_path = $configuration['drupal_filter_file'];
$filter_status = is_writable($filter_path);
if ($filter_status) {
$filter_status_message = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array())) . " ";
$filter_status_message .= t("Drupal filter at <b>!filter_path</b> is writable by the server.", array(
'!filter_path' => $filter_path,
));
}
else {
$filter_status_message = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())) . " ";
$filter_status_message .= t("Drupal filter at <b>!filter_path</b> is not writable by the server. Please make sure your webserver has permission to write to the Drupal filter. If the path given is incorrect, you will need to change it in your server's test config file, located in the Islandora module's 'tests' folder as test_config.ini or default.test_config.ini.", array(
'!filter_path' => $filter_path,
));
}
$form['tests'] = array( $form['tests'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Tests'), '#title' => t('Tests'),
'#description' => t('Select the test(s) or test group(s) you would like to run, and click <em>Run tests</em>.<br/><br/>NOTE: Tests in groups prefixed with <em>Islandora</em> generally require a configuration file, found in the Islandora module "tests" folder, as well as the use of the Islandora Drupal filter. Before any tests are run, please ensure that your web server has the appropriate permissions to alter the drupal_filter.xml file in your Fedora config folder. Your original Islandora Drupal filter configuration will NOT be overwritten by tests; HOWEVER, if PHP exits abnormally before the filter is reset, please use the "Repair Drupal Filter" button below.'), '#description' => t("Select the test(s) or test group(s) you would like to run, and click <em>Run tests</em>.<p>NOTE: Tests in groups prefixed with <em>Islandora</em> generally require a configuration file, found in the Islandora module 'tests' folder, as well as the use of the Islandora Drupal filter. Before any tests are run, please ensure that your web server has the appropriate permissions to alter the drupal-filter.xml file in your Fedora config folder. Your original Islandora Drupal filter configuration will NOT be overwritten by tests; HOWEVER, if PHP exits abnormally before the filter is reset, please use the 'Repair Drupal Filter' button below.</p><p><em>Drupal Filter Write Status:</em> !filter_status_message</p>", array(
'!filter_status_message' => $filter_status_message,
)),
); );
$form['tests']['table'] = array( $form['tests']['table'] = array(
@ -1789,6 +1856,9 @@ function islandora_form_simpletest_test_form_alter(array &$form) {
'#title' => filter_xss($info['name']), '#title' => filter_xss($info['name']),
'#description' => filter_xss($info['description']), '#description' => filter_xss($info['description']),
); );
if (is_subclass_of($class, 'IslandoraWebTestCase', TRUE) && !$filter_status) {
$form['tests']['table'][$group][$class]['#disabled'] = TRUE;
}
} }
} }
@ -1813,7 +1883,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) {
} }
/** /**
* Submit handler for islandora_form_simpletest_test_form_alter(). * Removes simpletest entries from the Drupal filter.
*/ */
function islandora_repair_drupal_filter() { function islandora_repair_drupal_filter() {
@ -1940,6 +2010,8 @@ function islandora_menu_local_tasks_alter(&$data, $router_item, $root_path) {
if ($tab['#link']['path'] == 'islandora/object/%/print_object') { if ($tab['#link']['path'] == 'islandora/object/%/print_object') {
if ($root_path == 'islandora/object/%') { if ($root_path == 'islandora/object/%') {
$islandora_path = drupal_get_path('module', 'islandora'); $islandora_path = drupal_get_path('module', 'islandora');
$tab['#prefix'] = '<li>';
$tab['#suffix'] = '</li>';
$tab['#theme'] = 'link'; $tab['#theme'] = 'link';
$tab['#text'] = theme('image', array( $tab['#text'] = theme('image', array(
'path' => "$islandora_path/images/print-icon.png", 'path' => "$islandora_path/images/print-icon.png",
@ -1974,3 +2046,15 @@ function islandora_islandora_object_alter(AbstractObject $object, array &$contex
islandora_set_defer_derivatives_flag($object); 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;
}
}

443
islandora.rules.inc

@ -21,6 +21,76 @@ 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' => 'islandora_datastream',
'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' => '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 on which the datastream existed, as an entity.'),
),
'datastream' => array(
'type' => 'text',
'label' => t('Datastream ID'),
'description' => t('The identifier of the purged datastream.'),
),
),
),
); );
} }
@ -60,6 +130,35 @@ 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(
'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 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(). * Implements hook_rules_condition_info().
*/ */
@ -71,6 +170,27 @@ function islandora_rules_condition_info() {
'group' => t('Islandora'), 'group' => t('Islandora'),
'parameter' => islandora_rules_relationship_parameter_array(), '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'),
'parameter' => islandora_rules_base_xpath_parameters(),
);
return $cond; return $cond;
} }
@ -92,8 +212,132 @@ function islandora_rules_action_info() {
'group' => t('Islandora'), 'group' => t('Islandora'),
'parameter' => islandora_rules_relationship_parameter_array(), '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(
'datastream' => 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.'),
),
'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(
'type' => 'list<islandora_domnode>',
'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; 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. * Checks that there is a relationship match on the given object.
* *
@ -133,3 +377,202 @@ function islandora_object_remove_relationship($sub, $pred_uri, $pred, $object, $
function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $type) { function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $type) {
$sub->relationships->add($pred_uri, $pred, $object, $type); $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);
$xpath = new DOMXPath($doc);
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);
}
}
/**
* 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;
}
/**
* 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;
}
/**
* 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) {
$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_property_get',
'setter callback' => 'islandora_rules_property_set',
'property' => 'nodeValue',
'token type' => 'string',
),
'text_content' => array(
'type' => 'text',
'label' => t('Text content'),
'computed' => TRUE,
'getter callback' => 'islandora_rules_property_get',
'property' => 'textContent',
'token type' => 'string',
),
),
'wrap' => TRUE,
),
'islandora_domelement' => array(
'label' => t('DOMElement instance'),
'group' => t('Islandora'),
'parent' => 'islandora_domnode',
'wrap' => TRUE,
),
);
}
/**
* 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'];
}
/**
* 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.
* @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, DOMNode $context_node = NULL) {
return array('nodes' => iterator_to_array($xpath->query($query, $context_node)));
}
/**
* Rules condition callback; check for the datastream on an object.
*/
function islandora_object_has_datastream(AbstractObject $object, $datastream_id) {
return isset($object[$datastream_id]);
}

59
js/spinner.js

@ -22,36 +22,39 @@
}); });
for (var base in settings.spinner) { for (var base in settings.spinner) {
var id = '#' + base; var id = '#' + base;
$(id, context).once('spinner', function () { // Don't add spinner to the hidden next/ingest button.
var spinner = new Spinner(settings.spinner[base].opts); if (id != '#edit-hidden-next') {
$(id).parents('form').one('submit', function(event) { $(id, context).once('spinner', function () {
if ($(this).data('clicked').is(id)) { var spinner = new Spinner(settings.spinner[base].opts);
event.preventDefault(); $(id).parents('form').one('submit', function (event) {
// Add Message. if ($(this).data('clicked').is(id)) {
var message = $('<div/>').text(settings.spinner[base].message); event.preventDefault();
$(id).after(message); // Add Message.
// Make UI changes. var message = $('<div/>').text(settings.spinner[base].message);
spinner.spin(this); $(id).after(message);
$('#edit-next').hide(); // Make UI changes.
$('#edit-prev').hide(); spinner.spin(this);
// Submit the form after a set timeout, this handles problems with $('#edit-next').hide();
// safari, in that safari submit's immediately.. $('#edit-prev').hide();
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) { // Submit the form after a set timeout, this handles problems with
$(':submit').attr('disabled', 'disabled'); // safari, in that safari submit's immediately..
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
$(':submit').attr('disabled', 'disabled');
}
setTimeout(function () {
// Allow for the button to be clicked, then click it then
// prevent the default behavoir.
$(id).removeAttr('disabled')
.click()
.click(function (event) {
event.preventDefault();
});
}, 500);
} }
setTimeout(function() { return true;
// Allow for the button to be clicked, then click it then });
// prevent the default behavoir.
$(id).removeAttr('disabled')
.click()
.click(function(event) {
event.preventDefault();
});
}, 500);
}
return true;
}); });
}); }
} }
} }
}; };

87
tests/includes/islandora_web_test_case.inc

@ -21,6 +21,17 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
*/ */
protected $deleteObjectsOnTeardown = TRUE; protected $deleteObjectsOnTeardown = TRUE;
/**
* Instantiates an IslandoraWebTestCase with a configuration.
*
* @throws Exception
* If the required test config file is not found.
*/
public function __construct($test_id = NULL) {
$this->configuration = IslandoraTestUtilityClass::getTestConfiguration();
parent::__construct($test_id);
}
/** /**
* Defers to IslandoraTestUtilities for missing methods. * Defers to IslandoraTestUtilities for missing methods.
* *
@ -28,6 +39,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
* The method being called. * The method being called.
* @param array $args * @param array $args
* The arguments for that method. * The arguments for that method.
*
* @return bool
* TRUE if the result was a pass, or FALSE otherwise.
*/ */
public function __call($method, $args) { public function __call($method, $args) {
module_load_include('inc', 'islandora', 'tests/includes/utilities'); module_load_include('inc', 'islandora', 'tests/includes/utilities');
@ -57,6 +71,74 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
} }
} }
/**
* Run all tests in this class.
*
* Attempts to figure out if the Drupal filter is writable before running any
* tests.
*
* @see DrupalWebTestCase::run()
*/
public function run(array $methods = array()) {
// Determine if the Drupal filter is writable so we know if we can proceed.
if (is_writable($this->configuration['drupal_filter_file'])) {
// Set up the SimpleTest environment.
simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), get_class($this));
$this->httpauth_method = variable_get('simpletest_httpauth_method', CURLAUTH_BASIC);
$username = variable_get('simpletest_httpauth_username', NULL);
$password = variable_get('simpletest_httpauth_password', NULL);
if ($username && $password) {
$this->httpauth_credentials = $username . ':' . $password;
}
set_error_handler(array($this, 'errorHandler'));
$class = get_class($this);
// Iterate through all the methods in this class, unless a specific list
// of methods to run was passed.
$class_methods = get_class_methods($class);
if ($methods) {
$class_methods = array_intersect($class_methods, $methods);
}
foreach ($class_methods as $method) {
// If the current method starts with "test", run it - it's a test.
if (strtolower(substr($method, 0, 4)) == 'test') {
// Insert a fail record. This will be deleted on completion to ensure
// that testing completed.
$method_info = new ReflectionMethod($class, $method);
$caller = array(
'file' => $method_info->getFileName(),
'line' => $method_info->getStartLine(),
'function' => $class . '->' . $method . '()',
);
$completion_check_id = DrupalTestCase::insertAssert($this->testId, $class, FALSE, t('The test did not complete due to a fatal error.'), 'Completion check', $caller);
try {
$this->setUp();
$this->$method();
}
catch (Exception $e) {
$this->exceptionHandler($e);
}
$this->tearDown();
// Remove the completion check record.
DrupalTestCase::deleteAssert($completion_check_id);
}
}
}
// If the Drupal filter is not writable, skip testing and error out.
else {
$method_info = new ReflectionMethod($this, 'run');
$class = get_class($this);
set_error_handler(array($this, 'errorHandler'));
$caller = array(
'file' => $method_info->getFileName(),
'line' => $method_info->getStartLine(),
'function' => $class . '->run()',
);
$this->assert(FALSE, "Unable to proceed; the Drupal filter is not writable by the server.", "Completion check", $caller);
}
drupal_get_messages();
restore_error_handler();
}
/** /**
* Sets up the web test case. * Sets up the web test case.
* *
@ -74,7 +156,6 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); module_load_include('inc', 'islandora', 'includes/tuque_wrapper');
module_load_include('inc', 'islandora', 'tests/includes/utilities'); module_load_include('inc', 'islandora', 'tests/includes/utilities');
$this->configuration = IslandoraTestUtilityClass::getTestConfiguration();
if ($this->configuration['use_drupal_filter']) { if ($this->configuration['use_drupal_filter']) {
$this->setUpDrupalFilter(); $this->setUpDrupalFilter();
} }
@ -142,7 +223,6 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
islandora_repair_drupal_filter(); islandora_repair_drupal_filter();
} }
unset($this->admin); unset($this->admin);
unset($this->configuration);
parent::tearDown(); parent::tearDown();
} }
@ -178,6 +258,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase {
* The label of the first 'Delete' button * The label of the first 'Delete' button
* @param bool $safety * @param bool $safety
* If TRUE, this will only delete objects owned by users in $this->users. * If TRUE, this will only delete objects owned by users in $this->users.
*
* @return bool
* If the deletion fails, return FALSE.
*/ */
public function deleteObject($pid, $button = NULL, $safety = TRUE) { public function deleteObject($pid, $button = NULL, $safety = TRUE) {
$object = islandora_object_load($pid); $object = islandora_object_load($pid);

6
tests/scripts/travis_setup.sh

@ -22,8 +22,8 @@ tar xf drush-6.3.tar.gz
sudo mv drush-6.3 /opt/ sudo mv drush-6.3 /opt/
sudo ln -s /opt/drush-6.3/drush /usr/bin/drush sudo ln -s /opt/drush-6.3/drush /usr/bin/drush
wget http://alpha.library.yorku.ca/PHP_CodeSniffer-1.5.4.tgz wget http://alpha.library.yorku.ca/PHP_CodeSniffer-1.5.6.tgz
pear install PHP_CodeSniffer-1.5.4.tgz pear install PHP_CodeSniffer-1.5.6.tgz
wget http://alpha.library.yorku.ca/phpcpd.phar wget http://alpha.library.yorku.ca/phpcpd.phar
sudo mv phpcpd.phar /usr/local/bin/phpcpd sudo mv phpcpd.phar /usr/local/bin/phpcpd
@ -39,7 +39,7 @@ mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/is
mkdir sites/all/libraries mkdir sites/all/libraries
ln -s $HOME/tuque sites/all/libraries/tuque ln -s $HOME/tuque sites/all/libraries/tuque
drush dl --yes coder drush dl --yes coder
drush dl --yes potx drush dl --yes potx-7.x-1.0
drush en --yes coder_review drush en --yes coder_review
drush en --yes simpletest drush en --yes simpletest
drush en --yes potx drush en --yes potx

Loading…
Cancel
Save