From 24cb55c7261b723f4d26de2af2311bb7c373254b Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Thu, 6 Feb 2014 16:17:23 -0400 Subject: [PATCH 01/93] wrapped command to avoid errors for uninstalled objects --- islandora.drush.inc | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/islandora.drush.inc b/islandora.drush.inc index acf3994f..4013c9a6 100644 --- a/islandora.drush.inc +++ b/islandora.drush.inc @@ -177,23 +177,25 @@ function drush_islandora_solution_pack_install_content_models() { $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)) { + if ($object && 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; + if (count($objects_to_add) > 0) { + 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); } - } - $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); } } } From 024cf013fd79f1b712ba2a9160e20cc0596756e3 Mon Sep 17 00:00:00 2001 From: Jared Whiklo Date: Fri, 7 Mar 2014 16:14:39 -0600 Subject: [PATCH 02/93] Check for hostname You can enter the Djatoka URL as a relative path or full hostname. I did hostname and didn't get any errors. But this function assumes it is a path and sticks http(s):// in front which causes you to get a blank Jpeg when downloading the clip. --- islandora.module | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/islandora.module b/islandora.module index 9e7809eb..3c4ce68d 100644 --- a/islandora.module +++ b/islandora.module @@ -1540,9 +1540,12 @@ function islandora_entity_property_info() { */ function islandora_download_clip(AbstractObject $object) { if (isset($_GET['clip'])) { - $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; - $http_protocol = $is_https ? 'https' : 'http'; - $url = $http_protocol . '://' . $_SERVER['HTTP_HOST'] . $_GET['clip']; + $url = $_GET['clip']; + if (!preg_match('/^https?:\/\//',$url)) { + $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; + $http_protocol = $is_https ? 'https' : 'http'; + $url = $http_protocol . '://' . $_SERVER['HTTP_HOST'] . $url; + } $filename = $object->label; header("Content-Disposition: attachment; filename=\"{$filename}.jpg\""); header("Content-type: image/jpeg"); From ab437cceb20f156ffc424911a85a1c68e75ab05d Mon Sep 17 00:00:00 2001 From: Jared Whiklo Date: Mon, 10 Mar 2014 12:59:32 -0500 Subject: [PATCH 03/93] You wouldn't like Travis when he's angry. --- islandora.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.module b/islandora.module index 3c4ce68d..97e339d7 100644 --- a/islandora.module +++ b/islandora.module @@ -1541,7 +1541,7 @@ function islandora_entity_property_info() { function islandora_download_clip(AbstractObject $object) { if (isset($_GET['clip'])) { $url = $_GET['clip']; - if (!preg_match('/^https?:\/\//',$url)) { + if (!preg_match('/^https?:\/\//', $url)) { $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; $http_protocol = $is_https ? 'https' : 'http'; $url = $http_protocol . '://' . $_SERVER['HTTP_HOST'] . $url; From ecdc28d6198e942249273bb343dd6acf5ae8b547 Mon Sep 17 00:00:00 2001 From: vagrant Date: Wed, 21 May 2014 13:38:03 +0000 Subject: [PATCH 04/93] splitting out web test case and unit test case --- islandora.info | 6 +- tests/datastream_validator_tests.test | 9 +- tests/datastream_validators.inc | 781 -------------------------- tests/islandora_web_test_case.inc | 604 -------------------- 4 files changed, 8 insertions(+), 1392 deletions(-) delete mode 100644 tests/datastream_validators.inc delete mode 100644 tests/islandora_web_test_case.inc diff --git a/islandora.info b/islandora.info index e06d0de9..e19f4937 100644 --- a/islandora.info +++ b/islandora.info @@ -13,7 +13,10 @@ files[] = includes/dublin_core.inc files[] = includes/tuque.inc files[] = includes/tuque_wrapper.inc files[] = includes/object.entity_controller.inc -files[] = tests/islandora_web_test_case.inc +files[] = tests/includes/datastream_validators.inc +files[] = tests/includes/islandora_web_test_case.inc +files[] = tests/includes/islandora_unit_test_case.inc +files[] = tests/includes/utilities.inc files[] = tests/authtokens.test files[] = tests/hooks.test files[] = tests/ingest.test @@ -22,6 +25,5 @@ files[] = tests/islandora_manage_permissions.test files[] = tests/datastream_versions.test files[] = tests/datastream_cache.test files[] = tests/derivatives.test -files[] = tests/islandora_manage_temp_file.test files[] = tests/datastream_validator_tests.test php = 5.3 diff --git a/tests/datastream_validator_tests.test b/tests/datastream_validator_tests.test index 79e1df7f..40615ae4 100644 --- a/tests/datastream_validator_tests.test +++ b/tests/datastream_validator_tests.test @@ -4,8 +4,6 @@ * Tests for things that test tests. Madness. */ -include 'datastream_validators.inc'; - /** * A test DatastreamValidator for the DatastreamValidatorResultTestCase. */ @@ -62,7 +60,7 @@ class DatastreamValidatorResultTestCase extends IslandoraWebTestCase { * Generates a generic DatastreamValidatorResult and grabs its properties. */ public function testDatastreamValidatorResult() { - $result = new DatastreamValidatorResult(TRUE, 'true', array()); + $result = new IslandoraTestUtilityResult(TRUE, 'true', array()); $this->assertEqual($result->getMessage(), 'true', "Result message generated correctly.", 'Islandora'); $this->assertEqual($result->getType(), TRUE, "Result type generated correctly.", 'Islandora'); $this->assertEqual($result->getCaller(), array(), "Result caller generated correctly.", 'Islandora'); @@ -111,10 +109,11 @@ class DatastreamValidatorResultTestCase extends IslandoraWebTestCase { $this->assertTrue(substr($first_caller['file'], -48) === substr($second_caller['file'], -48), "Fail caller file matches the pass caller file.", 'Islandora'); $this->assertTrue($first_caller['function'] === 'TestDatastreamValidator->assertSomethingSuccessfully()', "Correct pass caller function returned (actual: {$first_caller['function']}; expected: TestDatastreamValidator->assertSomethingSuccessfully()).", 'Islandora'); $this->assertTrue($second_caller['function'] === 'TestDatastreamValidator->assertSomethingFailed()', "Correct fail caller function returned (actual: {$second_caller['function']}; expected: TestDatastreamValidator->assertSomethingFailed()).", 'Islandora'); - $this->assertTrue($first_caller['line'] === 18, "Correct pass line number returned (actual: {$first_caller['line']}; expected: 9).", 'Islandora'); - $this->assertTrue($second_caller['line'] === 25, "Correct fail line number returned (actual: {$second_caller['line']}; expected: 13).", 'Islandora'); + $this->assertTrue($first_caller['line'] === 16, "Correct pass line number returned (actual: {$first_caller['line']}; expected: 16).", 'Islandora'); + $this->assertTrue($second_caller['line'] === 23, "Correct fail line number returned (actual: {$second_caller['line']}; expected: 23).", 'Islandora'); } } + } /** diff --git a/tests/datastream_validators.inc b/tests/datastream_validators.inc deleted file mode 100644 index 151ec676..00000000 --- a/tests/datastream_validators.inc +++ /dev/null @@ -1,781 +0,0 @@ -message = $message; - $this->caller = $caller; - $this->type = $type; - } - - /** - * Get the message for this result. - * - * @return string - * The message for this result. - */ - public function getMessage() { - return $this->message; - } - - /** - * Get the caller for this result. - * - * @return string - * The caller for this result. - */ - public function getCaller() { - return $this->caller; - } - - /** - * Get the type of result. - * - * @return bool - * The type of pass (TRUE for pass, FALSE for fail). - */ - public function getType() { - return $this->type; - } -} - -/** - * Abstraction for datastream validators. - * - * Classes extended from DatastreamValidator don't require much to be useful. - * They accept an IslandoraFedoraObject and a DSID to perform assertions on; - * all you have to do is place a series of functions inside the extended class - * using the naming convention assertThing(); each of these functions should - * ideally assert one thing and one thing only (for simplicity's sake), and - * should generate either a pass or a fail message by calling addPass() or - * addFail(). That's it, really; they don't have to return any values, as - * addPass() and addFail() just add messages to the overall pass/fail array. - * - * As long as you use those rules and naming conventions, all the magic is done - * when you instantiate the new datastream validator object. - * - * The IslandoraWebTestCase::assertDatastreams() function accepts paired DSIDs - * and datastream validator names in order to do the rest of the work. It grabs - * all the test results using getPasses() and getFails() and transforms those - * into something that DrupalWebTestCase can use. - */ -abstract class DatastreamValidator { - - /** - * The IslandoraFedoraObject containing the datastream to test. - * - * @var IslandoraFedoraObject - */ - public $object; - - /** - * The DSID of the string to test. - * - * @var string - */ - public $datastream; - - /** - * The content of the datastream. - * - * @var string[] - */ - public $datastreamContent; - - /** - * An array of DatastreamValidatorResults. - * - * These should be generated using $this->addResult. - * - * @var DatastreamValidatorResult[] - */ - public $results = array(); - - /** - * An array of additional required parameters. - * - * @var array - */ - public $params = array(); - - /** - * Constructs a DatastreamValidator. - * - * @param IslandoraFedoraObject $object - * The object to grab the datastream from. - * @param string $datastream - * The DSID of the datastream itself. - * @param array $params - * An extra array of parameters the validator might need. - */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { - $this->object = $object; - $this->datastream = $datastream; - $this->params = $params; - if ($object[$datastream]) { - $this->datastreamContent = $object[$datastream]->content; - } - else { - drupal_set_message(t("Error grabbing content from datastream %datastream in object %id", array( - '%datastream' => $datastream, - '%id' => $object->id, - )), 'error'); - } - } - - /** - * Helper function to run all the validators in a class. - * - * On DatastreamValidator::__construct(), this looks for any functions - * within the class beginning in "assert" and runs them. In all current cases - * (and realistically in all future cases), this adds one or more passes or - * fails to $this->passes and/or $this->fails. - */ - public function runValidators() { - $methods = get_class_methods($this); - foreach ($methods as $method) { - if (substr($method, 0, 6) === 'assert') { - $this->$method(); - } - } - } - - /** - * Returns an array of DatastreamValidatorResults. - * - * @return DatastreamValidatorResult[] - * The results. - */ - public function getResults() { - if (empty($this->results)) { - $this->runValidators(); - } - return $this->results; - } - - /** - * Adds a result to $this->results. - * - * @param bool $type - * The type of result (TRUE for pass, FALSE for fail). - * @param string $message - * The message to put in the result. - */ - public function addResult($type, $message) { - $result = new DatastreamValidatorResult($type, $message, $this->getAssertionCall()); - $this->results[] = $result; - } - - /** - * Cycles through backtrace until the first non-assertion method is found. - * - * This is a manipulated version of DrupalWebTestCase::getAssertionCall(). - * We use it here so that we can pass back assertion calls from - * DatastreamValidator assertions instead of less useful TestCase functions. - * - * @return array - * Array representing the true caller. - */ - protected function getAssertionCall() { - $backtrace = debug_backtrace(); - - // While the current caller's function starts with 'assert', and another one - // exists after this function, keep poppin' em off. - while (substr($backtrace[1]['function'], 0, 6) !== 'assert' && isset($backtrace[2])) { - array_shift($backtrace); - } - - return _drupal_get_last_caller($backtrace); - } - -} - -/** - * Asserts that an object's given datastreams are common-type image files. - * - * Uses PHPGD to run the assertion check. This means that only certain kinds - * of image files can be checked. Please check the documentation for the PHPGD - * imagecreatefromstring() function to determine what filetypes are valid. - */ -class ImageDatastreamValidator extends DatastreamValidator { - - /** - * Asserts the validity of an image using PHPGD. - */ - protected function assertImageGeneration() { - $assertion = imagecreatefromstring($this->datastreamContent) !== FALSE; - $pass = "Image datastream {$this->datastream} is valid."; - $fail = "Image datastream {$this->datastream} is either invalid or corrupt."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - -} - -/** - * Asserts the validity of any .tif/.tiff datastream. - */ -class TIFFDatastreamValidator extends DatastreamValidator { - - /** - * Asserts that the TIFF contains an appropriate header. - */ - public function assertTIFFHeaderHex() { - $datastream_header_hex = self::getTIFFHeaderHex(); - if ($datastream_header_hex == "49492a00") { - // In this case, the ingested TIFF is designated as using the "Intel - // byte-order" (i.e. little-endian) by starting with the characters "II" - // (repeated so that byte order does not yet need to be significant). - // The number that follows is '42' in little-endian hex, a number of - // 'deep philosophical significance' to the TIFF format creators.' - $this->addResult(TRUE, "{$this->datastream} datastream asserts that it is a valid Intel-byte-orderded TIF/TIFF file."); - } - elseif ($datastream_header_hex == "4d4d002a") { - // In this case, the ingested TIFF is designated as using the "Motorola - // byte-order" (i.e. big-endian) by starting with the characters "MM" - // instead. 42 follows once again, this time in big-endian hex. - $this->addResult(TRUE, "{$this->datastream} datastream asserts that it is a valid Motorola-byte-ordered TIF/TIFF file."); - } - else { - $this->addResult(FALSE, "{$this->datastream} datastream does not assert that it is a valid TIF/TIFF file."); - } - } - - /** - * Grabs the first 8 characters from the TIFF datastream's hex. - * - * @return string - * The ... thing I just wrote up there. - */ - protected function getTIFFHeaderHex() { - return substr(bin2hex($this->datastreamContent), 0, 8); - } - -} - -/** - * Asserts the validity of a JP2 datastream. - */ -class JP2DatastreamValidator extends DatastreamValidator { - - /** - * Asserts the hex values at the head of the JP2 file. - * - * JP2 files begin with an offset header at the second 32-bit integer, - * 0x6A502020. This header is in all .jp2s, and we check for it here. - */ - protected function assertJP2Header() { - $assertion = substr(bin2hex($this->datastreamContent), 8, 8) == '6a502020'; - $pass = "Datastream {$this->datastream} contains the appropriate JP2 header."; - $fail = "Datastream {$this->datastream} does not contain the appropriate JP2 header."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts the marker at the end of the JP2 file. - * - * JP2 files have their codestream capped with a marker, 0xFFD9. We're just - * checking for it here to see if the .jp2 encoder finished okay. - */ - protected function assertJP2Marker() { - $assertion = substr(bin2hex($this->datastreamContent), strlen(bin2hex($this->datastreamContent)) - 4, 4) == 'ffd9'; - $pass = "Datastream {$this->datastream} contains the appropriate JP2 ending marker."; - $fail = "Datastream {$this->datastream} does not contain the appropriate JP2 ending marker. If this is the only JP2 validator that failed, it is likely that derivative generation was interrupted."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } -} - -/** - * Asserts the validity of a PDF datastream. - */ -class PDFDatastreamValidator extends DatastreamValidator { - - /** - * Validates the PDF signature. - */ - protected function assertPDFSignature() { - $assertion = substr($this->datastreamContent, 0, 5) == '%PDF-'; - $pdf_version = substr($this->datastreamContent, 5, 3); - $pass = "{$this->datastream} datastream asserts that it is a valid PDF file using PDF version {$pdf_version}"; - $fail = "{$this->datastream} datastream binary header appears to be corrupt and missing a valid PDF signature."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Counts the number of signatures in this PDF file and asserts there are any. - */ - protected function assertPDFStreamCount() { - $pdf_stream_count = substr_count(bin2hex($this->datastreamContent), '0a73747265616d0a'); - $assertion = $pdf_stream_count !== 0; - $pass = "{$this->datastream} datastream reports the existence of {$pdf_stream_count} PDF streams. Note that an extremely low number could still indicate corruption."; - $fail = "{$this->datastream} datastream contains zero PDF streams, and is likely not a PDF file."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Validates the PDF closing tag. - * - * @return bool - * TRUE if it was present; FALSE otherwise. - */ - protected function assertPDFClosingTag() { - $assertion = strpos(bin2hex($this->datastreamContent), '0a2525454f460a') == TRUE; - $pass = "{$this->datastream} datastream reports the existence of the closing 'EOF' tag required at the end of PDFs"; - $fail = "{$this->datastream} datastream does not contain the closing 'EOF' tag. If this is the only PDF validation that failed, it is likely that derivative generation was interrupted."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } -} - -/** - * Validates the number of times a string occurs in a datastream. - * - * Requires $this->params to be set to an array containing two keys - the first - * is the string we're looking to find in the datastream, and the second is an - * integer representing the number of times it should appear in the datastream. - */ -class TextDatastreamValidator extends DatastreamValidator { - /** - * Constructor override; blow up if we don't have our two values. - */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { - if (count($params) < 2) { - throw new InvalidArgumentException('$params must contain at least two values to instantiate a TextDatastreamValidator.'); - } - parent::__construct($object, $datastream, $params); - } - - /** - * Asserts that the string given appears the correct number of times. - */ - protected function assertTextStringCount() { - $string_count = self::getTextStringCount(); - list($string, $expected) = $this->params; - $assertion = $string_count === $expected; - $this->addResult($assertion, "{$this->datastream} datastream contains the word(s) '{$string}' repeated {$string_count} time(s) (expected: {$expected})."); - } - - /** - * The number of times key [0] in $this->params appears in the datastream. - * - * @return int - * That count I just mentioned up there. - */ - protected function getTextStringCount() { - return substr_count($this->datastreamContent, $this->params[0]); - } -} - -/** - * Asserts the validity a WAV datastream. - * - * WAV files contain a rigidly detailed header that contains all sorts of fun - * information we can use to validate things against other things. So, we check - * rigorously that the header contains properly constructed data by looking to - * see if certain values are at their expected byte offset. We also compare - * declared chunk sizes against actual sizes. If any of these are off, WAV - * players will fail to function. - */ -class WAVDatastreamValidator extends DatastreamValidator { - - /** - * We need a special constructor here to get the hex datastream content. - * - * @param IslandoraFedoraObject $object - * The object to grab the datastream from. - * @param string $datastream - * The DSID of the datastream itself. - * @param array $params - * An extra array of parameters the validator might need. - */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { - parent::__construct($object, $datastream, $params); - $this->datastreamContent = bin2hex($this->datastreamContent); - } - - /** - * Asserts that the datastream contains a valid WAV signature. - */ - protected function assertWAVSignature() { - $signatures = str_split(substr($this->datastreamContent, 0, 24), 8); - $assertion = $signatures[0] == '52494646' && $signatures[2] == '57415645'; - $pass = "Header of the {$this->datastream} datastream contains a valid file signature."; - $fail = "Header of the {$this->datastream} datastream contains corrupt file signature."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts that the chunksize in the header is correct. - */ - protected function assertWAVChunkSize() { - $assertion = islandora_hex2int(substr($this->datastreamContent, 8, 8)) === 36 + self::getDataSubChunkSize(); - $pass = "{$this->datastream} datastream chunksize in WAV header is correct"; - $fail = "{$this->datastream} datastream chunksize in WAV header does not match actual chunksize."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts that the datastream contains a 'fmt' subchunk. - */ - protected function assertWAVFmtSubChunk() { - $assertion = substr($this->datastreamContent, 24, 8) === '666d7420'; - $pass = "{$this->datastream} datastream contains a 'fmt' subchunk."; - $fail = "{$this->datastream} datastream is missing the required 'fmt' subchunk."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts that the byterate reported by the WAV header is valid. - */ - protected function assertWAVByteRate() { - $wav_samplerate = islandora_hex2int(substr($this->datastreamContent, 48, 8)); - $assertion = islandora_hex2int(substr($this->datastreamContent, 56, 8)) === $wav_samplerate * self::getNumChannels() * self::getBytesPerSample(); - $pass = "{$this->datastream} datastream byterate in the WAV header is correct."; - $fail = "{$this->datastream} datastream byterate in the WAV header does not match actual calculated byterate."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts that the block alignment is correct. - */ - protected function assertWAVBlockAlignment() { - $assertion = islandora_hex2int(substr($this->datastreamContent, 64, 4)) === self::getNumChannels() * self::getBytesPerSample(); - $pass = "{$this->datastream} datastream block alignment is set correctly."; - $fail = "{$this->datastream} datastream block alignment is off."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts the existence of a 'data' subchunk. - * - * Also asserts that the subchunk size is correct. - */ - protected function assertWAVDataSubChunk() { - if (substr($this->datastreamContent, 72, 8) !== '64617461') { - $this->addResult(FALSE, "{$this->datastream} datastream is missing the 'data' subchunk."); - return; - } - else { - $this->addResult(TRUE, "{$this->datastream} datastream contains 'data' subchunk."); - $wav_numsamples = strlen(substr($this->datastreamContent, 88)) / self::getNumChannels() / self::getBytesPerSample() / 2; - $assertion = self::getDataSubChunkSize() === $wav_numsamples * self::getNumChannels() * self::getBytesPerSample(); - $pass = "{$this->datastream} datastream 'data' chunk is the correct size."; - $fail = "{$this->datastream} datastream 'data' chunk is sized incorrectly."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - } - - /** - * Gets the number of channels reported by the WAV header. - * - * @return int - * The number of channels reported by the datastream header. - */ - protected function getNumChannels() { - return islandora_hex2int(substr($this->datastreamContent, 44, 4)); - } - - /** - * Gets the reported number of byte rates per sample. - * - * @return int - * The number of bytes per sample reported by the datastream header. - */ - protected function getBytesPerSample() { - return islandora_hex2int(substr($this->datastreamContent, 68, 4)) / 8; - } - - /** - * Gets the size of the 'data' subchunk. - * - * @return int - * The size of the 'data' subchunk. - */ - protected function getDataSubChunkSize() { - return islandora_hex2int(substr($this->datastreamContent, 80, 8)); - } -} - -/** - * Asserts the validity of any .mp3 datastream. - * - * Our default setup tries to create an MP3 using VBR, but we do some extra - * checks in case someone turns that off. If the header contains the characters - * 'Xing', it is flagged as VBR, and we can do an in-depth check on each of the - * VBR settings. Otherwise, we look for the basic MP3 signature 'fffa' or 'fffb' - * at the start of the binary. - */ -class MP3DatastreamValidator extends DatastreamValidator { - - /** - * Asserts the validity of the MP3. - * - * The MP3 file format is a bit of a mess; the entire makeup of the file - * depends on whether it uses variable bit rate or static bit rate. So, I'm - * breaking my own rules here and using a single assert function so that I - * can handle the weird logic. - */ - protected function assertValidMP3() { - $this->datastreamContent = bin2hex($this->datastreamContent); - - // If it's not a VBR MP3, we don't have to check much, so let's get that - // out of the way first before we go doing a bunch of potentially pointless - // math. Check to see if the VBR flag (58696e67) isn't there. - if (strpos($this->datastreamContent, '58696e67') == FALSE && substr($this->datastreamContent, 0, 4) == 'fffa') { - $this->addResult(TRUE, "{$this->datastream} datastream is encoded as a valid MPEG-1 Layer 3 file with CRC protection"); - return; - } - if (strpos($this->datastreamContent, '58696e67') == FALSE && substr($this->datastreamContent, 0, 4) == 'fffb') { - $this->addResult(TRUE, "{$this->datastream} datastream is encoded as a valid unprotected MPEG-1 Layer 3 file"); - return; - } - - // And what if the flag IS set? - if (strpos($this->datastreamContent, '58696e67')) { - // Check the field flags. VBR-formatted MP3 files contain a 32-bit - // integer (stored as $mp3_flag_value) that is a combination of four - // bits, each one indicating the on-off status of a VBR setting, via - // logical OR. Rather than disassembling this value into individual - // bits, we use the algorithm "if (binary_total+bit_value*2)/bit_value*2 - // is greater than or equal to bit_value, that bit is turned on" to find - // the status of each bit, so we know whether to offset the rest. - $mp3_field_offset = array(0, 0, 0); - $mp3_vbrheader = substr($this->datastreamContent, strpos($this->datastreamContent, '58696e67'), 240); - $mp3_flag_value = hexdec(substr($mp3_vbrheader, 8, 8)); - - // We can't use the first flag, but we still need to offset the rest. - if (($mp3_flag_value + 1) % 2 == 0) { - $mp3_field_offset[0] += 8; - $mp3_field_offset[1] += 8; - $mp3_field_offset[2] += 8; - } - - // The second flag leads us to filesize data, which we can verify. - if (($mp3_flag_value + 4) % 4 > 1) { - $mp3_field_bytes = hexdec(substr($mp3_vbrheader, $mp3_field_offset[0] + 16, 8)); - $mp3_size = strlen($this->datastreamContent) / 2; - $assertion = $mp3_size == $mp3_field_bytes; - $pass = "{$this->datastream} datastream reported filesize of {$mp3_size} bytes matches size field value of {$mp3_field_bytes}"; - $fail = "{$this->datastream} datastream reported filesize of {$mp3_size} bytes does not match size field value of {$mp3_field_bytes}"; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - $mp3_field_offset[1] += 8; - $mp3_field_offset[2] += 8; - } - - // We can't use the third flag for anything, but we still have to offset. - if (($mp3_flag_value + 8) % 8 > 3) { - $mp3_field_offset[2] += 200; - } - - // The fourth flag leads us to VBR quality data, which we can validate. - if ($mp3_flag_value > 7) { - $mp3_field_quality = hexdec(substr($mp3_vbrheader, $mp3_field_offset[2] + 16, 8)); - $assertion = $mp3_field_quality <= 100 && $mp3_field_quality >= 0; - $pass = "{$this->datastream} datastream reports valid VBR quality of {$mp3_field_quality} (expected: between 0-100)"; - $fail = "{$this->datastream} datastream reports invalid VBR quality of {$mp3_field_quality} (expected: between 0-100)"; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - } - - // If none of that works out, fail. - else { - $this->addResult(FALSE, "{$this->datastream} datastream is corrupt and does not identify as a valid MP3."); - } - } - -} - -/** - * Attempts to validate an .mp4 datastream. - * - * MP4 files are a subset of the ISO file format specification, and as such need - * to contain a 64-bit declaration of type within the first eight eight bytes of - * the file. This declaration is comprised of the characters 'ftyp', followed by - * a four-character filetype code. Here, we look for 'ftyp', and then pass the - * filetype code to the test message. - */ -class MP4DatastreamValidator extends DatastreamValidator { - - /** - * Asserts that the datastream is ISO-formatted video. - */ - protected function assertISOVideo() { - $mp4_ftyp = substr(strpos($this->datastreamContent, 'ftyp'), 4, 4); - $assertion = strpos($this->datastreamContent, 'ftyp') !== 0; - $pass = "{$this->datastream} datastream asserts that it is a valid ISO-formatted video file using ftyp {$mp4_ftyp}"; - $fail = "{$this->datastream} datastream is not a valid ISO-formatted video"; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } -} - -/** - * Attempts to validate an .ogg/ogv datastream using Vorbis and Theora encoding. - * - * OGG files are made up of several 'pages' of OGG data, each prefaced with an - * OGG marker - the letters 'OggS'. The file header also contains information on - * what encoders were used to create the file. Here, we're looking for at least - * one OGG page, and confirming that the file asserts the Theora and Vorbis - * codecs were used to create the file. - */ -class OGGDatastreamValidator extends DatastreamValidator { - - /** - * Asserts that the datastream contains ogg pages. - */ - protected function assertOGGPages() { - $ogg_pages = substr_count($this->datastreamContent, 'OggS'); - $assertion = $ogg_pages !== 0; - $pass = "{$this->datastream} datastream asserts that it contains {$ogg_pages} Ogg pages (even a very small file should contain several)."; - $fail = "{$this->datastream} datastream contains no Ogg pages."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts that the datastream contains Theora-encoded video. - */ - protected function assertTheoraVideo() { - $assertion = substr_count($this->datastreamContent, 'theora') !== 0; - $pass = "{$this->datastream} datastream asserts that it contains Theora-encoded video data."; - $fail = "{$this->datastream} datastream contains no marker indicating the presence of Theora-encoded video data."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts that the datastream contains Vorbis-encoded audio. - */ - protected function assertVorbisAudio() { - $assertion = substr_count($this->datastreamContent, 'vorbis') !== 0; - $pass = "{$this->datastream} datastream asserts that it contains Vorbis-encoded audio data"; - $fail = "{$this->datastream} datastream contains no marker indicating the presence of Vorbis-encoded audio data."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } -} - -/** - * Attempts to validate an .mkv datastream. - * - * There's not much we can do to check an MKV file, since the format is really, - * really loose. We do know a couple of things though - first, since MKV is an - * EBML format, the first four characters will always be the same. Since they're - * non-standard characters, we're looking at their hex values instead. And - * second, we know that the file will contain the declaration 'matroska' soon - * after. - */ -class MKVDatastreamValidator extends DatastreamValidator { - - /** - * Asserts that the datastream is an EBML-format file. - */ - protected function assertEBMLFormat() { - $assertion = substr(bin2hex($this->datastreamContent), 0, 8) == '1a45dfa3'; - $pass = "{$this->datastream} datastream asserts that it is an EBML-formatted file"; - $fail = "{$this->datastream} datastream is not an EBML-formatted file."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } - - /** - * Asserts that the datastream contains a matroska marker. - */ - protected function assertMatroskaMarker() { - $assertion = substr_count($this->datastreamContent, 'matroska') == 1; - $pass = "{$this->datastream} datastream asserts that its EBML DocType is Matroska"; - $fail = "{$this->datastream} datastream does not contain a Matroska EBML DocType marker."; - $message = $assertion ? $pass : $fail; - $this->addResult($assertion, $message); - } -} diff --git a/tests/islandora_web_test_case.inc b/tests/islandora_web_test_case.inc deleted file mode 100644 index 8a1e5ce8..00000000 --- a/tests/islandora_web_test_case.inc +++ /dev/null @@ -1,604 +0,0 @@ -configuration = $this->getTestConfiguration(); - if ($this->configuration['use_drupal_filter']) { - $this->backUpDrupalFilter(); - $this->setUpDrupalFilter(); - } - $this->admin = $this->createAdminUser(); - } - - /** - * Parses and returns the settings from the test configuration file. - * - * If no install specific test_config.ini file is found, it will use the - * assumed default configs found in default.test_config.ini. - * - * @return array - * The test configuration. - * - * @see parse_ini_file() - */ - protected function getTestConfiguration() { - $path = drupal_get_path('module', 'islandora'); - if (file_exists("$path/tests/test_config.ini")) { - $this->pass('Using custom test configuration.'); - return parse_ini_file("$path/tests/test_config.ini"); - } - elseif (file_exists("$path/tests/default.test_config.ini")) { - $this->pass('Using default test configuration.'); - return parse_ini_file("$path/tests/default.test_config.ini"); - } - throw new Exception('Required default.test_config.ini/test_config.ini file not found'); - } - - /** - * Stores the content of the Drupal Filter for later restoration. - */ - protected function backUpDrupalFilter() { - if (file_exists($this->configuration['drupal_filter_file'])) { - $this->originalDrupalFilterContent = file_get_contents($this->configuration['drupal_filter_file']); - } - else { - throw new Exception('Failed to find the required Drupal Filter configuration file.'); - } - } - - /** - * Sets up a drupal filter that can read for the tests users table. - */ - protected function setUpDrupalFilter() { - $connection_info = Database::getConnectionInfo('default'); - $drupal_filter_dom = new DomDocument(); - $drupal_filter_dom->loadXML($this->originalDrupalFilterContent); - $server = $connection_info['default']['host']; - $dbname = $connection_info['default']['database']; - $user = $connection_info['default']['username']; - $password = $connection_info['default']['password']; - $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; - $prefix = $connection_info['default']['prefix']['default']; - $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0); - $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0); - $connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node); - $connection_node->setAttributeNode(new DOMAttr('server', $server)); - $connection_node->setAttributeNode(new DOMAttr('dbname', $dbname)); - $connection_node->setAttributeNode(new DOMAttr('user', $user)); - $connection_node->setAttributeNode(new DOMAttr('password', $password)); - $connection_node->setAttributeNode(new DOMAttr('port', $port)); - $sql_node = $connection_node->appendChild(new DOMElement('sql')); - $sql_node->appendChild($drupal_filter_dom->createTextNode("SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;")); - file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); - } - - /** - * Creates the a full fedora admin user with a repository connection. - */ - protected function createAdminUser() { - $roles = user_roles(); - $index = array_search('administrator', $roles); - $user = $this->drupalCreateUser(); - $user->roles[$index] = 'administrator'; - $user->name = $this->configuration['admin_user']; - $user->pass = $this->configuration['admin_pass']; - $user = user_save($user); - $url = variable_get('islandora_base_url', $this->configuration['fedora_url']); - $connection = islandora_get_tuque_connection($user, $url); - $user->repository = $connection->repository; - return $user; - } - - /** - * Logs in the given user, handles the special case where the user is admin. - * - * @see DrupalWebTestCase::drupalLogin() - */ - protected function drupalLogin(stdClass $account) { - if ($account->uid == $this->admin->uid) { - // Create password for Drupal. - $edit = array('pass' => user_password()); - $account = user_save($account, $edit); - // Raw password is used to login. - $account->pass_raw = $edit['pass']; - // We must login before changing the password for fedora. - parent::drupalLogin($account); - $account->name = $this->configuration['admin_user']; - $account->pass = $this->configuration['admin_pass']; - // Save the fedora admin credentials for later GET/POST requests. - $account = user_save($account); - } - else { - parent::drupalLogin($account); - $this->users[] = $account->name; - } - } - - - /** - * Stores the content of the Drupal Filter for later restoration. - */ - protected function restoreDrupalFilter() { - $file = $this->configuration['drupal_filter_file']; - if (isset($this->originalDrupalFilterContent)) { - file_put_contents($file, $this->originalDrupalFilterContent); - } - elseif (file_exists($file)) { - // Remove if there was never an original. - drupal_unlink($file); - } - } - - /** - * Restores the original Drupal filter, frees any allocated resources. - * - * To safeguard against leaving test objects in the repository, tearDown() - * calls deleteUserCreatedObjects() every time by default. This feature can be - * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. - * - * @see DrupalWebTestCase::tearDown() - */ - public function tearDown() { - if ($this->deleteObjectsOnTeardown) { - foreach ($this->users as $user) { - $this->deleteUserCreatedObjects($user); - } - } - if ($this->configuration['use_drupal_filter']) { - $this->restoreDrupalFilter(); - } - unset($this->admin); - unset($this->configuration); - parent::tearDown(); - } - - /** - * Asserts that the given datastreams exist correctly on the object. - * - * @param AbstractObject $object - * 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 { - $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; - } - } - - /** - * Attempts to validate an array of datastreams, generally via binary checks. - * - * Datastream validation classes exist in, and can be added to, the file - * 'datastream_validators.inc', which is found in this folder. Datastream - * validator classes use the naming convention 'PrefixDatastreamValidator', - * and that 'Prefix' is what this function uses to determine what class to - * instantiate. - * - * $param IslandoraFedoraObject $object - * The object to load datastreams from. - * $param array $datastreams - * An array of arrays that pair DSIDs, DatastreamValidator class prefixes, - * and optional params. You can check some of the existing implementations - * for examples. - */ - public function validateDatastreams($object, array $datastreams) { - - if (!is_object($object)) { - $this->fail("Datastream validation failed; Object passed in is invalid.", 'Islandora'); - return; - } - - module_load_include('inc', 'islandora', 'tests/datastream_validators'); - - foreach ($datastreams as $datastream) { - // Let's give them conventional names. - $dsid = $datastream[0]; - $prefix = $datastream[1]; - $params = array(); - if (isset($datastream[2])) { - $params = $datastream[2]; - } - - // Legacy tests were created before the CamelCase conventions of the class - // system now in place. So, we need to automagically seek out prefixes - // that start with a lower-case letter and convert them to the proper - // format (rather than fixing every single legacy test). - if (ctype_lower(substr($prefix, 0, 1))) { - // Handle the case where the prefix is "image". - if ($prefix === 'image') { - $prefix = 'Image'; - } - // Handle the case where the prefix is "text". - elseif ($prefix === 'text') { - $prefix = 'Text'; - } - // All other cases involve just converting everything to caps. - else { - $prefix = strtoupper($prefix); - } - } - - // Instantiate the appropriate class, and grab the results. - $class_name = "{$prefix}DatastreamValidator"; - if (class_exists($class_name)) { - $validator = new $class_name($object, $dsid, $params); - foreach ($validator->getResults() as $result) { - $this->assert($result->getType(), $result->getMessage(), 'Islandora', $result->getCaller()); - } - } - else { - $this->fail("No DatastreamValidator class was found with the name '$class_name'; are you sure the prefix given to IslandoraWebTestCase->validateDatastreams() was entered correctly, or that such a validator exists?", 'Islandora'); - } - } - } - - /** - * Gets a tuque object from a path. - * - * @param string $path - * A full or partial path to an islandora object. - * - * @return AbstractObject - * The pid of the object or FALSE if a PID is not found. - */ - public function getObjectFromPath($path) { - $path_parts = explode('/', $path); - $array_length = count($path_parts); - for ($i = 0; $i < $array_length; $i++) { - if ($path_parts[$i] == 'islandora' && isset($path_parts[$i + 1]) && $path_parts[$i + 1] == 'object') { - if (isset($path_parts[$i + 2])) { - return islandora_object_load(urldecode($path_parts[$i + 2])); - } - } - } - $this->fail("Failed to parse path: $path."); - return FALSE; - } - - /** - * Deletes an object using the PID. This does the deletion using the UI. - * - * @param string $pid - * 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, $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 { - $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; - } - } - - /** - * Constructs and ingests a Fedora object and datastream(s) via tuque. - * - * All keys inside the parameter arrays for this function are optional. it - * can be run simply by calling $this->ingestConstructedObject();. - * - * @param array $properties - * An array containing object information using these keys: - * '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, or a - * string containing a single content model PID. - * '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: - * 'dsid' - the datastream ID; randomized if not set. - * 'path' - The path to the file to use; defaults to fixtures/test.jpg. - * 'control_group' - The single-letter control group identifier. - * 'mimetype' - The datastream's mimetype. - * - * @return bool|array - * FALSE if the object ingest failed, or the object array if successful. - */ - public function ingestConstructedObject(array $properties = array(), array $datastreams = array()) { - $tuque = islandora_get_tuque_connection($this->admin); - $repository = $tuque->repository; - if (!isset($properties['pid'])) { - $properties['pid'] = "islandora"; - } - $object = $repository->constructObject($properties['pid']); - - // Set the object properties before ingesting it. - if (isset($properties['label'])) { - $object->label = $properties['label']; - } - else { - $properties['label'] = $this->randomName(16); - $object->label = $properties['label']; - } - - if (isset($properties['owner'])) { - $object->owner = $properties['owner']; - } - elseif ($this->loggedInUser !== FALSE) { - $object->owner = $this->loggedInUser->name; - } - - if (isset($properties['models'])) { - try { - $object->models = (array) $properties['models']; - } - catch (Exception $e) { - $this->fail("Encountered an exception when trying to add content models to {$object->id}: $e"); - return FALSE; - } - } - - $repository->ingestObject($object); - if (!$object) { - $this->fail(t("Failed to ingest object."), 'Islandora'); - return FALSE; - } - else { - $this->pass(t("Ingested object %object", array('%object' => $object->id)), 'Islandora'); - } - - // Chuck in some datastreams. - if (!empty($datastreams)) { - foreach ($datastreams as $datastream) { - if (!isset($datastream['dsid'])) { - $datastream['dsid'] = $this->randomName(8); - } - if (!isset($datastream['path'])) { - $datastream['path'] = drupal_get_path('module', 'islandora') . '/tests/fixtures/test.jpg'; - } - if (!isset($datastream['control_group'])) { - $new_datastream = $object->constructDatastream($datastream['dsid']); - } - else { - $new_datastream = $object->constructDatastream($datastream['dsid'], $datastream['control_group']); - } - $new_datastream->label = $datastream['dsid']; - if (isset($datastream['mimetype'])) { - $new_datastream->mimetype = $datastream['mimetype']; - } - $new_datastream->setContentFromFile($datastream['path']); - $object->ingestDatastream($new_datastream); - } - } - - // Add a parent relationship, if necessary. - if (isset($properties['parent'])) { - $object->relationships->add(FEDORA_RELS_EXT_URI, 'isMemberOfCollection', $properties['parent']); - } - - return $object; - } - - /** - * Deletes all objects created by the given user. - * - * To safeguard against leaving test objects in the repository, this is called - * each time DrupalTestCase::run() calls tearDown(). This feature can be - * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. - * - * @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; - } - } - - /** - * These are a few quick helper functions to fill in a gap in the standard - * DrupalWebTestCase where no function exists to test for the simple existence - * or non-existence of an error. - */ - - /** - * Asserts that an error is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertError($message = '', $group = 'Other') { - if (!$message) { - $message = "Error found on current page when error was expected."; - } - return $this->assertRaw("
", $message, $group); - } - - /** - * Asserts that no error is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertNoError($message = '', $group = 'Other') { - if (!$message) { - $message = "No error found on current page when no error was expected."; - } - return $this->assertNoRaw("
", $message, $group); - } - - /** - * Asserts that a warning is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertWarning($message = '', $group = 'Other') { - if (!$message) { - $message = "Warning found on current page when warning was expected."; - } - return $this->assertRaw("
", $message, $group); - } - - /** - * Asserts that no warning is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertNoWarning($message = '', $group = 'Other') { - if (!$message) { - $message = "No warning found on current page when no warning was expected."; - } - return $this->assertNoRaw("
", $message, $group); - } - - /** - * Makes a drupalPost() request, using the form submit button's ID. - * - * Because drupalPost() is silly and doesn't let us choose which button we'd - * like to select if multiple buttons have the same value, this function - * allows us to select whatever button we'd like based on the ID instead. - * - * This is done via the absolutely hilarious method of fudging the actual - * button labels that don't have the ID we want, so that the only one left - * with the correct label is the one with the right ID. - * - * @see DrupalWebTestCase::drupalPost() - * - * @param string $path - * Location of the post form. - * @param array $edit - * Field data in an associative array. - * @param string $submit - * Value of the submit button whose click is to be emulated. - * @param string $id - * ID of the submit button whose click is to be emulated. - * @param array $options - * Options to be forwarded to url(). - * @param array $headers - * An array containing additional HTTP request headers, each formatted as - * "name: value". - * @param null $form_html_id - * (optional) HTML ID of the form to be submitted. - * @param null $extra_post - * (optional) A string of additional data to append to the POST submission. - * - * @return bool|string - * The content from the POST request's curlExec, or FALSE on fail. - */ - public function drupalPostByID($path, $edit, $submit, $id, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { - $buttons = $this->xpath("//input[@type=\"submit\" and @value=\"{$submit}\"]"); - if (empty($buttons)) { - $this->fail("No buttons found on the page with value '$submit'"); - return FALSE; - } - $i = 0; - foreach ($buttons as $button) { - if ($button['id'] !== $id) { - $button['value'] .= "_$i"; - $i++; - } - } - return $this->drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post); - } - -} From 23e8201afe68e9a36da876b2562b30dea65a52b5 Mon Sep 17 00:00:00 2001 From: qadan Date: Wed, 21 May 2014 13:43:36 +0000 Subject: [PATCH 05/93] er, add the includes too --- tests/includes/datastream_validators.inc | 699 ++++++++++++++++++++ tests/includes/islandora_unit_test_case.inc | 97 +++ tests/includes/islandora_web_test_case.inc | 340 ++++++++++ tests/includes/utilities.inc | 554 ++++++++++++++++ 4 files changed, 1690 insertions(+) create mode 100644 tests/includes/datastream_validators.inc create mode 100644 tests/includes/islandora_unit_test_case.inc create mode 100644 tests/includes/islandora_web_test_case.inc create mode 100644 tests/includes/utilities.inc diff --git a/tests/includes/datastream_validators.inc b/tests/includes/datastream_validators.inc new file mode 100644 index 00000000..c0241c84 --- /dev/null +++ b/tests/includes/datastream_validators.inc @@ -0,0 +1,699 @@ +object = $object; + $this->datastream = $datastream; + $this->params = $params; + if ($object[$datastream]) { + $this->datastreamContent = $object[$datastream]->content; + } + else { + drupal_set_message(t("Error grabbing content from datastream %datastream in object %id", array( + '%datastream' => $datastream, + '%id' => $object->id, + )), 'error'); + } + } + + /** + * Helper function to run all the validators in a class. + * + * On DatastreamValidator::__construct(), this looks for any functions + * within the class beginning in "assert" and runs them. In all current cases + * (and realistically in all future cases), this adds one or more passes or + * fails to $this->passes and/or $this->fails. + */ + public function runValidators() { + $methods = get_class_methods($this); + foreach ($methods as $method) { + if (substr($method, 0, 6) === 'assert') { + $this->$method(); + } + } + } + + /** + * Returns an array of IslandoraTestUtilityResults. + * + * @return IslandoraTestUtilityResult[] + * The results. + */ + public function getResults() { + if (empty($this->results)) { + $this->runValidators(); + } + return $this->results; + } + + /** + * Adds a result to $this->results. + * + * @param bool $type + * The type of result (TRUE for pass, FALSE for fail). + * @param string $message + * The message to put in the result. + */ + public function addResult($type, $message) { + $result = new IslandoraTestUtilityResult($type, $message, $this->getAssertionCall()); + $this->results[] = $result; + } + + /** + * Cycles through backtrace until the first non-assertion method is found. + * + * This is a manipulated version of DrupalWebTestCase::getAssertionCall(). + * We use it here so that we can pass back assertion calls from + * DatastreamValidator assertions instead of less useful TestCase functions. + * + * @return array + * Array representing the true caller. + */ + public function getAssertionCall() { + $backtrace = debug_backtrace(); + + // While the current caller's function starts with 'assert', and another one + // exists after this function, keep poppin' em off. + while (substr($backtrace[1]['function'], 0, 6) !== 'assert' && isset($backtrace[2])) { + array_shift($backtrace); + } + + return _drupal_get_last_caller($backtrace); + } + +} + +/** + * Asserts that an object's given datastreams are common-type image files. + * + * Uses PHPGD to run the assertion check. This means that only certain kinds + * of image files can be checked. Please check the documentation for the PHPGD + * imagecreatefromstring() function to determine what filetypes are valid. + */ +class ImageDatastreamValidator extends DatastreamValidator { + + /** + * Asserts the validity of an image using PHPGD. + */ + protected function assertImageGeneration() { + $assertion = imagecreatefromstring($this->datastreamContent) !== FALSE; + $pass = "Image datastream {$this->datastream} is valid."; + $fail = "Image datastream {$this->datastream} is either invalid or corrupt."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + +} + +/** + * Asserts the validity of any .tif/.tiff datastream. + */ +class TIFFDatastreamValidator extends DatastreamValidator { + + /** + * Asserts that the TIFF contains an appropriate header. + */ + public function assertTIFFHeaderHex() { + $datastream_header_hex = self::getTIFFHeaderHex(); + if ($datastream_header_hex == "49492a00") { + // In this case, the ingested TIFF is designated as using the "Intel + // byte-order" (i.e. little-endian) by starting with the characters "II" + // (repeated so that byte order does not yet need to be significant). + // The number that follows is '42' in little-endian hex, a number of + // 'deep philosophical significance' to the TIFF format creators.' + $this->addResult(TRUE, "{$this->datastream} datastream asserts that it is a valid Intel-byte-orderded TIF/TIFF file."); + } + elseif ($datastream_header_hex == "4d4d002a") { + // In this case, the ingested TIFF is designated as using the "Motorola + // byte-order" (i.e. big-endian) by starting with the characters "MM" + // instead. 42 follows once again, this time in big-endian hex. + $this->addResult(TRUE, "{$this->datastream} datastream asserts that it is a valid Motorola-byte-ordered TIF/TIFF file."); + } + else { + $this->addResult(FALSE, "{$this->datastream} datastream does not assert that it is a valid TIF/TIFF file."); + } + } + + /** + * Grabs the first 8 characters from the TIFF datastream's hex. + * + * @return string + * The ... thing I just wrote up there. + */ + protected function getTIFFHeaderHex() { + return substr(bin2hex($this->datastreamContent), 0, 8); + } + +} + +/** + * Asserts the validity of a JP2 datastream. + */ +class JP2DatastreamValidator extends DatastreamValidator { + + /** + * Asserts the hex values at the head of the JP2 file. + * + * JP2 files begin with an offset header at the second 32-bit integer, + * 0x6A502020. This header is in all .jp2s, and we check for it here. + */ + protected function assertJP2Header() { + $assertion = substr(bin2hex($this->datastreamContent), 8, 8) == '6a502020'; + $pass = "Datastream {$this->datastream} contains the appropriate JP2 header."; + $fail = "Datastream {$this->datastream} does not contain the appropriate JP2 header."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts the marker at the end of the JP2 file. + * + * JP2 files have their codestream capped with a marker, 0xFFD9. We're just + * checking for it here to see if the .jp2 encoder finished okay. + */ + protected function assertJP2Marker() { + $assertion = substr(bin2hex($this->datastreamContent), strlen(bin2hex($this->datastreamContent)) - 4, 4) == 'ffd9'; + $pass = "Datastream {$this->datastream} contains the appropriate JP2 ending marker."; + $fail = "Datastream {$this->datastream} does not contain the appropriate JP2 ending marker. If this is the only JP2 validator that failed, it is likely that derivative generation was interrupted."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } +} + +/** + * Asserts the validity of a PDF datastream. + */ +class PDFDatastreamValidator extends DatastreamValidator { + + /** + * Validates the PDF signature. + */ + protected function assertPDFSignature() { + $assertion = substr($this->datastreamContent, 0, 5) == '%PDF-'; + $pdf_version = substr($this->datastreamContent, 5, 3); + $pass = "{$this->datastream} datastream asserts that it is a valid PDF file using PDF version {$pdf_version}"; + $fail = "{$this->datastream} datastream binary header appears to be corrupt and missing a valid PDF signature."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Counts the number of signatures in this PDF file and asserts there are any. + */ + protected function assertPDFStreamCount() { + $pdf_stream_count = substr_count(bin2hex($this->datastreamContent), '0a73747265616d0a'); + $assertion = $pdf_stream_count !== 0; + $pass = "{$this->datastream} datastream reports the existence of {$pdf_stream_count} PDF streams. Note that an extremely low number could still indicate corruption."; + $fail = "{$this->datastream} datastream contains zero PDF streams, and is likely not a PDF file."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Validates the PDF closing tag. + * + * @return bool + * TRUE if it was present; FALSE otherwise. + */ + protected function assertPDFClosingTag() { + $assertion = strpos(bin2hex($this->datastreamContent), '0a2525454f460a') == TRUE; + $pass = "{$this->datastream} datastream reports the existence of the closing 'EOF' tag required at the end of PDFs"; + $fail = "{$this->datastream} datastream does not contain the closing 'EOF' tag. If this is the only PDF validation that failed, it is likely that derivative generation was interrupted."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } +} + +/** + * Validates the number of times a string occurs in a datastream. + * + * Requires $this->params to be set to an array containing two keys - the first + * is the string we're looking to find in the datastream, and the second is an + * integer representing the number of times it should appear in the datastream. + */ +class TextDatastreamValidator extends DatastreamValidator { + /** + * Constructor override; blow up if we don't have our two values. + */ + public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + if (count($params) < 2) { + throw new InvalidArgumentException('$params must contain at least two values to instantiate a TextDatastreamValidator.'); + } + parent::__construct($object, $datastream, $params); + } + + /** + * Asserts that the string given appears the correct number of times. + */ + protected function assertTextStringCount() { + $string_count = self::getTextStringCount(); + list($string, $expected) = $this->params; + $assertion = $string_count === $expected; + $this->addResult($assertion, "{$this->datastream} datastream contains the word(s) '{$string}' repeated {$string_count} time(s) (expected: {$expected})."); + } + + /** + * The number of times key [0] in $this->params appears in the datastream. + * + * @return int + * That count I just mentioned up there. + */ + protected function getTextStringCount() { + return substr_count($this->datastreamContent, $this->params[0]); + } +} + +/** + * Asserts the validity a WAV datastream. + * + * WAV files contain a rigidly detailed header that contains all sorts of fun + * information we can use to validate things against other things. So, we check + * rigorously that the header contains properly constructed data by looking to + * see if certain values are at their expected byte offset. We also compare + * declared chunk sizes against actual sizes. If any of these are off, WAV + * players will fail to function. + */ +class WAVDatastreamValidator extends DatastreamValidator { + + /** + * We need a special constructor here to get the hex datastream content. + * + * @param IslandoraFedoraObject $object + * The object to grab the datastream from. + * @param string $datastream + * The DSID of the datastream itself. + * @param array $params + * An extra array of parameters the validator might need. + */ + public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + parent::__construct($object, $datastream, $params); + $this->datastreamContent = bin2hex($this->datastreamContent); + } + + /** + * Asserts that the datastream contains a valid WAV signature. + */ + protected function assertWAVSignature() { + $signatures = str_split(substr($this->datastreamContent, 0, 24), 8); + $assertion = $signatures[0] == '52494646' && $signatures[2] == '57415645'; + $pass = "Header of the {$this->datastream} datastream contains a valid file signature."; + $fail = "Header of the {$this->datastream} datastream contains corrupt file signature."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts that the chunksize in the header is correct. + */ + protected function assertWAVChunkSize() { + $assertion = islandora_hex2int(substr($this->datastreamContent, 8, 8)) === 36 + self::getDataSubChunkSize(); + $pass = "{$this->datastream} datastream chunksize in WAV header is correct"; + $fail = "{$this->datastream} datastream chunksize in WAV header does not match actual chunksize."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts that the datastream contains a 'fmt' subchunk. + */ + protected function assertWAVFmtSubChunk() { + $assertion = substr($this->datastreamContent, 24, 8) === '666d7420'; + $pass = "{$this->datastream} datastream contains a 'fmt' subchunk."; + $fail = "{$this->datastream} datastream is missing the required 'fmt' subchunk."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts that the byterate reported by the WAV header is valid. + */ + protected function assertWAVByteRate() { + $wav_samplerate = islandora_hex2int(substr($this->datastreamContent, 48, 8)); + $assertion = islandora_hex2int(substr($this->datastreamContent, 56, 8)) === $wav_samplerate * self::getNumChannels() * self::getBytesPerSample(); + $pass = "{$this->datastream} datastream byterate in the WAV header is correct."; + $fail = "{$this->datastream} datastream byterate in the WAV header does not match actual calculated byterate."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts that the block alignment is correct. + */ + protected function assertWAVBlockAlignment() { + $assertion = islandora_hex2int(substr($this->datastreamContent, 64, 4)) === self::getNumChannels() * self::getBytesPerSample(); + $pass = "{$this->datastream} datastream block alignment is set correctly."; + $fail = "{$this->datastream} datastream block alignment is off."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts the existence of a 'data' subchunk. + * + * Also asserts that the subchunk size is correct. + */ + protected function assertWAVDataSubChunk() { + if (substr($this->datastreamContent, 72, 8) !== '64617461') { + $this->addResult(FALSE, "{$this->datastream} datastream is missing the 'data' subchunk."); + return; + } + else { + $this->addResult(TRUE, "{$this->datastream} datastream contains 'data' subchunk."); + $wav_numsamples = strlen(substr($this->datastreamContent, 88)) / self::getNumChannels() / self::getBytesPerSample() / 2; + $assertion = self::getDataSubChunkSize() === $wav_numsamples * self::getNumChannels() * self::getBytesPerSample(); + $pass = "{$this->datastream} datastream 'data' chunk is the correct size."; + $fail = "{$this->datastream} datastream 'data' chunk is sized incorrectly."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + } + + /** + * Gets the number of channels reported by the WAV header. + * + * @return int + * The number of channels reported by the datastream header. + */ + protected function getNumChannels() { + return islandora_hex2int(substr($this->datastreamContent, 44, 4)); + } + + /** + * Gets the reported number of byte rates per sample. + * + * @return int + * The number of bytes per sample reported by the datastream header. + */ + protected function getBytesPerSample() { + return islandora_hex2int(substr($this->datastreamContent, 68, 4)) / 8; + } + + /** + * Gets the size of the 'data' subchunk. + * + * @return int + * The size of the 'data' subchunk. + */ + protected function getDataSubChunkSize() { + return islandora_hex2int(substr($this->datastreamContent, 80, 8)); + } +} + +/** + * Asserts the validity of any .mp3 datastream. + * + * Our default setup tries to create an MP3 using VBR, but we do some extra + * checks in case someone turns that off. If the header contains the characters + * 'Xing', it is flagged as VBR, and we can do an in-depth check on each of the + * VBR settings. Otherwise, we look for the basic MP3 signature 'fffa' or 'fffb' + * at the start of the binary. + */ +class MP3DatastreamValidator extends DatastreamValidator { + + /** + * Asserts the validity of the MP3. + * + * The MP3 file format is a bit of a mess; the entire makeup of the file + * depends on whether it uses variable bit rate or static bit rate. So, I'm + * breaking my own rules here and using a single assert function so that I + * can handle the weird logic. + */ + protected function assertValidMP3() { + $this->datastreamContent = bin2hex($this->datastreamContent); + + // If it's not a VBR MP3, we don't have to check much, so let's get that + // out of the way first before we go doing a bunch of potentially pointless + // math. Check to see if the VBR flag (58696e67) isn't there. + if (strpos($this->datastreamContent, '58696e67') == FALSE && substr($this->datastreamContent, 0, 4) == 'fffa') { + $this->addResult(TRUE, "{$this->datastream} datastream is encoded as a valid MPEG-1 Layer 3 file with CRC protection"); + return; + } + if (strpos($this->datastreamContent, '58696e67') == FALSE && substr($this->datastreamContent, 0, 4) == 'fffb') { + $this->addResult(TRUE, "{$this->datastream} datastream is encoded as a valid unprotected MPEG-1 Layer 3 file"); + return; + } + + // And what if the flag IS set? + if (strpos($this->datastreamContent, '58696e67')) { + // Check the field flags. VBR-formatted MP3 files contain a 32-bit + // integer (stored as $mp3_flag_value) that is a combination of four + // bits, each one indicating the on-off status of a VBR setting, via + // logical OR. Rather than disassembling this value into individual + // bits, we use the algorithm "if (binary_total+bit_value*2)/bit_value*2 + // is greater than or equal to bit_value, that bit is turned on" to find + // the status of each bit, so we know whether to offset the rest. + $mp3_field_offset = array(0, 0, 0); + $mp3_vbrheader = substr($this->datastreamContent, strpos($this->datastreamContent, '58696e67'), 240); + $mp3_flag_value = hexdec(substr($mp3_vbrheader, 8, 8)); + + // We can't use the first flag, but we still need to offset the rest. + if (($mp3_flag_value + 1) % 2 == 0) { + $mp3_field_offset[0] += 8; + $mp3_field_offset[1] += 8; + $mp3_field_offset[2] += 8; + } + + // The second flag leads us to filesize data, which we can verify. + if (($mp3_flag_value + 4) % 4 > 1) { + $mp3_field_bytes = hexdec(substr($mp3_vbrheader, $mp3_field_offset[0] + 16, 8)); + $mp3_size = strlen($this->datastreamContent) / 2; + $assertion = $mp3_size == $mp3_field_bytes; + $pass = "{$this->datastream} datastream reported filesize of {$mp3_size} bytes matches size field value of {$mp3_field_bytes}"; + $fail = "{$this->datastream} datastream reported filesize of {$mp3_size} bytes does not match size field value of {$mp3_field_bytes}"; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + $mp3_field_offset[1] += 8; + $mp3_field_offset[2] += 8; + } + + // We can't use the third flag for anything, but we still have to offset. + if (($mp3_flag_value + 8) % 8 > 3) { + $mp3_field_offset[2] += 200; + } + + // The fourth flag leads us to VBR quality data, which we can validate. + if ($mp3_flag_value > 7) { + $mp3_field_quality = hexdec(substr($mp3_vbrheader, $mp3_field_offset[2] + 16, 8)); + $assertion = $mp3_field_quality <= 100 && $mp3_field_quality >= 0; + $pass = "{$this->datastream} datastream reports valid VBR quality of {$mp3_field_quality} (expected: between 0-100)"; + $fail = "{$this->datastream} datastream reports invalid VBR quality of {$mp3_field_quality} (expected: between 0-100)"; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + } + + // If none of that works out, fail. + else { + $this->addResult(FALSE, "{$this->datastream} datastream is corrupt and does not identify as a valid MP3."); + } + } + +} + +/** + * Attempts to validate an .mp4 datastream. + * + * MP4 files are a subset of the ISO file format specification, and as such need + * to contain a 64-bit declaration of type within the first eight eight bytes of + * the file. This declaration is comprised of the characters 'ftyp', followed by + * a four-character filetype code. Here, we look for 'ftyp', and then pass the + * filetype code to the test message. + */ +class MP4DatastreamValidator extends DatastreamValidator { + + /** + * Asserts that the datastream is ISO-formatted video. + */ + protected function assertISOVideo() { + $mp4_ftyp = substr(strpos($this->datastreamContent, 'ftyp'), 4, 4); + $assertion = strpos($this->datastreamContent, 'ftyp') !== 0; + $pass = "{$this->datastream} datastream asserts that it is a valid ISO-formatted video file using ftyp {$mp4_ftyp}"; + $fail = "{$this->datastream} datastream is not a valid ISO-formatted video"; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } +} + +/** + * Attempts to validate an .ogg/ogv datastream using Vorbis and Theora encoding. + * + * OGG files are made up of several 'pages' of OGG data, each prefaced with an + * OGG marker - the letters 'OggS'. The file header also contains information on + * what encoders were used to create the file. Here, we're looking for at least + * one OGG page, and confirming that the file asserts the Theora and Vorbis + * codecs were used to create the file. + */ +class OGGDatastreamValidator extends DatastreamValidator { + + /** + * Asserts that the datastream contains ogg pages. + */ + protected function assertOGGPages() { + $ogg_pages = substr_count($this->datastreamContent, 'OggS'); + $assertion = $ogg_pages !== 0; + $pass = "{$this->datastream} datastream asserts that it contains {$ogg_pages} Ogg pages (even a very small file should contain several)."; + $fail = "{$this->datastream} datastream contains no Ogg pages."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts that the datastream contains Theora-encoded video. + */ + protected function assertTheoraVideo() { + $assertion = substr_count($this->datastreamContent, 'theora') !== 0; + $pass = "{$this->datastream} datastream asserts that it contains Theora-encoded video data."; + $fail = "{$this->datastream} datastream contains no marker indicating the presence of Theora-encoded video data."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts that the datastream contains Vorbis-encoded audio. + */ + protected function assertVorbisAudio() { + $assertion = substr_count($this->datastreamContent, 'vorbis') !== 0; + $pass = "{$this->datastream} datastream asserts that it contains Vorbis-encoded audio data"; + $fail = "{$this->datastream} datastream contains no marker indicating the presence of Vorbis-encoded audio data."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } +} + +/** + * Attempts to validate an .mkv datastream. + * + * There's not much we can do to check an MKV file, since the format is really, + * really loose. We do know a couple of things though - first, since MKV is an + * EBML format, the first four characters will always be the same. Since they're + * non-standard characters, we're looking at their hex values instead. And + * second, we know that the file will contain the declaration 'matroska' soon + * after. + */ +class MKVDatastreamValidator extends DatastreamValidator { + + /** + * Asserts that the datastream is an EBML-format file. + */ + protected function assertEBMLFormat() { + $assertion = substr(bin2hex($this->datastreamContent), 0, 8) == '1a45dfa3'; + $pass = "{$this->datastream} datastream asserts that it is an EBML-formatted file"; + $fail = "{$this->datastream} datastream is not an EBML-formatted file."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } + + /** + * Asserts that the datastream contains a matroska marker. + */ + protected function assertMatroskaMarker() { + $assertion = substr_count($this->datastreamContent, 'matroska') == 1; + $pass = "{$this->datastream} datastream asserts that its EBML DocType is Matroska"; + $fail = "{$this->datastream} datastream does not contain a Matroska EBML DocType marker."; + $message = $assertion ? $pass : $fail; + $this->addResult($assertion, $message); + } +} diff --git a/tests/includes/islandora_unit_test_case.inc b/tests/includes/islandora_unit_test_case.inc new file mode 100644 index 00000000..ce9216d8 --- /dev/null +++ b/tests/includes/islandora_unit_test_case.inc @@ -0,0 +1,97 @@ + FALSE); + $utilities = new IslandoraTestUtilities($this->configuration, $params); + if (!method_exists($utilities, $method)) { + $caller = $this->getAssertionCall(); + throw new BadMethodCallException("Exception: undefined method $method in {$caller['file']}, line {$caller['line']}."); + } + $result = call_user_func_array(array(&$utilities, $method), $args); + $this->parseUtilityResults($utilities); + return $result; + } + + /** + * Parses utility results and passes them to the test results as an assertion. + * + * @param IslandoraTestUtilities $utility + * An instance of IslandoraTestUtilities with populated results. + */ + public function parseUtilityResults($utility) { + foreach ($utility->getResults() as $result) { + $this->assert($result->getType(), $result->getMessage(), 'Islandora', $result->getCaller()); + } + } + + /** + * Sets up the Drupal filter to access this test Drupal instances database. + * + * @see DrupalWebTestCase::setUp() + */ + public function setUp() { + parent::setUp(); + + // It's possible test are running before class autoloading. + module_load_include('inc', 'islandora', 'includes/tuque'); + module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); + module_load_include('inc', 'islandora', 'tests/includes/utilities'); + + $this->configuration = islandora_get_test_configuration(); + if ($this->configuration['use_drupal_filter']) { + $this->filterManipulator = new IslandoraDrupalFilterManipulator($this->configuration); + $this->filterManipulator->setUpDrupalFilter(); + } + + $this->connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); + $api = new FedoraApi($this->connection); + $this->repository = new FedoraRepository($api, new SimpleCache()); + } + + /** + * Restores the original Drupal filter, frees any allocated resources. + * + * To safeguard against leaving test objects in the repository, tearDown() + * calls deleteUserCreatedObjects() every time by default. This feature can be + * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. + * + * @see DrupalWebTestCase::tearDown() + */ + public function tearDown() { + if ($this->configuration['use_drupal_filter']) { + $this->filterManipulator->restoreDrupalFilter(); + } + unset($this->configuration); + parent::tearDown(); + } + +} diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc new file mode 100644 index 00000000..383e2b02 --- /dev/null +++ b/tests/includes/islandora_web_test_case.inc @@ -0,0 +1,340 @@ + $this->loggedInUser, + 'db_access' => TRUE, + ); + $utilities = new IslandoraTestUtilities($this->configuration, $params); + if (!method_exists($utilities, $method)) { + $caller = $this->getAssertionCall(); + throw new BadMethodCallException("Exception: undefined method $method in {$caller['file']}, line {$caller['line']}."); + } + $result = call_user_func_array(array(&$utilities, $method), $args); + $this->parseUtilityResults($utilities); + return $result; + } + + /** + * Parses utility results and passes them to the test results as an assertion. + * + * @param IslandoraTestUtilities $utility + * An instance of IslandoraTestUtilities with populated results. + */ + public function parseUtilityResults($utility) { + foreach ($utility->getResults() as $result) { + $this->assert($result->getType(), $result->getMessage(), 'Islandora', $result->getCaller()); + } + } + + /** + * Sets up the Drupal filter to access this test Drupal instances database. + * + * @see DrupalWebTestCase::setUp() + */ + public function setUp() { + $args = func_get_args(); + $args = (isset($args[0]) && is_array($args[0])) ? $args[0] : $args; + // Always enable islandora. + $args[] = 'islandora'; + parent::setUp($args); + + // It's possible test are running before class autoloading. + module_load_include('inc', 'islandora', 'includes/tuque'); + module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); + module_load_include('inc', 'islandora', 'tests/includes/utilities'); + + $this->configuration = islandora_get_test_configuration(); + if ($this->configuration['use_drupal_filter']) { + $this->filterManipulator = new IslandoraDrupalFilterManipulator($this->configuration); + $this->filterManipulator->setUpDrupalFilter(); + } + $this->admin = $this->createAdminUser(); + } + + /** + * Creates the a full fedora admin user with a repository connection. + */ + protected function createAdminUser() { + $roles = user_roles(); + $index = array_search('administrator', $roles); + $user = $this->drupalCreateUser(); + $user->roles[$index] = 'administrator'; + $user->name = $this->configuration['admin_user']; + $user->pass = $this->configuration['admin_pass']; + $user = user_save($user); + $url = variable_get('islandora_base_url', $this->configuration['fedora_url']); + $connection = islandora_get_tuque_connection($user, $url); + $user->repository = $connection->repository; + return $user; + } + + /** + * Logs in the given user, handles the special case where the user is admin. + * + * @see DrupalWebTestCase::drupalLogin() + */ + protected function drupalLogin(stdClass $account) { + if ($account->uid == $this->admin->uid) { + // Create password for Drupal. + $edit = array('pass' => user_password()); + $account = user_save($account, $edit); + // Raw password is used to login. + $account->pass_raw = $edit['pass']; + // We must login before changing the password for fedora. + parent::drupalLogin($account); + $account->name = $this->configuration['admin_user']; + $account->pass = $this->configuration['admin_pass']; + // Save the fedora admin credentials for later GET/POST requests. + $account = user_save($account); + } + else { + parent::drupalLogin($account); + $this->users[] = $account->name; + } + } + + /** + * Restores the original Drupal filter, frees any allocated resources. + * + * To safeguard against leaving test objects in the repository, tearDown() + * calls deleteUserCreatedObjects() every time by default. This feature can be + * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. + * + * @see DrupalWebTestCase::tearDown() + */ + public function tearDown() { + if ($this->deleteObjectsOnTeardown) { + foreach ($this->users as $user) { + $this->deleteUserCreatedObjects($user); + } + } + if ($this->configuration['use_drupal_filter']) { + $this->filterManipulator->restoreDrupalFilter(); + } + unset($this->admin); + unset($this->configuration); + parent::tearDown(); + } + + /** + * Gets a tuque object from a path. + * + * @param string $path + * A full or partial path to an islandora object. + * + * @return AbstractObject + * The pid of the object or FALSE if a PID is not found. + */ + public function getObjectFromPath($path) { + $path_parts = explode('/', $path); + $array_length = count($path_parts); + for ($i = 0; $i < $array_length; $i++) { + if ($path_parts[$i] == 'islandora' && isset($path_parts[$i + 1]) && $path_parts[$i + 1] == 'object') { + if (isset($path_parts[$i + 2])) { + return islandora_object_load(urldecode($path_parts[$i + 2])); + } + } + } + $this->fail("Failed to parse path: $path."); + return FALSE; + } + + /** + * Deletes an object using the PID. This does the deletion using the UI. + * + * @param string $pid + * 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, $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 { + $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; + } + } + + /** + * These are a few quick helper functions to fill in a gap in the standard + * DrupalWebTestCase where no function exists to test for the simple existence + * or non-existence of an error. + */ + + /** + * Asserts that an error is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertError($message = '', $group = 'Other') { + if (!$message) { + $message = "Error found on current page when error was expected."; + } + return $this->assertRaw("
", $message, $group); + } + + /** + * Asserts that no error is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertNoError($message = '', $group = 'Other') { + if (!$message) { + $message = "No error found on current page when no error was expected."; + } + return $this->assertNoRaw("
", $message, $group); + } + + /** + * Asserts that a warning is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertWarning($message = '', $group = 'Other') { + if (!$message) { + $message = "Warning found on current page when warning was expected."; + } + return $this->assertRaw("
", $message, $group); + } + + /** + * Asserts that no warning is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertNoWarning($message = '', $group = 'Other') { + if (!$message) { + $message = "No warning found on current page when no warning was expected."; + } + return $this->assertNoRaw("
", $message, $group); + } + + /** + * Makes a drupalPost() request, using the form submit button's ID. + * + * Because drupalPost() is silly and doesn't let us choose which button we'd + * like to select if multiple buttons have the same value, this function + * allows us to select whatever button we'd like based on the ID instead. + * + * This is done via the absolutely hilarious method of fudging the actual + * button labels that don't have the ID we want, so that the only one left + * with the correct label is the one with the right ID. + * + * @see DrupalWebTestCase::drupalPost() + * + * @param string $path + * Location of the post form. + * @param array $edit + * Field data in an associative array. + * @param string $submit + * Value of the submit button whose click is to be emulated. + * @param string $id + * ID of the submit button whose click is to be emulated. + * @param array $options + * Options to be forwarded to url(). + * @param array $headers + * An array containing additional HTTP request headers, each formatted as + * "name: value". + * @param null $form_html_id + * (optional) HTML ID of the form to be submitted. + * @param null $extra_post + * (optional) A string of additional data to append to the POST submission. + * + * @return bool|string + * The content from the POST request's curlExec, or FALSE on fail. + */ + public function drupalPostByID($path, $edit, $submit, $id, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { + $buttons = $this->xpath("//input[@type=\"submit\" and @value=\"{$submit}\"]"); + if (empty($buttons)) { + $this->fail("No buttons found on the page with value '$submit'"); + return FALSE; + } + $i = 0; + foreach ($buttons as $button) { + if ($button['id'] !== $id) { + $button['value'] .= "_$i"; + $i++; + } + } + return $this->drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post); + } + +} diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc new file mode 100644 index 00000000..e0672fd8 --- /dev/null +++ b/tests/includes/utilities.inc @@ -0,0 +1,554 @@ +configuration = $configuration; + $this->backUpDrupalFilter(); + } + + /** + * Stores the content of the Drupal Filter for later restoration. + */ + public function restoreDrupalFilter() { + $file = $this->configuration['drupal_filter_file']; + if (isset($this->originalDrupalFilterContent)) { + file_put_contents($file, $this->originalDrupalFilterContent); + } + elseif (file_exists($file)) { + // Remove if there was never an original. + drupal_unlink($file); + } + } + + /** + * Sets up a drupal filter that can read for the tests users table. + */ + public function setUpDrupalFilter() { + $connection_info = Database::getConnectionInfo('default'); + $drupal_filter_dom = new DomDocument(); + $drupal_filter_dom->loadXML($this->originalDrupalFilterContent); + $server = $connection_info['default']['host']; + $dbname = $connection_info['default']['database']; + $user = $connection_info['default']['username']; + $password = $connection_info['default']['password']; + $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; + $prefix = $connection_info['default']['prefix']['default']; + $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0); + $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0); + $connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node); + $connection_node->setAttributeNode(new DOMAttr('server', $server)); + $connection_node->setAttributeNode(new DOMAttr('dbname', $dbname)); + $connection_node->setAttributeNode(new DOMAttr('user', $user)); + $connection_node->setAttributeNode(new DOMAttr('password', $password)); + $connection_node->setAttributeNode(new DOMAttr('port', $port)); + $sql_node = $connection_node->appendChild(new DOMElement('sql')); + $sql_node->appendChild($drupal_filter_dom->createTextNode("SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;")); + file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); + } + + /** + * Stores the content of the Drupal Filter for later restoration. + */ + protected function backUpDrupalFilter() { + if (file_exists($this->configuration['drupal_filter_file'])) { + $this->originalDrupalFilterContent = file_get_contents($this->configuration['drupal_filter_file']); + } + else { + throw new Exception('Failed to find the required Drupal Filter configuration file.'); + } + } +} + +/** + * A result from a utility method; $type defines TRUE/FALSE as pass/fail. + */ +class IslandoraTestUtilityResult { + + /** + * The message for this result. + * + * @var string + */ + protected $message; + + /** + * The caller for this result. + * + * @var array + */ + protected $caller; + + /** + * The type of result this is - TRUE for pass, FALSE for fail. + * + * @var bool + */ + protected $type; + + /** + * Constructs an IslandoraTestUtilityResult. + * + * @param bool $type + * Whether this result should indicate a pass (TRUE) or fail (FALSE). + * @param string $message + * The message that will be used by this result. + * @param array $caller + * The caller for this result. + */ + public function __construct($type, $message, array $caller) { + $this->message = $message; + $this->caller = $caller; + $this->type = $type; + } + + /** + * Get the message for this result. + * + * @return string + * The message for this result. + */ + public function getMessage() { + return $this->message; + } + + /** + * Get the caller for this result. + * + * @return string + * The caller for this result. + */ + public function getCaller() { + return $this->caller; + } + + /** + * Get the type of result. + * + * @return bool + * The type of pass (TRUE for pass, FALSE for fail). + */ + public function getType() { + return $this->type; + } +} + +/** + * Abstraction for test utility classes. + * + * This is to be implemented in any class that wants to have test utility + * functionality (i.e. that wants to pass back results to tests). Check out the + * datastream validator class for a good example of how this is implemented. + */ +abstract class IslandoraTestUtilityClass { + + /** + * An array of IslandoraTestUtilityResults. + * + * These should be generated using $this->addResult. + * + * @var IslandoraTestUtilityResult[] + */ + public $results = array(); + + /** + * Returns an array of IslandoraTestUtilityResults. + * + * The particular testing implementation you are using should use this to + * parse results from utilities and pass them through. + * + * @return IslandoraTestUtilityResult[] + * The results. + */ + abstract public function getResults(); + + /** + * Adds a result to $this->results. + * + * @param bool $type + * The type of result (TRUE for pass, FALSE for fail). + * @param string $message + * The message to put in the result. + */ + abstract public function addResult($type, $message); + + /** + * Gets the caller of the method that passed a result. + * + * @return array + * Array representing the true caller. + */ + abstract public function getAssertionCall(); + +} + +class IslandoraTestUtilities extends IslandoraTestUtilityClass { + + protected $configuration; + + protected $params; + + public $results = array(); + + protected $repository; + + /** + * Constructs an IslandoraTestUtilities object. + * + * @param array $configuration + * The parsed test configuration. + * @param array $params + * Any additional parameters the method called may need to function. + */ + public function __construct($configuration, array $params = array()) { + $this->configuration = $configuration; + $this->params = $params; + // If we have DB access, we want to use the Islandora Tuque wrappers. + if ($params['db_access']) { + $connection = islandora_get_tuque_connection(); + $this->repository = $connection->repository; + } + // Otherwise, get a generic Tuque repository. + else { + $connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); + $api = new FedoraApi($connection); + $this->repository = new FedoraRepository($api, new SimpleCache()); + } + } + + /** + * Returns an array of IslandoraTestUtilityResults. + * + * @return IslandoraTestUtilityResult[] + * The results. + */ + public function getResults() { + return $this->results; + } + + /** + * Adds a result to $this->results. + * + * @param bool $type + * The type of result (TRUE for pass, FALSE for fail). + * @param string $message + * The message to put in the result. + */ + public function addResult($type, $message) { + $result = new IslandoraTestUtilityResult($type, $message, $this->getAssertionCall()); + $this->results[] = $result; + } + + /** + * Cycles through backtrace until the first non-assertion method is found. + * + * This is a manipulated version of DrupalWebTestCase::getAssertionCall(). + * We use it here so that we can pass back assertion calls from + * DatastreamValidator assertions instead of less useful TestCase functions. + * + * @return array + * Array representing the true caller. + */ + public function getAssertionCall() { + $backtrace = debug_backtrace(); + array_shift($backtrace); + return _drupal_get_last_caller($backtrace); + } + + /** + * Asserts that the given datastreams exist correctly on the object. + * + * @param AbstractObject $object + * 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->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); + } + else { + $missing_datastreams = array_diff_key(array_flip($datastreams), $object->repository->api->a->listDatastreams($object->id)); + + if (!empty($missing_datastreams)) { + $this->addResult(FALSE, "Failed to find datastream(s) " . implode(', ', array_flip($missing_datastreams)) . " in object {$object->id}."); + return FALSE; + } + + $this->addResult(TRUE, "Found required datastream(s) in object {$object->id}"); + return TRUE; + } + } + + /** + * Attempts to validate an array of datastreams, generally via binary checks. + * + * Datastream validation classes exist in, and can be added to, the file + * 'datastream_validators.inc', which is found in this folder. Datastream + * validator classes use the naming convention 'PrefixDatastreamValidator', + * and that 'Prefix' is what this function uses to determine what class to + * instantiate. + * + * $param IslandoraFedoraObject $object + * The object to load datastreams from. + * $param array $datastreams + * An array of arrays that pair DSIDs, DatastreamValidator class prefixes, + * and optional params. You can check some of the existing implementations + * for examples. + */ + public function validateDatastreams($object, array $datastreams) { + + if (!is_object($object)) { + $this->addResult(FALSE, "Datastream validation failed; Object passed in is invalid.", 'Islandora'); + return; + } + + module_load_include('inc', 'islandora', 'tests/includes/datastream_validators'); + + foreach ($datastreams as $datastream) { + // Let's give them conventional names. + $dsid = $datastream[0]; + $prefix = $datastream[1]; + $params = array(); + if (isset($datastream[2])) { + $params = $datastream[2]; + } + + // Legacy tests were created before the CamelCase conventions of the class + // system now in place. So, we need to automagically seek out prefixes + // that start with a lower-case letter and convert them to the proper + // format (rather than fixing every single legacy test). + if (ctype_lower(substr($prefix, 0, 1))) { + // Handle the case where the prefix is "image". + if ($prefix === 'image') { + $prefix = 'Image'; + } + // Handle the case where the prefix is "text". + elseif ($prefix === 'text') { + $prefix = 'Text'; + } + // All other cases involve just converting everything to caps. + else { + $prefix = strtoupper($prefix); + } + } + + // Instantiate the appropriate class, and grab the results. + $class_name = "{$prefix}DatastreamValidator"; + if (class_exists($class_name)) { + $validator = new $class_name($object, $dsid, $params); + foreach ($validator->getResults() as $result) { + $this->addResult($result->getType(), $result->getMessage()); + } + } + else { + $this->addResult(FALSE, "No DatastreamValidator class was found with the name '$class_name'; are you sure the prefix given to IslandoraWebTestCase->validateDatastreams() was entered correctly, or that such a validator exists?", 'Islandora'); + } + } + } + + /** + * Constructs and ingests a Fedora object and datastream(s) via tuque. + * + * All keys inside the parameter arrays for this function are optional. it + * can be run simply by calling the method with no arguments. + * + * If your test case supports logged in Drupal users, IslandoraTestUtilities + * can be instantiated with $params['logged_in_user'] as that user object, and + * this method will set the owner of the ingested object as that user by + * default. + * + * @param array $properties + * An array containing object information using these keys: + * '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, or a + * string containing a single content model PID. + * '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: + * 'dsid' - the datastream ID; randomized if not set. + * 'path' - The path to the file to use; defaults to fixtures/test.jpg. + * 'control_group' - The single-letter control group identifier. + * 'mimetype' - The datastream's mimetype. + * + * @return bool|array + * FALSE if the object ingest failed, or the object array if successful. + */ + public function ingestConstructedObject(array $properties = array(), array $datastreams = array()) { + if (!isset($properties['pid'])) { + $properties['pid'] = "islandora"; + } + $object = $this->repository->constructObject($properties['pid']); + + // Set the object properties before ingesting it. + if (isset($properties['label'])) { + $object->label = $properties['label']; + } + else { + $properties['label'] = DrupalUnitTestCase::randomName(); + $object->label = $properties['label']; + } + + if (isset($properties['owner'])) { + $object->owner = $properties['owner']; + } + elseif (isset($this->params['logged_in_user'])) { + $object->owner = $this->params['logged_in_user']->name; + } + + if (isset($properties['models'])) { + try { + $object->models = (array) $properties['models']; + } + catch (Exception $e) { + $this->addResult(FALSE, "Encountered an exception when trying to add content models to {$object->id}: $e"); + return FALSE; + } + } + + // Chuck in some datastreams. + if (!empty($datastreams)) { + foreach ($datastreams as $datastream) { + if (!isset($datastream['dsid'])) { + $datastream['dsid'] = DrupalUnitTestCase::randomName(); + } + if (!isset($datastream['path'])) { + $datastream['path'] = drupal_get_path('module', 'islandora') . '/tests/fixtures/test.jpg'; + } + if (!isset($datastream['control_group'])) { + $new_datastream = $object->constructDatastream($datastream['dsid']); + } + else { + $new_datastream = $object->constructDatastream($datastream['dsid'], $datastream['control_group']); + } + $new_datastream->label = $datastream['dsid']; + if (isset($datastream['mimetype'])) { + $new_datastream->mimetype = $datastream['mimetype']; + } + $new_datastream->setContentFromFile($datastream['path']); + $object->ingestDatastream($new_datastream); + } + } + + $this->repository->ingestObject($object); + if (!$object) { + $this->addResult(FALSE, t("Failed to ingest object."), 'Islandora'); + return FALSE; + } + else { + $this->addResult(TRUE, t("Ingested object %object", array('%object' => $object->id)), 'Islandora'); + } + + // Add a parent relationship, if necessary. + if (isset($properties['parent'])) { + $object->relationships->add(FEDORA_RELS_EXT_URI, 'isMemberOfCollection', $properties['parent']); + } + + return $object; + } + + /** + * Deletes all objects created by the given user. + * + * To safeguard against leaving test objects in the repository, this is called + * each time DrupalTestCase::run() calls tearDown(). This feature can be + * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. + * + * @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->addResult(FALSE, "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->repository->ri->sparqlQuery($query); + foreach ($objects as $object) { + $loaded_object = islandora_object_load($object['object']['value']); + $this->repository->api->m->purgeObject($loaded_object->id); + if (islandora_object_load($object['object']['value'])) { + $this->addResult(TRUE, "Object {$object['object']['value']} successfully removed from repository."); + return TRUE; + } + $this->addResult(FALSE, "Unable to remove object {$object['object']['value']} from the repository."); + return FALSE; + } + } + +} From 83960d6f02e6696dae238982fdee2a9eb7ca1043 Mon Sep 17 00:00:00 2001 From: qadan Date: Mon, 26 May 2014 15:22:49 -0300 Subject: [PATCH 06/93] better error handling --- tests/includes/datastream_validators.inc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/includes/datastream_validators.inc b/tests/includes/datastream_validators.inc index c0241c84..49c090e5 100644 --- a/tests/includes/datastream_validators.inc +++ b/tests/includes/datastream_validators.inc @@ -128,12 +128,17 @@ abstract class DatastreamValidator extends IslandoraTestUtilityClass { * fails to $this->passes and/or $this->fails. */ public function runValidators() { - $methods = get_class_methods($this); - foreach ($methods as $method) { - if (substr($method, 0, 6) === 'assert') { - $this->$method(); + if ($this->object[$this->datastream]) { + $methods = get_class_methods($this); + foreach ($methods as $method) { + if (substr($method, 0, 6) === 'assert') { + $this->$method(); + } } } + else { + $this->addResult(FALSE, "Unable to load the requested datastream {$this->datastream} from object {$this->object->id}."); + } } /** From a13fa13451cc9c9e5da4d9e8690dd0f2f6cd7b48 Mon Sep 17 00:00:00 2001 From: qadan Date: Tue, 27 May 2014 11:06:03 -0300 Subject: [PATCH 07/93] accept objects with/without wrapper --- tests/includes/datastream_validators.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/includes/datastream_validators.inc b/tests/includes/datastream_validators.inc index 49c090e5..d96166a3 100644 --- a/tests/includes/datastream_validators.inc +++ b/tests/includes/datastream_validators.inc @@ -69,7 +69,7 @@ abstract class DatastreamValidator extends IslandoraTestUtilityClass { /** * The IslandoraFedoraObject containing the datastream to test. * - * @var IslandoraFedoraObject + * @var IslandoraFedoraObject|FedoraObject */ public $object; @@ -97,14 +97,14 @@ abstract class DatastreamValidator extends IslandoraTestUtilityClass { /** * Constructs a DatastreamValidator. * - * @param IslandoraFedoraObject $object + * @param IslandoraFedoraObject|FedoraObject $object * The object to grab the datastream from. * @param string $datastream * The DSID of the datastream itself. * @param array $params * An extra array of parameters the validator might need. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { $this->object = $object; $this->datastream = $datastream; $this->params = $params; From 8b11d4a05129ca8424843d81369d78e911fb7368 Mon Sep 17 00:00:00 2001 From: qadan Date: Tue, 27 May 2014 11:20:54 -0300 Subject: [PATCH 08/93] erm, forgot one --- tests/includes/datastream_validators.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/includes/datastream_validators.inc b/tests/includes/datastream_validators.inc index d96166a3..445205ab 100644 --- a/tests/includes/datastream_validators.inc +++ b/tests/includes/datastream_validators.inc @@ -343,7 +343,7 @@ class TextDatastreamValidator extends DatastreamValidator { /** * Constructor override; blow up if we don't have our two values. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { if (count($params) < 2) { throw new InvalidArgumentException('$params must contain at least two values to instantiate a TextDatastreamValidator.'); } From fc357e6f7df95c0a0dc2d85d0d68bc06fb352a88 Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 29 May 2014 12:32:12 +0000 Subject: [PATCH 09/93] last bit of bugfixes and cleanup --- includes/derivatives.inc | 36 +++++++++++--------- tests/includes/datastream_validators.inc | 16 ++++----- tests/includes/utilities.inc | 42 ++++++++++++++++-------- 3 files changed, 57 insertions(+), 37 deletions(-) diff --git a/includes/derivatives.inc b/includes/derivatives.inc index 47ec4fad..8a36512e 100644 --- a/includes/derivatives.inc +++ b/includes/derivatives.inc @@ -21,25 +21,29 @@ function islandora_run_derivatives(AbstractObject $object, $dsid) { islandora_derivative_logging($logging_results); } else { - batch_set( - // Title won't show for batch in a batch. + $operations = islandora_do_batch_derivatives( + $object, array( - 'init_message' => t('Preparing derivatives for @label', array('@label' => $object->label)), - 'error_message' => t('An error occured creating derivatives.'), - 'progress_message' => t( - 'Creating derivatives for @label
Time elapsed: @elapsed
- Estimated time remaining @estimate.', - array('@label' => $object->label) - ), - 'file' => drupal_get_path('module', 'islandora') . '/includes/regenerate_derivatives.form.inc', - 'operations' => islandora_do_batch_derivatives( - $object, - array( - 'source_dsid' => $dsid, - ) - ), + 'source_dsid' => $dsid, ) ); + if ($operations) { + batch_set( + // Title won't show for batch in a batch. + array( + 'init_message' => t('Preparing derivatives for @label', array('@label' => $object->label)), + 'error_message' => t('An error occured creating derivatives.'), + 'progress_message' => t( + 'Creating derivatives for @label
Time elapsed: @elapsed
+ Estimated time remaining @estimate.', + array('@label' => $object->label) + ), + 'file' => drupal_get_path('module', 'islandora') . '/includes/regenerate_derivatives.form.inc', + 'operations' => $operations, + 'finished' => 'islandora_regenerate_derivative_batch_finished', + ) + ); + } } } diff --git a/tests/includes/datastream_validators.inc b/tests/includes/datastream_validators.inc index 49c090e5..4768e989 100644 --- a/tests/includes/datastream_validators.inc +++ b/tests/includes/datastream_validators.inc @@ -48,7 +48,7 @@ function islandora_hex2int($hex) { * Abstraction for datastream validators. * * Classes extended from DatastreamValidator don't require much to be useful. - * They accept an IslandoraFedoraObject and a DSID to perform assertions on; + * They accept a Fedora object and a DSID to perform assertions on; * all you have to do is place a series of functions inside the extended class * using the naming convention assertThing(); each of these functions should * ideally assert one thing and one thing only (for simplicity's sake), and @@ -67,9 +67,9 @@ function islandora_hex2int($hex) { abstract class DatastreamValidator extends IslandoraTestUtilityClass { /** - * The IslandoraFedoraObject containing the datastream to test. + * The Fedora object containing the datastream to test. * - * @var IslandoraFedoraObject + * @var IslandoraFedoraObject|FedoraObject */ public $object; @@ -97,14 +97,14 @@ abstract class DatastreamValidator extends IslandoraTestUtilityClass { /** * Constructs a DatastreamValidator. * - * @param IslandoraFedoraObject $object + * @param IslandoraFedoraObject|FedoraObject $object * The object to grab the datastream from. * @param string $datastream * The DSID of the datastream itself. * @param array $params * An extra array of parameters the validator might need. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { $this->object = $object; $this->datastream = $datastream; $this->params = $params; @@ -343,7 +343,7 @@ class TextDatastreamValidator extends DatastreamValidator { /** * Constructor override; blow up if we don't have our two values. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { if (count($params) < 2) { throw new InvalidArgumentException('$params must contain at least two values to instantiate a TextDatastreamValidator.'); } @@ -386,14 +386,14 @@ class WAVDatastreamValidator extends DatastreamValidator { /** * We need a special constructor here to get the hex datastream content. * - * @param IslandoraFedoraObject $object + * @param IslandoraFedoraObject|FedoraObject $object * The object to grab the datastream from. * @param string $datastream * The DSID of the datastream itself. * @param array $params * An extra array of parameters the validator might need. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { parent::__construct($object, $datastream, $params); $this->datastreamContent = bin2hex($this->datastreamContent); } diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc index e0672fd8..e0682397 100644 --- a/tests/includes/utilities.inc +++ b/tests/includes/utilities.inc @@ -261,17 +261,9 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { public function __construct($configuration, array $params = array()) { $this->configuration = $configuration; $this->params = $params; - // If we have DB access, we want to use the Islandora Tuque wrappers. - if ($params['db_access']) { - $connection = islandora_get_tuque_connection(); - $this->repository = $connection->repository; - } - // Otherwise, get a generic Tuque repository. - else { - $connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); - $api = new FedoraApi($connection); - $this->repository = new FedoraRepository($api, new SimpleCache()); - } + $connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); + $api = new FedoraApi($connection); + $this->repository = new FedoraRepository($api, new SimpleCache()); } /** @@ -317,9 +309,9 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { * Asserts that the given datastreams exist correctly on the object. * * @param AbstractObject $object - * The PID of the object + * The object to check. * @param array $datastreams - * An array of strings containing datastream names + * An array of strings containing datastream names. * * @return bool * TRUE on success, FALSE on fail. @@ -341,6 +333,30 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { } } + /** + * Asserts that the given datastreams do not exist on the object. + * + * @param AbstractObject $object + * The object to check. + * @param array $datastreams + * An array of datastreams to confirm not present. + */ + public function assertNoDatastreams($object, array $datastreams) { + if (!is_object($object)) { + $this->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); + return; + } + $found_datastreams = array_intersect_key(array_flip($datastreams), $object->repository->api->a->listDatastreams($object->id)); + + if (!empty($found_datastreams)) { + $this->addResult(FALSE, "Found unwanted datastream(s)" . implode(', ', array_flip($found_datastreams)) . " in object {$object->id}."); + return FALSE; + } + + $this->addResult(TRUE, "Unwanted datastream(s) not found in object {$object->id}"); + return TRUE; + } + /** * Attempts to validate an array of datastreams, generally via binary checks. * From 02de114401a58e211366c053dbeaf7c5d437a06f Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 29 May 2014 16:45:16 +0000 Subject: [PATCH 10/93] better, simpler drupal filter handling --- islandora.module | 17 ++- tests/includes/islandora_unit_test_case.inc | 22 +--- tests/includes/islandora_web_test_case.inc | 14 +-- tests/includes/utilities.inc | 110 +++++--------------- 4 files changed, 37 insertions(+), 126 deletions(-) diff --git a/islandora.module b/islandora.module index ff077212..3f3d777b 100644 --- a/islandora.module +++ b/islandora.module @@ -1813,18 +1813,13 @@ function islandora_form_simpletest_test_form_alter(array &$form) { * Submit handler for islandora_form_simpletest_test_form_alter(). */ function islandora_repair_drupal_filter() { - - // Grab the config. - $path = drupal_get_path('module', 'islandora'); - if (file_exists("$path/tests/test_config.ini")) { - $configuration = parse_ini_file("$path/tests/test_config.ini"); - } - elseif (file_exists("$path/tests/default.test_config.ini")) { - $configuration = parse_ini_file("$path/tests/default.test_config.ini"); + module_load_include('inc', 'islandora', 'tests/includes/utilities'); + try { + $configuration = islandora_get_test_configuration(); } - else { - drupal_set_message(t('Required default.test_config.ini/test_config.ini file not found'), 'error'); - return FALSE; + catch (Exception $e) { + drupal_set_message("Unable to get the test configuration: $e", 'error'); + return; } // Xpath to the filter 'sql' elements. diff --git a/tests/includes/islandora_unit_test_case.inc b/tests/includes/islandora_unit_test_case.inc index ce9216d8..4cdf30bd 100644 --- a/tests/includes/islandora_unit_test_case.inc +++ b/tests/includes/islandora_unit_test_case.inc @@ -13,13 +13,6 @@ class IslandoraUnitTestCase extends DrupalUnitTestCase { */ protected $deleteObjectsOnTeardown = TRUE; - /** - * An instance of IslandoraDrupalFilterManipulator carried between tests. - * - * @var IslandoraDrupalFilterManipulator - */ - protected $filterManipulator; - /** * Defers to IslandoraTestUtilities for missing methods. * @@ -64,32 +57,19 @@ class IslandoraUnitTestCase extends DrupalUnitTestCase { // It's possible test are running before class autoloading. module_load_include('inc', 'islandora', 'includes/tuque'); module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); - module_load_include('inc', 'islandora', 'tests/includes/utilities'); $this->configuration = islandora_get_test_configuration(); - if ($this->configuration['use_drupal_filter']) { - $this->filterManipulator = new IslandoraDrupalFilterManipulator($this->configuration); - $this->filterManipulator->setUpDrupalFilter(); - } - $this->connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); $api = new FedoraApi($this->connection); $this->repository = new FedoraRepository($api, new SimpleCache()); } /** - * Restores the original Drupal filter, frees any allocated resources. - * - * To safeguard against leaving test objects in the repository, tearDown() - * calls deleteUserCreatedObjects() every time by default. This feature can be - * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. + * Frees any allocated resources. * * @see DrupalWebTestCase::tearDown() */ public function tearDown() { - if ($this->configuration['use_drupal_filter']) { - $this->filterManipulator->restoreDrupalFilter(); - } unset($this->configuration); parent::tearDown(); } diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc index 383e2b02..211f0df0 100644 --- a/tests/includes/islandora_web_test_case.inc +++ b/tests/includes/islandora_web_test_case.inc @@ -21,13 +21,6 @@ class IslandoraWebTestCase extends DrupalWebTestCase { */ protected $deleteObjectsOnTeardown = TRUE; - /** - * An instance of IslandoraDrupalFilterManipulator carried between tests. - * - * @var IslandoraDrupalFilterManipulator - */ - protected $filterManipulator; - /** * Defers to IslandoraTestUtilities for missing methods. * @@ -65,7 +58,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { } /** - * Sets up the Drupal filter to access this test Drupal instances database. + * Sets up the web test case. * * @see DrupalWebTestCase::setUp() */ @@ -83,8 +76,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { $this->configuration = islandora_get_test_configuration(); if ($this->configuration['use_drupal_filter']) { - $this->filterManipulator = new IslandoraDrupalFilterManipulator($this->configuration); - $this->filterManipulator->setUpDrupalFilter(); + $this->setUpDrupalFilter(); } $this->admin = $this->createAdminUser(); } @@ -147,7 +139,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { } } if ($this->configuration['use_drupal_filter']) { - $this->filterManipulator->restoreDrupalFilter(); + islandora_repair_drupal_filter(); } unset($this->admin); unset($this->configuration); diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc index e0682397..28179c05 100644 --- a/tests/includes/utilities.inc +++ b/tests/includes/utilities.inc @@ -35,89 +35,6 @@ function islandora_get_test_configuration() { throw new Exception('Required default.test_config.ini/test_config.ini file not found'); } -/** - * Methods specifically for working with the Drupal filter for tests. - */ -class IslandoraDrupalFilterManipulator { - - /** - * Parsed test configuration. - * - * @var array - */ - public $configuration; - - /** - * The original contents of the drupal filter. - * - * @var string - */ - protected $originalDrupalFilterContent; - - /** - * Constructs an IslandoraTestUtilities object. - * - * @param array $configuration - * The parsed test configuration. - */ - public function __construct($configuration) { - $this->configuration = $configuration; - $this->backUpDrupalFilter(); - } - - /** - * Stores the content of the Drupal Filter for later restoration. - */ - public function restoreDrupalFilter() { - $file = $this->configuration['drupal_filter_file']; - if (isset($this->originalDrupalFilterContent)) { - file_put_contents($file, $this->originalDrupalFilterContent); - } - elseif (file_exists($file)) { - // Remove if there was never an original. - drupal_unlink($file); - } - } - - /** - * Sets up a drupal filter that can read for the tests users table. - */ - public function setUpDrupalFilter() { - $connection_info = Database::getConnectionInfo('default'); - $drupal_filter_dom = new DomDocument(); - $drupal_filter_dom->loadXML($this->originalDrupalFilterContent); - $server = $connection_info['default']['host']; - $dbname = $connection_info['default']['database']; - $user = $connection_info['default']['username']; - $password = $connection_info['default']['password']; - $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; - $prefix = $connection_info['default']['prefix']['default']; - $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0); - $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0); - $connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node); - $connection_node->setAttributeNode(new DOMAttr('server', $server)); - $connection_node->setAttributeNode(new DOMAttr('dbname', $dbname)); - $connection_node->setAttributeNode(new DOMAttr('user', $user)); - $connection_node->setAttributeNode(new DOMAttr('password', $password)); - $connection_node->setAttributeNode(new DOMAttr('port', $port)); - $sql_node = $connection_node->appendChild(new DOMElement('sql')); - $sql_node->appendChild($drupal_filter_dom->createTextNode("SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;")); - file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); - } - - /** - * Stores the content of the Drupal Filter for later restoration. - */ - protected function backUpDrupalFilter() { - if (file_exists($this->configuration['drupal_filter_file'])) { - $this->originalDrupalFilterContent = file_get_contents($this->configuration['drupal_filter_file']); - } - else { - throw new Exception('Failed to find the required Drupal Filter configuration file.'); - } - } -} - /** * A result from a utility method; $type defines TRUE/FALSE as pass/fail. */ @@ -266,6 +183,33 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { $this->repository = new FedoraRepository($api, new SimpleCache()); } + /** + * Sets up a drupal filter that can read for the tests users table. + */ + public function setUpDrupalFilter() { + $original_drupal_filter_content = file_get_contents($this->configuration['drupal_filter_file']); + $connection_info = Database::getConnectionInfo('default'); + $drupal_filter_dom = new DomDocument(); + $drupal_filter_dom->loadXML($original_drupal_filter_content); + $server = $connection_info['default']['host']; + $dbname = $connection_info['default']['database']; + $user = $connection_info['default']['username']; + $password = $connection_info['default']['password']; + $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; + $prefix = $connection_info['default']['prefix']['default']; + $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0); + $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0); + $connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node); + $connection_node->setAttributeNode(new DOMAttr('server', $server)); + $connection_node->setAttributeNode(new DOMAttr('dbname', $dbname)); + $connection_node->setAttributeNode(new DOMAttr('user', $user)); + $connection_node->setAttributeNode(new DOMAttr('password', $password)); + $connection_node->setAttributeNode(new DOMAttr('port', $port)); + $sql_node = $connection_node->appendChild(new DOMElement('sql')); + $sql_node->appendChild($drupal_filter_dom->createTextNode("SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;")); + file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); + } + /** * Returns an array of IslandoraTestUtilityResults. * From 723a8f54d7db69ef3e4aac60f9df9bd46f75803a Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 29 May 2014 17:21:23 +0000 Subject: [PATCH 11/93] not using t() like some sorta madman --- islandora.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.module b/islandora.module index 3f3d777b..dbca8844 100644 --- a/islandora.module +++ b/islandora.module @@ -1818,7 +1818,7 @@ function islandora_repair_drupal_filter() { $configuration = islandora_get_test_configuration(); } catch (Exception $e) { - drupal_set_message("Unable to get the test configuration: $e", 'error'); + drupal_set_message(t("Unable to get the test configuration: %e", array('%e' => $e)), 'error'); return; } From 16d24a3f9c6ee615df609934b0cf68bd8e624e26 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 4 Jun 2014 15:32:01 -0300 Subject: [PATCH 12/93] Operator precedence allowed for strings that were not 4 or 8 characters long. --- tests/includes/datastream_validators.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/includes/datastream_validators.inc b/tests/includes/datastream_validators.inc index 4768e989..b8ff785e 100644 --- a/tests/includes/datastream_validators.inc +++ b/tests/includes/datastream_validators.inc @@ -28,7 +28,7 @@ function islandora_hex2int($hex) { drupal_set_message(t('String passed to islandora_hex2int() contains non-hexidecimal characters.'), 'error'); return FALSE; } - if (!strlen($hex) === 4 || !strlen($hex) === 8) { + if (!(strlen($hex) == 4 || strlen($hex) == 8)) { drupal_set_message(t('String passed to islandora_hex2int() cannot create a 16- or 32-bit little-endian signed integer'), 'error'); return FALSE; } From 1c0fa96099467e1cd157e25f0c61fd1b85b8d5eb Mon Sep 17 00:00:00 2001 From: qadan Date: Fri, 6 Jun 2014 17:37:09 +0000 Subject: [PATCH 13/93] moving around some stuffs --- islandora.info | 1 + islandora.module | 10 +- tests/includes/islandora_unit_test_case.inc | 2 +- tests/includes/islandora_web_test_case.inc | 2 +- tests/includes/test_utility_abstraction.inc | 153 ++++++++++++++++++++ tests/includes/utilities.inc | 144 ------------------ tests/scripts/travis_setup.sh | 1 + 7 files changed, 163 insertions(+), 150 deletions(-) create mode 100644 tests/includes/test_utility_abstraction.inc diff --git a/islandora.info b/islandora.info index e19f4937..55c50359 100644 --- a/islandora.info +++ b/islandora.info @@ -17,6 +17,7 @@ files[] = tests/includes/datastream_validators.inc files[] = tests/includes/islandora_web_test_case.inc files[] = tests/includes/islandora_unit_test_case.inc files[] = tests/includes/utilities.inc +files[] = tests/includes/test_utility_abstraction.inc files[] = tests/authtokens.test files[] = tests/hooks.test files[] = tests/ingest.test diff --git a/islandora.module b/islandora.module index dbca8844..aeb2c059 100644 --- a/islandora.module +++ b/islandora.module @@ -1813,13 +1813,15 @@ function islandora_form_simpletest_test_form_alter(array &$form) { * Submit handler for islandora_form_simpletest_test_form_alter(). */ function islandora_repair_drupal_filter() { - module_load_include('inc', 'islandora', 'tests/includes/utilities'); + + // Grab the config. + module_load_include('inc', 'islandora', 'tests/utilities/test_utility_abstraction'); try { - $configuration = islandora_get_test_configuration(); + IslandoraTestUtilityClass::getTestConfiguration(); } catch (Exception $e) { - drupal_set_message(t("Unable to get the test configuration: %e", array('%e' => $e)), 'error'); - return; + drupal_set_message(t("Error parsing test configuration: $e"), 'error'); + return FALSE; } // Xpath to the filter 'sql' elements. diff --git a/tests/includes/islandora_unit_test_case.inc b/tests/includes/islandora_unit_test_case.inc index 4cdf30bd..baffc62e 100644 --- a/tests/includes/islandora_unit_test_case.inc +++ b/tests/includes/islandora_unit_test_case.inc @@ -58,7 +58,7 @@ class IslandoraUnitTestCase extends DrupalUnitTestCase { module_load_include('inc', 'islandora', 'includes/tuque'); module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); - $this->configuration = islandora_get_test_configuration(); + $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); $this->connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); $api = new FedoraApi($this->connection); $this->repository = new FedoraRepository($api, new SimpleCache()); diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc index 211f0df0..56645f83 100644 --- a/tests/includes/islandora_web_test_case.inc +++ b/tests/includes/islandora_web_test_case.inc @@ -74,7 +74,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); module_load_include('inc', 'islandora', 'tests/includes/utilities'); - $this->configuration = islandora_get_test_configuration(); + $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); if ($this->configuration['use_drupal_filter']) { $this->setUpDrupalFilter(); } diff --git a/tests/includes/test_utility_abstraction.inc b/tests/includes/test_utility_abstraction.inc new file mode 100644 index 00000000..aa09004b --- /dev/null +++ b/tests/includes/test_utility_abstraction.inc @@ -0,0 +1,153 @@ +message = $message; + $this->caller = $caller; + $this->type = $type; + } + + /** + * Get the message for this result. + * + * @return string + * The message for this result. + */ + public function getMessage() { + return $this->message; + } + + /** + * Get the caller for this result. + * + * @return string + * The caller for this result. + */ + public function getCaller() { + return $this->caller; + } + + /** + * Get the type of result. + * + * @return bool + * The type of pass (TRUE for pass, FALSE for fail). + */ + public function getType() { + return $this->type; + } +} + +/** + * Abstraction for test utility classes. + * + * This is to be implemented in any class that wants to have test utility + * functionality (i.e. that wants to pass back results to tests). Check out the + * datastream validator class for a good example of how this is implemented. + * + * Test utility classes should store per-installation configuration options in + * a test_config.ini file, contained in the islandora/tests folder. A function + * is included with the abstraction to parse the configuration file. + */ +abstract class IslandoraTestUtilityClass { + + /** + * An array of IslandoraTestUtilityResults. + * + * These should be generated using $this->addResult. + * + * @var IslandoraTestUtilityResult[] + */ + public $results = array(); + + /** + * Parses and returns the settings from the test configuration file. + * + * If no install specific test_config.ini file is found, it will use the + * assumed default configs found in default.test_config.ini. + * + * @return array + * The test configuration. + * + * @see parse_ini_file() + */ + public static function getTestConfiguration() { + $path = drupal_get_path('module', 'islandora'); + if (file_exists("$path/tests/test_config.ini")) { + return parse_ini_file("$path/tests/test_config.ini"); + } + elseif (file_exists("$path/tests/default.test_config.ini")) { + return parse_ini_file("$path/tests/default.test_config.ini"); + } + throw new Exception('Required default.test_config.ini/test_config.ini file not found'); + } + + /** + * Returns an array of IslandoraTestUtilityResults. + * + * The particular testing implementation you are using should use this to + * parse results from utilities and pass them through. + * + * @return IslandoraTestUtilityResult[] + * The results. + */ + abstract public function getResults(); + + /** + * Adds a result to $this->results. + * + * @param bool $type + * The type of result (TRUE for pass, FALSE for fail). + * @param string $message + * The message to put in the result. + */ + abstract public function addResult($type, $message); + + /** + * Gets the caller of the method that passed a result. + * + * @return array + * Array representing the true caller. + */ + abstract public function getAssertionCall(); + +} diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc index 28179c05..81758880 100644 --- a/tests/includes/utilities.inc +++ b/tests/includes/utilities.inc @@ -13,150 +13,6 @@ * breaking existing implementations. */ -/** - * Parses and returns the settings from the test configuration file. - * - * If no install specific test_config.ini file is found, it will use the - * assumed default configs found in default.test_config.ini. - * - * @return array - * The test configuration. - * - * @see parse_ini_file() - */ -function islandora_get_test_configuration() { - $path = drupal_get_path('module', 'islandora'); - if (file_exists("$path/tests/test_config.ini")) { - return parse_ini_file("$path/tests/test_config.ini"); - } - elseif (file_exists("$path/tests/default.test_config.ini")) { - return parse_ini_file("$path/tests/default.test_config.ini"); - } - throw new Exception('Required default.test_config.ini/test_config.ini file not found'); -} - -/** - * A result from a utility method; $type defines TRUE/FALSE as pass/fail. - */ -class IslandoraTestUtilityResult { - - /** - * The message for this result. - * - * @var string - */ - protected $message; - - /** - * The caller for this result. - * - * @var array - */ - protected $caller; - - /** - * The type of result this is - TRUE for pass, FALSE for fail. - * - * @var bool - */ - protected $type; - - /** - * Constructs an IslandoraTestUtilityResult. - * - * @param bool $type - * Whether this result should indicate a pass (TRUE) or fail (FALSE). - * @param string $message - * The message that will be used by this result. - * @param array $caller - * The caller for this result. - */ - public function __construct($type, $message, array $caller) { - $this->message = $message; - $this->caller = $caller; - $this->type = $type; - } - - /** - * Get the message for this result. - * - * @return string - * The message for this result. - */ - public function getMessage() { - return $this->message; - } - - /** - * Get the caller for this result. - * - * @return string - * The caller for this result. - */ - public function getCaller() { - return $this->caller; - } - - /** - * Get the type of result. - * - * @return bool - * The type of pass (TRUE for pass, FALSE for fail). - */ - public function getType() { - return $this->type; - } -} - -/** - * Abstraction for test utility classes. - * - * This is to be implemented in any class that wants to have test utility - * functionality (i.e. that wants to pass back results to tests). Check out the - * datastream validator class for a good example of how this is implemented. - */ -abstract class IslandoraTestUtilityClass { - - /** - * An array of IslandoraTestUtilityResults. - * - * These should be generated using $this->addResult. - * - * @var IslandoraTestUtilityResult[] - */ - public $results = array(); - - /** - * Returns an array of IslandoraTestUtilityResults. - * - * The particular testing implementation you are using should use this to - * parse results from utilities and pass them through. - * - * @return IslandoraTestUtilityResult[] - * The results. - */ - abstract public function getResults(); - - /** - * Adds a result to $this->results. - * - * @param bool $type - * The type of result (TRUE for pass, FALSE for fail). - * @param string $message - * The message to put in the result. - */ - abstract public function addResult($type, $message); - - /** - * Gets the caller of the method that passed a result. - * - * @return array - * Array representing the true caller. - */ - abstract public function getAssertionCall(); - -} - class IslandoraTestUtilities extends IslandoraTestUtilityClass { protected $configuration; diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 66061c72..56c651ab 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -8,6 +8,7 @@ cd $HOME git clone git://github.com/Islandora/tuque.git git clone -b $FEDORA_VERSION git://github.com/Islandora/islandora_tomcat.git cd islandora_tomcat +git fsck --full --verbose export CATALINA_HOME='.' export JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=512m -XX:+CMSClassUnloadingEnabled -Djavax.net.ssl.trustStore=$CATALINA_HOME/fedora/server/truststore -Djavax.net.ssl.trustStorePassword=tomcat" ./bin/startup.sh From 835d16739759a88aa56e99e3bd20091bf656941f Mon Sep 17 00:00:00 2001 From: qadan Date: Fri, 6 Jun 2014 18:02:22 +0000 Subject: [PATCH 14/93] let's not go nuts --- tests/scripts/travis_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 56c651ab..336087ed 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -8,7 +8,7 @@ cd $HOME git clone git://github.com/Islandora/tuque.git git clone -b $FEDORA_VERSION git://github.com/Islandora/islandora_tomcat.git cd islandora_tomcat -git fsck --full --verbose +git fsck export CATALINA_HOME='.' export JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=512m -XX:+CMSClassUnloadingEnabled -Djavax.net.ssl.trustStore=$CATALINA_HOME/fedora/server/truststore -Djavax.net.ssl.trustStorePassword=tomcat" ./bin/startup.sh From 1c5e2a1893615f9cd0bb680288d0930aac8d62d5 Mon Sep 17 00:00:00 2001 From: qadan Date: Fri, 6 Jun 2014 18:19:26 +0000 Subject: [PATCH 15/93] i did an incredibly stupid thing --- islandora.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.module b/islandora.module index aeb2c059..635708bc 100644 --- a/islandora.module +++ b/islandora.module @@ -1817,7 +1817,7 @@ function islandora_repair_drupal_filter() { // Grab the config. module_load_include('inc', 'islandora', 'tests/utilities/test_utility_abstraction'); try { - IslandoraTestUtilityClass::getTestConfiguration(); + $configuration = IslandoraTestUtilityClass::getTestConfiguration(); } catch (Exception $e) { drupal_set_message(t("Error parsing test configuration: $e"), 'error'); From 3b797253add6c7f965d56e9a47600eb15af5604a Mon Sep 17 00:00:00 2001 From: qadan Date: Fri, 6 Jun 2014 18:47:18 +0000 Subject: [PATCH 16/93] the stupidity continues --- islandora.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.module b/islandora.module index 635708bc..fd40790b 100644 --- a/islandora.module +++ b/islandora.module @@ -1820,7 +1820,7 @@ function islandora_repair_drupal_filter() { $configuration = IslandoraTestUtilityClass::getTestConfiguration(); } catch (Exception $e) { - drupal_set_message(t("Error parsing test configuration: $e"), 'error'); + drupal_set_message(t("Error parsing test configuration: %e", array('%e' => $e)), 'error'); return FALSE; } From f0a49fd44e6b003a3f7ad23b34a007965a39bbe0 Mon Sep 17 00:00:00 2001 From: qadan Date: Wed, 16 Jul 2014 12:43:29 -0300 Subject: [PATCH 17/93] code review changes --- tests/includes/islandora_unit_test_case.inc | 1 - tests/includes/islandora_web_test_case.inc | 8 +-- tests/includes/utilities.inc | 68 ++++++++------------- 3 files changed, 30 insertions(+), 47 deletions(-) diff --git a/tests/includes/islandora_unit_test_case.inc b/tests/includes/islandora_unit_test_case.inc index baffc62e..da0a8a71 100644 --- a/tests/includes/islandora_unit_test_case.inc +++ b/tests/includes/islandora_unit_test_case.inc @@ -56,7 +56,6 @@ class IslandoraUnitTestCase extends DrupalUnitTestCase { // It's possible test are running before class autoloading. module_load_include('inc', 'islandora', 'includes/tuque'); - module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); $this->connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc index 56645f83..55ee5824 100644 --- a/tests/includes/islandora_web_test_case.inc +++ b/tests/includes/islandora_web_test_case.inc @@ -222,7 +222,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { if (!$message) { $message = "Error found on current page when error was expected."; } - return $this->assertRaw("
", $message, $group); + return $this->assertFieldByXPath('//div[contains(@class, "message") and contains(@class, "error")]', NULL, $message, $group); } /** @@ -240,7 +240,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { if (!$message) { $message = "No error found on current page when no error was expected."; } - return $this->assertNoRaw("
", $message, $group); + return $this->assertNoFieldByXPath('//div[contains(@class, "message") and contains(@class, "error")]', NULL, $message, $group); } /** @@ -258,7 +258,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { if (!$message) { $message = "Warning found on current page when warning was expected."; } - return $this->assertRaw("
", $message, $group); + return $this->assertFieldByXPath('//div[contains(@class, "message") and contains(@class, "warning")]', NULL, $message, $group); } /** @@ -276,7 +276,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { if (!$message) { $message = "No warning found on current page when no warning was expected."; } - return $this->assertNoRaw("
", $message, $group); + return $this->assertNoFieldByXPath('//div[contains(@class, "message") and contains(@class, "warning")]', NULL, $message, $group); } /** diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc index 81758880..719e76ca 100644 --- a/tests/includes/utilities.inc +++ b/tests/includes/utilities.inc @@ -40,29 +40,24 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { } /** - * Sets up a drupal filter that can read for the tests users table. + * Sets up a drupal filter that can read from the tests users table. */ public function setUpDrupalFilter() { $original_drupal_filter_content = file_get_contents($this->configuration['drupal_filter_file']); $connection_info = Database::getConnectionInfo('default'); $drupal_filter_dom = new DomDocument(); $drupal_filter_dom->loadXML($original_drupal_filter_content); - $server = $connection_info['default']['host']; - $dbname = $connection_info['default']['database']; - $user = $connection_info['default']['username']; - $password = $connection_info['default']['password']; - $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; $prefix = $connection_info['default']['prefix']['default']; $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0); $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0); $connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node); - $connection_node->setAttributeNode(new DOMAttr('server', $server)); - $connection_node->setAttributeNode(new DOMAttr('dbname', $dbname)); - $connection_node->setAttributeNode(new DOMAttr('user', $user)); - $connection_node->setAttributeNode(new DOMAttr('password', $password)); - $connection_node->setAttributeNode(new DOMAttr('port', $port)); - $sql_node = $connection_node->appendChild(new DOMElement('sql')); - $sql_node->appendChild($drupal_filter_dom->createTextNode("SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;")); + $connection_node->setAttribute('server', $connection_info['default']['host']); + $connection_node->setAttribute('dbname', $connection_info['default']['database']); + $connection_node->setAttribute('user', $connection_info['default']['username']); + $connection_node->setAttribute('password', $connection_info['default']['password']); + $connection_node->setAttribute('port', $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'); + $sql_node = $drupal_filter_dom->createElement('sql', "SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;"); + $connection_node->appendChild($sql_node); file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); } @@ -117,7 +112,7 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { * TRUE on success, FALSE on fail. */ public function assertDatastreams($object, array $datastreams) { - if (!is_object($object)) { + if (!$this->assertFedoraObject($object)) { $this->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); } else { @@ -142,7 +137,7 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { * An array of datastreams to confirm not present. */ public function assertNoDatastreams($object, array $datastreams) { - if (!is_object($object)) { + if (!$this->assertFedoraObject($object)) { $this->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); return; } @@ -175,7 +170,7 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { */ public function validateDatastreams($object, array $datastreams) { - if (!is_object($object)) { + if (!$this->assertFedoraObject($object)) { $this->addResult(FALSE, "Datastream validation failed; Object passed in is invalid.", 'Islandora'); return; } @@ -183,32 +178,8 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { module_load_include('inc', 'islandora', 'tests/includes/datastream_validators'); foreach ($datastreams as $datastream) { - // Let's give them conventional names. - $dsid = $datastream[0]; - $prefix = $datastream[1]; - $params = array(); - if (isset($datastream[2])) { - $params = $datastream[2]; - } - - // Legacy tests were created before the CamelCase conventions of the class - // system now in place. So, we need to automagically seek out prefixes - // that start with a lower-case letter and convert them to the proper - // format (rather than fixing every single legacy test). - if (ctype_lower(substr($prefix, 0, 1))) { - // Handle the case where the prefix is "image". - if ($prefix === 'image') { - $prefix = 'Image'; - } - // Handle the case where the prefix is "text". - elseif ($prefix === 'text') { - $prefix = 'Text'; - } - // All other cases involve just converting everything to caps. - else { - $prefix = strtoupper($prefix); - } - } + // XXX: The "+ array (2 => array())" is to allow the value to be optional. + list($dsid, $prefix, $params) = $datastream + array(2 => array()); // Instantiate the appropriate class, and grab the results. $class_name = "{$prefix}DatastreamValidator"; @@ -367,4 +338,17 @@ QUERY; } } + /** + * Asserts that an object is a FedoraObject or an IslandoraFedoraObject. + * + * @param object $object + * The object to assess. + * + * @return bool + * TRUE if it is either of those object types, or FALSE otherwise. + */ + public static function assertFedoraObject($object) { + return ($object instanceof FedoraObject); + } + } From 0a6e8300c1e6e6f78e6b2115d9dbd3d418c7a671 Mon Sep 17 00:00:00 2001 From: qadan Date: Wed, 16 Jul 2014 14:54:05 -0300 Subject: [PATCH 18/93] static context non-static statics contexts pfft --- tests/includes/utilities.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc index 719e76ca..078e60d6 100644 --- a/tests/includes/utilities.inc +++ b/tests/includes/utilities.inc @@ -112,7 +112,7 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { * TRUE on success, FALSE on fail. */ public function assertDatastreams($object, array $datastreams) { - if (!$this->assertFedoraObject($object)) { + if (!self::assertFedoraObject($object)) { $this->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); } else { @@ -137,7 +137,7 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { * An array of datastreams to confirm not present. */ public function assertNoDatastreams($object, array $datastreams) { - if (!$this->assertFedoraObject($object)) { + if (!self::assertFedoraObject($object)) { $this->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); return; } @@ -170,7 +170,7 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { */ public function validateDatastreams($object, array $datastreams) { - if (!$this->assertFedoraObject($object)) { + if (!self::assertFedoraObject($object)) { $this->addResult(FALSE, "Datastream validation failed; Object passed in is invalid.", 'Islandora'); return; } From e65bddc8dc1356d938e86d628971ab804a5cfa6d Mon Sep 17 00:00:00 2001 From: willtp87 Date: Sat, 19 Jul 2014 15:51:01 +0000 Subject: [PATCH 19/93] php 5.5 in test matrix --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 84b226d0..ed29dbcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: php php: - 5.3.3 - 5.4 + - 5.5 branches: only: - /^7.x/ From 5d1d9a863033e67c554b51f1ed02e303368041f8 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Mon, 28 Jul 2014 17:00:13 +0000 Subject: [PATCH 20/93] validator fix --- includes/datastream.version.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index 515094bd..83a94aef 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -289,7 +289,7 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract $ext = array_merge($ext, $extensions); } } - + $valid_extensions = implode(' ', $ext); $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); return array( 'dsid_fieldset' => array( @@ -313,7 +313,7 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract '#description' => t('Select a file to upload.
Files must be less than @size MB.', array('@size' => $upload_size)), '#upload_location' => 'temporary://', '#upload_validators' => array( - 'file_validate_extensions' => $ext, + 'file_validate_extensions' => array($valid_extensions), // Assume its specified in MB. 'file_validate_size' => array($upload_size * 1024 * 1024), ), From 80f076f38579d67343719d641ba0f154e354053b Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Thu, 31 Jul 2014 16:15:34 +0000 Subject: [PATCH 21/93] Added application/rdf for file extension rng in mimedetect. --- includes/datastream.inc | 2 -- includes/mime_detect.inc | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/datastream.inc b/includes/datastream.inc index ba2872c2..eabb9f94 100644 --- a/includes/datastream.inc +++ b/includes/datastream.inc @@ -54,7 +54,6 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F // Browsers will not append all extensions. $mime_detect = new MimeDetect(); $extension = '.' . $mime_detect->getExtension($datastream->mimetype); - // Prevent adding on a duplicate extension. $label = $datastream->label; $extension_length = strlen($extension); @@ -65,7 +64,6 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F if ($duplicate_extension_position === FALSE) { $filename .= $extension; } - header("Content-Disposition: attachment; filename=\"$filename\""); } diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index c1be1b92..93066bf1 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -226,6 +226,7 @@ class MimeDetect { "tar" => "application/x-tar", "gtar" => "application/x-gtar", "zip" => "application/x-zip", + "rng" => "application/xml", // others: 'bin' => 'application/octet-stream', ); From f49f709bad1911f8f7607148116a5dfc4cfd6787 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Wed, 20 Aug 2014 16:45:10 -0300 Subject: [PATCH 22/93] Recursive things are exclusively magical. ... Magically delicious, even. --- includes/tuque_wrapper.inc | 46 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 348ac60a..21a1d0b2 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -166,16 +166,21 @@ class IslandoraFedoraObject extends FedoraObject { public function __set($name, $value) { parent::__set($name, $value); - // XXX: Due to the structure of the code, we cannot use property_exists() - // (because many of the properties are declared in the class, and the magic - // triggers due them being NULLed), nor can we use isset() (because it is - // implemented as another magic function...). - $vars = get_object_vars($this); - while (isset($vars[$name])) { - $new_value = $this->$name; - unset($this->$name); - parent::__set($name, $new_value); + // Recursion only matters for magic properties... "Plain" properties cannot + // call other code in order to start recursing, and in fact we would get + // stuck looping with a "plain" property. + if ($this->propertyIsMagical($name)) { + // XXX: Due to the structure of the code, we cannot use property_exists() + // (because many of the properties are declared in the class, and the magic + // triggers due them being NULLed), nor can we use isset() (because it is + // implemented as another magic function...). $vars = get_object_vars($this); + while (isset($vars[$name])) { + $new_value = $this->$name; + unset($this->$name); + parent::__set($name, $new_value); + $vars = get_object_vars($this); + } } } @@ -430,16 +435,21 @@ class IslandoraFedoraDatastream extends FedoraDatastream { public function __set($name, $value) { parent::__set($name, $value); - // XXX: Due to the structure of the code, we cannot use property_exists() - // (because many of the properties are declared in the class, and the magic - // triggers due them being NULLed), nor can we use isset() (because it is - // implemented as another magic function...). - $vars = get_object_vars($this); - while (isset($vars[$name])) { - $new_value = $this->$name; - unset($this->$name); - parent::__set($name, $new_value); + // Recursion only matters for magic properties... "Plain" properties cannot + // call other code in order to start recursing, and in fact we would get + // stuck looping with a "plain" property. + if ($this->propertyIsMagical($name)) { + // XXX: Due to the structure of the code, we cannot use property_exists() + // (because many of the properties are declared in the class, and the magic + // triggers due them being NULLed), nor can we use isset() (because it is + // implemented as another magic function...). $vars = get_object_vars($this); + while (isset($vars[$name])) { + $new_value = $this->$name; + unset($this->$name); + parent::__set($name, $new_value); + $vars = get_object_vars($this); + } } } From 862bde2d64aeb47a8d51c32c37f3c963f8c5cefe Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Thu, 21 Aug 2014 09:31:06 -0300 Subject: [PATCH 23/93] Ermahgerd, lerns ter lerng. --- includes/tuque_wrapper.inc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 21a1d0b2..2f4ea1df 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -171,9 +171,9 @@ class IslandoraFedoraObject extends FedoraObject { // stuck looping with a "plain" property. if ($this->propertyIsMagical($name)) { // XXX: Due to the structure of the code, we cannot use property_exists() - // (because many of the properties are declared in the class, and the magic - // triggers due them being NULLed), nor can we use isset() (because it is - // implemented as another magic function...). + // (because many of the properties are declared in the class, and the + // magic triggers due them being NULLed), nor can we use isset() (because + // it is implemented as another magic function...). $vars = get_object_vars($this); while (isset($vars[$name])) { $new_value = $this->$name; @@ -440,9 +440,9 @@ class IslandoraFedoraDatastream extends FedoraDatastream { // stuck looping with a "plain" property. if ($this->propertyIsMagical($name)) { // XXX: Due to the structure of the code, we cannot use property_exists() - // (because many of the properties are declared in the class, and the magic - // triggers due them being NULLed), nor can we use isset() (because it is - // implemented as another magic function...). + // (because many of the properties are declared in the class, and the + // magic triggers due them being NULLed), nor can we use isset() (because + // it is implemented as another magic function...). $vars = get_object_vars($this); while (isset($vars[$name])) { $new_value = $this->$name; From 6b69518988bd8803a1d0859acf79daa185df5fa7 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Thu, 21 Aug 2014 13:27:39 -0300 Subject: [PATCH 24/93] defer derivatives api --- includes/derivatives.inc | 39 ++++++++++++++++++++++++ includes/regenerate_derivatives.form.inc | 2 ++ islandora.module | 8 +++++ 3 files changed, 49 insertions(+) diff --git a/includes/derivatives.inc b/includes/derivatives.inc index 72d61235..7248f03f 100644 --- a/includes/derivatives.inc +++ b/includes/derivatives.inc @@ -4,6 +4,9 @@ * Defines functions used when constructing derivatives. */ +// Relations. +define('ISLANDORA_DEFER_DERIVATIVES_FLAG', 'deferDerivatives'); + /** * Decides which derivative function to call and runs it. * @@ -245,3 +248,39 @@ function islandora_filter_derivatives($hooks, $options, AbstractObject $object) $hooks = array_filter($hooks, $filter_function); return $hooks; } + +/** + * Set the defer derivatives flag on an object. + */ +function islandora_set_defer_derivatives_flag(AbstractObject $object) { + $object->relationships->add( + ISLANDORA_RELS_EXT_URI, + ISLANDORA_DEFER_DERIVATIVES_FLAG, + 'true', + RELS_TYPE_PLAIN_LITERAL + ); +} + +/** + * Get the defer derivatives flag on an object. + */ +function islandora_get_defer_derivatives_flag(AbstractObject $object) { + return $object->relationships->get( + ISLANDORA_RELS_EXT_URI, + ISLANDORA_DEFER_DERIVATIVES_FLAG, + 'true', + RELS_TYPE_PLAIN_LITERAL + ); +} + +/** + * Remove the defer derivatives flag on an object. + */ +function islandora_remove_defer_derivatives_flag(AbstractObject $object) { + $object->relationships->remove( + ISLANDORA_RELS_EXT_URI, + ISLANDORA_DEFER_DERIVATIVES_FLAG, + 'true', + RELS_TYPE_PLAIN_LITERAL + ); +} diff --git a/includes/regenerate_derivatives.form.inc b/includes/regenerate_derivatives.form.inc index 9a7a097e..437288c9 100644 --- a/includes/regenerate_derivatives.form.inc +++ b/includes/regenerate_derivatives.form.inc @@ -95,6 +95,7 @@ function islandora_regenerate_object_derivatives_form_submit(array $form, array */ function islandora_regenerate_object_derivatives_batch(AbstractObject $object) { module_load_include('inc', 'islandora', 'includes/derivatives'); + islandora_remove_defer_derivatives_flag($object); return array( 'title' => t('Regenerating all derivatives for @label', array('@label' => $object->label)), 'operations' => islandora_do_batch_derivatives($object, array('force' => TRUE)), @@ -117,6 +118,7 @@ function islandora_regenerate_object_derivatives_batch(AbstractObject $object) { */ function islandora_regenerate_datastream_derivative_batch(AbstractDatastream $datastream) { module_load_include('inc', 'islandora', 'includes/derivatives'); + islandora_remove_defer_derivatives_flag($object); return array( 'title' => t('Regenerating derivatives for the @dsid datastream', array('@dsid' => $datastream->id)), 'operations' => islandora_do_batch_derivatives($datastream->parent, array( diff --git a/islandora.module b/islandora.module index f0bf796b..9c811611 100644 --- a/islandora.module +++ b/islandora.module @@ -1719,6 +1719,10 @@ function islandora_islandora_basic_collection_get_query_filters() { */ function islandora_islandora_object_ingested(AbstractObject $object) { module_load_include('inc', 'islandora', 'includes/derivatives'); + // Defer derivatives if necessary. + if (islandora_get_defer_derivatives_flag($object)) { + return; + } islandora_run_derivatives($object, NULL); } @@ -1730,6 +1734,10 @@ function islandora_islandora_object_ingested(AbstractObject $object) { */ function islandora_islandora_datastream_ingested(AbstractObject $object, AbstractDatastream $datastream) { module_load_include('inc', 'islandora', 'includes/derivatives'); + // Defer derivatives if necessary. + if (islandora_get_defer_derivatives_flag($object)) { + return; + } islandora_run_derivatives($object, $datastream->id); } From 3850720e65785e101934b054f0a6c4a0e8687474 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Thu, 21 Aug 2014 15:30:42 -0300 Subject: [PATCH 25/93] global defer derivatives setting --- includes/admin.form.inc | 7 +++++++ islandora.install | 1 + islandora.module | 11 +++++++++++ 3 files changed, 19 insertions(+) diff --git a/includes/admin.form.inc b/includes/admin.form.inc index 69f09fd2..a1edde0b 100644 --- a/includes/admin.form.inc +++ b/includes/admin.form.inc @@ -61,6 +61,13 @@ function islandora_repository_admin(array $form, array &$form_state) { '#description' => t('HTTP caching can reduce network traffic, by allowing clients to used cached copies.'), '#default_value' => variable_get('islandora_use_datastream_cache_headers', TRUE), ), + 'islandora_defer_derivataves_on_ingest' => array( + '#type' => 'checkbox', + '#title' => t('Defer derivative generation during ingest'), + '#description' => t('Prevent derivatives from running during ingest, + useful if derivatives are to be created by an external service.'), + '#default_value' => variable_get('islandora_defer_derivataves_on_ingest', FALSE), + ), ), 'islandora_namespace' => array( '#type' => 'fieldset', diff --git a/islandora.install b/islandora.install index f37894e9..8f88dcdd 100644 --- a/islandora.install +++ b/islandora.install @@ -46,6 +46,7 @@ function islandora_uninstall() { // Add new variables to clean up. $variables = array( 'islandora_ds_replace_exclude_enforced', + 'islandora_defer_derivataves_on_ingest', ); array_walk($variables, 'variable_del'); } diff --git a/islandora.module b/islandora.module index 9c811611..4691028e 100644 --- a/islandora.module +++ b/islandora.module @@ -1962,3 +1962,14 @@ function islandora_menu_local_tasks_alter(&$data, $router_item, $root_path) { } } } + +/** + * Implements hook_islandora_object_alter(). + */ +function islandora_islandora_object_alter(AbstractObject $object, array &$context) { + // Prevent derivative creation during ingest if var is set. + if ($context['action'] == 'ingest' && variable_get('islandora_defer_derivataves_on_ingest', FALSE)) { + module_load_include('inc', 'islandora', 'includes/derivatives'); + islandora_set_defer_derivatives_flag($object); + } +} From 25b53ba01b8270072fc7cfaeb9d7879e5bf68932 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Mon, 25 Aug 2014 10:13:24 -0300 Subject: [PATCH 26/93] copypasta omm nom --- includes/regenerate_derivatives.form.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/regenerate_derivatives.form.inc b/includes/regenerate_derivatives.form.inc index 437288c9..1312bec3 100644 --- a/includes/regenerate_derivatives.form.inc +++ b/includes/regenerate_derivatives.form.inc @@ -118,7 +118,7 @@ function islandora_regenerate_object_derivatives_batch(AbstractObject $object) { */ function islandora_regenerate_datastream_derivative_batch(AbstractDatastream $datastream) { module_load_include('inc', 'islandora', 'includes/derivatives'); - islandora_remove_defer_derivatives_flag($object); + islandora_remove_defer_derivatives_flag($datastream->parent); return array( 'title' => t('Regenerating derivatives for the @dsid datastream', array('@dsid' => $datastream->id)), 'operations' => islandora_do_batch_derivatives($datastream->parent, array( From 4dbf46adc9f523bd5cdc8e8de831457161d931a4 Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Mon, 25 Aug 2014 15:23:49 -0300 Subject: [PATCH 27/93] Removed rng => application/xml mapping. --- includes/mime_detect.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index 67566743..74b87fd6 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -223,7 +223,6 @@ class MimeDetect { "tar" => "application/x-tar", "gtar" => "application/x-gtar", "zip" => "application/x-zip", - "rng" => "application/xml", // others: 'bin' => 'application/octet-stream', // Web Archives: From f1de655d9a7294867eeed6b0d9252cbc967182e6 Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Mon, 25 Aug 2014 16:25:08 -0300 Subject: [PATCH 28/93] Updated mime detect class. --- includes/mime_detect.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index 74b87fd6..29fcf1e5 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -134,7 +134,7 @@ class MimeDetect { 'xhtml' => 'application/xhtml+xml', 'xsl' => 'text/xsl', 'xslt' => 'text/xsl', - 'xml' => 'text/xml', + 'xml' => 'application/xml', 'csv' => 'text/csv', 'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', From 2b2c5c8601aa62b13d8d926e9d58d24c9d051c7b Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Wed, 27 Aug 2014 16:43:53 -0300 Subject: [PATCH 29/93] Updating extestion and mime detection on download to use drupals file_g=default_mimetype_mapping() funcitons. --- includes/datastream.inc | 4 ++-- includes/utilities.inc | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/includes/datastream.inc b/includes/datastream.inc index edc8ceb1..e31ed254 100644 --- a/includes/datastream.inc +++ b/includes/datastream.inc @@ -30,6 +30,7 @@ function islandora_download_datastream(AbstractDatastream $datastream) { * The version of the datastream to display */ function islandora_view_datastream(AbstractDatastream $datastream, $download = FALSE, $version = NULL) { + module_load_include('inc', 'islandora', 'includes/utilities'); // XXX: Certain features of the Devel module rely on the use of "shutdown // handlers", such as query logging... The problem is that they might blindly // add additional output which will break things if what is actually being @@ -45,7 +46,6 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F return drupal_not_found(); } } - header('Content-type: ' . $datastream->mimetype); if ($datastream->controlGroup == 'M' || $datastream->controlGroup == 'X') { header('Content-length: ' . $datastream->size); @@ -53,7 +53,7 @@ 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); + $extension = '.' . islandora_get_extension_for_mimetype($datastream->mimetype);//$mime_detect->getExtension($datastream->mimetype); // Prevent adding on a duplicate extension. $label = $datastream->label; $extension_length = strlen($extension); diff --git a/includes/utilities.inc b/includes/utilities.inc index 26633df5..4892a4eb 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -46,6 +46,15 @@ function islandora_convert_bytes_to_human_readable($bytes, $precision = 2) { } } +function islandora_get_extension_for_mimetype($mimetype) { + module_load_include('inc', 'file', 'file.mimetypes.inc'); + dsm($mimetype, "mimetype"); + $mimetype_mapping = file_default_mimetype_mapping(); + $extenstion_index = array_search($mimetype, $mimetype_mapping['mimetypes']); + $extenstion = array_search($extenstion_index, $mimetype_mapping['extensions']); + dsm($extenstion, "extenstion"); + return $extenstion; +} /** * Add a file as managed if is not already. * From 5caf983df8cc8e285f3b6633de5047945b2ba135 Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 28 Aug 2014 13:41:52 -0300 Subject: [PATCH 30/93] make the drush webserver logs go somewhere real --- tests/scripts/travis_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 336087ed..70665985 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -40,7 +40,7 @@ drush si minimal --db-url=mysql://drupal:drupal@localhost/drupal --yes sudo chmod a+w sites/default/settings.php echo "include_once '$HOME/.composer/vendor/autoload.php';" >> sites/default/settings.php sudo chmod a-w sites/default/settings.php -drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/dev/null & +drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/tmp/drush_webserver.log & ln -s $ISLANDORA_DIR sites/all/modules/islandora mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/islandora/tests/test_config.ini mkdir sites/all/libraries From 8efd0c23fc0a060e31cd608ef9eb13ff70260a78 Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Thu, 28 Aug 2014 15:34:22 -0300 Subject: [PATCH 31/93] Updated file extension logic on ds download. --- includes/mime_detect.inc | 1 - includes/utilities.inc | 29 ++++++++++++++++++++++------- islandora.module | 1 + 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index 29fcf1e5..da2c69f3 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -134,7 +134,6 @@ class MimeDetect { 'xhtml' => 'application/xhtml+xml', 'xsl' => 'text/xsl', 'xslt' => 'text/xsl', - 'xml' => 'application/xml', 'csv' => 'text/csv', 'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', diff --git a/includes/utilities.inc b/includes/utilities.inc index 4892a4eb..0612f895 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -46,15 +46,30 @@ function islandora_convert_bytes_to_human_readable($bytes, $precision = 2) { } } +/** + * Retrieve the correct file extension for a give mimetype. + * + * @param string $mimetype + * The mimetype whose extension is required. + * + * @return string + * The extension mapped to the given mimetype. + */ function islandora_get_extension_for_mimetype($mimetype) { - module_load_include('inc', 'file', 'file.mimetypes.inc'); - dsm($mimetype, "mimetype"); - $mimetype_mapping = file_default_mimetype_mapping(); - $extenstion_index = array_search($mimetype, $mimetype_mapping['mimetypes']); - $extenstion = array_search($extenstion_index, $mimetype_mapping['extensions']); - dsm($extenstion, "extenstion"); - return $extenstion; + require_once(DRUPAL_ROOT . '/includes/file.mimetypes.inc'); + + $mimetype_mapping = file_mimetype_mapping(); + $extension_index = array_search($mimetype, $mimetype_mapping['mimetypes']); + $mime_array_flipped = array_reverse($mimetype_mapping['extensions']); + $extension = array_search($extension_index, $mime_array_flipped); + + // We can only have one mapping in drupal for 'xml'. + if ($mimetype == "text/xml") { + return "xml"; + } + return $extension; } + /** * Add a file as managed if is not already. * diff --git a/islandora.module b/islandora.module index b2bb99b8..c930ba5f 100644 --- a/islandora.module +++ b/islandora.module @@ -1590,6 +1590,7 @@ function islandora_download_clip(AbstractObject $object) { function islandora_file_mimetype_mapping_alter(&$mapping) { $mime_detect = new MimeDetect(); $types = $mime_detect->getMimeTypes(); + $diff = array_diff_key($types, $mapping['extensions']); foreach ($diff as $ext => $mime) { $mapping['mimetypes'][] = $mime; From 4311be0f0dbde303e402a1e6b6165030734cf86e Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Thu, 28 Aug 2014 15:36:49 -0300 Subject: [PATCH 32/93] removed unused code. --- includes/datastream.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/datastream.inc b/includes/datastream.inc index e31ed254..5f48211c 100644 --- a/includes/datastream.inc +++ b/includes/datastream.inc @@ -53,7 +53,7 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F if ($download) { // Browsers will not append all extensions. $mime_detect = new MimeDetect(); - $extension = '.' . islandora_get_extension_for_mimetype($datastream->mimetype);//$mime_detect->getExtension($datastream->mimetype); + $extension = '.' . islandora_get_extension_for_mimetype($datastream->mimetype); // Prevent adding on a duplicate extension. $label = $datastream->label; $extension_length = strlen($extension); From 212276f6d01b87be1d9a799d835b64d8e0517c7f Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Fri, 29 Aug 2014 08:55:21 -0300 Subject: [PATCH 33/93] Corrected small travis issue. --- includes/utilities.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/utilities.inc b/includes/utilities.inc index 0612f895..d3d2bbc3 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -56,7 +56,7 @@ function islandora_convert_bytes_to_human_readable($bytes, $precision = 2) { * The extension mapped to the given mimetype. */ function islandora_get_extension_for_mimetype($mimetype) { - require_once(DRUPAL_ROOT . '/includes/file.mimetypes.inc'); + require_once DRUPAL_ROOT . "/includes/file.mimetypes.inc"; $mimetype_mapping = file_mimetype_mapping(); $extension_index = array_search($mimetype, $mimetype_mapping['mimetypes']); From d2ecd81ba8ac8999d705184b26399fb992e77159 Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Tue, 2 Sep 2014 16:19:42 -0300 Subject: [PATCH 34/93] Code review feedback. --- includes/datastream.inc | 2 +- includes/mimetype.utils.inc | 31 +++++++++++++++++++++++++++++++ includes/utilities.inc | 24 ------------------------ 3 files changed, 32 insertions(+), 25 deletions(-) create mode 100644 includes/mimetype.utils.inc diff --git a/includes/datastream.inc b/includes/datastream.inc index 5f48211c..592870b7 100644 --- a/includes/datastream.inc +++ b/includes/datastream.inc @@ -30,7 +30,7 @@ function islandora_download_datastream(AbstractDatastream $datastream) { * The version of the datastream to display */ function islandora_view_datastream(AbstractDatastream $datastream, $download = FALSE, $version = NULL) { - module_load_include('inc', 'islandora', 'includes/utilities'); + module_load_include('inc', 'islandora', 'includes/mimetype.utils'); // XXX: Certain features of the Devel module rely on the use of "shutdown // handlers", such as query logging... The problem is that they might blindly // add additional output which will break things if what is actually being diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc new file mode 100644 index 00000000..989c55cd --- /dev/null +++ b/includes/mimetype.utils.inc @@ -0,0 +1,31 @@ + Date: Thu, 4 Sep 2014 10:29:09 -0300 Subject: [PATCH 35/93] typo fix --- includes/admin.form.inc | 4 ++-- islandora.install | 2 +- islandora.module | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/includes/admin.form.inc b/includes/admin.form.inc index a1edde0b..42612ea0 100644 --- a/includes/admin.form.inc +++ b/includes/admin.form.inc @@ -61,12 +61,12 @@ function islandora_repository_admin(array $form, array &$form_state) { '#description' => t('HTTP caching can reduce network traffic, by allowing clients to used cached copies.'), '#default_value' => variable_get('islandora_use_datastream_cache_headers', TRUE), ), - 'islandora_defer_derivataves_on_ingest' => array( + 'islandora_defer_derivatives_on_ingest' => array( '#type' => 'checkbox', '#title' => t('Defer derivative generation during ingest'), '#description' => t('Prevent derivatives from running during ingest, useful if derivatives are to be created by an external service.'), - '#default_value' => variable_get('islandora_defer_derivataves_on_ingest', FALSE), + '#default_value' => variable_get('islandora_defer_derivatives_on_ingest', FALSE), ), ), 'islandora_namespace' => array( diff --git a/islandora.install b/islandora.install index 8f88dcdd..12c92009 100644 --- a/islandora.install +++ b/islandora.install @@ -46,7 +46,7 @@ function islandora_uninstall() { // Add new variables to clean up. $variables = array( 'islandora_ds_replace_exclude_enforced', - 'islandora_defer_derivataves_on_ingest', + 'islandora_defer_derivatives_on_ingest', ); array_walk($variables, 'variable_del'); } diff --git a/islandora.module b/islandora.module index 3b10d64b..27d0a894 100644 --- a/islandora.module +++ b/islandora.module @@ -1965,7 +1965,7 @@ function islandora_menu_local_tasks_alter(&$data, $router_item, $root_path) { */ function islandora_islandora_object_alter(AbstractObject $object, array &$context) { // Prevent derivative creation during ingest if var is set. - if ($context['action'] == 'ingest' && variable_get('islandora_defer_derivataves_on_ingest', FALSE)) { + if ($context['action'] == 'ingest' && variable_get('islandora_defer_derivatives_on_ingest', FALSE)) { module_load_include('inc', 'islandora', 'includes/derivatives'); islandora_set_defer_derivatives_flag($object); } From 087db8b3b05febb2388732cdea816906313998d1 Mon Sep 17 00:00:00 2001 From: lutaylor Date: Fri, 5 Sep 2014 15:06:35 -0300 Subject: [PATCH 36/93] adding reliable install source --- tests/scripts/travis_setup.sh | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 0b33d4b0..5a8c78ef 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -1,14 +1,13 @@ #!/bin/bash - mysql -u root -e 'create database drupal;' mysql -u root -e "create database fedora;" mysql -u root -e "GRANT ALL PRIVILEGES ON fedora.* To 'fedora'@'localhost' IDENTIFIED BY 'fedora';" mysql -u root -e "GRANT ALL PRIVILEGES ON drupal.* To 'drupal'@'localhost' IDENTIFIED BY 'drupal';" cd $HOME git clone git://github.com/Islandora/tuque.git -git clone -b $FEDORA_VERSION git://github.com/Islandora/islandora_tomcat.git +wget http://alpha.library.yorku.ca/islandora_tomcat.$FEDORA_VERSION.tar.gz +tar xf islandora_tomcat.$FEDORA_VERSION.tar.gz cd islandora_tomcat -git fsck export CATALINA_HOME='.' export JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=512m -XX:+CMSClassUnloadingEnabled -Djavax.net.ssl.trustStore=$CATALINA_HOME/fedora/server/truststore -Djavax.net.ssl.trustStorePassword=tomcat" ./bin/startup.sh @@ -18,20 +17,22 @@ pear upgrade --force Console_Getopt pear upgrade --force pear pear channel-discover pear.drush.org -# "prefer-source" required due to SSL shenanigans on the Travis boxes... -composer global require --prefer-source 'squizlabs/php_codesniffer=*' 'sebastian/phpcpd=*' 'drush/drush:6.3.0' -# Because we can't add to the PATH here and this file is used in many repos, -# let's just throw symlinks in. -find $HOME/.composer/vendor/bin -executable \! -type d -exec sudo ln -s {} /usr/local/sbin/ \; +wget http://alpha.library.yorku.ca/drush-6.3.tar.gz +tar xf drush-6.3.tar.gz +sudo mv drush-6.3 /opt/ +sudo ln -s /opt/drush-6.3/drush /usr/bin/drush + +wget http://alpha.library.yorku.ca/PHP_CodeSniffer-1.5.4.tgz +pear install PHP_CodeSniffer-1.5.4.tgz + +wget http://alpha.library.yorku.ca/phpcpd.phar +sudo mv phpcpd.phar /usr/local/bin/phpcpd +sudo chmod +x /usr/local/bin/phpcpd phpenv rehash drush dl --yes drupal cd drupal-* drush si minimal --db-url=mysql://drupal:drupal@localhost/drupal --yes -# Needs to make things from Composer be available (PHP CS, primarily) -sudo chmod a+w sites/default/settings.php -echo "include_once '$HOME/.composer/vendor/autoload.php';" >> sites/default/settings.php -sudo chmod a-w sites/default/settings.php drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/tmp/drush_webserver.log & ln -s $ISLANDORA_DIR sites/all/modules/islandora mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/islandora/tests/test_config.ini From dcc81b1fcde787c7e6f6b3c695abf61a482e0304 Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Thu, 11 Sep 2014 11:26:32 -0300 Subject: [PATCH 37/93] added islandora xml default for datastrram replacement --- includes/datastream.version.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index 83a94aef..bd0b8942 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -275,6 +275,7 @@ function islandora_get_object_extensions(AbstractObject $object) { function islandora_datastream_version_replace_form($form, &$form_state, AbstractDatastream $datastream) { module_load_include('inc', 'islandora', 'includes/content_model'); module_load_include('inc', 'islandora', 'includes/utilities'); + module_load_include('inc', 'islandora', 'includes/mimetype.utils'); $object = islandora_object_load($datastream->parent->id); $form_state['object_id'] = $object->id; @@ -290,6 +291,7 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract } } $valid_extensions = implode(' ', $ext); + $valid_extensions .= ' ' . islandora_get_extension_for_mimetype($datastream->mimetype); $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); return array( 'dsid_fieldset' => array( From b7d20be402298b7fb145acdd2925dab13611f123 Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Fri, 12 Sep 2014 09:39:03 -0300 Subject: [PATCH 38/93] updated mimetype selection --- includes/datastream.version.inc | 14 +++++--------- includes/mimetype.utils.inc | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index bd0b8942..8e7b789c 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -282,16 +282,12 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract $form_state['dsid'] = $datastream->id; $form_state['object'] = $object; $datastream_mime_map = islandora_get_object_extensions($object); - $mime_detect = new MimeDetect(); - $ext = array(); - if (isset($datastream_mime_map[$datastream->id])) { - foreach ($datastream_mime_map[$datastream->id]['mime'] as $key => $value) { - $extensions = $mime_detect->getValidExtensions($value); - $ext = array_merge($ext, $extensions); - } + $mimes = $datastream_mime_map[$datastream->id]['mime']; + $drupal_extensions = array(); + foreach ($mimes as $mime) { + $drupal_extensions = array_merge($drupal_extensions, islandora_get_extensions_for_mimetype($mime)); } - $valid_extensions = implode(' ', $ext); - $valid_extensions .= ' ' . islandora_get_extension_for_mimetype($datastream->mimetype); + $valid_extensions = implode(' ', $drupal_extensions); $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); return array( 'dsid_fieldset' => array( diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc index 989c55cd..a9ca5a6c 100644 --- a/includes/mimetype.utils.inc +++ b/includes/mimetype.utils.inc @@ -1,4 +1,5 @@ Date: Fri, 12 Sep 2014 09:42:26 -0300 Subject: [PATCH 39/93] updated mimetype selection --- includes/datastream.version.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index 8e7b789c..be08fb38 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -283,11 +283,11 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract $form_state['object'] = $object; $datastream_mime_map = islandora_get_object_extensions($object); $mimes = $datastream_mime_map[$datastream->id]['mime']; - $drupal_extensions = array(); + $extensions = array(); foreach ($mimes as $mime) { - $drupal_extensions = array_merge($drupal_extensions, islandora_get_extensions_for_mimetype($mime)); + $extensions = array_merge($extensions, islandora_get_extensions_for_mimetype($mime)); } - $valid_extensions = implode(' ', $drupal_extensions); + $valid_extensions = implode(' ', $extensions); $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); return array( 'dsid_fieldset' => array( From cc49c28ddd809784861b0cdf6b278f524729875c Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Fri, 12 Sep 2014 10:10:00 -0300 Subject: [PATCH 40/93] coder --- includes/mimetype.utils.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc index a9ca5a6c..3aef6994 100644 --- a/includes/mimetype.utils.inc +++ b/includes/mimetype.utils.inc @@ -57,4 +57,4 @@ function islandora_get_extensions_for_mimetype($mimetype) { $extensions[] = 'xml'; } return $extensions; -} \ No newline at end of file +} From 9b49ec7bbfd07c8b661e3f53bbed62325e26ab44 Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Fri, 12 Sep 2014 10:28:41 -0300 Subject: [PATCH 41/93] allwed for existing non-standard mimetype --- includes/datastream.version.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index be08fb38..e97640d4 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -282,7 +282,11 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract $form_state['dsid'] = $datastream->id; $form_state['object'] = $object; $datastream_mime_map = islandora_get_object_extensions($object); + $current_mime = $datastream->mimetype; $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)); From d47e777adde0c0a58fb9e5d9c76eb27531f131b2 Mon Sep 17 00:00:00 2001 From: Alan Stanley Date: Mon, 15 Sep 2014 10:42:11 -0300 Subject: [PATCH 42/93] removed unused variable, added default --- includes/datastream.inc | 1 - includes/mimetype.utils.inc | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/includes/datastream.inc b/includes/datastream.inc index 592870b7..edc0026b 100644 --- a/includes/datastream.inc +++ b/includes/datastream.inc @@ -52,7 +52,6 @@ function islandora_view_datastream(AbstractDatastream $datastream, $download = F } if ($download) { // Browsers will not append all extensions. - $mime_detect = new MimeDetect(); $extension = '.' . islandora_get_extension_for_mimetype($datastream->mimetype); // Prevent adding on a duplicate extension. $label = $datastream->label; diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc index 3aef6994..1a20859d 100644 --- a/includes/mimetype.utils.inc +++ b/includes/mimetype.utils.inc @@ -12,13 +12,13 @@ * The mimetype whose extension is required. * * @return string - * The extension mapped to the given mimetype. + * The extension mapped to the given mimetype. Defaults to 'bin'. */ function islandora_get_extension_for_mimetype($mimetype) { // file.mimetypes.inc is a part of Drupal core, however is not // automatically loaded. Manually require it. require_once DRUPAL_ROOT . "/includes/file.mimetypes.inc"; - + $extension = 'bin'; $mimetype_mapping = file_mimetype_mapping(); $extension_index = array_search($mimetype, $mimetype_mapping['mimetypes']); if ($extension_index !== FALSE) { From 79f9cc9f96dff4fb1e636f464da1f0ca5f312c10 Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 25 Sep 2014 12:09:34 -0300 Subject: [PATCH 43/93] simple at-a-glance verification of drupal filter writability --- islandora.module | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/islandora.module b/islandora.module index b692438e..5f098bab 100644 --- a/islandora.module +++ b/islandora.module @@ -1766,10 +1766,20 @@ function islandora_islandora_datastream_modified(AbstractObject $object, Abstrac */ function islandora_form_simpletest_test_form_alter(array &$form) { module_load_include('inc', 'simpletest', 'simpletest.pages'); + + $filter_path = variable_get('islandora_simpletest_drupal_filter_path', '/usr/local/fedora/server/config/filter-drupal.xml'); + $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 $filter_path is writable by the server."); + } else { + $filter_status_message = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())); + $filter_status_message .= t(" Drupal filter at $filter_path 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, try setting the correct path in the test settings to see if this error is no longer present."); + } $form['tests'] = array( '#type' => 'fieldset', '#title' => t('Tests'), - '#description' => t('Select the test(s) or test group(s) you would like to run, and click Run tests.

NOTE: Tests in groups prefixed with Islandora 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 Run tests.

NOTE: Tests in groups prefixed with Islandora 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.

Drupal Filter Write Status: $filter_status_message

"), ); $form['tests']['table'] = array( @@ -1812,6 +1822,24 @@ function islandora_form_simpletest_test_form_alter(array &$form) { ); } +/** + * Implements hook_form_simpletest_settings_form_alter() + */ +function islandora_form_simpletest_settings_form_alter(&$form, &$form_state) { + $form['islandora_drupal_filter'] = array( + '#type' => 'fieldset', + '#title' => t('Islandora Drupal Filter Location'), + '#collapsible' => FALSE, + '#collapsed' => FALSE, + ); + $form['islandora_drupal_filter']['islandora_simpletest_drupal_filter_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to the Drupal Filter'), + '#description' => t('The absolute path on the server to the Drupal filter XML file in the Fedora configuration. NOTE: This is simply used to populate the at-a-glance "is the Filter writeable" notice on the top of the list of tests, and DOES NOT actually have any bearing on its function within Islandora.'), + '#default_value' => variable_get('islandora_simpletest_drupal_filter_path', '/usr/local/fedora/server/config/filter-drupal.xml'), + ); +} + /** * Submit handler for islandora_form_simpletest_test_form_alter(). */ From b29a66d99093d1c8eb0d30187049c686f44ad4f8 Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 25 Sep 2014 12:42:23 -0300 Subject: [PATCH 44/93] oh qadan you goof --- islandora.module | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/islandora.module b/islandora.module index 5f098bab..0b41329f 100644 --- a/islandora.module +++ b/islandora.module @@ -1770,16 +1770,23 @@ function islandora_form_simpletest_test_form_alter(array &$form) { $filter_path = variable_get('islandora_simpletest_drupal_filter_path', '/usr/local/fedora/server/config/filter-drupal.xml'); $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 $filter_path is writable by the server."); - } else { - $filter_status_message = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())); - $filter_status_message .= t(" Drupal filter at $filter_path 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, try setting the correct path in the test settings to see if this error is no longer present."); + $filter_status_message = theme_image(array('path' => 'misc/watchdog-ok.png', 'attributes' => array())) . " "; + $filter_status_message .= t("Drupal filter at !filter_path 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 !filter_path 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, try setting the correct path in the test settings to see if this error is no longer present.", array( + '!filter_path' => $filter_path, + )); } $form['tests'] = array( '#type' => 'fieldset', '#title' => t('Tests'), - '#description' => t("Select the test(s) or test group(s) you would like to run, and click Run tests.

NOTE: Tests in groups prefixed with Islandora 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.

Drupal Filter Write Status: $filter_status_message

"), + '#description' => t("Select the test(s) or test group(s) you would like to run, and click Run tests.

NOTE: Tests in groups prefixed with Islandora 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.

Drupal Filter Write Status: !filter_status_message

", array( + '!filter_status_message' => $filter_status_message, + )), ); $form['tests']['table'] = array( @@ -1823,7 +1830,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { } /** - * Implements hook_form_simpletest_settings_form_alter() + * Implements hook_form_alter(). */ function islandora_form_simpletest_settings_form_alter(&$form, &$form_state) { $form['islandora_drupal_filter'] = array( From e6eb5d93f2d54ad14f1b3bfbf7e9c5bfc2c48d20 Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 25 Sep 2014 14:58:17 -0300 Subject: [PATCH 45/93] doing this the less stupid way --- islandora.module | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/islandora.module b/islandora.module index 0b41329f..87082468 100644 --- a/islandora.module +++ b/islandora.module @@ -1766,8 +1766,9 @@ function islandora_islandora_datastream_modified(AbstractObject $object, Abstrac */ function islandora_form_simpletest_test_form_alter(array &$form) { module_load_include('inc', 'simpletest', 'simpletest.pages'); - - $filter_path = variable_get('islandora_simpletest_drupal_filter_path', '/usr/local/fedora/server/config/filter-drupal.xml'); + module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction'); + $configuration = IslandoraTestUtilityClass::getTestConfiguration(); + $filter_path = variable_get('islandora_simpletest_drupal_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())) . " "; @@ -1781,6 +1782,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { '!filter_path' => $filter_path, )); } + $form['tests'] = array( '#type' => 'fieldset', '#title' => t('Tests'), @@ -1833,6 +1835,8 @@ function islandora_form_simpletest_test_form_alter(array &$form) { * Implements hook_form_alter(). */ function islandora_form_simpletest_settings_form_alter(&$form, &$form_state) { + module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction'); + $configuration = IslandoraTestUtilityClass::getTestConfiguration(); $form['islandora_drupal_filter'] = array( '#type' => 'fieldset', '#title' => t('Islandora Drupal Filter Location'), @@ -1843,7 +1847,7 @@ function islandora_form_simpletest_settings_form_alter(&$form, &$form_state) { '#type' => 'textfield', '#title' => t('Path to the Drupal Filter'), '#description' => t('The absolute path on the server to the Drupal filter XML file in the Fedora configuration. NOTE: This is simply used to populate the at-a-glance "is the Filter writeable" notice on the top of the list of tests, and DOES NOT actually have any bearing on its function within Islandora.'), - '#default_value' => variable_get('islandora_simpletest_drupal_filter_path', '/usr/local/fedora/server/config/filter-drupal.xml'), + '#default_value' => variable_get('islandora_simpletest_drupal_filter_path', $configuration['drupal_filter_file']), ); } From 0bc26560fb269d461754fc71dc4891482adb3a3e Mon Sep 17 00:00:00 2001 From: qadan Date: Fri, 26 Sep 2014 09:56:59 -0300 Subject: [PATCH 46/93] let's just do this right --- islandora.module | 27 ++++---------------- tests/includes/islandora_web_test_case.inc | 29 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/islandora.module b/islandora.module index 87082468..626f4921 100644 --- a/islandora.module +++ b/islandora.module @@ -1768,7 +1768,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { module_load_include('inc', 'simpletest', 'simpletest.pages'); module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction'); $configuration = IslandoraTestUtilityClass::getTestConfiguration(); - $filter_path = variable_get('islandora_simpletest_drupal_filter_path', $configuration['drupal_filter_file']); + $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())) . " "; @@ -1778,7 +1778,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { } else { $filter_status_message = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())) . " "; - $filter_status_message .= t("Drupal filter at !filter_path 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, try setting the correct path in the test settings to see if this error is no longer present.", array( + $filter_status_message .= t("Drupal filter at !filter_path is not writable by the server. Tests relying on the filter will be disabled. Please make sure your webserver has permission to write to the Drupal filter. If the path given is incorrect, you will need to change the drupal_filter_file entry in the Islandora module's tests/test_config.ini or tests/default.test_config.ini file.", array( '!filter_path' => $filter_path, )); } @@ -1808,6 +1808,9 @@ function islandora_form_simpletest_test_form_alter(array &$form) { '#title' => filter_xss($info['name']), '#description' => filter_xss($info['description']), ); + if (is_subclass_of($class, 'IslandoraWebTestCase') && !$filter_status) { + $form['tests']['table'][$group][$class]['#disabled'] = TRUE; + } } } @@ -1831,26 +1834,6 @@ function islandora_form_simpletest_test_form_alter(array &$form) { ); } -/** - * Implements hook_form_alter(). - */ -function islandora_form_simpletest_settings_form_alter(&$form, &$form_state) { - module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction'); - $configuration = IslandoraTestUtilityClass::getTestConfiguration(); - $form['islandora_drupal_filter'] = array( - '#type' => 'fieldset', - '#title' => t('Islandora Drupal Filter Location'), - '#collapsible' => FALSE, - '#collapsed' => FALSE, - ); - $form['islandora_drupal_filter']['islandora_simpletest_drupal_filter_path'] = array( - '#type' => 'textfield', - '#title' => t('Path to the Drupal Filter'), - '#description' => t('The absolute path on the server to the Drupal filter XML file in the Fedora configuration. NOTE: This is simply used to populate the at-a-glance "is the Filter writeable" notice on the top of the list of tests, and DOES NOT actually have any bearing on its function within Islandora.'), - '#default_value' => variable_get('islandora_simpletest_drupal_filter_path', $configuration['drupal_filter_file']), - ); -} - /** * Submit handler for islandora_form_simpletest_test_form_alter(). */ diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc index 55ee5824..8e29d893 100644 --- a/tests/includes/islandora_web_test_case.inc +++ b/tests/includes/islandora_web_test_case.inc @@ -57,6 +57,34 @@ 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()) { + $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); + if (is_writable($this->configuration['drupal_filter_file'])) { + parent::run($methods); + } + 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. * @@ -74,7 +102,6 @@ class IslandoraWebTestCase extends DrupalWebTestCase { module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); module_load_include('inc', 'islandora', 'tests/includes/utilities'); - $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); if ($this->configuration['use_drupal_filter']) { $this->setUpDrupalFilter(); } From d275eb43a446d60423f3566afb5e547bc0dcee73 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Mon, 29 Sep 2014 11:15:31 -0300 Subject: [PATCH 47/93] potx no longer having recomended dl --- tests/scripts/travis_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 5a8c78ef..84b6c44c 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -39,7 +39,7 @@ mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/is mkdir sites/all/libraries ln -s $HOME/tuque sites/all/libraries/tuque drush dl --yes coder -drush dl --yes potx +drush dl --yes potx-7.x-1.0 drush en --yes coder_review drush en --yes simpletest drush en --yes potx From 3f9ed4cf461dd2619fd5952bb1d43f53dcea3ef3 Mon Sep 17 00:00:00 2001 From: Matthew Perry Date: Thu, 2 Oct 2014 10:52:37 -0300 Subject: [PATCH 48/93] Fixing spelling mistake in ingest message: notifiy changed to notify. --- includes/ingest.form.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/ingest.form.inc b/includes/ingest.form.inc index b5a4740d..6c802f44 100644 --- a/includes/ingest.form.inc +++ b/includes/ingest.form.inc @@ -799,7 +799,7 @@ function islandora_ingest_form_submit(array $form, array &$form_state) { WATCHDOG_ERROR ); 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)), 'error' ); From d2a86da5457ae45fe5c7fc0a048a1c1280c4f0ed Mon Sep 17 00:00:00 2001 From: Jordan Dukart Date: Thu, 2 Oct 2014 14:56:40 -0300 Subject: [PATCH 49/93] Fix the invalid markup generated by the printer icon. --- islandora.module | 2 ++ 1 file changed, 2 insertions(+) diff --git a/islandora.module b/islandora.module index b692438e..207c9367 100644 --- a/islandora.module +++ b/islandora.module @@ -1940,6 +1940,8 @@ function islandora_menu_local_tasks_alter(&$data, $router_item, $root_path) { if ($tab['#link']['path'] == 'islandora/object/%/print_object') { if ($root_path == 'islandora/object/%') { $islandora_path = drupal_get_path('module', 'islandora'); + $tab['#prefix'] = '
  • '; + $tab['#suffix'] = '
  • '; $tab['#theme'] = 'link'; $tab['#text'] = theme('image', array( 'path' => "$islandora_path/images/print-icon.png", From 35e965c7b874f998839d85bb9fab7f4dfa2b5ba7 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Thu, 2 Oct 2014 16:11:29 -0300 Subject: [PATCH 50/93] datastream entity --- islandora.module | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/islandora.module b/islandora.module index b692438e..3e4525d4 100644 --- a/islandora.module +++ b/islandora.module @@ -1511,6 +1511,14 @@ function islandora_entity_info() { 'label' => 'label', ), ); + $entities['islandora_datastream'] = array( + 'label' => t('Islandora Datastream'), + 'fieldable' => FALSE, + 'entity keys' => array( + 'id' => 'id', + 'label' => 'label', + ), + ); return $entities; } @@ -1523,39 +1531,56 @@ function islandora_entity_info() { function islandora_entity_property_info() { $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', 'label' => t('ID'), 'description' => t('The identifier of the object.'), ); - $p['label'] = array( + $object_properties['label'] = array( 'type' => 'text', 'label' => t('Object Label'), 'description' => t('The label of the object.'), ); - $p['owner'] = array( + $object_properties['owner'] = array( 'type' => 'text', 'label' => t('Object Owner'), 'description' => t('The name of the owner of the object.'), ); - $p['state'] = array( + $object_properties['state'] = array( 'type' => 'text', 'label' => t('Object State'), 'description' => t('An initial representing the state of the object.'), ); - $p['models'] = array( + $object_properties['models'] = array( 'type' => 'list', 'label' => t('Content Models'), 'description' => t('The list of content models which the object has.'), ); - $p['createdDate'] = array( + $object_properties['createdDate'] = array( 'type' => 'text', 'label' => t('Created Date'), '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['label'] = array( + 'type' => 'text', + 'label' => t('Datastream Label'), + 'description' => t('The label of the datastream.'), + ); + $datastream_properties['mimetype'] = array( + 'type' => 'text', + 'label' => t('MIME type'), + 'description' => t('Content type of the datastream.'), + ); + return $info; } From f548041cf7dbea2234e4b9d91720a77441dc03a6 Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Fri, 3 Oct 2014 13:59:12 -0300 Subject: [PATCH 51/93] Updated mimetype mappings. --- includes/mime_detect.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index da2c69f3..a0c3dcf2 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -214,7 +214,6 @@ class MimeDetect { // OpenDX Grid. "dx" => "chemical/x-jcamp-dx", // MOPAC Cartesian. - "mop" => "chemical/x-mopac-input", // Compressed formats: // (note: http://svn.cleancode.org/svn/email/trunk/mime.types) "tgz" => "application/x-gzip", @@ -222,6 +221,7 @@ class MimeDetect { "tar" => "application/x-tar", "gtar" => "application/x-gtar", "zip" => "application/x-zip", + "dat" => "application/octet-stream", // others: 'bin' => 'application/octet-stream', // Web Archives: From 4d9a1ce26ef558c53d5b95d7975d95b7530d6fb2 Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Fri, 3 Oct 2014 14:14:22 -0300 Subject: [PATCH 52/93] added mop back to mimedetect. --- includes/mime_detect.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index a0c3dcf2..8da0ebe7 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -214,6 +214,7 @@ class MimeDetect { // OpenDX Grid. "dx" => "chemical/x-jcamp-dx", // MOPAC Cartesian. + "mop" => "chemical/x-mopac-input", // Compressed formats: // (note: http://svn.cleancode.org/svn/email/trunk/mime.types) "tgz" => "application/x-gzip", From 03ca4ac116d546f3f4a6811315f355b5013f9cc8 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Wed, 8 Oct 2014 17:09:40 -0300 Subject: [PATCH 53/93] Basic implentation of other hooks and XPath Rules condition. --- includes/tuque_wrapper.inc | 4 ++ islandora.module | 8 ++++ islandora.rules.inc | 92 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 2f4ea1df..53942554 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -228,6 +228,10 @@ class IslandoraFedoraObject extends FedoraObject { if ($this->state == 'D') { islandora_invoke_object_hooks(ISLANDORA_OBJECT_PURGED_HOOK, $this->models, $this->id); } + // Fire of event if rules is enabled. + if (module_exists('rules')) { + rules_invoke_event('islandora_object_modified', $object); + } } catch (Exception $e) { watchdog('islandora', 'Failed to modify object: @pid
    code: @code
    message: @msg', array( diff --git a/islandora.module b/islandora.module index 603ab6a0..0b2a3ce8 100644 --- a/islandora.module +++ b/islandora.module @@ -1762,6 +1762,10 @@ function islandora_islandora_object_ingested(AbstractObject $object) { * equal to the current ingested datastream's id. */ function islandora_islandora_datastream_ingested(AbstractObject $object, AbstractDatastream $datastream) { + if (module_exists('rules')) { + rules_invoke_event('islandora_datastream_ingested', $object, $datastream->id); + } + module_load_include('inc', 'islandora', 'includes/derivatives'); // Defer derivatives if necessary. if (islandora_get_defer_derivatives_flag($object)) { @@ -1778,6 +1782,10 @@ function islandora_islandora_datastream_ingested(AbstractObject $object, Abstrac * existing derivatives will be updated to reflect the change in the source. */ function islandora_islandora_datastream_modified(AbstractObject $object, AbstractDatastream $datastream) { + if (module_exists('rules')) { + rules_invoke_event('islandora_datastream_modified', $object, $datastream->id); + } + module_load_include('inc', 'islandora', 'includes/derivatives'); $logging_results = islandora_do_derivatives($object, array( 'source_dsid' => $datastream->id, diff --git a/islandora.rules.inc b/islandora.rules.inc index 02f5c822..17fa8227 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -21,6 +21,49 @@ function islandora_rules_event_info() { ), ), ), + 'islandora_datastream_ingested' => array( + 'group' => t('Islandora'), + 'label' => t('Datastream ingested'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('The ingested object'), + 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), + ), + 'datastream' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('The ID of the ingested datastream.'), + ), + ), + ), + 'islandora_object_modified' => array( + 'group' => t('Islandora'), + 'label' => t('Object modified'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('The modified object'), + 'description' => t('A Tuque object for the modified Fedora object, as an entity.'), + ), + ), + ), + 'islandora_datastream_modified' => array( + 'group' => t('Islandora'), + 'label' => t('Datastream modified'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('The modified object'), + 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), + ), + 'datastream' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('The ID of the ingested datastream.'), + ), + ), + ), ); } @@ -60,6 +103,32 @@ function islandora_rules_relationship_parameter_array() { ); } +function islandora_rules_base_xpath_parameters() { + return array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The object in which to check the XPath.'), + ), + 'datastream' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('The ID of the XML datastream to check.'), + ), + 'xpath' => array( + 'type' => 'text', + 'label' => t('XPath'), + 'description' => t('An XPath to evaluate.'), + ), + 'xpath_namespaces' => array( + 'type' => 'taxonomy_vocabulary', + 'label' => t('XPath Namespace Taxonomy'), + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + ), + ); +} + + /** * Implements hook_rules_condition_info(). */ @@ -71,6 +140,11 @@ function islandora_rules_condition_info() { 'group' => t('Islandora'), 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_rules_datastream_has_xpath'] = array( + 'label' => t('Check for an XPath match in an XML datastream'), + 'group' => t('Islandora'), + 'parameter' => islandora_rules_base_xpath_parameters(), + ); return $cond; } @@ -92,6 +166,7 @@ function islandora_rules_action_info() { 'group' => t('Islandora'), 'parameter' => islandora_rules_relationship_parameter_array(), ); + return $cond; } /** @@ -133,3 +208,20 @@ function islandora_object_remove_relationship($sub, $pred_uri, $pred, $object, $ function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $type) { $sub->relationships->add($pred_uri, $pred, $object, $type); } + +function islandora_rules_datastream_has_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab) { + if (!isset($object[$datastream_id])) { + // Datastream does no exists... No match. + return FALSE; + } + + $datastream = $object[$datastream_id]; + $doc = new DOMDocument(); + $doc->loadXML($datastream->content); + $xpath = new DOMXPath($doc); + foreach (taxonomy_get_tree($xpath_vocab->vid, 0, 1, FALSE) as $term) { + $xpath->registerNamespace($term->name, $term->description); + } + $result = $xpath->query($search_xpath); + return $result->length > 0; +} From 5ee659430739a7b01260dfe840c5177c4927f8df Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 9 Oct 2014 12:03:50 -0300 Subject: [PATCH 54/93] forgot you can't just run the parent run() --- islandora.module | 26 ++++++++- tests/includes/islandora_web_test_case.inc | 66 ++++++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/islandora.module b/islandora.module index 626f4921..a79391f0 100644 --- a/islandora.module +++ b/islandora.module @@ -1768,7 +1768,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { 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_path = variable_get('islandora_simpletest_drupal_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())) . " "; @@ -1778,7 +1778,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { } else { $filter_status_message = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())) . " "; - $filter_status_message .= t("Drupal filter at !filter_path is not writable by the server. Tests relying on the filter will be disabled. Please make sure your webserver has permission to write to the Drupal filter. If the path given is incorrect, you will need to change the drupal_filter_file entry in the Islandora module's tests/test_config.ini or tests/default.test_config.ini file.", array( + $filter_status_message .= t("Drupal filter at !filter_path 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, try setting the correct path in the test settings to see if this error is no longer present.", array( '!filter_path' => $filter_path, )); } @@ -1808,7 +1808,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { '#title' => filter_xss($info['name']), '#description' => filter_xss($info['description']), ); - if (is_subclass_of($class, 'IslandoraWebTestCase') && !$filter_status) { + if (get_parent_class($class) === 'IslandoraWebTestCase' && !$filter_path) { $form['tests']['table'][$group][$class]['#disabled'] = TRUE; } } @@ -1834,6 +1834,26 @@ function islandora_form_simpletest_test_form_alter(array &$form) { ); } +/** + * Implements hook_form_alter(). + */ +function islandora_form_simpletest_settings_form_alter(&$form, &$form_state) { + module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction'); + $configuration = IslandoraTestUtilityClass::getTestConfiguration(); + $form['islandora_drupal_filter'] = array( + '#type' => 'fieldset', + '#title' => t('Islandora Drupal Filter Location'), + '#collapsible' => FALSE, + '#collapsed' => FALSE, + ); + $form['islandora_drupal_filter']['islandora_simpletest_drupal_filter_path'] = array( + '#type' => 'textfield', + '#title' => t('Path to the Drupal Filter'), + '#description' => t('The absolute path on the server to the Drupal filter XML file in the Fedora configuration. NOTE: This is simply used to populate the at-a-glance "is the Filter writeable" notice on the top of the list of tests, and DOES NOT actually have any bearing on its function within Islandora.'), + '#default_value' => variable_get('islandora_simpletest_drupal_filter_path', $configuration['drupal_filter_file']), + ); +} + /** * Submit handler for islandora_form_simpletest_test_form_alter(). */ diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc index 8e29d893..fac1b725 100644 --- a/tests/includes/islandora_web_test_case.inc +++ b/tests/includes/islandora_web_test_case.inc @@ -21,6 +21,17 @@ class IslandoraWebTestCase extends DrupalWebTestCase { */ 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. * @@ -28,6 +39,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * The method being called. * @param array $args * The arguments for that method. + * + * @return bool + * TRUE if the result was a pass, or FALSE otherwise. */ public function __call($method, $args) { module_load_include('inc', 'islandora', 'tests/includes/utilities'); @@ -66,10 +80,50 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * @see DrupalWebTestCase::run() */ public function run(array $methods = array()) { - $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); + // Determine if the Drupal filter is writable so we know if we can proceed. if (is_writable($this->configuration['drupal_filter_file'])) { - parent::run($methods); + // 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); @@ -80,9 +134,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase { '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(); } + drupal_get_messages(); + restore_error_handler(); } /** @@ -169,7 +223,6 @@ class IslandoraWebTestCase extends DrupalWebTestCase { islandora_repair_drupal_filter(); } unset($this->admin); - unset($this->configuration); parent::tearDown(); } @@ -205,6 +258,9 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * The label of the first 'Delete' button * @param bool $safety * If TRUE, this will only delete objects owned by users in $this->users. + * + * @return bool + * If the deletion fails, return FALSE TRUE, this will only delete objects owned by users in $this->users. */ public function deleteObject($pid, $button = NULL, $safety = TRUE) { $object = islandora_object_load($pid); From 8d296881f0c30dae59c8b108f868fba869ce945a Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 9 Oct 2014 12:45:07 -0300 Subject: [PATCH 55/93] coder issues --- tests/includes/islandora_web_test_case.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc index fac1b725..16146b12 100644 --- a/tests/includes/islandora_web_test_case.inc +++ b/tests/includes/islandora_web_test_case.inc @@ -92,8 +92,8 @@ class IslandoraWebTestCase extends DrupalWebTestCase { } 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. + // 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); @@ -260,7 +260,8 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * If TRUE, this will only delete objects owned by users in $this->users. * * @return bool - * If the deletion fails, return FALSE TRUE, this will only delete objects owned by users in $this->users. + * If the deletion fails, return FALSE TRUE, this will only delete objects + * owned by users in $this->users. */ public function deleteObject($pid, $button = NULL, $safety = TRUE) { $object = islandora_object_load($pid); From e77af2db9c478c1401ff8c7e77fb75d76481a564 Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 9 Oct 2014 13:59:19 -0300 Subject: [PATCH 56/93] accidental reversion of features --- islandora.module | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/islandora.module b/islandora.module index 920025c2..eceaecbf 100644 --- a/islandora.module +++ b/islandora.module @@ -1793,7 +1793,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { module_load_include('inc', 'simpletest', 'simpletest.pages'); module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction'); $configuration = IslandoraTestUtilityClass::getTestConfiguration(); - $filter_path = variable_get('islandora_simpletest_drupal_filter_path', $configuration['drupal_filter_file']); + $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())) . " "; @@ -1803,7 +1803,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { } else { $filter_status_message = theme_image(array('path' => 'misc/watchdog-error.png', 'attributes' => array())) . " "; - $filter_status_message .= t("Drupal filter at !filter_path 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, try setting the correct path in the test settings to see if this error is no longer present.", array( + $filter_status_message .= t("Drupal filter at !filter_path 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, )); } @@ -1860,27 +1860,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { } /** - * Implements hook_form_alter(). - */ -function islandora_form_simpletest_settings_form_alter(&$form, &$form_state) { - module_load_include('inc', 'islandora', 'tests/includes/test_utility_abstraction'); - $configuration = IslandoraTestUtilityClass::getTestConfiguration(); - $form['islandora_drupal_filter'] = array( - '#type' => 'fieldset', - '#title' => t('Islandora Drupal Filter Location'), - '#collapsible' => FALSE, - '#collapsed' => FALSE, - ); - $form['islandora_drupal_filter']['islandora_simpletest_drupal_filter_path'] = array( - '#type' => 'textfield', - '#title' => t('Path to the Drupal Filter'), - '#description' => t('The absolute path on the server to the Drupal filter XML file in the Fedora configuration. NOTE: This is simply used to populate the at-a-glance "is the Filter writeable" notice on the top of the list of tests, and DOES NOT actually have any bearing on its function within Islandora.'), - '#default_value' => variable_get('islandora_simpletest_drupal_filter_path', $configuration['drupal_filter_file']), - ); -} - -/** - * Submit handler for islandora_form_simpletest_test_form_alter(). + * Removes simpletest entries from the Drupal filter. */ function islandora_repair_drupal_filter() { From e96ae222895f65292686ddf0ab150906fee8a61b Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 9 Oct 2014 14:01:57 -0300 Subject: [PATCH 57/93] accidental reversion of even more features --- islandora.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.module b/islandora.module index eceaecbf..f43b545c 100644 --- a/islandora.module +++ b/islandora.module @@ -1833,7 +1833,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { '#title' => filter_xss($info['name']), '#description' => filter_xss($info['description']), ); - if (get_parent_class($class) === 'IslandoraWebTestCase' && !$filter_path) { + if (is_subclass_of($class, 'IslandoraWebTestCase') && !$filter_path) { $form['tests']['table'][$group][$class]['#disabled'] = TRUE; } } From 3f2e923c928f66d7908ed208d136ae27fd6ff804 Mon Sep 17 00:00:00 2001 From: qadan Date: Thu, 9 Oct 2014 14:20:28 -0300 Subject: [PATCH 58/93] will the accidental reversions never end --- islandora.module | 2 +- tests/default.test_config.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/islandora.module b/islandora.module index f43b545c..b6c40f1b 100644 --- a/islandora.module +++ b/islandora.module @@ -1833,7 +1833,7 @@ function islandora_form_simpletest_test_form_alter(array &$form) { '#title' => filter_xss($info['name']), '#description' => filter_xss($info['description']), ); - if (is_subclass_of($class, 'IslandoraWebTestCase') && !$filter_path) { + if (is_subclass_of($class, 'IslandoraWebTestCase', TRUE) && !$filter_status) { $form['tests']['table'][$group][$class]['#disabled'] = TRUE; } } diff --git a/tests/default.test_config.ini b/tests/default.test_config.ini index f07fb4c3..28128574 100644 --- a/tests/default.test_config.ini +++ b/tests/default.test_config.ini @@ -1,6 +1,6 @@ [fedora] fedora_url = "http://localhost:8080/fedora" use_drupal_filter = TRUE -drupal_filter_file = "/usr/local/fedora/server/config/filter-drupal.xml" +drupal_filter_file = "/usr/local/fedora/server/config/filter-drupal.xmll" admin_user = "fedoraAdmin" admin_pass = "fedoraAdmin" From fd95c8dce72a8c6fbb7e36021b448781a3055909 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Fri, 10 Oct 2014 16:08:23 -0300 Subject: [PATCH 59/93] Somewhat further. Have a one-off and the ability to query, iterate and set values of multiple nodes in XML in one shot. Going to add some temporary "entity" implementation. --- includes/object.entity_controller.inc | 1 + includes/tuque_wrapper.inc | 8 - includes/utilities.inc | 10 +- islandora.info | 1 + islandora.module | 25 ++- islandora.rules.inc | 234 +++++++++++++++++++++++--- 6 files changed, 242 insertions(+), 37 deletions(-) diff --git a/includes/object.entity_controller.inc b/includes/object.entity_controller.inc index 9464d7b5..87b5e4de 100644 --- a/includes/object.entity_controller.inc +++ b/includes/object.entity_controller.inc @@ -30,6 +30,7 @@ class IslandoraObjectEntityController implements DrupalEntityControllerInterface */ public function load($ids = array(), $conditions = array()) { if (!empty($conditions)) { + // TODO: Allow loading by specifying IDs in the condition. throw new Exception('Conditions not implemented.'); } diff --git a/includes/tuque_wrapper.inc b/includes/tuque_wrapper.inc index 53942554..d14a86b3 100644 --- a/includes/tuque_wrapper.inc +++ b/includes/tuque_wrapper.inc @@ -115,10 +115,6 @@ class IslandoraFedoraRepository extends FedoraRepository { foreach ($object as $dsid => $datastream) { islandora_invoke_datastream_hooks(ISLANDORA_DATASTREAM_INGESTED_HOOK, $object->models, $dsid, $object, $datastream); } - // Fire of event if rules is enabled. - if (module_exists('rules')) { - rules_invoke_event('islandora_object_ingested', $object); - } return $ret; } catch (Exception $e) { @@ -228,10 +224,6 @@ class IslandoraFedoraObject extends FedoraObject { if ($this->state == 'D') { islandora_invoke_object_hooks(ISLANDORA_OBJECT_PURGED_HOOK, $this->models, $this->id); } - // Fire of event if rules is enabled. - if (module_exists('rules')) { - rules_invoke_event('islandora_object_modified', $object); - } } catch (Exception $e) { watchdog('islandora', 'Failed to modify object: @pid
    code: @code
    message: @msg', array( diff --git a/includes/utilities.inc b/includes/utilities.inc index 26633df5..7476948b 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -188,12 +188,18 @@ function islandora_describe_repository($url = NULL) { */ function islandora_invoke_hook_list($hook, array $refinements, array $args) { $return = array(); - foreach (islandora_build_hook_list($hook, $refinements) as $hook) { - array_unshift($args, $hook); + foreach (islandora_build_hook_list($hook, $refinements) as $refined_hook) { + array_unshift($args, $refined_hook); $result = call_user_func_array('module_invoke_all', $args); $return = array_merge_recursive($return, $result); array_shift($args); } + if (module_exists('rules') && $event = rules_get_cache("event_$hook")) { + $parameters = $event->parameterInfo(); + $rule_args = array_slice($args, 0, count($parameters)); + array_unshift($rule_args, $hook); + $result = call_user_func_array('rules_invoke_event', $rule_args); + } return $return; } diff --git a/islandora.info b/islandora.info index 55c50359..580a81ac 100644 --- a/islandora.info +++ b/islandora.info @@ -13,6 +13,7 @@ files[] = includes/dublin_core.inc files[] = includes/tuque.inc files[] = includes/tuque_wrapper.inc files[] = includes/object.entity_controller.inc +files[] = includes/datastream.entity_controller.inc files[] = tests/includes/datastream_validators.inc files[] = tests/includes/islandora_web_test_case.inc files[] = tests/includes/islandora_unit_test_case.inc diff --git a/islandora.module b/islandora.module index 0b2a3ce8..85bcd2bc 100644 --- a/islandora.module +++ b/islandora.module @@ -1543,21 +1543,25 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Object Label'), 'description' => t('The label of the object.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['owner'] = array( 'type' => 'text', 'label' => t('Object Owner'), 'description' => t('The name of the owner of the object.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['state'] = array( 'type' => 'text', 'label' => t('Object State'), 'description' => t('An initial representing the state of the object.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['models'] = array( 'type' => 'list', 'label' => t('Content Models'), 'description' => t('The list of content models which the object has.'), + 'setter callback' => 'entity_property_verbatim_set', ); $object_properties['createdDate'] = array( 'type' => 'text', @@ -1574,11 +1578,24 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Datastream Label'), 'description' => t('The label of the datastream.'), + 'setter callback' => 'entity_property_verbatim_set', ); $datastream_properties['mimetype'] = array( 'type' => 'text', 'label' => t('MIME type'), 'description' => t('Content type of the datastream.'), + 'setter callback' => 'entity_property_verbatim_set', + ); + $datastream_properties['parent'] = array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The Tuque object on which this datastream exists.'), + ); + $datastream_properties['content'] = array( + 'type' => 'text', + 'label' => t('Datastream content'), + 'description' => t('The contents of the datastream; only useful when the content is textual.'), + 'setter callback' => 'entity_property_verbatim_set', ); return $info; @@ -1762,10 +1779,6 @@ function islandora_islandora_object_ingested(AbstractObject $object) { * equal to the current ingested datastream's id. */ function islandora_islandora_datastream_ingested(AbstractObject $object, AbstractDatastream $datastream) { - if (module_exists('rules')) { - rules_invoke_event('islandora_datastream_ingested', $object, $datastream->id); - } - module_load_include('inc', 'islandora', 'includes/derivatives'); // Defer derivatives if necessary. if (islandora_get_defer_derivatives_flag($object)) { @@ -1782,10 +1795,6 @@ function islandora_islandora_datastream_ingested(AbstractObject $object, Abstrac * existing derivatives will be updated to reflect the change in the source. */ function islandora_islandora_datastream_modified(AbstractObject $object, AbstractDatastream $datastream) { - if (module_exists('rules')) { - rules_invoke_event('islandora_datastream_modified', $object, $datastream->id); - } - module_load_include('inc', 'islandora', 'includes/derivatives'); $logging_results = islandora_do_derivatives($object, array( 'source_dsid' => $datastream->id, diff --git a/islandora.rules.inc b/islandora.rules.inc index 17fa8227..aca1d9dc 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -31,7 +31,7 @@ function islandora_rules_event_info() { 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), ), 'datastream' => array( - 'type' => 'text', + 'type' => 'islandora_datastream', 'label' => t('Datastream ID'), 'description' => t('The ID of the ingested datastream.'), ), @@ -57,10 +57,37 @@ function islandora_rules_event_info() { 'label' => t('The modified object'), 'description' => t('A Tuque object for the Fedora object on which the datastream exists, as an entity.'), ), + 'datastream' => array( + 'type' => 'islandora_datastream', + 'label' => t('Datastream'), + 'description' => t('The modified datastream.'), + ), + ), + ), + 'islandora_object_purged' => array( + 'group' => t('Islandora'), + 'label' => t('Object purged'), + 'variables' => array( + 'object' => array( + 'type' => 'text', + 'label' => t('Object ID'), + 'description' => t('The ID of the purged object.'), + ), + ), + ), + 'islandora_datastream_purged' => array( + 'group' => t('Islandora'), + 'label' => t('Datastream purged'), + 'variables' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('A Tuque object for the Fedora object from which the datastream exists, as an entity.'), + ), 'datastream' => array( 'type' => 'text', 'label' => t('Datastream ID'), - 'description' => t('The ID of the ingested datastream.'), + 'description' => t('The identifier of the purged datastream.'), ), ), ), @@ -105,15 +132,10 @@ function islandora_rules_relationship_parameter_array() { function islandora_rules_base_xpath_parameters() { return array( - 'object' => array( - 'type' => 'islandora_object', - 'label' => t('Object'), - 'description' => t('The object in which to check the XPath.'), - ), 'datastream' => array( - 'type' => 'text', - 'label' => t('Datastream ID'), - 'description' => t('The ID of the XML datastream to check.'), + 'type' => 'islandora_datastream', + 'label' => t('Datastream'), + 'description' => t('The XML datastream to check.'), ), 'xpath' => array( 'type' => 'text', @@ -167,8 +189,94 @@ function islandora_rules_action_info() { 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_rules_datastream_load_domxpath'] = array( + 'label' => t('Load a DOMXPath for a given XML.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'datastrea,' => array( + 'type' => 'text', + 'label' => t('XML'), + 'description' => t('A string containing the XML to load.'), + ), + ), + 'provides' => array( + 'islandora_domxpath' => array( + 'type' => 'islandora_domxpath', + 'label' => t('Loaded DOMXPath instance'), + ), + ), + ); + $cond['islandora_rules_datastream_load_xpath'] = array( + 'label' => t('Load a DOMXPath from a datastream.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'datastream' => array( + 'type' => 'islandora_datastream', + 'label' => t('Datastream'), + 'description' => t('A datastream containing the XML to load.'), + ), + ), + 'provides' => array( + 'islandora_domxpath' => array( + 'type' => 'islandora_domxpath', + 'label' => t('Loaded DOMXPath instance'), + ), + ), + ); + $cond['islandora_rules_datastream_load_namespace_vocab'] = array( + 'label' => t('Register namespaces on a DOMXPath instance.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'value' => array( + 'type' => 'islandora_domxpath', + 'label' => t('DOMXPath instance'), + 'description' => t('The DOMXPath instance on which to register the namespaces.'), + ), + 'xpath_namespaces' => array( + 'type' => 'taxonomy_vocabulary', + 'label' => t('XPath Namespace Taxonomy'), + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + ), + ), + ); + $cond['islandora_rules_datastream_query_xpath'] = array( + 'label' => t('Query nodes from DOMXPath instance.'), + 'group' => t('Islandora DOMXPath'), + 'parameter' => array( + 'xpath' => array( + 'type' => 'islandora_domxpath', + 'label' => t('DOMXPath instance'), + 'description' => t('The DOMXPath instance on which to perform the query.'), + ), + 'query' => array( + 'type' => 'text', + 'label' => t('XPath query'), + 'description' => t('The XPath query to perform.') + ), + ), + 'provides' => array( + 'nodes' => array( + 'type' => 'list', + 'label' => t('Queried DOMNode elements'), + ), + ), + ); + + $cond['islandora_rules_datastream_set_xpath'] = array( + 'label' => t('Set value in elements matched by an XPath in an XML datastream'), + 'group' => t('Islandora'), + 'parameter' => islandora_rules_base_xpath_parameters() + array( + 'value' => array( + 'type' => 'text', + 'label' => t('Value'), + 'description' => t('The value to set in the XML on elements matched by the XPath.'), + ), + ), + ); + return $cond; } + /** * Checks that there is a relationship match on the given object. * @@ -209,19 +317,107 @@ function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $typ $sub->relationships->add($pred_uri, $pred, $object, $type); } -function islandora_rules_datastream_has_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab) { - if (!isset($object[$datastream_id])) { - // Datastream does no exists... No match. - return FALSE; - } - - $datastream = $object[$datastream_id]; +function islandora_rules_datastream_load_domxpath($string) { $doc = new DOMDocument(); - $doc->loadXML($datastream->content); + $doc->loadXML($string); $xpath = new DOMXPath($doc); + return array('islandora_domxpath' => $xpath); +} + +function islandora_rules_datastream_load_namespace_vocab($xpath, $xpath_vocab) { foreach (taxonomy_get_tree($xpath_vocab->vid, 0, 1, FALSE) as $term) { $xpath->registerNamespace($term->name, $term->description); } - $result = $xpath->query($search_xpath); +} + +/** + * Rules XPath helper; grab the datastream content and build a DOMXPath. + */ +function islandora_rules_datastream_load_xpath(AbstractDatastream $datastream, $xpath_vocab) { + $result = islandora_rules_datastream_load_domxpath($datastream->content, $xpath_vocab); + islandora_rules_datastream_load_namespace_vocab($result['islandora_domxpath'], $xpath_vocab); + return $result; +} + +function islandora_rules_datastream_has_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab) { + $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); + $result = $xpath['islandora_domxpath']->query($search_xpath); return $result->length > 0; } + +function islandora_rules_datastream_set_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab, $value) { + $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); + $result = $xpath['islandora_domxpath']->query($search_xpath); + foreach ($result as $node) { + $node->nodeValue = $value; + } + $datastream->content = $xpath['islandora_domxpath']->document->saveXML(); +} + +/** + * Implements hook_rules_data_info(). + */ +function islandora_rules_data_info() { + return array( + 'islandora_domxpath' => array( + 'label' => t('DOMXPath instance'), + 'group' => t('Islandora'), + 'property info' => array( + 'content' => array( + 'type' => 'text', + 'label' => t('XML Content'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_get_domxpath_document_content', + 'token type' => 'string', + ), + ), + 'wrap' => TRUE, + ), + 'islandora_domnode' => array( + 'label' => t('DOMNode instance'), + 'group' => t('Islandora'), + 'property info' => array( + 'node_value' => array( + 'type' => 'text', + 'label' => t('Node value'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_domnode_get', + 'setter callback' => 'islandora_rules_domnode_set', + 'property' => 'nodeValue', + 'token type' => 'string', + ), + 'text_content' => array( + 'type' => 'text', + 'label' => t('Text content'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_domnode_get', + 'property' => 'textContent', + 'token type' => 'string', + ), + ), + 'wrap' => TRUE, + ), + 'islandora_domelement' => array( + 'label' => t('DOMElement instance'), + 'group' => t('Islandora'), + 'parent' => 'islandora_domnode', + 'wrap' => TRUE, + ), + ); +} + +function islandora_rules_domnode_get($data, $options, $name, $type, $info) { + return $data->$info['property']; +} + +function islandora_rules_domnode_set(&$data, $name, $value, $langcode, $type, $info) { + $data->$info['property'] = $value; +} + +function islandora_rules_get_domxpath_document_content(DOMXPath $xpath) { + return $xpath->document->saveXML(); +} + +function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query) { + return array('nodes' => iterator_to_array($xpath->query($query))); +} From 2bc288d8b125bed581ca15280c0c55b75a4c1cd1 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 13:27:30 -0300 Subject: [PATCH 60/93] Fix setting of properties and flesh out comments a bit. --- islandora.module | 26 +++++-- islandora.rules.inc | 184 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 183 insertions(+), 27 deletions(-) diff --git a/islandora.module b/islandora.module index 85bcd2bc..b98b713a 100644 --- a/islandora.module +++ b/islandora.module @@ -1543,25 +1543,25 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Object Label'), 'description' => t('The label of the object.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['owner'] = array( 'type' => 'text', 'label' => t('Object Owner'), 'description' => t('The name of the owner of the object.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['state'] = array( 'type' => 'text', 'label' => t('Object State'), 'description' => t('An initial representing the state of the object.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['models'] = array( 'type' => 'list', 'label' => t('Content Models'), 'description' => t('The list of content models which the object has.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $object_properties['createdDate'] = array( 'type' => 'text', @@ -1578,13 +1578,13 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Datastream Label'), 'description' => t('The label of the datastream.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $datastream_properties['mimetype'] = array( 'type' => 'text', 'label' => t('MIME type'), 'description' => t('Content type of the datastream.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); $datastream_properties['parent'] = array( 'type' => 'islandora_object', @@ -1595,7 +1595,7 @@ function islandora_entity_property_info() { 'type' => 'text', 'label' => t('Datastream content'), 'description' => t('The contents of the datastream; only useful when the content is textual.'), - 'setter callback' => 'entity_property_verbatim_set', + 'setter callback' => 'islandora_entity_set_property', ); return $info; @@ -2018,3 +2018,15 @@ function islandora_islandora_object_alter(AbstractObject $object, array &$contex islandora_set_defer_derivatives_flag($object); } } + +/** + * Set property on an entity. + * + * Drupal's usual entity_property_verbatim_set() does not work, as tries to use + * ArrayAccess stuff instead of setting properties directly. + */ +function islandora_entity_set_property(&$data, $name, $value, $langcode) { + if (is_object($data)) { + $data->$name = $value; + } +} diff --git a/islandora.rules.inc b/islandora.rules.inc index aca1d9dc..96e1fa5c 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -130,12 +130,20 @@ function islandora_rules_relationship_parameter_array() { ); } +/** + * Helper function; get default parameters for the XPath condition and action. + */ function islandora_rules_base_xpath_parameters() { return array( - 'datastream' => array( - 'type' => 'islandora_datastream', + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The object containing the datastream to check.'), + ), + 'datastream_id' => array( + 'type' => 'text', 'label' => t('Datastream'), - 'description' => t('The XML datastream to check.'), + 'description' => t('The identifier of the XML datastream to check.'), ), 'xpath' => array( 'type' => 'text', @@ -145,7 +153,7 @@ function islandora_rules_base_xpath_parameters() { 'xpath_namespaces' => array( 'type' => 'taxonomy_vocabulary', 'label' => t('XPath Namespace Taxonomy'), - 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.'), ), ); } @@ -162,6 +170,22 @@ function islandora_rules_condition_info() { 'group' => t('Islandora'), 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_object_has_datastream'] = array( + 'label' => t('Check object for existence of a datastream'), + 'group' => t('Islandora'), + 'parameter' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('The object containing the datastream to check.'), + ), + 'datastream_id' => array( + 'type' => 'text', + 'label' => t('Datastream'), + 'description' => t('The identifier of the datastream to check.'), + ), + ), + ); $cond['islandora_rules_datastream_has_xpath'] = array( 'label' => t('Check for an XPath match in an XML datastream'), 'group' => t('Islandora'), @@ -189,11 +213,33 @@ function islandora_rules_action_info() { 'parameter' => islandora_rules_relationship_parameter_array(), ); + $cond['islandora_rules_datastream_load'] = array( + 'label' => t('Load a datastream from an object.'), + 'group' => t('Islandora'), + 'parameter' => array( + 'object' => array( + 'type' => 'islandora_object', + 'label' => t('Object'), + 'description' => t('A Tuque object for the Fedora object from which to load the datastream, as an entity.'), + ), + 'datastream_id' => array( + 'type' => 'text', + 'label' => t('Datastream ID'), + 'description' => t('A string containing the identity of the datastream to load from the object.'), + ), + ), + 'provides' => array( + 'datastream' => array( + 'type' => 'islandora_datastream', + 'label' => t('Loaded datastream instance'), + ), + ), + ); $cond['islandora_rules_datastream_load_domxpath'] = array( 'label' => t('Load a DOMXPath for a given XML.'), 'group' => t('Islandora DOMXPath'), 'parameter' => array( - 'datastrea,' => array( + 'datastream' => array( 'type' => 'text', 'label' => t('XML'), 'description' => t('A string containing the XML to load.'), @@ -235,7 +281,7 @@ function islandora_rules_action_info() { 'xpath_namespaces' => array( 'type' => 'taxonomy_vocabulary', 'label' => t('XPath Namespace Taxonomy'), - 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.') + 'description' => t('A flat taxonomy of which the terms are namespace prefixes and the description contains the URI for the namespace.'), ), ), ); @@ -251,7 +297,7 @@ function islandora_rules_action_info() { 'query' => array( 'type' => 'text', 'label' => t('XPath query'), - 'description' => t('The XPath query to perform.') + 'description' => t('The XPath query to perform.'), ), ), 'provides' => array( @@ -277,6 +323,13 @@ function islandora_rules_action_info() { return $cond; } +/** + * Rules action callback; grab a datastream from an object. + */ +function islandora_rules_datastream_load(AbstractObject $object, $datastream_id) { + return array('datastream' => $object[$datastream_id]); +} + /** * Checks that there is a relationship match on the given object. * @@ -317,6 +370,9 @@ function islandora_object_add_relationship($sub, $pred_uri, $pred, $object, $typ $sub->relationships->add($pred_uri, $pred, $object, $type); } +/** + * Rules Action callback; instantiate a DOMXPath with some XML. + */ function islandora_rules_datastream_load_domxpath($string) { $doc = new DOMDocument(); $doc->loadXML($string); @@ -324,6 +380,15 @@ function islandora_rules_datastream_load_domxpath($string) { return array('islandora_domxpath' => $xpath); } +/** + * Rules Action callback; load namespaces onto a DOMXPath instance. + * + * @param DOMXPath $xpath + * A DOMXPath instance. + * @param object $xpath_vocab + * A loaded Drupal taxonomy vocabulary object, in which terms are understood + * to be namespace prefixes and descriptions are the namespace URIs. + */ function islandora_rules_datastream_load_namespace_vocab($xpath, $xpath_vocab) { foreach (taxonomy_get_tree($xpath_vocab->vid, 0, 1, FALSE) as $term) { $xpath->registerNamespace($term->name, $term->description); @@ -339,13 +404,21 @@ function islandora_rules_datastream_load_xpath(AbstractDatastream $datastream, $ return $result; } -function islandora_rules_datastream_has_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab) { +/** + * Rules Condition callback; test that an XPath returns a non-empty result set. + */ +function islandora_rules_datastream_has_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab) { + $datastream = $object[$datastream_id]; $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); $result = $xpath['islandora_domxpath']->query($search_xpath); return $result->length > 0; } -function islandora_rules_datastream_set_xpath(AbstractDatastream $datastream, $search_xpath, $xpath_vocab, $value) { +/** + * Rules Action callback; set the value of all matched nodes. + */ +function islandora_rules_datastream_set_xpath(AbstractObject $object, $datastream_id, $search_xpath, $xpath_vocab, $value) { + $datastream = $object[$datastream_id]; $xpath = islandora_rules_datastream_load_xpath($datastream, $xpath_vocab); $result = $xpath['islandora_domxpath']->query($search_xpath); foreach ($result as $node) { @@ -364,12 +437,12 @@ function islandora_rules_data_info() { 'group' => t('Islandora'), 'property info' => array( 'content' => array( - 'type' => 'text', - 'label' => t('XML Content'), - 'computed' => TRUE, - 'getter callback' => 'islandora_rules_get_domxpath_document_content', - 'token type' => 'string', - ), + 'type' => 'text', + 'label' => t('XML Content'), + 'computed' => TRUE, + 'getter callback' => 'islandora_rules_get_domxpath_document_content', + 'token type' => 'string', + ), ), 'wrap' => TRUE, ), @@ -381,8 +454,8 @@ function islandora_rules_data_info() { 'type' => 'text', 'label' => t('Node value'), 'computed' => TRUE, - 'getter callback' => 'islandora_rules_domnode_get', - 'setter callback' => 'islandora_rules_domnode_set', + 'getter callback' => 'islandora_rules_property_get', + 'setter callback' => 'islandora_rules_property_set', 'property' => 'nodeValue', 'token type' => 'string', ), @@ -390,7 +463,7 @@ function islandora_rules_data_info() { 'type' => 'text', 'label' => t('Text content'), 'computed' => TRUE, - 'getter callback' => 'islandora_rules_domnode_get', + 'getter callback' => 'islandora_rules_property_get', 'property' => 'textContent', 'token type' => 'string', ), @@ -406,18 +479,89 @@ function islandora_rules_data_info() { ); } -function islandora_rules_domnode_get($data, $options, $name, $type, $info) { +/** + * Property setter helper; set a property on an object. + * + * In Rules, properties can contain lowercase and numeric characters. Since + * we want to refer to "nodeValue", we have to step around the Rules constraint. + * + * @param object $data + * The object on which to set the property, described by $info['property']. + * @param array $options + * An array of options... Not sure how it's used? Not touched by us, in any + * case. :P + * @param string $name + * The name of the property to set, as used by Rules. + * @param string $type + * The type of object on which the property is being set, as used by Rules. + * @param array $info + * An associative array describing this property. In addition to that + * required by Rules/the Entity API, should contain: + * - property: A string indicate the actual property on the $data we wish to + * set. + */ +function islandora_rules_property_get($data, array $options, $name, $type, $info) { return $data->$info['property']; } -function islandora_rules_domnode_set(&$data, $name, $value, $langcode, $type, $info) { +/** + * Property setter helper; set a property on an object. + * + * In Rules, properties can contain lowercase and numeric characters. Since + * we want to refer to "nodeValue", we have to step around the Rules constraint. + * + * @param object $data + * The object on which to set the property, described by $info['property']. + * @param string $name + * The name of the property to set, as used by Rules. + * @param mixed $value + * The value to set on the property. + * @param string $langcode + * A string indicating the language being set, or NULL. + * @param string $type + * The type of object on which the property is being set, as used by Rules. + * @param array $info + * An associative array describing this property. In addition to that + * required by Rules/the Entity API, should contain: + * - property: A string indicate the actual property on the $data we wish to + * set. + */ +function islandora_rules_property_set(&$data, $name, $value, $langcode, $type, $info) { $data->$info['property'] = $value; } +/** + * Rules property "get" callback; get XML contained in a DOMXPath instance. + * + * @param DOMXPath $xpath + * A DOMXPath instance. + * + * @return string + * The XML contained inside of the DOMXPath instance. + */ function islandora_rules_get_domxpath_document_content(DOMXPath $xpath) { return $xpath->document->saveXML(); } +/** + * Rules action callback; perform a query on a DOMXPath instance. + * + * @param DOMXPath $xpath + * A DOMXPath instance. + * @param string $query + * An XPath query. + * + * @return array + * An array containing: + * - nodes: An array containing the results of the query. + */ function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query) { return array('nodes' => iterator_to_array($xpath->query($query))); } + +/** + * Rules condition callback; check for the datastream on an object. + */ +function islandora_object_has_datastream(AbstractObject $object, $datastream_id) { + return isset($object[$datastream_id]); +} From f82be2ed9c39f933fa7540f699ad8995a00485bd Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 13:47:00 -0300 Subject: [PATCH 61/93] Adjust string. --- islandora.rules.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.rules.inc b/islandora.rules.inc index 96e1fa5c..be6ec855 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -82,7 +82,7 @@ function islandora_rules_event_info() { 'object' => array( 'type' => 'islandora_object', 'label' => t('Object'), - 'description' => t('A Tuque object for the Fedora object from which the datastream exists, as an entity.'), + 'description' => t('A Tuque object for the Fedora object on which the datastream existed, as an entity.'), ), 'datastream' => array( 'type' => 'text', From 3b9eeef6da1c6d31c069fce95024944e28127103 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 14:55:02 -0300 Subject: [PATCH 62/93] Add "state" property to datastreams. --- islandora.module | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/islandora.module b/islandora.module index b98b713a..ac63069c 100644 --- a/islandora.module +++ b/islandora.module @@ -1574,6 +1574,12 @@ function islandora_entity_property_info() { 'label' => t('ID'), 'description' => t('The identifier of the datastream.'), ); + $datastream_properties['state'] = array( + 'type' => 'text', + 'label' => t('Datastream State'), + 'description' => t('An initial representing the state of the datastream.'), + 'setter callback' => 'islandora_entity_set_property', + ); $datastream_properties['label'] = array( 'type' => 'text', 'label' => t('Datastream Label'), From 969ef483dc110debf69f908ca0d61667fe55b7db Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 14:55:34 -0300 Subject: [PATCH 63/93] Add ability to specify context nodes when XPathing. --- islandora.rules.inc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/islandora.rules.inc b/islandora.rules.inc index be6ec855..2562656a 100644 --- a/islandora.rules.inc +++ b/islandora.rules.inc @@ -299,6 +299,14 @@ function islandora_rules_action_info() { 'label' => t('XPath query'), 'description' => t('The XPath query to perform.'), ), + 'context_node' => array( + 'type' => 'islandora_domnode', + 'label' => t('Context Node'), + 'description' => t('If provided, the query will be performed relative to the provided node.'), + 'optional' => TRUE, + 'default value' => NULL, + 'allow null' => TRUE, + ), ), 'provides' => array( 'nodes' => array( @@ -550,13 +558,16 @@ function islandora_rules_get_domxpath_document_content(DOMXPath $xpath) { * A DOMXPath instance. * @param string $query * An XPath query. + * @param DOMNode $context_node + * An optional DOMNode. If provided, the query will be performed relative to + * the given node. * * @return array * An array containing: * - nodes: An array containing the results of the query. */ -function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query) { - return array('nodes' => iterator_to_array($xpath->query($query))); +function islandora_rules_datastream_query_xpath(DOMXPath $xpath, $query, DOMNode $context_node = NULL) { + return array('nodes' => iterator_to_array($xpath->query($query, $context_node))); } /** From c4922ff4b41b2e07553444d5572aa4366ec17214 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Tue, 14 Oct 2014 14:56:16 -0300 Subject: [PATCH 64/93] Drop reference to deleted "datastream entity controller". --- islandora.info | 1 - 1 file changed, 1 deletion(-) diff --git a/islandora.info b/islandora.info index 580a81ac..55c50359 100644 --- a/islandora.info +++ b/islandora.info @@ -13,7 +13,6 @@ files[] = includes/dublin_core.inc files[] = includes/tuque.inc files[] = includes/tuque_wrapper.inc files[] = includes/object.entity_controller.inc -files[] = includes/datastream.entity_controller.inc files[] = tests/includes/datastream_validators.inc files[] = tests/includes/islandora_web_test_case.inc files[] = tests/includes/islandora_unit_test_case.inc From 040ce23d84987c319b4afbec5e898410c7431e00 Mon Sep 17 00:00:00 2001 From: qadan Date: Wed, 15 Oct 2014 09:13:40 -0300 Subject: [PATCH 65/93] apparently not --- tests/default.test_config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/default.test_config.ini b/tests/default.test_config.ini index 28128574..f07fb4c3 100644 --- a/tests/default.test_config.ini +++ b/tests/default.test_config.ini @@ -1,6 +1,6 @@ [fedora] fedora_url = "http://localhost:8080/fedora" use_drupal_filter = TRUE -drupal_filter_file = "/usr/local/fedora/server/config/filter-drupal.xmll" +drupal_filter_file = "/usr/local/fedora/server/config/filter-drupal.xml" admin_user = "fedoraAdmin" admin_pass = "fedoraAdmin" From 1f26d08c5cd3455877b5ea9582d62a3217f5dde9 Mon Sep 17 00:00:00 2001 From: qadan Date: Wed, 15 Oct 2014 09:26:34 -0300 Subject: [PATCH 66/93] super typo city --- tests/includes/islandora_web_test_case.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc index 16146b12..5e422418 100644 --- a/tests/includes/islandora_web_test_case.inc +++ b/tests/includes/islandora_web_test_case.inc @@ -260,8 +260,7 @@ class IslandoraWebTestCase extends DrupalWebTestCase { * If TRUE, this will only delete objects owned by users in $this->users. * * @return bool - * If the deletion fails, return FALSE TRUE, this will only delete objects - * owned by users in $this->users. + * If the deletion fails, return FALSE. */ public function deleteObject($pid, $button = NULL, $safety = TRUE) { $object = islandora_object_load($pid); From 0732b7d142da13d67790d3445333fd8a48d4c0fb Mon Sep 17 00:00:00 2001 From: willtp87 Date: Wed, 5 Nov 2014 17:42:20 -0400 Subject: [PATCH 67/93] Less unnecessary datastream versions on replacement. --- includes/datastream.version.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index e97640d4..4f2a9960 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -344,7 +344,9 @@ function islandora_datastream_version_replace_form_submit($form, &$form_state) { $file = file_load($form_state['values']['file']); try { $ds = $object[$form_state['dsid']]; - $ds->mimetype = $file->filemime; + if ($ds->mimetype != $file->filemime) { + $ds->mimetype = $file->filemime; + } $path = drupal_realpath($file->uri); $ds->setContentFromFile($path); file_delete($file); From 64413a62a721788132fc5bfe7b60924d48ff7ab8 Mon Sep 17 00:00:00 2001 From: Jordan Dukart Date: Wed, 12 Nov 2014 15:27:49 -0400 Subject: [PATCH 68/93] Add json as a MIME. --- includes/mime_detect.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index 8da0ebe7..8eb681ab 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -227,6 +227,7 @@ class MimeDetect { 'bin' => 'application/octet-stream', // Web Archives: "warc" => "application/warc", + "json" => "application/json", ); protected $protectedFileExtensions; protected $extensionExceptions = array( From 1316c9a3841846c78840c4951028b65b033b71f6 Mon Sep 17 00:00:00 2001 From: Matthew Perry Date: Fri, 14 Nov 2014 12:55:56 -0400 Subject: [PATCH 69/93] Add a hidden next button at the top of the form so that it becomes the first button triggered if a user submits the form with the enter key. --- includes/ingest.form.inc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/includes/ingest.form.inc b/includes/ingest.form.inc index 6c802f44..c83b140c 100644 --- a/includes/ingest.form.inc +++ b/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['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'] = '
    '; + $form['hidden_next']['#suffix'] = '
    '; + } // Allow for a hook_form_FORM_ID_alter(). drupal_alter(array('form_' . $step['form_id'], 'form'), $form, $form_state, $step['form_id']); return $form; From b726f2d5b6cdd10e73a179b8dc39a9285293b4d5 Mon Sep 17 00:00:00 2001 From: Matthew Perry Date: Tue, 18 Nov 2014 09:05:56 -0400 Subject: [PATCH 70/93] Updated JS to ignore hidden next button when adding the spinner to the form so it does not add two. --- js/spinner.js | 59 +++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/js/spinner.js b/js/spinner.js index a755bcb4..0b4a3c74 100755 --- a/js/spinner.js +++ b/js/spinner.js @@ -22,36 +22,39 @@ }); for (var base in settings.spinner) { var id = '#' + base; - $(id, context).once('spinner', function () { - var spinner = new Spinner(settings.spinner[base].opts); - $(id).parents('form').one('submit', function(event) { - if ($(this).data('clicked').is(id)) { - event.preventDefault(); - // Add Message. - var message = $('
    ').text(settings.spinner[base].message); - $(id).after(message); - // Make UI changes. - spinner.spin(this); - $('#edit-next').hide(); - $('#edit-prev').hide(); - // Submit the form after a set timeout, this handles problems with - // safari, in that safari submit's immediately.. - if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) { - $(':submit').attr('disabled', 'disabled'); + // Don't add spinner to the hidden next/ingest button. + if (id != '#edit-hidden-next') { + $(id, context).once('spinner', function () { + var spinner = new Spinner(settings.spinner[base].opts); + $(id).parents('form').one('submit', function (event) { + if ($(this).data('clicked').is(id)) { + event.preventDefault(); + // Add Message. + var message = $('
    ').text(settings.spinner[base].message); + $(id).after(message); + // Make UI changes. + spinner.spin(this); + $('#edit-next').hide(); + $('#edit-prev').hide(); + // Submit the form after a set timeout, this handles problems with + // 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() { - // 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; + return true; + }); }); - }); + } } } }; From 7df506eaaab9a5a342542e3f65b51c6c26bfa509 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 21 Nov 2014 19:44:36 -0500 Subject: [PATCH 71/93] Allow users to re-create derivatives even if only one hook is available. --- includes/object_properties.form.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/object_properties.form.inc b/includes/object_properties.form.inc index f9c8e23b..b8fcef7e 100644 --- a/includes/object_properties.form.inc +++ b/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'); $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) { + if (count($hooks) >= 1) { $regenerate_derivatives_access = TRUE; } } From b7fb48017ec6dc0eb0bc43a54595c63522d801f0 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Fri, 5 Dec 2014 14:26:50 -0400 Subject: [PATCH 72/93] Some fixes and improovements for datastream uploads. --- includes/add_datastream.form.inc | 58 +++++++++++++++----------------- includes/datastream.version.inc | 20 ++++------- includes/mimetype.utils.inc | 28 +++++++++++++++ 3 files changed, 61 insertions(+), 45 deletions(-) diff --git a/includes/add_datastream.form.inc b/includes/add_datastream.form.inc index dd5b0dbb..380880d2 100644 --- a/includes/add_datastream.form.inc +++ b/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... // Causes issues with derivative generation when we try to use it. $form_state['object'] = $object; - $form_state['datastream_requirements'] = islandora_get_missing_datastreams_requirements($object); - $unused_datastreams = array_keys($form_state['datastream_requirements']); + $datastream_requirements = islandora_get_missing_datastreams_requirements($object); + $unused_datastreams = array_keys($datastream_requirements); $unused_datastreams = "'" . implode("', '", $unused_datastreams) . "'"; $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); return array( @@ -38,7 +38,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract ), 'dsid_fieldset' => array( '#type' => 'fieldset', - '#title' => 'Add a datastream', + '#title' => 'Add a Datastream', '#collapsible' => FALSE, '#collapsed' => FALSE, 'dsid' => array( @@ -60,7 +60,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract '#required' => TRUE, '#size' => 64, '#maxlength' => 64, - '#description' => t('A human-readable label'), + '#description' => t('A human-readable label.'), '#type' => 'textfield', '#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, '#title' => t('Upload Document'), '#size' => 48, - '#description' => t('Select a file to upload.
    Files must be less than @size MB.', array('@size' => $upload_size)), + '#description' => t('Select a file to upload.
    Files must be less than @size MB.', array('@size' => $upload_size)), '#default_value' => isset($form_state['values']['files']) ? $form_state['values']['files'] : NULL, - '#upload_location' => 'temporary://', + '#upload_location' => 'public://', '#upload_validators' => array( + // Disable default file_validate_extensions; we need direct control. 'file_validate_extensions' => array(NULL), // Assume its specified in MB. 'file_validate_size' => array($upload_size * 1024 * 1024), @@ -136,6 +137,26 @@ 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']); + if ($file) { + $errors = file_validate_extensions($file, implode(' ', $extensions)); + if (count($errors) > 0) { + form_set_error('file', "{$errors[0]} (for the set DSID)"); + } + } +} + /** * Checks if the given form field contains a "/" character. * @@ -152,31 +173,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. * diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index 4f2a9960..225e5ff3 100644 --- a/includes/datastream.version.inc +++ b/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['dsid'] = $datastream->id; $form_state['object'] = $object; - $datastream_mime_map = islandora_get_object_extensions($object); - $current_mime = $datastream->mimetype; - $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)); - } + + $extensions = islandora_get_extensions_for_datastream($object, $datastream->id); $valid_extensions = implode(' ', $extensions); $upload_size = min((int) ini_get('post_max_size'), (int) ini_get('upload_max_filesize')); return array( 'dsid_fieldset' => array( '#type' => 'fieldset', - '#title' => t("Update datastream with latest version"), + '#title' => t("Update Datastream"), '#collapsible' => FALSE, '#collapsed' => FALSE, 'dsid' => array( '#type' => 'markup', - '#markup' => "
    DSID: $datastream->id
    ", + '#markup' => "
    DSID: $datastream->id
    ", ), 'label' => array( '#type' => 'markup', - '#markup' => "
    Label: $datastream->label
    ", + '#markup' => "
    Label: $datastream->label
    ", ), 'file' => array( '#type' => 'managed_file', @@ -313,7 +305,7 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract '#title' => t('Upload Document'), '#size' => 64, '#description' => t('Select a file to upload.
    Files must be less than @size MB.', array('@size' => $upload_size)), - '#upload_location' => 'temporary://', + '#upload_location' => 'public://', '#upload_validators' => array( 'file_validate_extensions' => array($valid_extensions), // Assume its specified in MB. diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc index 1a20859d..4d87409d 100644 --- a/includes/mimetype.utils.inc +++ b/includes/mimetype.utils.inc @@ -58,3 +58,31 @@ function islandora_get_extensions_for_mimetype($mimetype) { } return $extensions; } + +/** + * Mimes accepted by a datastream. + * + * @param AbstractObject $object + * Object to check for extensions. + * @param string $dsid + * Datastream ID to check for extensions. + * + * @return array + * 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 = $datastream_mime_map[$dsid]['mime']; + 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 $extensions; +} From 548701584b0a0bc60bd68d5e02ed1c08991cf8d9 Mon Sep 17 00:00:00 2001 From: nruest Date: Fri, 5 Dec 2014 14:10:25 -0500 Subject: [PATCH 73/93] Update codesniffer. --- tests/scripts/travis_setup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 84b6c44c..22fe9d7c 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -22,8 +22,8 @@ tar xf drush-6.3.tar.gz sudo mv drush-6.3 /opt/ sudo ln -s /opt/drush-6.3/drush /usr/bin/drush -wget http://alpha.library.yorku.ca/PHP_CodeSniffer-1.5.4.tgz -pear install PHP_CodeSniffer-1.5.4.tgz +wget http://alpha.library.yorku.ca/PHP_CodeSniffer-1.5.6.tgz +pear install PHP_CodeSniffer-1.5.6.tgz wget http://alpha.library.yorku.ca/phpcpd.phar sudo mv phpcpd.phar /usr/local/bin/phpcpd From e9b47575513d9a65330c81c9514c68907d44188d Mon Sep 17 00:00:00 2001 From: willtp87 Date: Fri, 5 Dec 2014 16:22:29 -0400 Subject: [PATCH 74/93] More translation. --- includes/add_datastream.form.inc | 7 +++++-- includes/datastream.version.inc | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/includes/add_datastream.form.inc b/includes/add_datastream.form.inc index 380880d2..40e1537d 100644 --- a/includes/add_datastream.form.inc +++ b/includes/add_datastream.form.inc @@ -38,7 +38,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract ), 'dsid_fieldset' => array( '#type' => 'fieldset', - '#title' => 'Add a Datastream', + '#title' => t('Add a Datastream'), '#collapsible' => FALSE, '#collapsed' => FALSE, 'dsid' => array( @@ -152,7 +152,10 @@ function islandora_add_datastream_form_validate(array $form, array &$form_state) if ($file) { $errors = file_validate_extensions($file, implode(' ', $extensions)); if (count($errors) > 0) { - form_set_error('file', "{$errors[0]} (for the set DSID)"); + form_set_error( + 'file', + t("!error (for the set DSID)", array('!error' => $errors[0])) + ); } } } diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index 225e5ff3..a8ecf102 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -293,11 +293,11 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract '#collapsed' => FALSE, 'dsid' => array( '#type' => 'markup', - '#markup' => "
    DSID: $datastream->id
    ", + '#markup' => t("
    DSID: @dsid
    ", array('@dsid' => $datastream->id)), ), 'label' => array( '#type' => 'markup', - '#markup' => "
    Label: $datastream->label
    ", + '#markup' => t("
    Label: @label
    ", array('@label' => $datastream->label)), ), 'file' => array( '#type' => 'managed_file', From 578b3e64ef048ffb4378e662c5b9768593cca3e0 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Fri, 5 Dec 2014 21:30:52 -0400 Subject: [PATCH 75/93] better documentation --- includes/mimetype.utils.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc index 4d87409d..beb4dfef 100644 --- a/includes/mimetype.utils.inc +++ b/includes/mimetype.utils.inc @@ -60,15 +60,15 @@ function islandora_get_extensions_for_mimetype($mimetype) { } /** - * Mimes accepted by a datastream. + * 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 array - * Mimes accepted by the datastream ID on the object. + * @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'); From f6523225a3c842cf101e32fbdf6dfc5f3d27f34d Mon Sep 17 00:00:00 2001 From: willtp87 Date: Mon, 8 Dec 2014 11:01:31 -0400 Subject: [PATCH 76/93] Can have non-defined DSs again. --- includes/add_datastream.form.inc | 3 ++- includes/mimetype.utils.inc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/add_datastream.form.inc b/includes/add_datastream.form.inc index 40e1537d..4484a903 100644 --- a/includes/add_datastream.form.inc +++ b/includes/add_datastream.form.inc @@ -149,7 +149,8 @@ function islandora_add_datastream_form_validate(array $form, array &$form_state) $form_state['values']['dsid'] ); $file = file_load($form_state['values']['file']); - if ($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( diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc index beb4dfef..312302b2 100644 --- a/includes/mimetype.utils.inc +++ b/includes/mimetype.utils.inc @@ -73,7 +73,7 @@ function islandora_get_extensions_for_mimetype($mimetype) { 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 = $datastream_mime_map[$dsid]['mime']; + $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)) { From fd2214695686c6ba2b5ee1c9812f2486d1535c65 Mon Sep 17 00:00:00 2001 From: willtp87 Date: Mon, 8 Dec 2014 11:13:38 -0400 Subject: [PATCH 77/93] Stripping out duplicate extensions. --- includes/mimetype.utils.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/mimetype.utils.inc b/includes/mimetype.utils.inc index 312302b2..03708f99 100644 --- a/includes/mimetype.utils.inc +++ b/includes/mimetype.utils.inc @@ -84,5 +84,5 @@ function islandora_get_extensions_for_datastream(AbstractObject $object, $dsid) foreach ($mimes as $mime) { $extensions = array_merge($extensions, islandora_get_extensions_for_mimetype($mime)); } - return $extensions; + return array_unique($extensions); } From ccfc302cf99e2137530243adc0b99e5bb97d7d0a Mon Sep 17 00:00:00 2001 From: willtp87 Date: Thu, 11 Dec 2014 16:21:43 -0400 Subject: [PATCH 78/93] Using file_default_schema now that consensus has been reached. --- includes/add_datastream.form.inc | 2 +- includes/datastream.version.inc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/add_datastream.form.inc b/includes/add_datastream.form.inc index 4484a903..55b45a04 100644 --- a/includes/add_datastream.form.inc +++ b/includes/add_datastream.form.inc @@ -71,7 +71,7 @@ function islandora_add_datastream_form(array $form, array &$form_state, Abstract '#size' => 48, '#description' => t('Select a file to upload.
    Files must be less than @size MB.', array('@size' => $upload_size)), '#default_value' => isset($form_state['values']['files']) ? $form_state['values']['files'] : NULL, - '#upload_location' => 'public://', + '#upload_location' => file_default_scheme() . '://', '#upload_validators' => array( // Disable default file_validate_extensions; we need direct control. 'file_validate_extensions' => array(NULL), diff --git a/includes/datastream.version.inc b/includes/datastream.version.inc index a8ecf102..b79b5a80 100644 --- a/includes/datastream.version.inc +++ b/includes/datastream.version.inc @@ -305,7 +305,7 @@ function islandora_datastream_version_replace_form($form, &$form_state, Abstract '#title' => t('Upload Document'), '#size' => 64, '#description' => t('Select a file to upload.
    Files must be less than @size MB.', array('@size' => $upload_size)), - '#upload_location' => 'public://', + '#upload_location' => file_default_scheme() . '://', '#upload_validators' => array( 'file_validate_extensions' => array($valid_extensions), // Assume its specified in MB. From d4343e265f6b08c224098b24d56eb027b05421be Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 17 Dec 2014 15:32:54 -0500 Subject: [PATCH 79/93] This changes the spinner element such that the another widget can prevent the submission of the form if need be. --- js/spinner.js | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/js/spinner.js b/js/spinner.js index 0b4a3c74..0c83f393 100755 --- a/js/spinner.js +++ b/js/spinner.js @@ -1,9 +1,11 @@ +/*jshint browser: true, devel:true*/ +/*global jQuery, Drupal, Spinner*/ /** * @file * Triggers the display of a spinning icon when the form is submitted. */ (function ($) { - + 'use strict'; Drupal.behaviors.spinner = { attach: function(context, settings) { // Store what triggered the submit. @@ -15,35 +17,41 @@ // On enter the first submit button is assumed as is most often the // case and this is part of the HTML 5 specification, although some // Browsers may choose the button with the lowest tab-index. - if (event.which == 13) { + if (event.which === 13) { $(this).data('clicked', $(':submit', this).first()); } }); }); - for (var base in settings.spinner) { - var id = '#' + base; - // Don't add spinner to the hidden next/ingest button. - if (id != '#edit-hidden-next') { + $.each(settings.spinner, function (base, value) { + var id = '#' + base, + message = $('
    ').text(settings.spinner[base].message); + if (id !== '#edit-hidden-next') { $(id, context).once('spinner', function () { var spinner = new Spinner(settings.spinner[base].opts); - $(id).parents('form').one('submit', function (event) { - if ($(this).data('clicked').is(id)) { + $(id).parents('form').submit(function (event) { + // If some other widget is preventing form submission we should + // not attempt to submit at this time. + if (event.isDefaultPrevented()) { + return; + } + if ($(this).data('clicked').is(id) && $(this).data('submitted') === undefined) { event.preventDefault(); + // Prevent this from being entered a second time. + $(this).data('submitted', true); // Add Message. - var message = $('
    ').text(settings.spinner[base].message); $(id).after(message); // Make UI changes. spinner.spin(this); $('#edit-next').hide(); $('#edit-prev').hide(); // Submit the form after a set timeout, this handles problems with - // safari, in that safari submit's immediately.. - if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) { + // safari, in that safari submits 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. + // prevent the default behaviour. $(id).removeAttr('disabled') .click() .click(function (event) { @@ -55,7 +63,7 @@ }); }); } - } + }); } }; })(jQuery); From 2df476dc13f2f098fe58940d418ccd05b37af82d Mon Sep 17 00:00:00 2001 From: qadan Date: Mon, 29 Dec 2014 14:38:17 -0400 Subject: [PATCH 80/93] slight test update --- tests/includes/utilities.inc | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc index 078e60d6..e12fab72 100644 --- a/tests/includes/utilities.inc +++ b/tests/includes/utilities.inc @@ -218,13 +218,14 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { * 'parent' - The PID of the parent collection. * @param array $datastreams * An array containing zero or more datastream arrays that use the keys: - * 'dsid' - the datastream ID; randomized if not set. + * 'dsid' - The datastream ID; randomized if not set. * 'path' - The path to the file to use; defaults to fixtures/test.jpg. + * 'string' - A string to set the datastream from; overrides 'path'. * 'control_group' - The single-letter control group identifier. * 'mimetype' - The datastream's mimetype. * - * @return bool|array - * FALSE if the object ingest failed, or the object array if successful. + * @return bool|AbstractObject + * FALSE if the object ingest failed, or the object if successful. */ public function ingestConstructedObject(array $properties = array(), array $datastreams = array()) { if (!isset($properties['pid'])) { @@ -264,9 +265,6 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { if (!isset($datastream['dsid'])) { $datastream['dsid'] = DrupalUnitTestCase::randomName(); } - if (!isset($datastream['path'])) { - $datastream['path'] = drupal_get_path('module', 'islandora') . '/tests/fixtures/test.jpg'; - } if (!isset($datastream['control_group'])) { $new_datastream = $object->constructDatastream($datastream['dsid']); } @@ -277,7 +275,13 @@ class IslandoraTestUtilities extends IslandoraTestUtilityClass { if (isset($datastream['mimetype'])) { $new_datastream->mimetype = $datastream['mimetype']; } - $new_datastream->setContentFromFile($datastream['path']); + if (isset($datastream['string'])) { + $new_datastream->setContentFromString($datastream['string']); + } + else { + $path = isset($datastream['path']) ? $datastream['path'] : drupal_get_path('module', 'islandora') . '/tests/fixtures/test.jpg'; + $new_datastream->setContentFromFile($path); + } $object->ingestDatastream($new_datastream); } } From a8e67e2dcfb56ea449890ce2d2e37c78cdefe29a Mon Sep 17 00:00:00 2001 From: DiegoPino Date: Tue, 30 Dec 2014 01:18:52 -0300 Subject: [PATCH 81/93] breadcrumbs now xacml compliant Changed itql to sparql and added xacml filters to query to check if current user has access to parent collection --- includes/breadcrumb.inc | 61 +++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/includes/breadcrumb.inc b/includes/breadcrumb.inc index 6f975ae3..b0e7beed 100644 --- a/includes/breadcrumb.inc +++ b/includes/breadcrumb.inc @@ -77,35 +77,56 @@ function islandora_get_breadcrumbs_recursive($pid, FedoraRepository $repository, ); } else { - $query_string = 'select $parentObject $title $content from <#ri> - where ( - $title - and $parentObject $content - and ( - $parentObject - or $parentObject - or $parentObject - ) - and $parentObject - ) - minus $content - minus $parentObject - order by $title desc'; - $results = $repository->ri->itqlQuery($query_string); + + + $query= << + PREFIX fedora-rels-ext: + SELECT DISTINCT ?object ?label + FROM <#ri> + WHERE { + ?collection_predicate ?object; + ?label . + ?object + . + !optionals + !filters + } +EOQ; + + $query_optionals = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_optionals', 'view'); + $query_filters = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_filters'); + $filter_map = function ($filter) { + return "FILTER($filter)"; + }; + $query_filters[] = "sameTerm(?collection_predicate, ) || sameTerm(?collection_predicate, )"; + + + + $query = format_string($query, array( + '!optionals' => !empty($query_optionals) ? ('OPTIONAL {{' . implode('} UNION {', $query_optionals) . '}}') : '', + '!filters' => implode(' ', array_map($filter_map, $query_filters)), + )); + + $query = format_string($query, array( + '!pid' => $pid, + )); + + $results = $repository->ri->sparqlQuery($query,'unlimited'); if (count($results) > 0 && $context['level'] > 0) { - $parent = $results[0]['parentObject']['value']; - $this_title = $results[0]['title']['value']; + $parent = $results[0]['object']['value']; + $this_label = $results[0]['label']['value']; - if (empty($this_title)) { - $this_title = t('-'); + if (empty($this_label)) { + $this_label = t('-'); } $context['level']--; return array_merge( islandora_get_breadcrumbs_recursive($parent, $repository, $context), array( - l($this_title, "islandora/object/$pid"), + l($this_label, "islandora/object/$pid"), ) ); } From f34e1eae720db4d59ac812f5b40346afedec8774 Mon Sep 17 00:00:00 2001 From: DiegoPino Date: Tue, 30 Dec 2014 10:42:43 -0300 Subject: [PATCH 82/93] Coding standards corrections Minor coding standards fixes to pass Travis tests --- includes/breadcrumb.inc | 66 ++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/includes/breadcrumb.inc b/includes/breadcrumb.inc index b0e7beed..38be8322 100644 --- a/includes/breadcrumb.inc +++ b/includes/breadcrumb.inc @@ -77,58 +77,50 @@ function islandora_get_breadcrumbs_recursive($pid, FedoraRepository $repository, ); } else { - - - $query= << - PREFIX fedora-rels-ext: - SELECT DISTINCT ?object ?label - FROM <#ri> - WHERE { + + $query = << + PREFIX fedora-rels-ext: + SELECT DISTINCT ?object ?label + FROM <#ri> + WHERE { ?collection_predicate ?object; - ?label . - ?object - . - !optionals - !filters - } + ?label . + ?object + . + !optionals + !filters + } EOQ; - - $query_optionals = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_optionals', 'view'); - $query_filters = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_filters'); - $filter_map = function ($filter) { - return "FILTER($filter)"; - }; - $query_filters[] = "sameTerm(?collection_predicate, ) || sameTerm(?collection_predicate, )"; - - - - $query = format_string($query, array( - '!optionals' => !empty($query_optionals) ? ('OPTIONAL {{' . implode('} UNION {', $query_optionals) . '}}') : '', - '!filters' => implode(' ', array_map($filter_map, $query_filters)), - )); + $query_optionals = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_optionals', 'view'); + $query_filters = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_filters'); + $filter_map = function ($filter) { + return "FILTER($filter)"; + }; + $query_filters[] = "sameTerm(?collection_predicate, ) || sameTerm(?collection_predicate, )"; + $query = format_string($query, array( + '!optionals' => !empty($query_optionals) ? ('OPTIONAL {{' . implode('} UNION {', $query_optionals) . '}}') : '', + '!filters' => implode(' ', array_map($filter_map, $query_filters)), + )); $query = format_string($query, array( '!pid' => $pid, )); - $results = $repository->ri->sparqlQuery($query,'unlimited'); + $results = $repository->ri->sparqlQuery($query, 'unlimited'); - if (count($results) > 0 && $context['level'] > 0) { + if (count($results) > 0 && $context['level'] > 0) { $parent = $results[0]['object']['value']; $this_label = $results[0]['label']['value']; if (empty($this_label)) { - $this_label = t('-'); + $this_label = t('-'); } $context['level']--; - return array_merge( - islandora_get_breadcrumbs_recursive($parent, $repository, $context), - array( - l($this_label, "islandora/object/$pid"), - ) - ); + return array_merge(islandora_get_breadcrumbs_recursive($parent, $repository, $context), array( + l($this_label, "islandora/object/$pid"), + )); } else { // Add an non-link, as we don't know how to get back to the root, and From 1019487d337f4cf7ddda4276c723359314d830c8 Mon Sep 17 00:00:00 2001 From: DiegoPino Date: Tue, 30 Dec 2014 11:22:10 -0300 Subject: [PATCH 83/93] More coding standards Fixes --- includes/breadcrumb.inc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/includes/breadcrumb.inc b/includes/breadcrumb.inc index 38be8322..a66177ab 100644 --- a/includes/breadcrumb.inc +++ b/includes/breadcrumb.inc @@ -77,7 +77,7 @@ function islandora_get_breadcrumbs_recursive($pid, FedoraRepository $repository, ); } else { - + $query = << PREFIX fedora-rels-ext: @@ -85,19 +85,19 @@ function islandora_get_breadcrumbs_recursive($pid, FedoraRepository $repository, FROM <#ri> WHERE { ?collection_predicate ?object; - ?label . + ?label . ?object - . - !optionals - !filters - } + . + !optionals + !filters + } EOQ; $query_optionals = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_optionals', 'view'); $query_filters = (array) module_invoke('islandora_xacml_api', 'islandora_basic_collection_get_query_filters'); $filter_map = function ($filter) { return "FILTER($filter)"; - }; + }; $query_filters[] = "sameTerm(?collection_predicate, ) || sameTerm(?collection_predicate, )"; $query = format_string($query, array( '!optionals' => !empty($query_optionals) ? ('OPTIONAL {{' . implode('} UNION {', $query_optionals) . '}}') : '', @@ -106,7 +106,7 @@ EOQ; $query = format_string($query, array( '!pid' => $pid, )); - + $results = $repository->ri->sparqlQuery($query, 'unlimited'); if (count($results) > 0 && $context['level'] > 0) { From fa14e839ce4533bc69c1f6aa2be65ad6d15d981c Mon Sep 17 00:00:00 2001 From: DiegoPino Date: Tue, 30 Dec 2014 11:40:06 -0300 Subject: [PATCH 84/93] Oh whitespaces 2015 and beyond: Always enable "view invisible characters" --- includes/breadcrumb.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/breadcrumb.inc b/includes/breadcrumb.inc index a66177ab..e75a0fdd 100644 --- a/includes/breadcrumb.inc +++ b/includes/breadcrumb.inc @@ -77,7 +77,7 @@ function islandora_get_breadcrumbs_recursive($pid, FedoraRepository $repository, ); } else { - + $query = << PREFIX fedora-rels-ext: @@ -106,7 +106,7 @@ EOQ; $query = format_string($query, array( '!pid' => $pid, )); - + $results = $repository->ri->sparqlQuery($query, 'unlimited'); if (count($results) > 0 && $context['level'] > 0) { From 9ef4065c37d6db964c9572d16c369fa5586ea4f2 Mon Sep 17 00:00:00 2001 From: qadan Date: Mon, 19 Jan 2015 11:10:09 -0400 Subject: [PATCH 85/93] coder specify --- tests/scripts/travis_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 22fe9d7c..95405c80 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -38,7 +38,7 @@ ln -s $ISLANDORA_DIR sites/all/modules/islandora mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/islandora/tests/test_config.ini mkdir sites/all/libraries ln -s $HOME/tuque sites/all/libraries/tuque -drush dl --yes coder +drush dl --yes coder-7.x-2.4 drush dl --yes potx-7.x-1.0 drush en --yes coder_review drush en --yes simpletest From 043be501791f3953dbeaa772fd11a0bc56d83a67 Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Thu, 18 Dec 2014 15:48:20 -0400 Subject: [PATCH 86/93] Added print icon as an option in islandora administration page. --- includes/admin.form.inc | 6 ++++++ islandora.module | 28 +++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/includes/admin.form.inc b/includes/admin.form.inc index 42612ea0..51116f5c 100644 --- a/includes/admin.form.inc +++ b/includes/admin.form.inc @@ -68,6 +68,12 @@ function islandora_repository_admin(array $form, array &$form_state) { useful if derivatives are to be created by an external service.'), '#default_value' => variable_get('islandora_defer_derivatives_on_ingest', FALSE), ), + 'islandora_show_print_option' => array( + '#type' => 'checkbox', + '#title' => t('Show print option on objects'), + '#description' => t('Displays an extra print tab, allowing an object to be printed'), + '#default_value' => variable_get('islandora_show_print_option', FALSE), + ), ), 'islandora_namespace' => array( '#type' => 'fieldset', diff --git a/islandora.module b/islandora.module index 31986877..8821aff6 100644 --- a/islandora.module +++ b/islandora.module @@ -147,7 +147,7 @@ function islandora_menu() { 'page callback' => 'islandora_printer_object', 'page arguments' => array(2), 'type' => MENU_LOCAL_TASK, - 'access callback' => 'islandora_object_access', + 'access callback' => 'islandora_print_object_access', 'access arguments' => array(ISLANDORA_VIEW_OBJECTS, 2), ); $items['islandora/object/%islandora_object/print'] = array( @@ -663,6 +663,31 @@ function islandora_print_object(AbstractObject $object) { return theme('islandora_object_print', array('object' => $object)); } +/** + * View print tab access. + * + * Configurable option in islandora configuration. + * + * @param string $op + * String identifying an operation to check. Should correspond to a + * permission declared via hook_permission(). + * @param AbstractObject $object + * An object to check for permissions. + * + * @return bool + * TRUE if at least one implementation of hook_islandora_object_access() + * returned TRUE, and no implementation return FALSE; FALSE otherwise, or + * FALSE if 'islandora_show_print_option' is not selected in islandora + * configuraton. + */ +function islandora_print_object_access($one, $two) { + $access = islandora_object_access($one, $two); + if ($access && variable_get('islandora_show_print_option', FALSE)) { + return $access; + } + return FALSE; +} + /** * Implements hook_forms(). */ @@ -1070,6 +1095,7 @@ function islandora_view_object(AbstractObject $object) { function islandora_printer_object(AbstractObject $object) { $output = array(); $temp_arr = array(); + // Dispatch print hook. foreach (islandora_build_hook_list(ISLANDORA_PRINT_HOOK, $object->models) as $hook) { $temp = module_invoke_all($hook, $object); From 7c27b78b8af38bb7e4b255753810fb608aedc77c Mon Sep 17 00:00:00 2001 From: MorganDawe Date: Thu, 18 Dec 2014 15:54:16 -0400 Subject: [PATCH 87/93] Changed parameter names. --- islandora.module | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/islandora.module b/islandora.module index 8821aff6..dfd9a53e 100644 --- a/islandora.module +++ b/islandora.module @@ -680,8 +680,8 @@ function islandora_print_object(AbstractObject $object) { * FALSE if 'islandora_show_print_option' is not selected in islandora * configuraton. */ -function islandora_print_object_access($one, $two) { - $access = islandora_object_access($one, $two); +function islandora_print_object_access($op, $object) { + $access = islandora_object_access($op, $object); if ($access && variable_get('islandora_show_print_option', FALSE)) { return $access; } From 3900ae5e1df42083c7e3414c6540fa7583df0dd7 Mon Sep 17 00:00:00 2001 From: Rosie Le Faive Date: Wed, 21 Jan 2015 12:40:14 -0400 Subject: [PATCH 88/93] Avoid unnecessary access call --- islandora.module | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/islandora.module b/islandora.module index dfd9a53e..270edf4e 100644 --- a/islandora.module +++ b/islandora.module @@ -663,7 +663,7 @@ function islandora_print_object(AbstractObject $object) { return theme('islandora_object_print', array('object' => $object)); } -/** +/** * View print tab access. * * Configurable option in islandora configuration. @@ -681,11 +681,11 @@ function islandora_print_object(AbstractObject $object) { * configuraton. */ function islandora_print_object_access($op, $object) { - $access = islandora_object_access($op, $object); - if ($access && variable_get('islandora_show_print_option', FALSE)) { - return $access; + if (!variable_get('islandora_show_print_option', FALSE)) { + return FALSE; } - return FALSE; + $access = islandora_object_access($op, $object); + return $access; } /** From 7323411bb67ec7f25205b473632b42b95086ec79 Mon Sep 17 00:00:00 2001 From: Mark Jordan Date: Thu, 22 Jan 2015 09:18:20 -0800 Subject: [PATCH 89/93] Address ISLANDORA-1162 --- islandora.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/islandora.module b/islandora.module index 270edf4e..fba7206a 100644 --- a/islandora.module +++ b/islandora.module @@ -1976,7 +1976,7 @@ function islandora_islandora_datastream_access($op, AbstractDatastream $datastre $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 && + if (isset($hook['destination_dsid']) && $hook['destination_dsid'] == $datastream->id && (is_null($hook['source_dsid']) || islandora_datastream_access(ISLANDORA_VIEW_OBJECTS, $object[$hook['source_dsid']], $user))) { $applicable_hook = TRUE; break; From f8864a2a76fa7a7e488b23e1ca33dc4933fdb7ae Mon Sep 17 00:00:00 2001 From: Jordan Dukart Date: Fri, 23 Jan 2015 11:28:56 -0400 Subject: [PATCH 90/93] Expand temp file MIME type sniffing. --- includes/utilities.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/includes/utilities.inc b/includes/utilities.inc index 7476948b..1459b0a5 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -72,6 +72,9 @@ function islandora_temp_file_entry($file_uri, $mime = NULL) { if (isset($mime)) { $file->filemime = $mime; } + else { + $file->filemime = file_get_mimetype($file_uri); + } } $file->status = 0; return file_save($file); From 598e68980e66f95d2a659e3aa64df876c776ccfc Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Mon, 26 Jan 2015 14:58:23 -0400 Subject: [PATCH 91/93] Allow "islandora_object" entities to be loaded. Should allow objects to be loaded in Rules, by specifying the "id" as a property. (trying to load by "id" directly is not possibly, since Rules assumes that IDs are numeric) --- includes/object.entity_controller.inc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/includes/object.entity_controller.inc b/includes/object.entity_controller.inc index 87b5e4de..dfd2d2fd 100644 --- a/includes/object.entity_controller.inc +++ b/includes/object.entity_controller.inc @@ -29,9 +29,13 @@ class IslandoraObjectEntityController implements DrupalEntityControllerInterface * An array of loaded objects. */ public function load($ids = array(), $conditions = array()) { - if (!empty($conditions)) { - // TODO: Allow loading by specifying IDs in the condition. - throw new Exception('Conditions not implemented.'); + if (isset($conditions['id'])) { + // Allow loading by specifying the ID as a property as a work-around, + // since some components dealing with entities expect IDs to be numeric. + $ids = array_merge($ids, (array) $conditions['id']); + } + elseif (!empty($conditions)) { + throw new Exception('Conditions other than "id" not implemented.'); } $loaded = array(); From 8641c469e38c994b5880600161df17c4c18c8744 Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Mon, 26 Jan 2015 15:00:58 -0400 Subject: [PATCH 92/93] Avoid watchdogging "Cache rebuild lock hit: event_islandora_*" stuff. --- includes/utilities.inc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/includes/utilities.inc b/includes/utilities.inc index 1459b0a5..4786e362 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -197,11 +197,14 @@ function islandora_invoke_hook_list($hook, array $refinements, array $args) { $return = array_merge_recursive($return, $result); array_shift($args); } - if (module_exists('rules') && $event = rules_get_cache("event_$hook")) { - $parameters = $event->parameterInfo(); - $rule_args = array_slice($args, 0, count($parameters)); - array_unshift($rule_args, $hook); - $result = call_user_func_array('rules_invoke_event', $rule_args); + if (module_exists('rules')) { + $event_info = rules_get_event_info($hook); + if (isset($event_info['module'])) { + $parameters = $event_info['variables']; + $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; } From 88304774a9692d3d8bf59cd7f95ee88edd31a74c Mon Sep 17 00:00:00 2001 From: Adam Vessey Date: Mon, 26 Jan 2015 17:20:34 -0400 Subject: [PATCH 93/93] Revert "Allow "islandora_object" entities to be loaded." This reverts commit 598e68980e66f95d2a659e3aa64df876c776ccfc. --- includes/object.entity_controller.inc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/includes/object.entity_controller.inc b/includes/object.entity_controller.inc index dfd2d2fd..87b5e4de 100644 --- a/includes/object.entity_controller.inc +++ b/includes/object.entity_controller.inc @@ -29,13 +29,9 @@ class IslandoraObjectEntityController implements DrupalEntityControllerInterface * An array of loaded objects. */ public function load($ids = array(), $conditions = array()) { - if (isset($conditions['id'])) { - // Allow loading by specifying the ID as a property as a work-around, - // since some components dealing with entities expect IDs to be numeric. - $ids = array_merge($ids, (array) $conditions['id']); - } - elseif (!empty($conditions)) { - throw new Exception('Conditions other than "id" not implemented.'); + if (!empty($conditions)) { + // TODO: Allow loading by specifying IDs in the condition. + throw new Exception('Conditions not implemented.'); } $loaded = array();