From 2068a6d59055790d297710436eee964d32f256d0 Mon Sep 17 00:00:00 2001 From: Julian Seidenberg Date: Mon, 16 Jul 2012 18:21:48 +1200 Subject: [PATCH] API-CHANGE: DMS interface and mock implementation --- code/DMS.php | 115 +++++++++++ ...{DocumentInterface.php => DMSDocument.php} | 114 +++++------ code/exceptions/FileNotFoundException.php | 8 + code/interface/DMSDocumentInterface.php | 179 ++++++++++++++++++ code/{ => interface}/DMSInterface.php | 6 +- tests/DMS-test-lorum-file.pdf | Bin 0 -> 58333 bytes tests/DMSTest.php | 80 ++++++++ 7 files changed, 446 insertions(+), 56 deletions(-) create mode 100644 code/DMS.php rename code/{DocumentInterface.php => DMSDocument.php} (73%) create mode 100644 code/exceptions/FileNotFoundException.php create mode 100644 code/interface/DMSDocumentInterface.php rename code/{ => interface}/DMSInterface.php (95%) create mode 100644 tests/DMS-test-lorum-file.pdf create mode 100644 tests/DMSTest.php diff --git a/code/DMS.php b/code/DMS.php new file mode 100644 index 0000000..aaa9a54 --- /dev/null +++ b/code/DMS.php @@ -0,0 +1,115 @@ +createStorageFolder(self::$dmsPath); + + return $dms; + } + + /** + * Takes a File object or a String (path to a file) and copies it into the DMS. The original file remains unchanged. + * When storing a document, sets the fields on the File has "tag" metadata. E.g: filename, path, etc. all become + * single-value tags on the Document. + * @param $file File object, or String that is path to a file to store + * @return DMSDocumentInstance Document object that we just created + */ + function storeDocument($file) { + //confirm we have a file + $fromPath = null; + if (is_string($file)) $fromPath = $file; + elseif (is_object($file) && $file->is_a("File")) $fromPath = $file->Filename; + + if (!$fromPath) throw new FileNotFoundException(); + + //create a new document and get its ID + $doc = new DMSDocument(); + $docID = $doc->write(); + + //calculate all the path to copy the file to + $fromFilename = basename($fromPath); + $toFilename = $docID . '~' . $fromFilename; //add the docID to the start of the Filename + $toFolder = self::getStorageFolder($docID); + $toPath = $toFolder . DIRECTORY_SEPARATOR . $toFilename; + $this->createStorageFolder($toFolder); + + //copy the file into place + copy($fromPath, self::$dmsPath . DIRECTORY_SEPARATOR . $toPath); + + //write the filename of the stored document + $doc->Filename = $toPath; + $doc->write(); + + //set an initial title for the document from the filename + $doc->addTag('title', $fromFilename, false); + + return $doc; + } + + /** + * + * Returns a number of Document objects based on the a search by tags. You can search by category alone, + * by tag value alone, or by both. I.e: getByTag("fruits",null); getByTag(null,"banana"); getByTag("fruits","banana") + * @param null $category The metadata category to search for + * @param null $value The metadata value to search for + * @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results + * @return DocumentInterface + */ + function getByTag($category = null, $value = null, $showEmbargoed = false) { + // TODO: Implement getByTag() method. + } + + /** + * Returns a number of Document objects that match a full-text search of the Documents and their contents + * (if contents is searchable and compatible search module is installed - e.g. FullTextSearch module) + * @param $searchText String to search for + * @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results + * @return DocumentInterface + */ + function getByFullTextSearch($searchText, $showEmbargoed = false) { + // TODO: Implement getByFullTextSearch() method. + } + + /** + * Returns a list of Document objects associated with a Page + * @param $page SiteTree to fetch the associated Documents from + * @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results + * @return DataList Document list associated with the Page + */ + function getByPage($page, $showEmbargoed = false) { + // TODO: Implement getByPage() method. + } + + /** + * Creates a storage folder for the given path + * @param $path Path to create a folder for + */ + protected function createStorageFolder($path) { + if (!is_dir($path)) { + mkdir($path, 0777); + } + } + + /** + * Calculates the storage path from a database DMSDocument ID + */ + static function getStorageFolder($id) { + $folderName = intval($id / self::$dmsFolderSize); + return self::$dmsPath . DIRECTORY_SEPARATOR . $folderName; + } +} \ No newline at end of file diff --git a/code/DocumentInterface.php b/code/DMSDocument.php similarity index 73% rename from code/DocumentInterface.php rename to code/DMSDocument.php index cf6d514..90e7bdf 100644 --- a/code/DocumentInterface.php +++ b/code/DMSDocument.php @@ -1,41 +1,36 @@ "Text", + ); /** * Associates this document with a Page. This method does nothing if the association already exists. * This could be a simple wrapper around $myDoc->Pages()->add($myPage) to add a has_many relation - * @abstract * @param $pageObject Page object to associate this Document with * @return null */ - function addPage($pageObject); + function addPage($pageObject) { + // TODO: Implement addPage() method. + } /** * Removes the association between this Document and a Page. This method does nothing if the association does not exist. - * @abstract * @param $pageObject Page object to remove the association to * @return mixed */ - function removePage($pageObject); + function removePage($pageObject) { + // TODO: Implement removePage() method. + } /** * Returns a list of the Page objects associated with this Document - * @abstract * @return DataList */ - function getPages(); + function getPages() { + // TODO: Implement getPages() method. + } /** * Adds a metadata tag to the Document. The tag has a category and a value. @@ -44,13 +39,14 @@ interface DocumentInterface { * addTag("fruit","banana") addTag("fruit", "apple") would result in a single metadata tag: fruit->apple. * Can could be implemented as a key/value store table (although it is more like category/value, because the * same category can occur multiple times) - * @abstract * @param $category String of a metadata category to add (required) * @param $value String of a metadata value to add (required) * @param bool $multiValue Boolean that determines if the category is multi-value or single-value (optional) * @return null */ - function addTag($category, $value, $multiValue = true); + function addTag($category, $value, $multiValue = true) { + // TODO: Implement addTag() method. + } /** * Quick way to add multiple tags to a Document. This takes a multidimensional array of category/value pairs. @@ -59,121 +55,133 @@ interface DocumentInterface { * array('fruit','banana'), * array('fruit','apple') * ); - * @abstract * @param $twoDimensionalArray array containing a list of arrays * @param bool $multiValue Boolean that determines if the category is multi-value or single-value (optional) * @return null */ - function addTags($twoDimensionalArray, $multiValue = true); + function addTags($twoDimensionalArray, $multiValue = true) { + // TODO: Implement addTags() method. + } /** * Removes a tag from the Document. If you only set a category, then all values in that category are deleted. * If you specify both a category and a value, then only that single category/value pair is deleted. * Nothing happens if the category or the value do not exist. - * @abstract * @param $category Category to remove (required) * @param null $value Value to remove (optional) * @return null */ - function removeTag($category, $value = null); + function removeTag($category, $value = null) { + // TODO: Implement removeTag() method. + } /** * Deletes all tags associated with this Document. - * @abstract * @return null */ - function removeAllTags(); + function removeAllTags() { + // TODO: Implement removeAllTags() method. + } /** * Returns a multi-dimensional array containing all Tags associated with this Document. The array has the * following structure: * $twoDimensionalArray = new array( - * array('fruit','banana'), - * array('fruit','apple') - * ); - * @abstract + * array('fruit','banana'), + * array('fruit','apple') + * ); * @return array Multi-dimensional array of tags */ - function getAllTags(); + function getAllTags() { + // TODO: Implement getAllTags() method. + } /** * Returns a link to download this document from the DMS store - * @abstract * @return String */ - function downloadLink(); + function downloadLink() { + // TODO: Implement downloadLink() method. + } /** * Hides the document, so it does not show up when getByPage($myPage) is called * (without specifying the $showEmbargoed = true parameter). This is similar to expire, except that this method * should be used to hide documents that have not yet gone live. - * @abstract * @return null */ - function embargo(); + function embargo() { + // TODO: Implement embargo() method. + } /** * Returns if this is Document is embargoed. - * @abstract * @return bool True or False depending on whether this document is embargoed */ - function isEmbargoed(); + function isEmbargoed() { + // TODO: Implement isEmbargoed() method. + } /** * Hides the document, so it does not show up when getByPage($myPage) is called. Automatically un-hides the * Document at a specific date. - * @abstract * @param $datetime String date time value when this Document should expire * @return null */ - function embargoUntilDate($datetime); + function embargoUntilDate($datetime) { + // TODO: Implement embargoUntilDate() method. + } /** * Clears any previously set embargos, so the Document always shows up in all queries. - * @abstract * @return null */ - function clearEmbargo(); + function clearEmbargo() { + // TODO: Implement clearEmbargo() method. + } /** * Hides the document, so it does not show up when getByPage($myPage) is called. * (without specifying the $showEmbargoed = true parameter). This is similar to embargo, except that it should be * used to hide documents that are no longer useful. - * @abstract * @return null */ - function expire(); + function expire() { + // TODO: Implement expire() method. + } /** * Returns if this is Document is expired. - * @abstract * @return bool True or False depending on whether this document is expired */ - function isExpired(); + function isExpired() { + // TODO: Implement isExpired() method. + } /** * Hides the document at a specific date, so it does not show up when getByPage($myPage) is called. - * @abstract * @param $datetime String date time value when this Document should expire * @return null */ - function expireAtDate($datetime); + function expireAtDate($datetime) { + // TODO: Implement expireAtDate() method. + } /** * Clears any previously set expiry. - * @abstract * @return null */ - function clearExpiry(); - - /*---- FROM HERE ON: optional API features ----*/ + function clearExpiry() { + // TODO: Implement clearExpiry() method. + } /** * Returns a DataList of all previous Versions of this document (check the LastEdited date of each * object to find the correct one) - * @abstract * @return DataList List of Document objects */ - function getVersions(); + function getVersions() { + // TODO: Implement getVersions() method. + } } \ No newline at end of file diff --git a/code/exceptions/FileNotFoundException.php b/code/exceptions/FileNotFoundException.php new file mode 100644 index 0000000..d1573ad --- /dev/null +++ b/code/exceptions/FileNotFoundException.php @@ -0,0 +1,8 @@ +Pages()->add($myPage) to add a has_many relation + * @abstract + * @param $pageObject Page object to associate this DMSDocument with + * @return null + */ + function addPage($pageObject); + + /** + * Removes the association between this DMSDocument and a Page. This method does nothing if the association does not exist. + * @abstract + * @param $pageObject Page object to remove the association to + * @return mixed + */ + function removePage($pageObject); + + /** + * Returns a list of the Page objects associated with this DMSDocument + * @abstract + * @return DataList + */ + function getPages(); + + /** + * Adds a metadata tag to the DMSDocument. The tag has a category and a value. + * Each category can have multiple values by default. So: addTag("fruit","banana") addTag("fruit", "apple") will add two items. + * However, if the third parameter $multiValue is set to 'false', then all updates to a category only ever update a single value. So: + * addTag("fruit","banana") addTag("fruit", "apple") would result in a single metadata tag: fruit->apple. + * Can could be implemented as a key/value store table (although it is more like category/value, because the + * same category can occur multiple times) + * @abstract + * @param $category String of a metadata category to add (required) + * @param $value String of a metadata value to add (required) + * @param bool $multiValue Boolean that determines if the category is multi-value or single-value (optional) + * @return null + */ + function addTag($category, $value, $multiValue = true); + + /** + * Quick way to add multiple tags to a DMSDocument. This takes a multidimensional array of category/value pairs. + * The array should look like this: + * $twoDimensionalArray = new array( + * array('fruit','banana'), + * array('fruit','apple') + * ); + * @abstract + * @param $twoDimensionalArray array containing a list of arrays + * @param bool $multiValue Boolean that determines if the category is multi-value or single-value (optional) + * @return null + */ + function addTags($twoDimensionalArray, $multiValue = true); + + /** + * Removes a tag from the DMSDocument. If you only set a category, then all values in that category are deleted. + * If you specify both a category and a value, then only that single category/value pair is deleted. + * Nothing happens if the category or the value do not exist. + * @abstract + * @param $category Category to remove (required) + * @param null $value Value to remove (optional) + * @return null + */ + function removeTag($category, $value = null); + + /** + * Deletes all tags associated with this DMSDocument. + * @abstract + * @return null + */ + function removeAllTags(); + + /** + * Returns a multi-dimensional array containing all Tags associated with this DMSDocument. The array has the + * following structure: + * $twoDimensionalArray = new array( + * array('fruit','banana'), + * array('fruit','apple') + * ); + * @abstract + * @return array Multi-dimensional array of tags + */ + function getAllTags(); + + /** + * Returns a link to download this DMSDocument from the DMS store + * @abstract + * @return String + */ + function downloadLink(); + + /** + * Hides the DMSDocument, so it does not show up when getByPage($myPage) is called + * (without specifying the $showEmbargoed = true parameter). This is similar to expire, except that this method + * should be used to hide DMSDocuments that have not yet gone live. + * @abstract + * @return null + */ + function embargo(); + + /** + * Returns if this is DMSDocument is embargoed. + * @abstract + * @return bool True or False depending on whether this DMSDocument is embargoed + */ + function isEmbargoed(); + + /** + * Hides the DMSDocument, so it does not show up when getByPage($myPage) is called. Automatically un-hides the + * DMSDocument at a specific date. + * @abstract + * @param $datetime String date time value when this DMSDocument should expire + * @return null + */ + function embargoUntilDate($datetime); + + /** + * Clears any previously set embargos, so the DMSDocument always shows up in all queries. + * @abstract + * @return null + */ + function clearEmbargo(); + + /** + * Hides the DMSDocument, so it does not show up when getByPage($myPage) is called. + * (without specifying the $showEmbargoed = true parameter). This is similar to embargo, except that it should be + * used to hide DMSDocuments that are no longer useful. + * @abstract + * @return null + */ + function expire(); + + /** + * Returns if this is DMSDocument is expired. + * @abstract + * @return bool True or False depending on whether this DMSDocument is expired + */ + function isExpired(); + + /** + * Hides the DMSDocument at a specific date, so it does not show up when getByPage($myPage) is called. + * @abstract + * @param $datetime String date time value when this DMSDocument should expire + * @return null + */ + function expireAtDate($datetime); + + /** + * Clears any previously set expiry. + * @abstract + * @return null + */ + function clearExpiry(); + + /*---- FROM HERE ON: optional API features ----*/ + + /** + * Returns a DataList of all previous Versions of this DMSDocument (check the LastEdited date of each + * object to find the correct one) + * @abstract + * @return DataList List of DMSDocument objects + */ + function getVersions(); + +} \ No newline at end of file diff --git a/code/DMSInterface.php b/code/interface/DMSInterface.php similarity index 95% rename from code/DMSInterface.php rename to code/interface/DMSInterface.php index 73d2a87..23d228c 100644 --- a/code/DMSInterface.php +++ b/code/interface/DMSInterface.php @@ -24,7 +24,7 @@ interface DMSInterface { * single-value tags on the Document. * @abstract * @param $file File object, or String that is path to a file to store - * @return boolean Success or Failure of the store operation + * @return DMSDocumentInstance Document object that we just created */ function storeDocument($file); @@ -36,7 +36,7 @@ interface DMSInterface { * @param null $category The metadata category to search for * @param null $value The metadata value to search for * @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results - * @return DocumentInterface + * @return DMSDocumentInterface */ function getByTag($category = null, $value = null, $showEmbargoed = false); @@ -46,7 +46,7 @@ interface DMSInterface { * @abstract * @param $searchText String to search for * @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results - * @return DocumentInterface + * @return DMSDocumentInterface */ function getByFullTextSearch($searchText, $showEmbargoed = false); diff --git a/tests/DMS-test-lorum-file.pdf b/tests/DMS-test-lorum-file.pdf new file mode 100644 index 0000000000000000000000000000000000000000..39bbba279caa941fff5b4703f2f9ff575d8a5955 GIT binary patch literal 58333 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 CW2_7S literal 0 HcmV?d00001 diff --git a/tests/DMSTest.php b/tests/DMSTest.php new file mode 100644 index 0000000..2b7621c --- /dev/null +++ b/tests/DMSTest.php @@ -0,0 +1,80 @@ +delete(BASE_PATH . DIRECTORY_SEPARATOR . DMS::$dmsFolder); + } + + function tearDown() { + //delete the test folder after the test runs + $this->delete(BASE_PATH . DIRECTORY_SEPARATOR . DMS::$dmsFolder); + + //set the old DMS folder back again + DMS::$dmsFolder = self::$dmsFolderOld; + DMS::$dmsFolderSize = self::$dmsFolderSizeOld; + } + + public function delete($path) { + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path), + RecursiveIteratorIterator::CHILD_FIRST + ); + foreach ($it as $file) { + if (in_array($file->getBasename(), array('.', '..'))) { + continue; + } elseif ($file->isDir()) { + rmdir($file->getPathname()); + } elseif ($file->isFile() || $file->isLink()) { + unlink($file->getPathname()); + } + } + rmdir($path); + } + + + function testDMSStorage() { + $dms = DMS::getDMSInstance(); + + $file = BASE_PATH . DIRECTORY_SEPARATOR . self::$testFile; + $document = $dms->storeDocument($file); + + $this->assertNotNull($document, "Document object created"); + $this->assertTrue(file_exists(DMS::$dmsPath . DIRECTORY_SEPARATOR . $document->Filename),"Document file copied into DMS folder"); + + //$title = $document->getTag('title'); + } + + function testDMSFolderSpanning() { + DMS::$dmsFolderSize = 5; + $dms = DMS::getDMSInstance(); + + $file = BASE_PATH . DIRECTORY_SEPARATOR . self::$testFile; + + for($i = 0; $i <= 16; $i++) { + $document = $dms->storeDocument($file); + $this->assertNotNull($document, "Document object created on run number: $i"); + } + + //test we created 4 folder to contain the 17 files + $this->assertTrue(is_dir(DMS::$dmsPath . DIRECTORY_SEPARATOR . '1')); + $this->assertTrue(is_dir(DMS::$dmsPath . DIRECTORY_SEPARATOR . '2')); + $this->assertTrue(is_dir(DMS::$dmsPath . DIRECTORY_SEPARATOR . '3')); + $this->assertTrue(is_dir(DMS::$dmsPath . DIRECTORY_SEPARATOR . '4')); + $this->assertFalse(is_dir(DMS::$dmsPath . DIRECTORY_SEPARATOR . '5')); + } + + +} \ No newline at end of file