diff --git a/css/islandora.admin.css b/css/islandora.admin.css index 1cd1976d..c7e51b28 100644 --- a/css/islandora.admin.css +++ b/css/islandora.admin.css @@ -13,5 +13,11 @@ .islandora-solution-pack-fieldset table th, .islandora-solution-pack-fieldset table td { - width: 33%; + width: 30%; +} + +.islandora-solution-pack-fieldset table th:first-child, +.islandora-solution-pack-fieldset table td:first-child +{ + width: 10%; } \ No newline at end of file diff --git a/includes/add_datastream.form.inc b/includes/add_datastream.form.inc index 3a1d964d..33decefc 100644 --- a/includes/add_datastream.form.inc +++ b/includes/add_datastream.form.inc @@ -161,7 +161,10 @@ function islandora_add_datastream_form_validate(array $form, array &$form_state) if (isset($form_state['datastream_requirements'][$dsid]) && $file) { $allowed_types = $form_state['datastream_requirements'][$dsid]['mime']; $mime_detect = new MimeDetect(); - $allowed_extensions = array_map(array($mime_detect, 'getExtension'), $allowed_types); + $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]); diff --git a/includes/breadcrumb.inc b/includes/breadcrumb.inc index 05ab1bac..6f975ae3 100644 --- a/includes/breadcrumb.inc +++ b/includes/breadcrumb.inc @@ -28,7 +28,7 @@ function islandora_get_breadcrumbs($object) { $breadcrumbs = islandora_get_breadcrumbs_recursive($object->id, $object->repository); array_pop($breadcrumbs); $context = 'islandora'; - drupal_alter('islandora_breadcrumbs', $breadcrumbs, $context); + drupal_alter('islandora_breadcrumbs', $breadcrumbs, $context, $object); return $breadcrumbs; } @@ -114,9 +114,7 @@ function islandora_get_breadcrumbs_recursive($pid, FedoraRepository $repository, // render the last two links and break (on the next pass). return array_merge( islandora_get_breadcrumbs_recursive($root, $repository, $context), - array( - '...', - ) + array('...') ); } } diff --git a/includes/datastream.inc b/includes/datastream.inc index 1d506e3d..ba2872c2 100644 --- a/includes/datastream.inc +++ b/includes/datastream.inc @@ -53,8 +53,19 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F if ($download) { // Browsers will not append all extensions. $mime_detect = new MimeDetect(); - $extension = $mime_detect->getExtension($datastream->mimetype); - $filename = $datastream->label . '.' . $extension; + $extension = '.' . $mime_detect->getExtension($datastream->mimetype); + + // Prevent adding on a duplicate extension. + $label = $datastream->label; + $extension_length = strlen($extension); + $duplicate_extension_position = strlen($label) > $extension_length ? + strripos($label, $extension, -$extension_length) : + FALSE; + $filename = $label; + if ($duplicate_extension_position === FALSE) { + $filename .= $extension; + } + header("Content-Disposition: attachment; filename=\"$filename\""); } @@ -70,14 +81,30 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F islandora_view_datastream_set_cache_headers($datastream); drupal_page_is_cacheable(FALSE); - // Try not to load the file into PHP memory! - // Close and flush ALL the output buffers! - while (@ob_end_flush()) { - }; // New content needed. if ($cache_check === 200) { - $datastream->getContent('php://output'); + // We need to see if the chunking is being requested. This will mainly + // happen with iOS video requests as they do not support any other way + // to receive content for playback. + $chunk_headers = FALSE; + if (isset($_SERVER['HTTP_RANGE'])) { + // Set headers specific to chunking. + $chunk_headers = islandora_view_datastream_set_chunk_headers($datastream); + } + // Try not to load the file into PHP memory! + // Close and flush ALL the output buffers! + while (@ob_end_flush()) { + }; + + if (isset($_SERVER['HTTP_RANGE'])) { + if ($chunk_headers) { + islandora_view_datastream_deliver_chunks($datastream, $chunk_headers); + } + } + else { + $datastream->getContent('php://output'); + } } exit(); } @@ -312,7 +339,7 @@ function islandora_edit_datastream(AbstractDatastream $datastream) { case 0: // No edit implementations. drupal_set_message(t('There are no edit methods specified for this datastream.')); - drupal_goto("islandora/object/{$object->id}/manage/datastreams"); + drupal_goto("islandora/object/{$datastream->parent->id}/manage/datastreams"); break; case 1: @@ -383,3 +410,125 @@ function islandora_datastream_get_view_link(AbstractDatastream $datastream) { 'datastream' => $datastream, )); } + +/** + * Set the headers for the chunking of our content. + * + * @param AbstractDatastream $datastream + * An AbstractDatastream representing a datastream on a Fedora object. + * + * @return bool + * TRUE if there are chunks to be returned, FALSE otherwise. + */ +function islandora_view_datastream_set_chunk_headers(AbstractDatastream $datastream) { + $file_uri = islandora_view_datastream_retrieve_file_uri($datastream); + // The meat of this has been taken from: + // http://mobiforge.com/design-development/content-delivery-mobile-devices. + $size = filesize($file_uri); + $length = $size; + $start = 0; + $end = $size - 1; + + header("Accept-Ranges: 0-$length"); + if (isset($_SERVER['HTTP_RANGE'])) { + $c_start = $start; + $c_end = $end; + // Extract the range string. + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + // Make sure the client hasn't sent us a multibyte range. + if (strpos($range, ',') !== FALSE) { + // Not a valid range, notify the client. + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + exit; + } + // If the range starts with an '-' we start from the beginning. If not, we + // forward the file pointer and make sure to get the end byte if specified. + if (strpos($range, '-') === 0) { + // The n-number of the last bytes is requested. + $c_start = $size - substr($range, 1); + } + else { + $range = explode('-', $range); + $c_start = $range[0]; + $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; + } + /* Check the range and make sure it's treated according to the specs. + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + */ + // End bytes can not be larger than $end. + $c_end = ($c_end > $end) ? $end : $c_end; + // Validate the requested range and return an error if it's not correct. + if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + exit; + } + $start = $c_start; + $end = $c_end; + // Calculate new content length. + $length = $end - $start + 1; + header('HTTP/1.1 206 Partial Content'); + } + // Notify the client the byte range we'll be outputting. + header("Content-Range: bytes $start-$end/$size"); + header("Content-Length: $length"); + return array( + 'start' => $start, + 'end' => $end, + ); +} + +/** + * Deliver back the specified chunks of a file. + * + * @param AbstractDatastream $datastream + * An AbstractDatastream representing a datastream on a Fedora object. + * @param array $params + * An associate array containing the start and ending chunk bytes. + */ +function islandora_view_datastream_deliver_chunks(AbstractDatastream $datastream, $params) { + $file_uri = islandora_view_datastream_retrieve_file_uri($datastream); + // The meat of this has been taken from: + // http://mobiforge.com/design-development/content-delivery-mobile-devices. + $fp = @fopen($file_uri, 'rb'); + fseek($fp, $params['start']); + // Start buffered download. + $buffer = 1024 * 8; + while (!feof($fp) && ($p = ftell($fp)) <= $params['end']) { + if ($p + $buffer > $params['end']) { + // In case we're only outputting a chunk, make sure we don't read past the + // length. + $buffer = $params['end'] - $p + 1; + } + // Reset time limit for big files. + set_time_limit(0); + echo fread($fp, $buffer); + } + fclose($fp); +} + +/** + * Creates/returns the file URI for the content of a datastream for chunking. + * + * @param AbstractDatastream $datastream + * An AbstractDatastream representing a datastream on a Fedora object. + * + * @return string + * The URI of the file. + */ +function islandora_view_datastream_retrieve_file_uri(AbstractDatastream $datastream) { + $mime_detect = new MimeDetect(); + $extension = $mime_detect->getExtension($datastream->mimetype); + $file_uri = 'temporary://chunk_' . $datastream->parent->id . '_' . $datastream->id . '_' . $datastream->createdDate->getTimestamp() . '.' . $extension; + if (!file_exists($file_uri)) { + $file = new stdClass(); + $file->uri = $file_uri; + $file->filename = drupal_basename($file_uri); + $file->filemime = $datastream->mimeType; + $file->status = 0; + $datastream->getContent($file_uri); + file_save($file); + } + return $file_uri; +} diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index 507a182f..8c02aaad 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -1,4 +1,5 @@ parent; drupal_set_title(t("@dsid Previous Versions", array('@dsid' => $datastream->id))); + $audit_values = islandora_get_audit_trail($parent->id, $datastream->id); $header = array(); $header[] = array('data' => t('Created Date')); $header[] = array('data' => t('Size')); $header[] = array('data' => t('Label')); + $header[] = array('data' => t('Responsibility')); $header[] = array('data' => t('Mime type')); $header[] = array('data' => t('Operations'), 'colspan' => '2'); $rows = array(); foreach ($datastream as $version => $datastream_version) { $row = array(); + $reponsibility = $parent->owner; + foreach ($audit_values as $audit_value) { + $internal = $datastream_version->createdDate; + if ($audit_value['date'] == $datastream_version->createdDate) { + $reponsibility = $audit_value['responsibility']; + } + } + $user = user_load_by_name($reponsibility); + if ($user) { + $user_id = $user->uid; + $user_val = l($reponsibility, "user/$user_id"); + } + else { + $user_val = $reponsibility; + } $row[] = array( 'class' => 'datastream-date', 'data' => theme('islandora_datastream_view_link', array( @@ -39,6 +57,10 @@ function islandora_datastream_version_table($datastream) { 'class' => 'datastream-label', 'data' => $datastream_version->label, ); + $row[] = array( + 'class' => 'datastream-responsibility', + 'data' => $user_val, + ); $row[] = array( 'class' => 'datastream-mime', 'data' => $datastream_version->mimeType, @@ -208,3 +230,39 @@ function islandora_revert_datastream_version_form_submit(array $form, array &$fo $form_state['redirect'] = "islandora/object/{$islandora_object->id}/datastream/{$datastream_to_revert->id}/version"; } + +/** + * Gets Audit datastream values from foxml. + * + * @param String $pid + * PID of parent object + * + * @return array + * Array of audit values + */ +function islandora_get_audit_trail($pid, $dsid) { + $url = variable_get('islandora_base_url', 'http://localhost:8080/fedora'); + $connection = islandora_get_tuque_connection(NULL, $url); + $xml = $connection->api->m->getObjectXml($pid); + $simple_xml = simplexml_load_string($xml); + $fox_ns = "info:fedora/fedora-system:def/foxml#"; + $audit_ns = 'info:fedora/fedora-system:def/audit#'; + $foxml_nodes = $simple_xml->children($fox_ns); + foreach ($foxml_nodes as $node) { + if ($node->attributes()->ID == "AUDIT") { + $content = $node->datastreamVersion->xmlContent; + $audit_nodes = $content->children($audit_ns); + } + } + $audit_values = array(); + if (isset($audit_nodes)) { + foreach ($audit_nodes->auditTrail->record as $record) { + if ($dsid == $record->componentID) { + $values['responsibility'] = $record->responsibility; + $values['date'] = $record->date; + $audit_values[] = $values; + } + } + } + return $audit_values; +} diff --git a/includes/derivatives.inc b/includes/derivatives.inc index 5ba4bb56..d25789cc 100644 --- a/includes/derivatives.inc +++ b/includes/derivatives.inc @@ -41,20 +41,7 @@ function islandora_do_derivatives(AbstractObject $object, array $options) { $hooks = islandora_invoke_hook_list(ISLANDORA_DERVIATIVE_CREATION_HOOK, $object->models, array($object)); uasort($hooks, 'drupal_sort_weight'); $results = array(); - - if (array_key_exists('source_dsid', $options)) { - $hooks = array_filter($hooks, function($filter_hook) use($options) { - return array_key_exists('source_dsid', $filter_hook) && - $filter_hook['source_dsid'] == $options['source_dsid']; - }); - } - - if (array_key_exists('destination_dsid', $options)) { - $hooks = array_filter($hooks, function($filter_hook) use($options) { - return array_key_exists('destination_dsid', $filter_hook) && - $filter_hook['destination_dsid'] == $options['destination_dsid']; - }); - } + $hooks = islandora_filter_derivatives($hooks, $options, $object); foreach ($hooks as $hook) { if (isset($hook['file'])) { @@ -121,3 +108,94 @@ function islandora_derivative_logging(array $logging_results) { } } } + +/** + * Kicks off derivative functions based upon hooks and conditions. + * + * @param AbstractObject $object + * An AbstractObject representing a FedoraObject. + * @param array $options + * An array of parameters containing: + * - force: Bool denoting whether we are forcing the generation of + * derivatives. + * - source_dsid: (Optional) String of the datastream id we are generating + * from or NULL if it's the object itself. + * - destination_dsid: (Optional) String of the datastream id that is being + * created. To be used in the UI. + * + * @return array + * An array of operations to be called from within a batch. + */ +function islandora_do_batch_derivatives(AbstractObject $object, array $options) { + module_load_include('inc', 'islandora', 'includes/utilities'); + $options += array( + 'force' => FALSE, + ); + $hooks = islandora_invoke_hook_list(ISLANDORA_DERVIATIVE_CREATION_HOOK, $object->models, array($object)); + uasort($hooks, 'drupal_sort_weight'); + $operations = array(); + + $hooks = islandora_filter_derivatives($hooks, $options, $object); + foreach ($hooks as $hook) { + $file = FALSE; + if (isset($hook['file'])) { + $file = $hook['file']; + } + foreach ($hook['function'] as $function) { + $operations[] = array('islandora_derivative_perform_batch_operation', array( + $function, + $file, + $object->id, + $options['force']), + ); + } + } + return $operations; +} + +/** + * Filter the derivative functions to only call those which are valid. + * + * @param array $hooks + * An array of hooks to be filtered depending on options. + * @param array $options + * An array of options for the derivative generation. + * @param AbstractObject $object + * An AbstractObject representing an object within Fedora. + * + * @return array + * Returns the filtered array of hooks to be ran. + */ +function islandora_filter_derivatives($hooks, $options, AbstractObject $object) { + if (array_key_exists('source_dsid', $options)) { + $hooks = array_filter($hooks, function ($filter_hook) use ($options) { + return array_key_exists('source_dsid', $filter_hook) && + $filter_hook['source_dsid'] == $options['source_dsid']; + }); + } + if (array_key_exists('destination_dsid', $options)) { + $hooks = array_filter($hooks, function ($filter_hook) use ($options) { + return array_key_exists('destination_dsid', $filter_hook) && + $filter_hook['destination_dsid'] == $options['destination_dsid']; + }); + } + // Do a final filtering to make sure that the source DSID exists on the object + // where needed. Using a defined function as opposed to the way above as + // it seems to break PHPCS as of 1.4.8. + $filter_function = function ($filter_hook) use ($object) { + $to_return = FALSE; + if (array_key_exists('source_dsid', $filter_hook)) { + if ($filter_hook['source_dsid'] != NULL) { + if (isset($object[$filter_hook['source_dsid']])) { + $to_return = TRUE; + } + } + else { + $to_return = TRUE; + } + } + return $to_return; + }; + $hooks = array_filter($hooks, $filter_function); + return $hooks; +} diff --git a/includes/ingest.form.inc b/includes/ingest.form.inc index b49556d7..89376f7d 100644 --- a/includes/ingest.form.inc +++ b/includes/ingest.form.inc @@ -769,10 +769,16 @@ function islandora_ingest_form_submit(array $form, array &$form_state) { islandora_ingest_form_execute_consecutive_callback_steps($form, $form_state, $step); } // Ingest the objects. + $set_redirect = TRUE; foreach ($form_state['islandora']['objects'] as &$object) { try { islandora_add_object($object); - $form_state['redirect'] = "islandora/object/{$object->id}"; + // We want to redirect to the first object as it's considered to be the + // primary object. + if ($set_redirect) { + $form_state['redirect'] = "islandora/object/{$object->id}"; + $set_redirect = FALSE; + } drupal_set_message( t('"@label" (ID: @pid) has been ingested.', array('@label' => $object->label, '@pid' => $object->id)), 'status'); diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index 4035afe5..e76975d6 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -383,4 +383,19 @@ class MimeDetect { return $this->protectedMimeTypes; } + /** + * Get all valid extensions for this MIME type. + * + * @param string $mimetype + * The MIME type we are searching for. + * + * @return array + * An array of valid extensions for this MIME type. + */ + public function getValidExtensions($mimetype) { + $filter = function ($mime) use ($mimetype) { + return $mime == $mimetype; + }; + return array_keys(array_filter($this->protectedMimeTypes, $filter)); + } } diff --git a/includes/mime_type.autocomplete.inc b/includes/mime_type.autocomplete.inc new file mode 100644 index 00000000..3ac31079 --- /dev/null +++ b/includes/mime_type.autocomplete.inc @@ -0,0 +1,27 @@ +getMimeTypes(); + $output = array(); + foreach ($mime_types as $mime_type) { + if (preg_match("/{$string}/i", $mime_type) !== 0) { + $output[$mime_type] = $mime_type; + } + } + return drupal_json_output($output); +} diff --git a/includes/object_properties.form.inc b/includes/object_properties.form.inc index 5295f067..f9c8e23b 100644 --- a/includes/object_properties.form.inc +++ b/includes/object_properties.form.inc @@ -26,6 +26,15 @@ function islandora_object_properties_form(array $form, array &$form_state, Abstr if (!empty($temp)) { $related_objects_pids = array_merge_recursive($related_objects_pids, $temp); } + $regenerate_derivatives_access = FALSE; + if (islandora_object_access(ISLANDORA_REGENERATE_DERIVATIVES, $object)) { + module_load_include('inc', 'islandora', 'includes/derivatives'); + $hooks = islandora_invoke_hook_list(ISLANDORA_DERVIATIVE_CREATION_HOOK, $object->models, array($object)); + $hooks = islandora_filter_derivatives($hooks, array('force' => TRUE), $object); + if (count($hooks) > 1) { + $regenerate_derivatives_access = TRUE; + } + } return array( 'pid' => array( '#type' => 'hidden', @@ -84,6 +93,12 @@ function islandora_object_properties_form(array $form, array &$form_state, Abstr '#submit' => array('islandora_object_properties_form_delete'), '#limit_validation_errors' => array(array('pid')), ), + 'regenerate' => array( + '#type' => 'submit', + '#access' => $regenerate_derivatives_access, + '#value' => t("Regenerate all derivatives"), + '#submit' => array('islandora_object_properties_regenerate_derivatives'), + ), ); } @@ -166,15 +181,15 @@ function islandora_object_properties_form_delete(array $form, array &$form_state /** * Updates object state. * - * @param String $pid + * @param string $pid * PID of object to be updated - * @param Boolean $update_states + * @param bool $update_states * If TRUE, update object state - * @param String $state + * @param string $state * Desired object state - * @param Boolean $update_owners + * @param bool $update_owners * If TRUE, update Owner - * @param String $owner + * @param string $owner * New Owner */ function islandora_update_object_properties($pid, $update_states, $state, $update_owners, $owner) { @@ -188,3 +203,15 @@ function islandora_update_object_properties($pid, $update_states, $state, $updat } } } + +/** + * Callback function for object properties regenerate all derivatives. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + */ +function islandora_object_properties_regenerate_derivatives(array $form, array &$form_state) { + drupal_goto("islandora/object/{$form_state['object']}/regenerate"); +} diff --git a/includes/regenerate_derivatives.form.inc b/includes/regenerate_derivatives.form.inc new file mode 100644 index 00000000..a7ef147c --- /dev/null +++ b/includes/regenerate_derivatives.form.inc @@ -0,0 +1,178 @@ + $datastream->id)), + "islandora/object/{$datastream->parent->id}/manage/datastreams", + t('This will create a new version of the datastream. Please wait while this happens.'), + t('Regenerate'), + t('Cancel') + ); +} + +/** + * Submit handler for the regenerate datastream derivative form. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + */ +function islandora_regenerate_datastream_derivative_form_submit(array $form, array &$form_state) { + module_load_include('inc', 'islandora', 'includes/derivatives'); + $datastream = $form_state['datastream']; + $batch = islandora_regenerate_datastream_derivative_batch($datastream); + batch_set($batch); + $form_state['redirect'] = "islandora/object/{$datastream->parent->id}/manage/datastreams"; +} + +/** + * Regenerate all derivatives on an object. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + * @param AbstractObject $object + * The object that is having its derivatives regenerated. + * + * @return array + * The Drupal form definition. + */ +function islandora_regenerate_object_derivatives_form(array $form, array &$form_state, AbstractObject $object) { + $form_state['object'] = $object; + return confirm_form($form, + t('Are you sure you want to regenerate all the derivatives for %title?', array('%title' => $object->label)), + "islandora/object/{$object->id}/manage/properties", + t('This will create a new version for every datastream on the object. Please wait while this happens.'), + t('Regenerate'), + t('Cancel') + ); +} + +/** + * Submit handler for the regenerate object derivativse form. + * + * @param array $form + * The Drupal form. + * @param array $form_state + * The Drupal form state. + */ +function islandora_regenerate_object_derivatives_form_submit(array $form, array &$form_state) { + $object = $form_state['object']; + $batch = islandora_regenerate_object_derivatives_batch($object); + batch_set($batch); + $form_state['redirect'] = "islandora/object/{$object->id}/manage/properties"; +} + +/** + * Creates a batch to go out and re-create all of the derivatives for an object. + * + * @param AbstractObject $object + * A AbstractObject representing an object within Fedora. + * + * @return array + * An array specifying the Drupal batch. + */ +function islandora_regenerate_object_derivatives_batch(AbstractObject $object) { + module_load_include('inc', 'islandora', 'includes/derivatives'); + return array( + 'title' => t('Regenerating all derivatives for @label', array('@label' => $object->label)), + 'operations' => islandora_do_batch_derivatives($object, array('force' => TRUE)), + 'init_message' => t('Preparing to regenerate derivatives...'), + 'progress_message' => t('Time elapsed: @elapsed
Estimated time remaning @estimate.'), + 'error_message' => t('An error has occurred.'), + 'file' => drupal_get_path('module', 'islandora') . '/includes/regenerate_derivatives.form.inc', + 'finished' => 'islandora_regenerate_derivative_batch_finished', + ); +} + +/** + * Creates a batch to go out and re-create the derivative for a datastream. + * + * @param AbstractDatastream $datastream + * A AbstractDatastream representing a datastream on an object within Fedora. + * + * @return array + * An array specifying the Drupal batch. + */ +function islandora_regenerate_datastream_derivative_batch(AbstractDatastream $datastream) { + module_load_include('inc', 'islandora', 'includes/derivatives'); + return array( + 'title' => t('Regenerating derivatives for the @dsid datastream', array('@dsid' => $datastream->id)), + 'operations' => islandora_do_batch_derivatives($datastream->parent, array( + 'force' => TRUE, + 'destination_dsid' => $datastream->id, + )), + 'init_message' => t('Preparing to regenerate derivatives...'), + 'progress_message' => t('Time elapsed: @elapsed
Estimated time remaning @estimate.'), + 'error_message' => t('An error has occurred.'), + 'file' => drupal_get_path('module', 'islandora') . '/includes/regenerate_derivatives.form.inc', + 'finished' => 'islandora_regenerate_derivative_batch_finished', + ); +} + +/** + * Wrapper to call out to batch operations. + * + * @param string $function + * The name of the function we are calling for derivatives. + * @param bool|string $file + * FALSE if there is no file to load, the path to require otherwise + * @param string $pid + * The pid of the object we are performing. + * @param bool $force + * Whether we are forcing derivative regeneration or not. + * @param array $context + * The context of the current batch operation. + */ +function islandora_derivative_perform_batch_operation($function, $file, $pid, $force, &$context) { + if ($file) { + require_once $file; + } + if (function_exists($function)) { + $logging = call_user_func($function, islandora_object_load($pid), $force); + if (!empty($logging)) { + $context['results']['logging'][] = $logging; + } + } + else { + watchdog('islandora', 'Unable to call derivative function @function as it was not found!', array('@function' => $function), WATCHDOG_ERROR); + } +} + +/** + * Finished function for derivative batch regeneration. + * + * @param array $success + * An array of success passed from the batch. + * @param array $results + * An array of results passed from the batch. + * @param array $operations + * An array of operations passed from the batch. + */ +function islandora_regenerate_derivative_batch_finished($success, $results, $operations) { + module_load_include('inc', 'islandora', 'includes/derivatives'); + if (!empty($results['logging'])) { + islandora_derivative_logging($results['logging']); + } +} diff --git a/includes/solution_packs.inc b/includes/solution_packs.inc index 65f62851..30cc8576 100644 --- a/includes/solution_packs.inc +++ b/includes/solution_packs.inc @@ -140,7 +140,14 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solution ); $status_severities = array_keys($status_info); $solution_pack_status_severity = array_search('up_to_date', $status_severities); - $table_rows = array(); + + // Prepair for tableselect. + $header = array( + 'label' => t('Label'), + 'pid' => t('PID'), + 'status' => t('Status')); + + $object_info = array(); foreach ($objects as $object) { $object_status = islandora_check_object_status($object); $object_status_info = $status_info[$object_status['status']]; @@ -151,11 +158,15 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solution $exists = $object_status['status'] != 'missing'; $label = $exists ? l($object->label, "islandora/object/{$object->id}") : $object->label; $status_msg = "{$object_status_info['image']} {$object_status['status_friendly']}"; - $table_rows[] = array($label, $object->id, $status_msg); + $object_info[] = array( + 'label' => $label, + 'pid' => $object->id, + 'status' => $status_msg); } $solution_pack_status = $status_severities[$solution_pack_status_severity]; $solution_pack_status_info = $status_info[$solution_pack_status]; - return array( + + $form = array( 'solution_pack' => array( '#type' => 'fieldset', '#collapsible' => FALSE, @@ -180,17 +191,20 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solution ), 'install_status' => array( '#markup' => t('Object status: !image !status', array( - '!image' => $solution_pack_status_info['image'], - '!status' => $solution_pack_status_info['solution_pack'], - )), + '!image' => $solution_pack_status_info['image'], + '!status' => $solution_pack_status_info['solution_pack'], + )), '#prefix' => '
', '#suffix' => '
', ), 'table' => array( - '#type' => 'item', - '#markup' => theme('table', array( - 'header' => array(t('Label'), t('PID'), t('Status')), - 'rows' => $table_rows)), + '#type' => 'tableselect', + '#header' => $header, + '#options' => $object_info, + ), + 'tablevalue' => array( + '#type' => 'hidden', + '#value' => json_encode($object_info), ), 'submit' => array( '#type' => 'submit', @@ -200,6 +214,7 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solution ), ), ); + return $form; } /** @@ -211,9 +226,21 @@ function islandora_solution_pack_form(array $form, array &$form_state, $solution * The state of the form submited. */ function islandora_solution_pack_form_submit(array $form, array &$form_state) { + $not_checked = array(); + $object_info = json_decode($form_state['values']['tablevalue']); + if (isset($form_state['values']['table'])) { + foreach ($form_state['values']['table'] as $key => $value) { + if ($value === 0) { + $not_checked[] = $object_info[$key]->pid; + } + } + } $solution_pack_module = $form_state['values']['solution_pack_module']; - $batch = islandora_solution_pack_get_batch($solution_pack_module); + // Use not_checked instead of checked. Remove not checked item from betch. so + // that get batch function can get all object ingest batch if not checked list + // is empty. + $batch = islandora_solution_pack_get_batch($solution_pack_module, $not_checked); batch_set($batch); // Hook to let solution pack objects be modified. // Not using module_invoke so solution packs can be expanded by other modules. @@ -227,11 +254,13 @@ function islandora_solution_pack_form_submit(array $form, array &$form_state) { * @param string $module * The name of the modules of which to grab the required objects for to setup * the batch. - * + * @param array $not_checked + * The object that will bot be install. + * * @return array * An array defining a batch which can be passed on to batch_set(). */ -function islandora_solution_pack_get_batch($module) { +function islandora_solution_pack_get_batch($module, $not_checked = array()) { $batch = array( 'title' => t('Installing / Updating solution pack objects'), 'file' => drupal_get_path('module', 'islandora') . '/includes/solution_packs.inc', @@ -239,6 +268,14 @@ function islandora_solution_pack_get_batch($module) { ); $info = islandora_solution_packs_get_required_objects($module); + foreach ($info['objects'] as $key => $object) { + foreach ($not_checked as $not) { + if ($object->id == $not) { + unset($info['objects'][$key]); + } + } + } + foreach ($info['objects'] as $object) { $batch['operations'][] = array('islandora_solution_pack_batch_operation_reingest_object', array($object)); } @@ -803,7 +840,6 @@ function islandora_get_viewer_callback($viewer_id = NULL) { } return FALSE; } - /** * @} End of "defgroup viewer-functions". */ diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 2aa85a8b..d30af147 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -8,7 +8,11 @@ * @todo Overload functions and apply pre/post hooks. */ -$islandora_module_path = drupal_get_path('module', 'islandora'); +// This function may not exist when a batch operation is running from a +// multistep form. +if (function_exists('drupal_get_path')) { + $islandora_module_path = drupal_get_path('module', 'islandora'); +} // @todo this until we expost these in a module or library @include_once 'sites/all/libraries/tuque/Datastream.php'; @@ -294,7 +298,14 @@ class IslandoraFedoraApiM extends FedoraApiM { default: $ret = parent::purgeDatastream($pid, $dsid, $params); - islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_PURGED_HOOK, $object->models, $dsid, $object, $dsid); + // We need to remove this object from the cache and reload it as + // Tuque may not have an updated copy. That is the datastream could + // still be present within the object even though it's purged out of + // Fedora. + $tuque = islandora_get_tuque_connection(); + $tuque->cache->delete($pid); + $non_cached_object = islandora_object_load($pid); + islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_PURGED_HOOK, $non_cached_object->models, $dsid, $non_cached_object, $dsid); return $ret; } } diff --git a/includes/utilities.inc b/includes/utilities.inc index d858ff0c..0710f8f3 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -981,3 +981,17 @@ function islandora_scale_thumbnail($file, $width, $height) { } return FALSE; } + +/** + * Determines if the server operating system is Windows. + * + * @return bool + * TRUE if Windows, FALSE otherwise. + */ +function islandora_deployed_on_windows() { + // Determine if PHP is currently running on Windows. + if (strpos(strtolower(php_uname('s')), 'windows') !== FALSE) { + return TRUE; + } + return FALSE; +} diff --git a/islandora.api.php b/islandora.api.php index 04d8bd49..51aeb343 100644 --- a/islandora.api.php +++ b/islandora.api.php @@ -691,8 +691,10 @@ function hook_islandora_update_related_objects_properties(AbstractObject $object * Breadcrumbs array to be altered by reference. Each element is markup. * @param string $context * Where the alter is originating from for distinguishing. + * @param AbstractObject $object + * (Optional) AbstractObject representing object providing breadcrumb path */ -function hook_islandora_breadcrumbs_alter(&$breadcrumbs, $context) { +function hook_islandora_breadcrumbs_alter(&$breadcrumbs, $context, $object = NULL) { } diff --git a/islandora.drush.inc b/islandora.drush.inc index 6ba73cb5..acf3994f 100644 --- a/islandora.drush.inc +++ b/islandora.drush.inc @@ -70,7 +70,23 @@ function islandora_drush_command() { ), 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN, ); - + $commands['islandora-solution-pack-install-content_models'] = array( + 'description' => dt('Install Solution Pack content models.'), + 'options' => array( + 'module' => array( + 'description' => dt('The module for which to install the content models.'), + 'required' => TRUE, + ), + ), + 'aliases' => array('ispicm'), + 'drupal dependencies' => array( + 'islandora', + ), + 'examples' => array( + 'drush -u 1 ispicm --module=islandora' => dt('Install missing solution pack objects for the "islandora" module.'), + ), + 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN, + ); return $commands; } @@ -83,9 +99,7 @@ function drush_islandora_solution_pack_install_required_objects() { $module = drush_get_option('module'); if (module_exists($module)) { islandora_install_solution_pack( - $module, - 'install', - drush_get_option('force', FALSE) + $module, 'install', drush_get_option('force', FALSE) ); } else { @@ -104,8 +118,7 @@ function drush_islandora_solution_pack_uninstall_required_objects() { $module = drush_get_option('module'); if (module_exists($module)) { islandora_uninstall_solution_pack( - $module, - drush_get_option('force', FALSE) + $module, drush_get_option('force', FALSE) ); } else { @@ -152,3 +165,36 @@ function drush_islandora_solution_pack_required_objects_status() { drush_print_table($rows, $header, $widths); } } + +/** + * Command callback to install required objects. + */ +function drush_islandora_solution_pack_install_content_models() { + module_load_include('inc', 'islandora', 'includes/solution_packs'); + $module = drush_get_option('module'); + if (module_exists($module)) { + $info = islandora_solution_packs_get_required_objects($module); + $objects_to_add = array(); + foreach ($info['objects'] as $key => $candidate) { + $object = islandora_object_load($candidate); + if (in_array('fedora-system:ContentModel-3.0', $object->models)) { + $objects_to_add[] = $candidate; + } + } + foreach ($objects_to_add as $object_to_add) { + $old_object = islandora_object_load($object_to_add->id); + if ($old_object) { + $deleted = islandora_delete_object($old_object); + if (!$deleted) { + drush_log(dt('@object did not delete.', array('@object' => $old_object->id), 'error')); + continue; + } + } + $new_object = islandora_add_object($object_to_add); + $verb = $deleted ? dt("Replaced") : dt("Added"); + if ($new_object) { + drush_log("$verb " . $object_to_add->id . " - " . $object_to_add->label); + } + } + } +} diff --git a/islandora.module b/islandora.module index 25244bbc..a2f2625e 100644 --- a/islandora.module +++ b/islandora.module @@ -36,6 +36,7 @@ define('ISLANDORA_MANAGE_PROPERTIES', 'manage object properties'); define('ISLANDORA_VIEW_DATASTREAM_HISTORY', 'view old datastream versions'); define('ISLANDORA_MANAGE_DELETED_OBJECTS', 'manage deleted objects'); define('ISLANDORA_REVERT_DATASTREAM', 'revert to old datastream'); +define('ISLANDORA_REGENERATE_DERIVATIVES', 'regenerate derivatives for an object'); // Hooks. @@ -61,6 +62,7 @@ define('ISLANDORA_DERVIATIVE_CREATION_HOOK', 'islandora_derivative'); // Autocomplete paths. define('ISLANDORA_CONTENT_MODELS_AUTOCOMPLETE', 'islandora/autocomplete/content-models'); +define('ISLANDORA_MIME_TYPES_AUTOCOMPLETE', 'islandora/autocomplete/mime-types'); /** * @deprecated Constants. @@ -209,6 +211,15 @@ function islandora_menu() { 'access callback' => 'islandora_object_access_callback', 'access arguments' => array(ISLANDORA_PURGE, 2), ); + $items['islandora/object/%islandora_object/regenerate'] = array( + 'title' => 'Regenerate all derivatives on an object', + 'file' => 'includes/regenerate_derivatives.form.inc', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('islandora_regenerate_object_derivatives_form', 2), + 'type' => MENU_CALLBACK, + 'access callback' => 'islandora_object_access_callback', + 'access arguments' => array(ISLANDORA_REGENERATE_DERIVATIVES, 2), + ); $items['islandora/object/%islandora_object/manage/datastreams/add'] = array( 'title' => 'Add a datastream', 'file' => 'includes/add_datastream.form.inc', @@ -314,6 +325,16 @@ function islandora_menu() { 'access arguments' => array(ISLANDORA_VIEW_DATASTREAM_HISTORY, 4), 'load arguments' => array(2), ); + $items['islandora/object/%islandora_object/datastream/%islandora_datastream/regenerate'] = array( + 'title' => 'Regenrate datastream derivative', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('islandora_regenerate_datastream_derivative_form', 4), + 'file' => 'includes/regenerate_derivatives.form.inc', + 'type' => MENU_CALLBACK, + 'access callback' => 'islandora_datastream_access', + 'access arguments' => array(ISLANDORA_REGENERATE_DERIVATIVES, 4), + 'load arguments' => array(2), + ); $items['islandora/object/%islandora_object/download_clip'] = array( 'page callback' => 'islandora_download_clip', 'page arguments' => array(2), @@ -338,6 +359,15 @@ function islandora_menu() { 'access arguments' => array('administer site configuration'), 'type' => MENU_CALLBACK, ); + $items[ISLANDORA_MIME_TYPES_AUTOCOMPLETE] = array( + 'title' => 'Autocomplete callback', + 'description' => 'Autocomplete MIME Types.', + 'file' => 'includes/mime_type.autocomplete.inc', + 'page callback' => 'islandora_mime_type_autocomplete', + 'page arguments' => array(3), + 'access arguments' => array('administer site configuration'), + 'type' => MENU_CALLBACK, + ); $items['admin/islandora/restore/prep'] = array( 'description' => 'Restore or permanantly remove objects with Deleted status', 'title' => 'Manage Deleted Objects', @@ -460,6 +490,10 @@ function islandora_theme() { 'file' => 'theme/theme.inc', 'variables' => array('datastream' => NULL), ), + 'islandora_datastream_regenerate_link' => array( + 'file' => 'theme/theme.inc', + 'variables' => array('datastream' => NULL), + ), 'islandora_dublin_core_display' => array( 'file' => 'theme/theme.inc', 'template' => 'theme/islandora-dublin-core-display', @@ -485,6 +519,11 @@ function islandora_theme() { 'pattern' => 'islandora_dublin_core_description__', 'variables' => array('islandora_object' => NULL), ), + // Table for install/reinstall content model and collections. + 'islandora_solution_pack_table' => array( + 'file' => 'includes/solution_packs.inc', + 'render element' => 'form', + ), ); } @@ -529,6 +568,10 @@ function islandora_permission() { 'title' => t('Manage deleted objects'), 'description' => t('Purge or revert deleted objects.'), ), + ISLANDORA_REGENERATE_DERIVATIVES => array( + 'title' => t('Regenerate derivatives'), + 'description' => t('Regenerate derivatives for an object or per datastream.'), + ), ); } @@ -994,11 +1037,9 @@ function islandora_view_object(AbstractObject $object) { /** * This will prepare an object to be printed. * - * By default, all fedora objects can print DC record data, - * however, Solution packs that wish to modify the form - * to be printed must implement hook_islandora_view_print_object_alter, - * create a theme.tpl.php file, and return its markup by calling - * theme(themename.tpl.php). + * By default all fedora objects can print DC record data. Solution packs that + * wish to modify the data to be printed can implement + * hook_islandora_view_print_object or hook_islandora_metadata_display_info. * * @param AbstractObject $object * The object to print. @@ -1064,7 +1105,7 @@ function islandora_default_islandora_view_object($object) { * * @param AbstractObject $object * The fedora object to print. - * @param unknown $alter + * @param string $alter * The string representation of the themed viewable object. * * @return array @@ -1796,3 +1837,29 @@ function islandora_islandora_metadata_display_info() { ), ); } + +/** + * Implements hook_islandora_datastream_access(). + */ +function islandora_islandora_datastream_access($op, AbstractDatastream $datastream, $user) { + module_load_include('inc', 'islandora', 'includes/utilities'); + $result = islandora_namespace_accessible($datastream->parent->id) && user_access($op, $user); + + if ($result && $op == ISLANDORA_REGENERATE_DERIVATIVES) { + module_load_include('inc', 'islandora', 'includes/derivatives'); + $applicable_hook = FALSE; + $object = $datastream->parent; + $hooks = islandora_invoke_hook_list(ISLANDORA_DERVIATIVE_CREATION_HOOK, $object->models, array($object)); + $hooks = islandora_filter_derivatives($hooks, array('force' => TRUE), $object); + foreach ($hooks as $hook) { + if ($hook['destination_dsid'] == $datastream->id && islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $object[$hook['source_dsid']], $user)) { + $applicable_hook = TRUE; + break; + } + } + if (!$applicable_hook) { + $result = FALSE; + } + } + return $result; +} diff --git a/tests/derivatives.test b/tests/derivatives.test index 13014880..b7eeae9b 100644 --- a/tests/derivatives.test +++ b/tests/derivatives.test @@ -159,7 +159,8 @@ class IslandoraDerivativesTestCase extends IslandoraWebTestCase { public function testDerivativeFilteringOnSourceDSID() { global $_islandora_derivative_test_derivative_functions; $_islandora_derivative_test_derivative_functions = array(); - $this->constructBaseObject(); + $object = $this->constructBaseObject(); + $this->constructSOMEWEIRDDATASTREAMDatastream($object); $object = islandora_object_load($this->pid); islandora_do_derivatives($object, array( 'source_dsid' => 'OBJ', @@ -207,7 +208,8 @@ class IslandoraDerivativesTestCase extends IslandoraWebTestCase { public function testNoSourceDSIDNoForce() { global $_islandora_derivative_test_derivative_functions; $_islandora_derivative_test_derivative_functions = array(); - $this->constructBaseObject(); + $object = $this->constructBaseObject(); + $object = $this->constructSOMEWEIRDDATASTREAMDatastream($object); $object = islandora_object_load($this->pid); islandora_do_derivatives($object, array()); $this->assertDatastreams($object, array( @@ -216,6 +218,8 @@ class IslandoraDerivativesTestCase extends IslandoraWebTestCase { 'OBJ', 'DERIV', 'NOSOURCE', + 'SOMEWEIRDDATASTREAM', + 'STANLEY', )); $this->assertEqual(3, count($_islandora_derivative_test_derivative_functions), 'Expected 3 derivative functions when there is no source_dsid, got ' . count($_islandora_derivative_test_derivative_functions) . '.'); } @@ -226,7 +230,8 @@ class IslandoraDerivativesTestCase extends IslandoraWebTestCase { public function testNoSourceDSIDForce() { global $_islandora_derivative_test_derivative_functions; $_islandora_derivative_test_derivative_functions = array(); - $this->constructBaseObject(); + $object = $this->constructBaseObject(); + $this->constructSOMEWEIRDDATASTREAMDatastream($object); $object = islandora_object_load($this->pid); islandora_do_derivatives($object, array( 'force' => TRUE, @@ -236,6 +241,8 @@ class IslandoraDerivativesTestCase extends IslandoraWebTestCase { 'RELS-EXT', 'OBJ', 'DERIV', + 'SOMEWEIRDDATASTREAM', + 'STANLEY', 'NOSOURCE', )); $this->assertEqual(3, count($_islandora_derivative_test_derivative_functions), 'Expected 3 derivative functions when there is no source_dsid, got ' . count($_islandora_derivative_test_derivative_functions) . '.'); @@ -293,4 +300,22 @@ class IslandoraDerivativesTestCase extends IslandoraWebTestCase { $object->ingestDatastream($ds); return $object; } + + /** + * Helper function to construct the SWD datastream without firing hooks. + * + * @param AbstractObject $object + * An AbstractObject representing a FedoraObject. + * + * @return AbstractObject + * The modified AbstractObject. + */ + public function constructSOMEWEIRDDATASTREAMDatastream(AbstractObject $object) { + $dsid = 'SOMEWEIRDDATASTREAM'; + $ds = $object->constructDatastream($dsid); + $ds->label = 'Test'; + $ds->content = 'Omnomnom'; + $object->ingestDatastream($ds); + return $object; + } } diff --git a/tests/islandora_derivatives_test.module b/tests/islandora_derivatives_test.module index 80530296..2e27c5c7 100644 --- a/tests/islandora_derivatives_test.module +++ b/tests/islandora_derivatives_test.module @@ -23,7 +23,7 @@ function islandora_derivatives_test_some_cmodel_islandora_derivative() { 'destination_dsid' => 'STANLEY', 'weight' => '-1', 'function' => array( - 'islandora_derivatives_test_create_some_weird_datastream', + 'islandora_derivatives_test_create_stanley_datastream', ), ), array( @@ -81,17 +81,20 @@ function islandora_derivatives_test_create_deriv_datastream(AbstractObject $obje } /** - * Stub function that used only for datastream filtering counts. + * Creates the STANLEY datastream for use in testing. * * @param AbstractObject $object * An AbstractObject representing a Fedora object. * @param bool $force * Whether the derivatives are being forcefully generated or not. */ -function islandora_derivatives_test_create_some_weird_datastream(AbstractObject $object, $force = FALSE) { +function islandora_derivatives_test_create_stanley_datastream(AbstractObject $object, $force = FALSE) { global $_islandora_derivative_test_derivative_functions; // Add to the global that we got to this function. $_islandora_derivative_test_derivative_functions[] = 'islandora_derivatives_test_create_some_weird_datastream'; + if (!isset($object['STANLEY']) || (isset($object['STANLEY']) && $force === TRUE)) { + islandora_derivatives_test_add_datastream($object, 'STANLEY', 'yum'); + } } /** diff --git a/tests/islandora_web_test_case.inc b/tests/islandora_web_test_case.inc index 0ea96e8e..d417ff37 100644 --- a/tests/islandora_web_test_case.inc +++ b/tests/islandora_web_test_case.inc @@ -7,6 +7,13 @@ class IslandoraWebTestCase extends DrupalWebTestCase { + /** + * An array of users that may be created over the course of a test. + * + * @var array + */ + protected $users = array(); + /** * Sets up the Drupal filter to access this test Drupal instances database. * @@ -131,6 +138,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { } else { parent::drupalLogin($account); + $this->users[] = $account->name; } } @@ -155,6 +163,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * @see DrupalWebTestCase::tearDown() */ public function tearDown() { + foreach ($this->users as $user) { + $this->deleteUserCreatedObjects($user); + } if ($this->configuration['use_drupal_filter']) { $this->restoreDrupalFilter(); } @@ -170,20 +181,24 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * The PID of the object * @param array $datastreams * An array of strings containing datastream names + * + * @return bool + * TRUE on success, FALSE on fail. */ public function assertDatastreams($object, array $datastreams) { if (!is_object($object)) { $this->fail("Failed. Object passed in is invalid.", 'Islandora'); } else { - foreach ($datastreams as $datastream) { - if (isset($object[$datastream])) { - $this->pass("Loaded datastream {$datastream} from PID {$object->id}.", 'Islandora'); - } - else { - $this->fail("Failed to load datastream {$datastream} from PID {$object->id}.", 'Islandora'); - } + $missing_datastreams = array_diff_key(array_flip($datastreams), $this->admin->repository->api->a->listDatastreams($object->id)); + + if (!empty($missing_datastreams)) { + $this->fail("Failed to find datastream(s) " . implode(', ', array_flip($missing_datastreams)) . " in object {$object->id}."); + return FALSE; } + + $this->pass("Found required datastream(s) in object {$object->id}"); + return TRUE; } } @@ -254,22 +269,29 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * The PID of the collection to be deleted * @param string $button * The label of the first 'Delete' button + * @param bool $safety + * If TRUE, this will only delete objects owned by users in $this->users. */ - public function deleteObject($pid, $button = NULL) { - $path = 'islandora/object/' . $pid . '/manage/properties'; - $edit = array(); - if (isset($button)) { - $this->drupalPost($path, $edit, $button); + public function deleteObject($pid, $button = NULL, $safety = TRUE) { + $object = islandora_object_load($pid); + if (!$safety || in_array($object->owner, $this->users)) { + $path = "islandora/object/$pid/manage/properties"; + if (isset($button)) { + $this->drupalPost($path, array(), $button); + } + else { + $object = islandora_object_load($pid); + $this->drupalPost($path, array(), "Permanently remove '{$object->label}' from repository"); + } + $this->drupalPost($this->url, array(), t('Delete')); + + $this->drupalGet("islandora/object/$pid"); + $this->assertResponse(404, "Object $pid successfully deleted."); } else { - $object = islandora_object_load($pid); - $this->drupalPost($path, $edit, "Permanently remove '{$object->label}' from repository"); + $this->fail("Cannot delete object {$pid}; it is owned by non-test user {$object->owner}, and this function was called with the safety on."); + return FALSE; } - $this->drupalPost($this->url, $edit, t('Delete')); - $object = islandora_object_load($pid); - - $this->drupalGet("islandora/object/$pid"); - $this->assertResponse(404, "Object $pid successfully deleted."); } /** @@ -283,7 +305,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * 'label' - The object label; randomized if not set. * 'pid' - 'namespace:pid', or just 'namespace' to generate the suffix. * 'models' - An array that can contain multiple content model PIDs. - * 'owner' - The object's owner. + * 'owner' - The object's owner. Defaults to the currently logged-in user, + * if available. It is recommended to set this to a value that can be found + * in $this->users; otherwise, this object will have to be manually deleted. * 'parent' - The PID of the parent collection. * @param array $datastreams * An array containing zero or more datastream arrays that use the keys: @@ -316,6 +340,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase { if (isset($properties['owner'])) { $object->owner = $properties['owner']; } + elseif ($this->loggedInUser !== FALSE) { + $object->owner = $this->loggedInUser->name; + } if (isset($properties['models']) && is_array($properties['models'])) { foreach ($properties['models'] as $model) { @@ -367,4 +394,37 @@ class IslandoraWebTestCase extends DrupalWebTestCase { return $object; } + /** + * Deletes all objects created by the given user. + * + * @param object $username + * The user whose objects we'd like to remove. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function deleteUserCreatedObjects($username) { + if ($username === $this->configuration['admin_user']) { + $this->fail("This function will under no circumstance attempt deletion of all objects owned by the configured Fedora admin user ({$this->configuration['admin_user']}), as this could irreparably damage the repository.", 'Islandora'); + return FALSE; + } + + $query = << WHERE +{ + ?object "$username" +} +QUERY; + + $objects = $this->admin->repository->ri->sparqlQuery($query); + foreach ($objects as $object) { + $loaded_object = islandora_object_load($object['object']['value']); + islandora_delete_object($loaded_object); + if ($this->assertFalse(islandora_object_load($object['object']['value']), "Object {$object['object']['value']} successfully removed from repository.", 'Islandora')) { + return FALSE; + } + return TRUE; + } + } + } diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 2fa3fd0e..3a52bb75 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -19,7 +19,7 @@ pear channel-discover pear.drush.org pear channel-discover pear.drush.org pear channel-discover pear.phpqatools.org pear channel-discover pear.netpirates.net -pear install pear/PHP_CodeSniffer +pear install pear/PHP_CodeSniffer-1.4.8 pear install pear.phpunit.de/phpcpd pear install drush/drush-5.9.0 phpenv rehash diff --git a/theme/islandora-object-img-print.tpl.php b/theme/islandora-object-img-print.tpl.php deleted file mode 100644 index 3ff21455..00000000 --- a/theme/islandora-object-img-print.tpl.php +++ /dev/null @@ -1,18 +0,0 @@ - - -
- -
- diff --git a/theme/theme.inc b/theme/theme.inc index 6a80fbb6..b783cfa8 100644 --- a/theme/theme.inc +++ b/theme/theme.inc @@ -25,7 +25,7 @@ function islandora_preprocess_islandora_default_edit(array &$variables) { $header[] = array('data' => t('Versions')); } - $header[] = array('data' => t('Operations'), 'colspan' => '3'); + $header[] = array('data' => t('Operations'), 'colspan' => '4'); $table_attributes = array('class' => array('manage-datastreams')); $rows = array(); @@ -53,7 +53,7 @@ function islandora_preprocess_islandora_default_edit(array &$variables) { 'class' => 'datastream-size', 'data' => islandora_datastream_get_human_readable_size($ds), ); - if (user_access(ISLANDORA_VIEW_DATASTREAM_HISTORY)) { + if (islandora_datastream_access(ISLANDORA_VIEW_DATASTREAM_HISTORY, $ds)) { $row[] = array( 'class' => 'datastream-versions', 'data' => theme('islandora_datastream_version_link', array( @@ -79,6 +79,12 @@ function islandora_preprocess_islandora_default_edit(array &$variables) { 'datastream' => $ds, )), ); + $row[] = array( + 'class' => 'datastream-regenerate', + 'data' => theme('islandora_datastream_regenerate_link', array( + 'datastream' => $ds, + )), + ); $rows[] = $row; } $caption = filter_xss($islandora_object->label) . ' - ' . $islandora_object->id; @@ -424,6 +430,23 @@ function theme_islandora_datastream_version_link(array $vars) { } } +/** + * Renders a link that will re-create derivatives for a datastream. + * + * @param array $vars + * An array containing: + * - datastream: An AbstractDatastream to generate the version link from. + * + * @return string + * Markup. + */ +function theme_islandora_datastream_regenerate_link(array $vars) { + $datastream = $vars['datastream']; + if (islandora_datastream_access(ISLANDORA_REGENERATE_DERIVATIVES, $datastream)) { + return l(t('regenerate'), "islandora/object/{$datastream->parent->id}/datastream/{$datastream->id}/regenerate"); + } +} + /** * Implements hook_preprocess(). */