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 0000000..39bbba2 Binary files /dev/null and b/tests/DMS-test-lorum-file.pdf differ 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