diff --git a/CHANGELOG b/CHANGELOG index 29b77e3ef..27696153b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -264,6 +264,9 @@ - various improvements of fulltext service - show number of documents per category in category manager - show number of keywords per category in keyword manager +- improve drag&drop with clipboard +- old pear packages for SeedDMS/Core, SeedDMS/Lucene, SeedDMS/Preview + are now based on composer and has moved into vendor dir -------------------------------------------------------------------------------- Changes in version 5.1.28 diff --git a/Makefile b/Makefile index 64114fe21..ba8176ffa 100644 --- a/Makefile +++ b/Makefile @@ -67,4 +67,32 @@ doc: apidoc: tools/apigen/bin/apigen SeedDMS_Core/Core --exclude "tests/*" --output html -.PHONY: doc webdav webapp repository +# Turn the package.xml file into CHANGELOG.md +# +# The idea is to form blocks of lines separated by an empty line. +# Echo block consists of the version number, release date und notes. +# This blocks are turned into single lines which are than sorted. +# Afterwards the single lines are turned back into blocks. +# +# It first uses sgrep to extract the version, date und notes. This is +# feed to sed to isolated the date and version and put them on separate +# lines. Each version +# forms a block of n lines with the first two being the version and date. +# All remaining lines are notes. Blocks are separated by an empty line. +# It's important to form blocks without ane empty lines because the following +# awk will create a single line from each block which can then be sorted +# (or simply reversed in order). +# Because the blocks are listed in the wrong order (last version first and +# previous version last, e.g. 5.1.29, 3.3.0, 3.3.1, ...., 5.1.27, 5.1.28) they +# need to be reversed in order. This is done by turning each block into line +# with the former new lines replaced by a '|'. So it's basically a '|' separated +# csv file which is then reversed in order by 'sort -r'. In order to separate +# blocks by a newline, each line of that output is appended by another +# line break. Result is put back +# into the original format by replacing all '|' by newline. +# +PKGFILE=SeedDMS_Core/package.xml +changelog: + @sgrep 'stag("DATE") .. etag("DATE") or ((stag("RELEASE") .. etag("RELEASE")) in (stag("VERSION") .. etag("VERSION"))) or inner(stag("NOTES") __ etag("NOTES"))' ${PKGFILE} | sed -e 's#^ *\([-0-9]*\)\([0-9.preRC]*\)#\n\n\2 (\1)\n---------------------#' | awk -F'\n' -vRS='' -vOFS='|' '{$$1=$$1}1' | sort -V -r | sed 's/$$/\n/' | tr '|' '\n' + +.PHONY: doc webdav webapp repository changelog diff --git a/SeedDMS_Core/CHANGELOG.md b/SeedDMS_Core/CHANGELOG.md new file mode 100644 index 000000000..4ac0e57c0 --- /dev/null +++ b/SeedDMS_Core/CHANGELOG.md @@ -0,0 +1,780 @@ +6.0.22 (2022-12-10) +--------------------- +- all changes from 5.1.29 merged + +6.0.21 (2022-11-18) +--------------------- +- all changes from 5.1.28 merged + + +6.0.20 (2022-09-18) +--------------------- +- all changes from 5.1.27 merged +- SeedDMЅ_Core_DMS::getDocumentsInRevision() returns status from revision log + +6.0.19 (2022-05-20) +--------------------- +- all changes from 5.1.26 +- removeFromProcesses() will not touch documents for which the new user does not have at least read access + +6.0.18 (2022-04-22) +--------------------- +- all changes from 5.1.25 +- fix searching for document content with a custom attribute having a value set +- SeedDMS_Core_DocumentContent::getWorkflow() has optional parameter to return data from table tblWorkflowDocumentContent + +6.0.17 (2021-12-11) +--------------------- +- all changes from 5.1.24 + +6.0.16 (2021-05-07) +--------------------- + +6.0.15 (2021-04-13) +--------------------- +- add searching for revision date +- expired documents can be skipped from counting in countTasks() +- SeedDMS_Core_DMS::getDocumentList() uses ambiguous column name when sorting by status +- add list type SleepingReviseByMe to SeedDMS_Core_DMS::getDocumentList() +- parameter 2 of SeedDMS_Core_DMS::getDocumentList() is treated as number of + days for list DueRevisions + +6.0.14 (2021-01-04) +--------------------- +better error checking in SeedDMS_Core_Document::cancelCheckOut() + +6.0.13 (2020-09-29) +--------------------- +- SeedDMS_Folder_DMS::getAccessList() and getDefaultAccess() do not return fals anymore if the parent does not exists. They just stop inheritance. + +6.0.12 (2020-06-05) +--------------------- + +6.0.11 (2020-06-05) +--------------------- +SeedDMS_Core_DMS::filterAccess() properly checks for documents + +6.0.10 (2020-05-22) +--------------------- +SeedDMS_Core_DocumentContent::delRevisor() returns -4 if user has already made a revision + +6.0.9 (2020-05-14) +--------------------- +- no changes, just keep same version as seeddms application + +6.0.8 (2020-03-02) +--------------------- +- no changes, just keep same version as seeddms application + +6.0.7 (2020-02-17) +--------------------- +SeedDMS_Core_Document::getTimeline() returns revision only for latest content +add callback onSetStatus in SeedDMS_Core_DocumentContent::setStatus() +add new list type 'DueRevision' in SeedDMS_Core_DMS::getDocumentList() +a revision can also be started if some revisors have already reviewed the document +remove a user from all its process can also be used to set a new user + +6.0.6 (2018-11-13) +--------------------- +SeedDMS_Core_Folder::addContent() uses currently logged in user as uploader instead of owner +SeedDMS_Core_DocumentContent::verifyStatus() will not set status to S_RELEASED +if currently in S_DRAFT status und no workflow, review, approval, or revision +is pending. + +6.0.5 (2018-02-27) +--------------------- +add list 'NeedsCorrectionOwner' to SeedDMS_Core_DMS::getDocumentList() + +6.0.4 (2018-02-14) +--------------------- +add lists of drafts and obsolete docs in SeedDMS_Core_DMS::getDocumentList() +add fast sql statement to SeedDMS_Core_Document::getReceiptStatus() if limit=1 +add callback onCheckAccessDocument to SeedDMS_Core_Document::getAccessMode() +add new document status 'needs correction' (S_NEEDS_CORRECTION) +do not use views as a replacement for temp. tables anymore, because they are much +slower. +add SeedDMS_Core_DocumentContent::getInstance() + +6.0.3 (2018-01-23) +--------------------- +pass 0 as default to getObjects() +SeedDMS_Core_AttributeDefinition::getStatistics() returns propper values for each item in a value set +SeedDMS_Core_DMS::getDocumentList() returns list of documents without a receiver + +6.0.2 (2017-12-19) +--------------------- +- speed up getting list of locked documents +- setting _logfile in inc.DBAccessPDO.php will log execution times in file +- fix sql statement to create temp table ttrevisionid and ttreceiptid +- SeedDMS_Core_DMS::noReadForStatus no longer needed +- SeedDMS_Core_Document::checkForDueRevisionWorkflow() also checks if there +are any waiting or pending revisions at all +- SeedDMS_Core_User::getReverseSubstitutes() works with new roles +- fix field name in getDocumentList() to make it work for pgsql +- views instead of temp. tables can be used +- ReceiveOwner list does not contain old versions anymore +- all changes up to 5.1.5 merged +- getTimeline() also returns data for documents with a scheduled revision + +6.0.1 (2017-05-28) +--------------------- +- speed up getting list of locked documents +- setting _logfile in inc.DBAccessPDO.php will log execution times in file + +6.0.0 (2017-02-28) +--------------------- +- all changes from 5.0.14 merged +- SeedDMS_Core_User::getReceiptStatus() and SeedDMS_Core_User::getReviewStatus() +only return entries of the latest document version if not specific document and +version is passed +- temp. table for revisions can be created +- new methods SeedDMS_Core_DMS::getDocumentsInReception() and +SeedDMS_Core_DMS::getDocumentsInRevision() +- limit hits of sql statement in SeedDMЅ_Core_DMS::getDuplicateDocumentContent() to 1000 +- finishRevsion() puts all revisors into state waiting, so a new revision can be started +- fix SeedDMS_Core_Group::getRevisionStatus(), which did not always return the last +log entry first +- add roles +- use classname from SeedDMS_Core_DMS::_classnames for SeedDMS_Core_DocumentContent +- add virtual access mode for document links and attachments plus callbacks to + check access mode in a hook +- add new method SeedDMS_Core_DMS::getDocumentsExpired() + +5.1.29 (2022-11-21) +--------------------- +- SeedDMS_Core_Folder::addDocument() does rollback transaction propperly when setting document categories fail +- add $skiproot and $sep parameter to SeedDMS_Core_Folder::getFolderPathPlain() +- add class name for 'documentfile' +- add method SeedDMS_Core_KeywordCategory::countKeywordLists() + +5.1.28 (2022-11-07) +--------------------- +- fix SeedDMS_Core_User::getDocumentContents() +- fix SeedDMS_Core_File::fileExtension() +- SeedDMS_Core_DMS::createPasswordRequest() creates a cryptographically secure hash +- fix sql error when deleting a folder attribute +- add SeedDMS_Core_Attribute::getParsedValue() and use it in SeedDMS_Core_Object::getAttributeValue() +- add SeedDMS_Core_DMS::getDuplicateSequenceNo() and SeedDMS_Core_Folder::reorderDocuments() +- add SeedDMS_Core_File::mimetype(), fix SeedDMS_Core_File::moveDir() +- all file operations use methods of SeedDMS_Core_File +- change namespace of iterators from SeedDMS to SeedDMS\Core + +5.1.27 (2022-08-31) +--------------------- +- fix SeedDMS_Core_DMS::addAttributeDefinition() when objtype is 0 +- sort search result even if sortorder is 'i' or 'n' +- pass an array as an attribute to search() will OR each element + +5.1.26 (2022-05-20) +--------------------- +- fix validating multi value attributes +- SeedDMS_Core_User::removeFromProcesses() can be limited to a list of documents. In that case only the last version will be modified. +- add more types to getStatisticalData() +- add optional parameter $op to SeedDMS_Core_AttributeDefinition::getObjects() +- SeedDMS_Core_AttributeDefinition::getObjects() will not filter by value if null is passed +- SeedDMS_Core_DMS::getAllAttributeDefinitions() has second parameter to filter attributes by type + +5.1.25 (2022-04-22) +--------------------- +- rename getLastWorkflowTransition() to getLastWorkflowLog() +- getLastWorkflowLog() returns a workflow entry even if the workflow has ended +- backport setFileType() from 6.0.x +- add SeedDMS_Core_File::fileExtension() +- add callbacks on onPostUpdateAttribute, onPostRemoveAttribute, onPostAddAttribute +- fix searching for document content with a custom attribute having a value set + +5.1.24 (2021-12-11) +--------------------- +- in SeedDMS_Core_DocumentContent::removeWorkflow() remove records from tblWorklflowLog before tblDWorkflowDocumentContent +- make all class variables of SeedDMS_Core_User protected +- fix various errors in SeedDMS_Core_AttributeDefinition::validate() +- add lots of unit tests +- replace incorrect use of array_search() by in_array() +- move method SeedDMS_Core_DMS::createDump() into SeedDMS_Core_DatabaseAccess +- lots of parameter checking when calling methods() +- make sure callbacks are callable +- SeedDMS_Core_Folder::getParent() returns null if there is no parent (used to be false) +- SeedDMS_Core_DMS::search() will not find document without an expiration date anymore, if the search is limited by an expiration end date but no start date +- add method SeedDMS_Core_Folder::getFoldersMinMax() +- init internal cache variables of SeedDMS_Core_Folder/SeedDMS_Core_Document and add method clearCache() +- SeedDMS_Core_Folder::hasDocuments() does not use the interal document cache anymore +- SeedDMS_Core_Document::addDocumentLink() returns an object of type SeedDMS_Core_DocumentLink in case of success +- trim email, comment, language, theme when setting data of user +- more checks whether an id > 0 when getting a database record + +5.1.23 (2021-08-19) +--------------------- +- SeedDMS_Core_DMS::getTimeline() uses status log instead of document content +- add methods SeedDMS_Core_DocumentContent::getReviewers() and SeedDMS_Core_DocumentContent::getApprovers() +- add methods SeedDMS_Core_DocumentContent::getApproveLog() and SeedDMS_Core_DocumentContent::getReviewLog() +- better handling of document with an empty workflow state +- fix checking of email addresses by using filter_var instead of regex +- add new method SeedDMS_Core_Document::hasCategory() +- add new method SeedDMS_Core_DocumentContent::removeReview() +- add new method SeedDMS_Core_DocumentContent::removeApproval() +- add new method SeedDMS_Core_User::getFolders() +- add new method SeedDMS_Core_User::getDocumentContents() +- add new method SeedDMS_Core_User::getDocumentFiles() +- add new method SeedDMS_Core_User::getDocumentLinks() +- add new type 'foldersperuser' to method SeedDMS_Core_DMS::getStatisticalData() + +5.1.22 (2021-03-15) +--------------------- +- add SeedDMS_Core_DatabaseAccess::hasTable() +- add SeedDMS_Core_User->isType() and SeedDMS_Core_Group->isType() +- add SeedDMS_Core_User->getDMS() and SeedDMS_Core_Group->getDMS() +- add new parameter to SeedDMS_Core_DMS->getDocumentList() for skipping expired documents +- add parameter $incdisabled to SeedDMS_Core_Folder::getNotifyList() +- do not validate value in SeedDMS_Core_Attribute::setValue(), it should have been done before +- SeedDMS_Core_DMS::search() can search for last date of document status change +- smarter caching in SeedDMS_Core_Document::getDocumentFiles() which fixes a potential + problem when removing a document + +5.1.21 (2020-09-29) +--------------------- +- SeedDMS_Folder_DMS::getAccessList() and getDefaultAccess() do not return fals anymore if the parent does not exists. They just stop inheritance. +- pass attribute value to callback 'onAttributeValidate' +- new paramter 'new' of methode SeedDMЅ_Core_AttributeDefinition::validate() +- check if folder/document is below rootDir can be turned on (default off) +- SeedDMS_Core_User::setHomeFolder() can be used to unset the home folder +- check if attribute definition exists when setting attributes of folders and documents + +5.1.20 (2020-09-29) +--------------------- +- SeedDMS_Core_DMS::getDocumentList() returns false, if an unknown list is passed +- SeedDMS_Core_Document::getDocumentFiles() has new parameter to select only those files attached to a specific version of the document +- removing a document version will not remove attachments of the document anymore +- set dms of new user instances in SeedDMS_Core_Group + +5.1.19 (2020-07-30) +--------------------- +- add method SeedDMS_Core_Document::setParent() as an alias for setFolder() +- clear the save content list and latest content in SeedDMS_Core_Document after + a version has been deleted. +- new method SeedDMS_Core_Document::isLatestVersion() +- add new attribute types 'document', 'folder', 'user', 'group' + +5.1.18 (2020-05-28) +--------------------- +- fixed remaining todos +- fixed parsing of file size in SeedDMS_Core_File::parse_filesize() +- fix SeedDMS_Core_DMS::getDocumentByOriginalFilename() + +5.1.17 (2020-05-22) +--------------------- +- add new callback onSetStatus +- fix SeedDMS_Core_DMS::getExpiredDocuments(), sql statement failed because temp. tables were not created +- add parameters $orderdir, $orderby, $update to SeedDMS_Core::getExpiredDocuments() + +5.1.16 (2020-04-14) +--------------------- +- fix call of hooks in SeedDMS_Core +- add variable lasterror in SeedDMS_Core_DMS which can be set by hooks to pass an + error msg to the calling application +- better error checking in SeedDMS_Core_Document::addDocumentFile() + +5.1.15 (2020-03-02) +--------------------- +- no changes, just keep same version as seeddms application + +5.1.14 (2020-02-17) +--------------------- +- speed up SeedDMS_Core_Folder::getSubFolders() SeedDMS_Core_Folder::getDocuments() by minimizing the number of sql queries. + +5.1.13 (2019-09-06) +--------------------- +- add decorators +- add new methods SeedDMS_Core_Document::isType(), SeedDMS_Core_Folder::isType(), SeedDMS_Core_DocumentContent::isType(). Use them instead of checking the class name. +- skip a fileType with just a '.' + +5.1.12 (2019-07-01) +--------------------- +- parameter $orderby passed to SeedDMS_Core_Folder::getDocuments() and SeedDMS_Core_Folder::getSubFolders() can be a string, but only the first char is evaluated +- SeedDMS_Core_DMS::search() excepts parameters as array, added orderby +- add SeedDMS_Core_Folder::hasSubFolderByName() +- fix SeedDMS_Core_Folder::hasDocumentByName() which returned an int > 0 if documents + has been loaded before and even if the document searching for was not among them. +- add new method SeedDMS_Core_Folder::empty() + +5.1.11 (2019-05-03) +--------------------- +- ??? + +5.1.10 (2019-04-04) +--------------------- +- fix php warning if workflow state doesn' have next transition +- add method SeedDMS_Core_DatabaseAccess::setLogFp() + +5.1.9 (2018-11-13) +--------------------- +- context can be passed to getAccessMode() +- call hook in SeedDMS_Core_Folder::getAccessMode() +- new optional parameter $listguest for SeedDMS_Core_Document::getReadAccessList() +- remove deprecated methods SeedDMS_Core_Document::convert(), SeedDMS_Core_Document::wasConverted(), SeedDMS_Core_Document::viewOnline(), SeedDMS_Core_Document::getUrl() + +5.1.8 (2018-07-02) +--------------------- +- SeedDMS_Core_DMS::search() returns false in case of an error +- do not use views in DBAccessPDO by default anymore, use temp. tables +- SeedDMS_Core_Document::getNotifyList() has new parameter to include disabled user in list +- fix possible sql injection in SeedDMS_Core_User + +5.1.7 (2018-04-05) +--------------------- +- just bump version + +5.1.6 (2018-02-14) +--------------------- +- add SeedDMS_Core_Folder::getDocumentsMinMax() +- add lots of DocBlocks from merge request #8 +- add SeedDMS_Core_AttributeDefinition::removeValue() + +5.1.5 (2017-11-07) +--------------------- +- use views instead of temp. tables +- add list of expired documents in SeedDMS_Core_DMS::getDocumentList() +- add methods to set comment, name, public, version of document files +- add method SeedDMS_Core_Document::transferToUser() +- SeedDMS_Core_Document::addDocumentFile() returns object of file +- add SeedDMS_Core_DocumentFile::setDate() +- remove SeedDMS_Core_DocumentCategory::addCategory() and getCategories() +- add optional parameters $limit and $offset to SeedDMS_Core_Folder::getDocuments() + and SeedDMS_Core_Folder::getSubFolders() +- getInstance() returns now null instead of false if the object was not found in the db +- add new methods SeedDMS_Core_Document::addCategories() and + SeedDMS_Core_Document::removeCategories() + +5.1.4 (2017-09-05) +--------------------- +- add virtual access mode for document links and attachments plus callbacks to + check access mode in a hook +- add new method SeedDMS_Core_DMS::getDocumentsExpired() +- all changes from 5.0.14 merged + +5.1.3 (2017-08-23) +--------------------- +- SeedDMS_Core_Document::getNotifyList() and SeedDMS_Core_Folder::getNotifyList() +returns just users which are not disabled +- add new methods removeFromProcesses(), getWorkflowsInvolved(), getKeywordCategories() to SeedDMS_Core_User +- add methods isMandatoryReviewerOf() and isMandatoryApproverOf() +- add methods transferDocumentsFolders() and transferEvents() +- add method SeedDMS_Core_DMS::getDocumentByOriginalFilename() + +5.1.2 (2017-03-23) +--------------------- +- SeedDMS_Core_DMS::filterDocumentFiles() returns also documents which are not public + if the owner tries to access them +- Check return value of onPreRemove[Document +Folder], return from calling method if bool +- Add SeedDMS_Core_DMS::getDocumentList() +- Limit number of duplicate files to 1000 +- Add hook on(Pre +Post)RemoveContent +- Add hook onAttributeValidate + +5.1.1 (2017-02-20) +--------------------- +- all changes from 5.0.11 merged + +5.1.0 (2017-02-20) +--------------------- +- added postgres support + +5.0.13 (2017-07-13) +--------------------- +- all changes from 4.3.36 merged + +5.0.12 (2017-03-23) +--------------------- +all sql statements can be logged to a file +do not sort some temporary tables anymore, because it causes an error in mysql if sql_mode=only_full_group_by is set + +5.0.11 (2017-02-28) +--------------------- +- all changes from 4.3.34 merged + +5.0.10 (2017-02-20) +--------------------- +- all changes from 4.3.33 merged + +5.0.9 (2016-11-02) +--------------------- +- all changes from 4.3.32 merged + +5.0.8 (2016-11-02) +--------------------- +- all changes from 4.3.31 merged + +5.0.7 (2016-11-02) +--------------------- +- all changes from 4.3.30 merged +- better attribute value checking + +5.0.6 (2016-09-06) +--------------------- +- all changes from 4.3.29 merged + +5.0.5 (2016-08-09) +--------------------- +- all changes from 4.3.28 merged + +5.0.4 (2016-05-03) +--------------------- +- all changes from 4.3.27 merged + +5.0.3 (2016-04-04) +--------------------- +- use classname from SeedDMS_Core_DMS::_classnames for SeedDMS_Core_DocumentContent +- all changes from 4.3.26 merged + +5.0.2 (2016-04-26) +--------------------- +- all changes from 4.3.25 merged + +5.0.1 (2016-01-22) +--------------------- +- all changes from 4.3.24 merged + +5.0.0 (2016-01-22) +--------------------- +- classes can be overloaded +- clean workflow log when a document version was deleted + +4.3.37 (2018-02-14) +--------------------- +- SeedDMS_Core_DMS::search() finds documents without a status log + +4.3.36 (2017-03-22) +--------------------- +- fix sql statement for creating temp. tables (sqlite) + +4.3.35 (2017-07-11) +--------------------- +do not sort some temporary tables anymore, because it causes an error in mysql if sql_mode=only_full_group_by is set + +4.3.34 (2017-02-28) +--------------------- +SeedDMS_Core_DMS::getDuplicateDocumentContent() returns complete document + +4.3.33 (2017-02-22) +--------------------- +- SeedDMЅ_Core_DMS::getTimeline() no longer returns duplicate documents +- SeedDMЅ_Core_Document::addContent() sets workflow after status was set +- SeedDMЅ_Core_Keyword::setOwner() fix sql statement +- SeedDMЅ_Core_User::setFullname() minor fix in sql statement + +4.3.32 (2017-01-12) +--------------------- +- order groups by name returned by getReadAccessList() +- add optional parameter to SeedDMS_Core_DMS::filterDocumentLinks() +- SeedDMS_Core_DMS::search() can search for document/folder id + +4.3.31 (2016-11-02) +--------------------- +- new method SeedDMЅ_Core_WorkflowAction::getTransitions() +- new method SeedDMЅ_Core_WorkflowState::getTransitions() +- new method SeedDMЅ_Core_AttributeDefinition::parseValue() +- add check for cycles in workflow SeedDMS_Core_Workflow::checkForCycles() + +4.3.30 (2016-10-07) +--------------------- +- new method SeedDMЅ_Core_AttributeDefinition::getValueSetSeparator() +- trim each value of a value set before saving the complete value set as a string + +4.3.29 (2016-09-06) +--------------------- +- SeedDMЅ_Core_Object::getAttributes() orders attributes by name of attribute definition +- SeedDMЅ_Core_Workflow::addTransition() force reload of transition list after adding a +- SeedDMЅ_Core_Document::rewrite[Review +Approval]Log() will also copy file if it exists +- add method SeedDMЅ_Core_Document::rewriteWorkflowLog() + +4.3.28 (2016-08-24) +--------------------- +- SeedDMЅ_Core_DMS::search() searches also comment of document version + +4.3.27 (2016-04-26) +--------------------- +- callbacks can have more then one user function +- fix some sql statements, because they didn't work with mysql 5.7.5 anymore + +4.3.26 (2016-04-04) +--------------------- +- add more callbacks + +4.3.25 (2016-03-08) +--------------------- +- rename SeedDMS_Core_Group::getNotificationsByGroup() to getNotifications() +- use __construct() for all constructors +- fix setting multi value attributes for versions + +4.3.24 (2016-01-22) +--------------------- +- make sure boolean attribute is saved as 0/1 +- add SeedDMS_Core_User::[g +s]etMandatoryWorkflows() +- add SeedDMS_Core_User::getNotifications() +- add SeedDMS_Core_Group::getNotifications() +- SeedDMS_Core_DMS::getNotificationsByGroup() and +SeedDMS_Core_DMS::getNotificationsByUser() are deprecated +- SeedDMS_Core_DocumentCategory::getDocumentsByCategory() now returns the documents +- add SeedDMS_Core_Group::getWorkflowStatus() +- SeedDMS_Core_User::getDocumentsLocked() sets locking user propperly + +4.3.24 (2016-01-21) +--------------------- +- make sure boolean attribute is saved as 0/1 +- add SeedDMS_Core_User::[g +s]etMandatoryWorkflows() +- add SeedDMS_Core_User::getNotifications() +- add SeedDMS_Core_Group::getNotifications() +- SeedDMS_Core_DMS::getNotificationsByGroup() and +SeedDMS_Core_DMS::getNotificationsByUser() are deprecated +- SeedDMS_Core_DocumentCategory::getDocumentsByCategory() now returns the documents +- add SeedDMS_Core_Group::getWorkflowStatus() +- SeedDMS_Core_User::getDocumentsLocked() sets locking user propperly + +4.3.23 (2016-01-21) +--------------------- +- new method SeedDMS_Core_DMS::createDump() +- minor improvements int SeedDMS_Core_Document::getReadAccessList() + +4.3.22 (2015-11-09) +--------------------- +- fix sql statement to reset password +- pass some more information for timeline + +4.3.21 (2015-09-28) +--------------------- +- add method SeedDMS_Core_Database::getCurrentTimestamp() +- add method SeedDMS_Core_Database::getCurrentDatetime() +- user getCurrentTimestamp() and getCurrentDatetime() whenever possible + +4.3.20 (2015-06-26) +--------------------- +- add method SeedDMS_Core_DMS::checkDate() +- add method SeedDMS_Core_Document::setDate() +- add method SeedDMS_Core_Folder::setDate() +- date can be passed to SeedDMS_Core_DocumentContent::setStatus() +- add method SeedDMS_Core_DocumentContent::rewriteStatusLog() +- add method SeedDMS_Core_DocumentContent::rewriteReviewLog() +- add method SeedDMS_Core_DocumentContent::rewriteApprovalLog() +- access rights for guest are also taken into account if set in an acl. Previously guest could gain read rights even if the access was probibited +by a group or user right + +4.3.19 (2015-06-26) +--------------------- +- add optional paramter $noclean to clearAccessList(), setDefaultAccess(), setInheritAccess() +- clearAccessList() will clean up the notifier list +- new method cleanNotifyList() + +4.3.18 (2015-06-09) +--------------------- +- add optional paramter $msg to SeedDMS_Core_DocumentContent::verifyStatus() +- add method SeedDMS_Core_DMS::getDuplicateDocumentContent() + +4.3.17 (2015-03-27) +--------------------- +clean workflow log when a document version was deleted + +4.3.16 (2015-03-20) +--------------------- +no changes + +4.3.15 (2015-02-12) +--------------------- +users returned by SeedDMS_Core_DMS::getAllUsers() have language and theme set again + +4.3.13 (2014-11-27) +--------------------- +- fix searching for attributes +- add some more documentation +- SeedDMS_Core_DMS::getDocumentCategories() returns categories sorted by name (Bug #181) +- new methode SeedDMS_Core_Document::replaceContent() which replaces the content of a version. + 4.3.14 +- add missing start transaction in SeedDMD_Core_Folder::remove() +- SeedDMD_Core_Folder::isSubFolder() doesn't compare object instances anymore (Bug #194) + +4.3.12 (2014-11-17) +--------------------- +- fix searching folders with multivalue attributes + +4.3.11 (2014-11-13) +--------------------- +- fixed saving multivalue attributes +- add method SeedDMS_Core_Attribute::getValueAsArray() + +4.3.10 (2014-10-22) +--------------------- +new release + +4.3.9 (2014-07-30) +--------------------- +- SeedDMS_Core_KeywordCategory::getKeywordLists() sorts keywords aphabetically +- SeedDMS_Core_DMS::addUser() doesn't throw an error if sql_mode is set to STRICT_TRANS_TABLES and pwdexpiration is not set to a valid date. + +4.3.8 (2014-04-09) +--------------------- +- new method SeedDMS_Core_DMS::getStatisticalData() + +4.3.7 (2014-03-21) +--------------------- +no changes + +4.3.6 (2014-03-18) +--------------------- +- add optional parameters $publiconly=false and $user=null to SeedDMS_Core_Document::getDocumentLinks() +- add new method SeedDMS_Core_Document::getReverseDocumentLinks() + +4.3.5 (2014-03-04) +--------------------- +no changes + +4.3.4 (2014-02-01) +--------------------- +- fix handling of multivalue attributes + +4.3.3 (2014-02-01) +--------------------- +- SeedDMS_Folder::getDocuments() and SeedDMS_Folder::getSubFolders() do not + do any sorting if $orderby is not set. +- database hostname can have port seperated by ':' +- make all functions in SeedDMS_Core_File static (fixes problem with php 5.5.x) + +4.3.2 (2013-11-27) +--------------------- +- new method SeedDMS_Core_Folder::isSubFolder() +- check for subFolder in SeedDMS_Core_Folder::setParent() +- new methods SeedDMS_Core_DMS::checkFolders() and SeedDMS_Core_DMS::checkDocuments() + +4.3.0 (2013-09-05) +--------------------- +- various small corrections +- comment of version is no longer taken from document if version comment is empty +- passing an array of users to SeedDMЅ_Core_DMS::search() instead of a single user ist now allowed +- turn on foreign key constraints for sqlite3 +- SeedDMЅ_Core_Folder::getPath() can handle a subfolder treated as a root folder + +4.2.2 (2013-05-17) +--------------------- +- admins can be added as reviewer/approver again + +4.2.1 (2013-04-30) +--------------------- +- fixed bug in SeedDMS_Core_DocumentContent::addIndApp() + +4.2.0 (2013-04-22) +--------------------- +- fixed bug in SeedDMS_Core_DocumentContent::addIndApp() + +4.1.3 (2013-04-08) +--------------------- +- stay in sync with seeddms application + +4.1.2 (2013-04-05) +--------------------- +- set propper folderList of sub folders after moving a folder + +4.1.1 (2013-04-05) +--------------------- +- stay in sync with seeddms application + +4.1.0 (2013-03-28) +--------------------- +- minor bugfixes + +4.0.0 (2013-02-26) +--------------------- +- minor bugfixes + +4.0.0pre5 (2013-02-14) +--------------------- +- changed name from letodms to seeddms +- fixed SeedDMS_Database::TableList() + +4.0.0pre4 (2013-02-11) +--------------------- +- calculate checksum for document versions +- some bug fixes +- some more documentation +- added new methods SeedDMS_Core_Document::getReadUserList() and + SeedDMS_Core_Folder::getReadUserList() which replaces getApproversList() +- fixed sql statement in getReadUserList() for sqlite3 + +4.0.0pre3 (2013-02-08) +--------------------- +- minor bug fixes + +4.0.0pre2 (2013-02-06) +--------------------- +- lots of bug fixes +- replaced more of old var declaration +- more code documentation + +4.0.0pre1 (2013-01-24) +--------------------- +- added database transactions +- new workflow +- replaced old var declaration + +4.0.0RC1 (2013-02-20) +--------------------- +- minor bugfixes + +3.4.0 (2012-12-13) +--------------------- +- added PDO database driver, several sql changes for better compatiblity +- fixed bug when adding a new document category +- make sure the database remains consistent even in case of errors + +3.3.9 (2012-09-19) +--------------------- +- version update to be in sync with letodms application + +3.3.8 (2012-09-16) +--------------------- +- more sql injection protection in LetoDMS_Core_User + +3.3.7 (2012-08-25) +--------------------- +- no changes, just keep same version as letodms application + +3.3.6 (2012-07-16) +--------------------- +- no changes, just keep same version as letodms application + +3.3.5 (2012-04-30) +--------------------- +- minor corrections + +3.3.4 (2012-04-11) +--------------------- +- fixed bug in LetoDMS_Core_DocumentFile::getPath() + +3.3.3 (2012-03-28) +--------------------- +- fixed bug in LetoDMS_Core_Document::getPath() + +3.3.2 (2012-03-22) +--------------------- +- fixed bug in LetoDMS_Core_Document::getDir() + +3.3.1 (2012-03-21) +--------------------- +- new release + +3.3.0 (2012-02-08) +--------------------- +- added methods to find and repair errors in document and folder records +- removed sendmail parameter from some methods in LetoDMS_Core_Document +- do not use some of the temporay tables anymore +- SetFetchMode(ADODB_FETCH_ASSOC) in LetoDMS_Core_DatabaseAccess::connect() + +3.2.0 (2011-07-23) +--------------------- +New release + +3.0.0 (2010-04-27) +--------------------- +Initial release + diff --git a/SeedDMS_Core/Core/inc.ClassDMS.php b/SeedDMS_Core/Core/inc.ClassDMS.php index bfa196e93..de02761b7 100644 --- a/SeedDMS_Core/Core/inc.ClassDMS.php +++ b/SeedDMS_Core/Core/inc.ClassDMS.php @@ -4067,7 +4067,7 @@ class SeedDMS_Core_DMS { * @return bool|SeedDMS_Core_Document[] */ function getWrongFiletypeDocumentContent() { /* {{{ */ - $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `mimeType` in ('application/pdf', 'image/png', 'image/gif', 'image/jpg')"; + $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `mimeType` in ('application/zip', 'application/pdf', 'image/png', 'image/gif', 'image/jpg', 'audio/mp3', 'text/rtf')"; $resArr = $this->db->getResultArray($queryStr); if ($resArr === false) return false; @@ -4077,10 +4077,13 @@ class SeedDMS_Core_DMS { foreach($resArr as $row) { $expect = ''; switch($row['mimeType']) { + case "application/zip": case "application/pdf": case "image/png": case "image/gif": case "image/jpg": + case "audio/mp3": + case "text/rtf": $expect = substr($row['mimeType'], -3, 3); break; } diff --git a/SeedDMS_Core/bootstrap-5.php b/SeedDMS_Core/bootstrap-5.php new file mode 100644 index 000000000..5d0d0e4a5 --- /dev/null +++ b/SeedDMS_Core/bootstrap-5.php @@ -0,0 +1,3 @@ + + 2010-04-27 3.0.0 3.0.0 @@ -125,13 +126,14 @@ support custom attributes for users stable stable - 2010-04-27 GPL License Initial release + 2011-07-23 + 3.2.0 3.2.0 @@ -140,8 +142,6 @@ Initial release stable stable - 2011-07-23 - GPL License New release @@ -701,21 +701,21 @@ no changes - 2014-07-30 - - - 4.3.9 - 4.3.9 - - - stable - stable - - GPL License - + 2014-07-30 + + + 4.3.9 + 4.3.9 + + + stable + stable + + GPL License + - SeedDMS_Core_KeywordCategory::getKeywordLists() sorts keywords aphabetically - SeedDMS_Core_DMS::addUser() doesn't throw an error if sql_mode is set to STRICT_TRANS_TABLES and pwdexpiration is not set to a valid date. - + 2014-10-22 @@ -957,7 +957,7 @@ by a group or user right - new method SeedDMS_Core_DMS::createDump() - minor improvements int SeedDMS_Core_Document::getReadAccessList() - + 2016-01-22 @@ -981,7 +981,7 @@ SeedDMS_Core_DMS::getNotificationsByUser() are deprecated - SeedDMS_Core_DocumentCategory::getDocumentsByCategory() now returns the documents - add SeedDMS_Core_Group::getWorkflowStatus() - SeedDMS_Core_User::getDocumentsLocked() sets locking user propperly - + 2016-01-22 @@ -1056,7 +1056,7 @@ SeedDMS_Core_DMS::getNotificationsByUser() are deprecated GPL License - add more callbacks - + 2016-04-26 @@ -1437,7 +1437,7 @@ do not sort some temporary tables anymore, because it causes an error in mysql i stable GPL License - + all sql statements can be logged to a file do not sort some temporary tables anymore, because it causes an error in mysql if sql_mode=only_full_group_by is set @@ -1503,13 +1503,13 @@ do not sort some temporary tables anymore, because it causes an error in mysql i GPL License -SeedDMS_Core_DMS::filterDocumentFiles() returns also documents which are not public -if the owner tries to access them -Check return value of onPreRemove[Document|Folder], return from calling method if bool -Add SeedDMS_Core_DMS::getDocumentList() -Limit number of duplicate files to 1000 -Add hook on(Pre|Post)RemoveContent -Add hook onAttributeValidate +- SeedDMS_Core_DMS::filterDocumentFiles() returns also documents which are not public + if the owner tries to access them +- Check return value of onPreRemove[Document|Folder], return from calling method if bool +- Add SeedDMS_Core_DMS::getDocumentList() +- Limit number of duplicate files to 1000 +- Add hook on(Pre|Post)RemoveContent +- Add hook onAttributeValidate @@ -1592,9 +1592,9 @@ returns just users which are not disabled GPL License -add SeedDMS_Core_Folder::getDocumentsMinMax() -add lots of DocBlocks from merge request #8 -add SeedDMS_Core_AttributeDefinition::removeValue() +- add SeedDMS_Core_Folder::getDocumentsMinMax() +- add lots of DocBlocks from merge request #8 +- add SeedDMS_Core_AttributeDefinition::removeValue() @@ -1610,7 +1610,7 @@ add SeedDMS_Core_AttributeDefinition::removeValue() GPL License -just bump version +- just bump version @@ -1626,10 +1626,10 @@ just bump version GPL License -SeedDMS_Core_DMS::search() returns false in case of an error -do not use views in DBAccessPDO by default anymore, use temp. tables -SeedDMS_Core_Document::getNotifyList() has new parameter to include disabled user in list -fix possible sql injection in SeedDMS_Core_User +- SeedDMS_Core_DMS::search() returns false in case of an error +- do not use views in DBAccessPDO by default anymore, use temp. tables +- SeedDMS_Core_Document::getNotifyList() has new parameter to include disabled user in list +- fix possible sql injection in SeedDMS_Core_User @@ -1645,10 +1645,10 @@ fix possible sql injection in SeedDMS_Core_User GPL License -context can be passed to getAccessMode() -call hook in SeedDMS_Core_Folder::getAccessMode() -new optional parameter $listguest for SeedDMS_Core_Document::getReadAccessList() -remove deprecated methods SeedDMS_Core_Document::convert(), SeedDMS_Core_Document::wasConverted(), SeedDMS_Core_Document::viewOnline(), SeedDMS_Core_Document::getUrl() +- context can be passed to getAccessMode() +- call hook in SeedDMS_Core_Folder::getAccessMode() +- new optional parameter $listguest for SeedDMS_Core_Document::getReadAccessList() +- remove deprecated methods SeedDMS_Core_Document::convert(), SeedDMS_Core_Document::wasConverted(), SeedDMS_Core_Document::viewOnline(), SeedDMS_Core_Document::getUrl() @@ -1664,8 +1664,8 @@ remove deprecated methods SeedDMS_Core_Document::convert(), SeedDMS_Core_Documen GPL License -fix php warning if workflow state doesn' have next transition -add method SeedDMS_Core_DatabaseAccess::setLogFp() +- fix php warning if workflow state doesn' have next transition +- add method SeedDMS_Core_DatabaseAccess::setLogFp() @@ -1681,7 +1681,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp() GPL License -??? +- ??? @@ -1737,10 +1737,10 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp() GPL License - speed up SeedDMS_Core_Folder::getSubFolders() SeedDMS_Core_Folder::getDocuments() by minimizing the number of sql queries. - + - 2020-02-18 + 2020-03-02 5.1.15 @@ -1753,7 +1753,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp() GPL License - no changes, just keep same version as seeddms application - + 2020-04-14 @@ -1908,7 +1908,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp() GPL License - SeedDMS_Core_DMS::getTimeline() uses status log instead of document content -- add methods SeedDMS_Core_DocumentContent::getReviewers() and SeedDMS_Core_DocumentContent::getApprovers() +- add methods SeedDMS_Core_DocumentContent::getReviewers() and SeedDMS_Core_DocumentContent::getApprovers() - add methods SeedDMS_Core_DocumentContent::getApproveLog() and SeedDMS_Core_DocumentContent::getReviewLog() - better handling of document with an empty workflow state - fix checking of email addresses by using filter_var instead of regex @@ -1971,6 +1971,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp() - backport setFileType() from 6.0.x - add SeedDMS_Core_File::fileExtension() - add callbacks on onPostUpdateAttribute, onPostRemoveAttribute, onPostAddAttribute +- fix searching for document content with a custom attribute having a value set diff --git a/SeedDMS_Core/phpunit.xml b/SeedDMS_Core/phpunit.xml new file mode 100644 index 000000000..103f11089 --- /dev/null +++ b/SeedDMS_Core/phpunit.xml @@ -0,0 +1,26 @@ + + + + + tests + + + + + + Core + + + diff --git a/SeedDMS_Core/tests/.phpunit.result.cache b/SeedDMS_Core/tests/.phpunit.result.cache new file mode 100644 index 000000000..007ae03d8 --- /dev/null +++ b/SeedDMS_Core/tests/.phpunit.result.cache @@ -0,0 +1 @@ +C:37:"PHPUnit\Runner\DefaultTestResultCache":106:{a:2:{s:7:"defects";a:1:{s:17:"DmsTest::testInit";i:3;}s:5:"times";a:1:{s:17:"DmsTest::testInit";d:0.002;}}} \ No newline at end of file diff --git a/SeedDMS_Core/tests/AttributeDefinitionTest.php b/SeedDMS_Core/tests/AttributeDefinitionTest.php new file mode 100644 index 000000000..f6276e536 --- /dev/null +++ b/SeedDMS_Core/tests/AttributeDefinitionTest.php @@ -0,0 +1,574 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\TestCase; + +/** + * Attribute definition test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class AttributeDefinitionTest extends TestCase +{ + + /** + * Create a real dms object with a mocked db + * + * This mock is only used if \SeedDMS_Core_DatabaseAccess::getResult() is + * called once. This is the case for all \SeedDMS_Core_AttributeDefinition::setXXX() + * methods like setName(). + * + * @return \SeedDMS_Core_DMS + */ + protected function getDmsWithMockedDb() : \SeedDMS_Core_DMS + { + $db = $this->createMock(\SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE ")) + ->willReturn(true); + $dms = new \SeedDMS_Core_DMS($db, ''); + return $dms; + } + + /** + * Create a mocked dms + * + * @return \SeedDMS_Core_DMS + */ + protected function getDmsMock() : \SeedDMS_Core_DMS + { + $dms = $this->createMock(\SeedDMS_Core_DMS::class); + $dms->expects($this->any()) + ->method('getDocument') + ->with(1) + ->willReturn(true); + $dms->expects($this->any()) + ->method('getFolder') + ->with(1) + ->willReturn(true); + $dms->expects($this->any()) + ->method('getUser') + ->will( + $this->returnValueMap( + array( + array(1, new \SeedDMS_Core_User(1, 'admin', 'pass', 'Joe Foo', 'baz@foo.de', 'en_GB', 'bootstrap', 'My comment', \SeedDMS_Core_User::role_admin)), + array(2, new \SeedDMS_Core_User(2, 'admin2', 'pass', 'Joe Bar', 'bar@foo.de', 'en_GB', 'bootstrap', 'My comment', \SeedDMS_Core_User::role_admin)), + array(3, null) + ) + ) + ); + $dms->expects($this->any()) + ->method('getGroup') + ->will( + $this->returnValueMap( + array( + array(1, new \SeedDMS_Core_Group(1, 'admin group 1', 'My comment')), + array(2, new \SeedDMS_Core_Group(2, 'admin group 2', 'My comment')), + array(3, null) + ) + ) + ); + return $dms; + } + + /** + * Create a mock attribute definition object + * + * @param int $type type of attribute + * @param boolean $multiple set to true for multi value attributes + * @param int $minvalues minimum number of attribute values + * @param int $maxvalues maximum number of attribute values + * @param string $valueset list of allowed values separated by the first char + * @param string $regex regular expression that must match the attribute value + * + * @return \SeedDMS_Core_AttributeDefinition + */ + protected function getAttributeDefinition($type, $multiple=false, $minvalues=0, $maxvalues=0, $valueset='', $regex='') + { + $attrdef = new \SeedDMS_Core_AttributeDefinition(1, 'foo attr', \SeedDMS_Core_AttributeDefinition::objtype_folder, $type, $multiple, $minvalues, $maxvalues, $valueset, $regex); + return $attrdef; + } + + /** + * Test getId() + * + * @return void + */ + public function testGetId() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEquals(1, $attrdef->getId()); + } + + /** + * Test getName() + * + * @return void + */ + public function testGetName() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEquals('foo attr', $attrdef->getName()); + } + + /** + * Test setName() + * + * @return void + */ + public function testSetName() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + $attrdef->setName('bar attr'); + $this->assertEquals('bar attr', $attrdef->getName()); + } + + /** + * Test getObjType() + * + * @return void + */ + public function testGetObjType() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::objtype_folder, $attrdef->getObjType()); + } + + /** + * Test setObjType() + * + * @return void + */ + public function testSetObjType() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + $attrdef->setObjType(\SeedDMS_Core_AttributeDefinition::objtype_document); + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::objtype_document, $attrdef->getObjType()); + } + + /** + * Test getType() + * + * @return void + */ + public function testGetType() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::type_int, $attrdef->getType()); + } + + /** + * Test setType() + * + * @return void + */ + public function testSetType() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + $attrdef->setType(\SeedDMS_Core_AttributeDefinition::type_string); + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::type_string, $attrdef->getType()); + } + + /** + * Test getMultipleValues() + * + * @return void + */ + public function testGetMultipleValues() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEquals(false, $attrdef->getMultipleValues()); + } + + /** + * Test setMultipleValues() + * + * @return void + */ + public function testSetMultipleValues() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + /* Toogle the current value of multiple values */ + $oldvalue = $attrdef->getMultipleValues(); + $attrdef->setMultipleValues(!$oldvalue); + $this->assertEquals(!$oldvalue, $attrdef->getMultipleValues()); + } + + /** + * Test getMinValues() + * + * @return void + */ + public function testGetMinValues() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEquals(0, $attrdef->getMinValues()); + } + + /** + * Test setMinValues() + * + * @return void + */ + public function testSetMinValues() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + /* add 5 to value of min values */ + $oldvalue = $attrdef->getMinValues(); + $attrdef->setMinValues($oldvalue+5); + $this->assertEquals($oldvalue+5, $attrdef->getMinValues()); + } + + /** + * Test getMaxValues() + * + * @return void + */ + public function testGetMaxValues() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEquals(0, $attrdef->getMaxValues()); + } + + /** + * Test setMaxValues() + * + * @return void + */ + public function testSetMaxValues() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + /* add 5 to value of max values */ + $oldvalue = $attrdef->getMaxValues(); + $attrdef->setMaxValues($oldvalue+5); + $this->assertEquals($oldvalue+5, $attrdef->getMaxValues()); + } + + /** + * Test getValueSet() + * + * @return void + */ + public function testGetValueSet() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '|foo|bar|baz'); + $this->assertEquals('|foo|bar|baz', $attrdef->getValueSet()); + } + + /** + * Test getValueSetSeparator() + * + * @return void + */ + public function testGetValueSetSeparator() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '|foo|bar|baz'); + $this->assertEquals('|', $attrdef->getValueSetSeparator()); + /* No value set will return no separator */ + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertEmpty($attrdef->getValueSetSeparator()); + /* Even a 1 char value set will return no separator */ + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '|'); + $this->assertEmpty($attrdef->getValueSetSeparator()); + /* Multiple users or groups always use a ',' as a separator */ + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_user, true); + $this->assertEquals(',', $attrdef->getValueSetSeparator()); + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_group, true); + $this->assertEquals(',', $attrdef->getValueSetSeparator()); + } + + /** + * Test getValueSetAsArray() + * + * @return void + */ + public function testGetValueSetAsArray() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '|foo|bar|baz '); + $valueset = $attrdef->getValueSetAsArray(); + $this->assertIsArray($valueset); + $this->assertCount(3, $valueset); + /* value set must contain 'baz' though 'baz ' was originally set */ + $this->assertContains('baz', $valueset); + /* No value set will return an empty array */ + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string); + $valueset = $attrdef->getValueSetAsArray(); + $this->assertIsArray($valueset); + $this->assertEmpty($valueset); + } + + /** + * Test getValueSetValue() + * + * @return void + */ + public function testGetValueSetValue() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '|foo|bar|baz '); + $this->assertEquals('foo', $attrdef->getValueSetValue(0)); + /* Check if trimming of 'baz ' worked */ + $this->assertEquals('baz', $attrdef->getValueSetValue(2)); + /* Getting the value of a none existing index returns false */ + $this->assertFalse($attrdef->getValueSetValue(3)); + + /* Getting a value from a none existing value set returns false as well */ + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string); + $this->assertFalse($attrdef->getValueSetValue(0)); + } + + /** + * Test setValueSet() + * + * @return void + */ + public function testSetValueSet() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + /* add 5 to value of min values */ + $attrdef->setValueSet(' |foo|bar | baz '); + $this->assertEquals('|foo|bar|baz', $attrdef->getValueSet()); + } + + /** + * Test getRegex() + * + * @return void + */ + public function testGetRegex() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '', '[0-9].*'); + $this->assertEquals('[0-9].*', $attrdef->getRegex()); + } + + /** + * Test setRegex() + * + * @return void + */ + public function testSetRegex() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + /* set a new valid regex */ + $this->assertTrue($attrdef->setRegex(' /[0-9].*/i ')); + $this->assertEquals('/[0-9].*/i', $attrdef->getRegex()); + /* set a new invalid regex will return false and keep the old regex */ + $this->assertFalse($attrdef->setRegex(' /([0-9].*/i ')); + $this->assertEquals('/[0-9].*/i', $attrdef->getRegex()); + } + + /** + * Test setEmptyRegex() + * + * @return void + */ + public function testSetEmptyRegex() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string); + /* A mocked dms is needed for updating the database */ + $attrdef->setDMS(self::getDmsWithMockedDb()); + /* set an empty regex */ + $this->assertTrue($attrdef->setRegex('')); + } + + /** + * Test parseValue() + * + * @return void + */ + public function testParseValue() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string); + $value = $attrdef->parseValue('foo'); + $this->assertIsArray($value); + $this->assertCount(1, $value); + $this->assertContains('foo', $value); + /* An attribute definition with multiple values will split the value by the first char */ + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, true, 0, 0, '|baz|bar|foo'); + $value = $attrdef->parseValue('|bar|baz'); + $this->assertIsArray($value); + $this->assertCount(2, $value); + /* An attribute definition without multiple values, will treat the value as a string */ + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '|baz|bar|foo'); + $value = $attrdef->parseValue('|bar|baz'); + $this->assertIsArray($value); + $this->assertCount(1, $value); + $this->assertContains('|bar|baz', $value); + } + + /** + * Test validate() + * + * @TODO Instead of having a lengthy list of assert calls, this could be + * implemented with data providers for each attribute type + * + * @return void + */ + public function testValidate() + { + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string); + $this->assertTrue($attrdef->validate('')); // even an empty string is valid + $this->assertTrue($attrdef->validate('foo')); // there is no invalid string + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '', '/[0-9]*S/'); + $this->assertFalse($attrdef->validate('foo')); // doesn't match the regex + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_regex, $attrdef->getValidationError()); + $this->assertTrue($attrdef->validate('S')); // no leading numbers needed + $this->assertTrue($attrdef->validate('8980S')); // leading numbers are ok + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, false, 0, 0, '|foo|bar|baz', ''); + $this->assertTrue($attrdef->validate('foo')); // is part of value map + $this->assertFalse($attrdef->validate('foz')); // is not part of value map + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_valueset, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, true, 0, 0, '|foo|bar|baz', ''); + $this->assertTrue($attrdef->validate('foo')); // is part of value map + $this->assertFalse($attrdef->validate('')); // an empty value cannot be in the valueset + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_valueset, $attrdef->getValidationError()); + $this->assertTrue($attrdef->validate('|foo|baz')); // both are part of value map + $this->assertFalse($attrdef->validate('|foz|baz')); // 'foz' is not part of value map + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_valueset, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_string, true, 1, 1, '|foo|bar|baz', ''); + $this->assertTrue($attrdef->validate('foo')); // is part of value map + $this->assertFalse($attrdef->validate('')); // empty string is invalid because of min values = 1 + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_min_values, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('|foo|baz')); // both are part of value map, but only value is allowed + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_max_values, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_boolean); + $this->assertTrue($attrdef->validate(0)); + $this->assertTrue($attrdef->validate(1)); + $this->assertFalse($attrdef->validate(2)); + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_boolean, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_int); + $this->assertTrue($attrdef->validate(0)); + $this->assertTrue($attrdef->validate('0')); + $this->assertFalse($attrdef->validate('a')); + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_int, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_date); + $this->assertTrue($attrdef->validate('2021-09-30')); + $this->assertTrue($attrdef->validate('1968-02-29')); // 1968 was a leap year + $this->assertTrue($attrdef->validate('2000-02-29')); // 2000 was a leap year + $this->assertFalse($attrdef->validate('1900-02-29')); // 1900 didn't was a leap year + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_date, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('1970-02-29')); // 1970 didn't was a leap year + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_date, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('2010/02/28')); // This has the wrong format + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_date, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('1970-00-29')); // 0 month is not allowed + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_date, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('1970-01-00')); // 0 day is not allowed + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_date, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_float); + $this->assertTrue($attrdef->validate('0.567')); + $this->assertTrue($attrdef->validate('1000')); + $this->assertTrue($attrdef->validate('1000e3')); + $this->assertTrue($attrdef->validate('1000e-3')); + $this->assertTrue($attrdef->validate('-1000')); + $this->assertTrue($attrdef->validate('+1000')); + $this->assertFalse($attrdef->validate('0,567')); // wrong decimal point + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_float, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('0.56.7')); // two decimal point + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_float, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_email); + $this->assertTrue($attrdef->validate('info@seeddms.org')); + $this->assertTrue($attrdef->validate('info@seeddms.verylongtopleveldomain')); + $this->assertFalse($attrdef->validate('@seeddms.org')); // no user + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_email, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('info@localhost')); // no tld + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_email, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('info@@seeddms.org')); // double @ + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_email, $attrdef->getValidationError()); + $this->assertTrue($attrdef->validate('info@subsubdomain.subdomain.seeddms.org')); // multiple subdomains are ok + $this->assertFalse($attrdef->validate('info@seeddms..org')); // double . is not allowed + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_email, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('info@s.org')); // 2nd level domain name is too short + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_email, $attrdef->getValidationError()); + $this->assertFalse($attrdef->validate('info@seeddms.o')); // top level domain name is too short + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_email, $attrdef->getValidationError()); + $this->assertTrue($attrdef->validate('info@0123456789-0123456789-0123456789-0123456789-0123456789-01234567.org')); // domain name is 63 chars long, which is the max length + $this->assertFalse($attrdef->validate('info@0123456789-0123456789-0123456789-0123456789-0123456789-012345678.org')); // domain name is 1 char longer than 63 chars + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_email, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_url); + $this->assertTrue($attrdef->validate('http://seeddms.org')); + $this->assertTrue($attrdef->validate('https://seeddms.org')); + $this->assertFalse($attrdef->validate('ftp://seeddms.org')); // ftp is not allowed + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_url, $attrdef->getValidationError()); + $this->assertTrue($attrdef->validate('http://localhost')); // no tld is just fine + $this->assertFalse($attrdef->validate('http://localhost.o')); // tld is to short + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_url, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_user); + $attrdef->setDMS(self::getDmsMock()); + $this->assertTrue($attrdef->validate(1)); + $this->assertFalse($attrdef->validate(3)); + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_user, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_group); + $attrdef->setDMS(self::getDmsMock()); + $this->assertTrue($attrdef->validate('1')); + $this->assertTrue($attrdef->validate('2')); + $this->assertFalse($attrdef->validate('3')); // there is no group with id=3 + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_group, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_group, true); + $attrdef->setDMS(self::getDmsMock()); + $this->assertTrue($attrdef->validate(',1,2')); + $this->assertFalse($attrdef->validate(',1,2,3')); // there is no group with id=3 + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_group, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_user); + $attrdef->setDMS(self::getDmsMock()); + $this->assertTrue($attrdef->validate('1')); + $this->assertTrue($attrdef->validate('2')); + $this->assertFalse($attrdef->validate('3')); // there is no user with id=3 + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_user, $attrdef->getValidationError()); + + $attrdef = self::getAttributeDefinition(\SeedDMS_Core_AttributeDefinition::type_user, true); + $attrdef->setDMS(self::getDmsMock()); + $this->assertTrue($attrdef->validate(',1,2')); + $this->assertFalse($attrdef->validate(',1,2,3')); // there is no user with id=3 + $this->assertEquals(\SeedDMS_Core_AttributeDefinition::val_error_user, $attrdef->getValidationError()); + } + +} diff --git a/SeedDMS_Core/tests/AttributeTest.php b/SeedDMS_Core/tests/AttributeTest.php new file mode 100644 index 000000000..66d7dba84 --- /dev/null +++ b/SeedDMS_Core/tests/AttributeTest.php @@ -0,0 +1,155 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\TestCase; + +/** + * Attribute and attribute definition test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class AttributeTest extends TestCase +{ + + /** + * Create a mock dms object + * + * @return SeedDMS_Core_DMS + */ + protected function getMockDMS() : SeedDMS_Core_DMS + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->any()) + ->method('getResult') + ->with($this->stringContains("UPDATE ")) + ->willReturn(true); + $dms = new SeedDMS_Core_DMS($db, ''); + return $dms; + } + + /** + * Create a mock attribute definition object + * + * @param int $type type of attribute + * @param boolean $multiple true if multiple values are allowed + * @param int $minvalues minimum number of required values + * @param int $maxvalues maximum number of required value + * @param string $valueset list of allowed values separated by the first char + * @param string $regex regular expression the attribute value must match + * + * @return SeedDMS_Core_AttributeDefinition + */ + protected function getAttributeDefinition($type, $multiple=false, $minvalues=0, $maxvalues=0, $valueset='', $regex='') + { + $attrdef = new SeedDMS_Core_AttributeDefinition(1, 'foo attrdef', SeedDMS_Core_AttributeDefinition::objtype_folder, $type, $multiple, $minvalues, $maxvalues, $valueset, $regex); + return $attrdef; + } + + /** + * Create a mock attribute object + * + * @param SeedDMS_Core_AttributeDefinition $attrdef attribute defintion of attribute + * @param mixed $value value of attribute + * + * @return SeedDMS_Core_Attribute + */ + static protected function getAttribute($attrdef, $value) + { + $folder = new SeedDMS_Core_Folder(1, 'Folder', null, '', '', '', 0, 0, 0); + $attribute = new SeedDMS_Core_Attribute(1, $folder, $attrdef, $value); + $attribute->setDMS($attrdef->getDMS()); + return $attribute; + } + + /** + * Test getId() + * + * @return void + */ + public function testGetId() + { + $attrdef = self::getAttributeDefinition(SeedDMS_Core_AttributeDefinition::type_int); + $attribute = self::getAttribute($attrdef, ''); + $this->assertEquals(1, $attribute->getId()); + } + + /** + * Test getValue() + * + * @return void + */ + public function testGetValue() + { + $attrdef = self::getAttributeDefinition(SeedDMS_Core_AttributeDefinition::type_int); + $attribute = self::getAttribute($attrdef, 7); + $this->assertEquals(7, $attribute->getValue()); + } + + /** + * Test getValueAsArray() + * + * @return void + */ + public function testGetValueAsArray() + { + $attrdef = self::getAttributeDefinition(SeedDMS_Core_AttributeDefinition::type_int); + $attribute = self::getAttribute($attrdef, 7); + $this->assertIsArray($attribute->getValueAsArray()); + $this->assertCount(1, $attribute->getValueAsArray()); + $this->assertContains(7, $attribute->getValueAsArray()); + + /* Test a multi value integer */ + $attrdef = self::getAttributeDefinition(SeedDMS_Core_AttributeDefinition::type_int, true); + $attribute = self::getAttribute($attrdef, ',3,4,6'); + $value = $attribute->getValueAsArray(); + $this->assertIsArray($attribute->getValueAsArray()); + $this->assertCount(3, $attribute->getValueAsArray()); + $this->assertContains('6', $attribute->getValueAsArray()); + } + + /** + * Test setValue() + * + * @return void + */ + public function testSetValue() + { + $attrdef = self::getAttributeDefinition(SeedDMS_Core_AttributeDefinition::type_int); + $attrdef->setDMS(self::getMockDMS()); + $attribute = self::getAttribute($attrdef, 0); + $this->assertTrue($attribute->setValue(9)); + $this->assertEquals(9, $attribute->getValue()); + /* Setting an array of values for a none multi value attribute will just take the + * element of the array. + */ + $this->assertTrue($attribute->setValue([8,9])); + $this->assertEquals(8, $attribute->getValue()); + + $attrdef = self::getAttributeDefinition(SeedDMS_Core_AttributeDefinition::type_int, true); + $attrdef->setDMS(self::getMockDMS()); + $attribute = self::getAttribute($attrdef, ',3,4,6'); + $attribute->setValue([8,9,10]); + $this->assertEquals(',8,9,10', $attribute->getValue()); + $this->assertIsArray($attribute->getValueAsArray()); + $this->assertCount(3, $attribute->getValueAsArray()); + $this->assertContains('9', $attribute->getValueAsArray()); + } +} diff --git a/SeedDMS_Core/tests/DatabaseTest.php b/SeedDMS_Core/tests/DatabaseTest.php new file mode 100644 index 000000000..bfbc5ce42 --- /dev/null +++ b/SeedDMS_Core/tests/DatabaseTest.php @@ -0,0 +1,324 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +namespace PHPUnit\Framework; + +use PHPUnit\Framework\SeedDmsTest; + +require_once('SeedDmsBase.php'); + +/** + * Low level Database test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DatabaseTest extends SeedDmsTest +{ + + /** + * Create a sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + } + + /** + * Check if connection to database exists + * + * @return void + */ + public function testIsConnected() + { + $this->assertTrue(self::$dbh->ensureConnected()); + } + + /** + * Test for number of tables in database + * + * @return void + */ + public function testTableList() + { + $tablelist = self::$dbh->TableList(); + $this->assertIsArray($tablelist); + // There are just 42 tables in SeedDMS5 and 55 tables in SeedDMS6, + // but one additional + // table 'sqlite_sequence' + $dms = new \SeedDMS_Core_DMS(null, ''); + if($dms->version[0] == '5') + $this->assertCount(43, $tablelist); + else + $this->assertCount(56, $tablelist); + } + + /** + * Test createTemporaryTable() + * + * @return void + */ + public function testCreateTemporaryTable() + { + foreach (['ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid'] as $temp) { + $ret = self::$dbh->createTemporaryTable($temp); + $rec = self::$dbh->getResultArray("SELECT * FROM `".$temp."`"); + $this->assertIsArray($rec); + } + /* Running it again will not harm */ + foreach (['ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid'] as $temp) { + $ret = self::$dbh->createTemporaryTable($temp); + $rec = self::$dbh->getResultArray("SELECT * FROM `".$temp."`"); + $this->assertIsArray($rec); + } + /* Running it again and overwrite the old table contents */ + foreach (['ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid'] as $temp) { + $ret = self::$dbh->createTemporaryTable($temp, true); + $rec = self::$dbh->getResultArray("SELECT * FROM `".$temp."`"); + $this->assertIsArray($rec); + } + } + + /** + * Test createTemporaryTable() based on views + * + * @return void + */ + public function testCreateTemporaryTableBasedOnViews() + { + self::$dbh->useViews(true); + foreach (['ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid'] as $temp) { + $ret = self::$dbh->createTemporaryTable($temp); + $rec = self::$dbh->getResultArray("SELECT * FROM `".$temp."`"); + $this->assertIsArray($rec); + } + $viewlist = self::$dbh->ViewList(); + $this->assertIsArray($viewlist); + $this->assertCount(4, $viewlist); + + /* Running it again will not harm */ + foreach (['ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid'] as $temp) { + $ret = self::$dbh->createTemporaryTable($temp); + $rec = self::$dbh->getResultArray("SELECT * FROM `".$temp."`"); + $this->assertIsArray($rec); + } + /* Running it again and replace the old view */ + foreach (['ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid'] as $temp) { + $ret = self::$dbh->createTemporaryTable($temp, true); + $rec = self::$dbh->getResultArray("SELECT * FROM `".$temp."`"); + $this->assertIsArray($rec); + } + } + + /** + * Test for number of views in database + * + * @return void + */ + public function testViewList() + { + $viewlist = self::$dbh->ViewList(); + $this->assertIsArray($viewlist); + // There are 0 views + $this->assertCount(0, $viewlist); + } + + /** + * Test getDriver() + * + * @return void + */ + public function testGetDriver() + { + $driver = self::$dbh->getDriver(); + $this->assertEquals('sqlite', $driver); + } + + /** + * Test rbt() + * + * @return void + */ + public function testRbt() + { + $str = self::$dbh->rbt("SELECT * FROM `tblUsers`"); + $this->assertEquals('SELECT * FROM "tblUsers"', $str); + } + + /** + * Test if table tblFolders has root folder + * + * @return void + */ + public function testInitialRootFolder() + { + $this->assertTrue(self::$dbh->hasTable('tblFolders')); + $query = 'SELECT * FROM `tblFolders`'; + $recs = self::$dbh->getResultArray($query); + $this->assertIsArray($recs); + $this->assertCount(1, $recs); + } + + /** + * Test if table tblUsers has two initial users + * + * @return void + */ + public function testInitialUsers() + { + $this->assertTrue(self::$dbh->hasTable('tblUsers')); + $query = 'SELECT * FROM `tblUsers`'; + $recs = self::$dbh->getResultArray($query); + $this->assertIsArray($recs); + $this->assertCount(2, $recs); + } + + /** + * Test getCurrentDatetime() + * + * @return void + */ + public function testGetCurrentDatetime() + { + $query = 'SELECT '.self::$dbh->getCurrentDatetime().' as a'; + $recs = self::$dbh->getResultArray($query); + $now = date('Y-m-d H:i:s'); + $this->assertIsArray($recs); + $this->assertEquals($now, $recs[0]['a'], 'Make sure php.ini has the proper timezone configured'); + } + + /** + * Test getCurrentTimestamp() + * + * @return void + */ + public function testGetCurrentTimestamp() + { + $query = 'SELECT '.self::$dbh->getCurrentTimestamp().' as a'; + $recs = self::$dbh->getResultArray($query); + $now = time(); + $this->assertIsArray($recs); + $this->assertEquals($now, $recs[0]['a'], 'Make sure php.ini has the proper timezone configured'); + } + + /** + * Test concat() + * + * @return void + */ + public function testConcat() + { + $query = 'SELECT '.self::$dbh->concat(["'foo'", "'baz'", "'bar'"]).' as a'; + $recs = self::$dbh->getResultArray($query); + $this->assertIsArray($recs); + $this->assertEquals('foobazbar', $recs[0]['a']); + } + + /** + * Test qstr() + * + * @return void + */ + public function testQstr() + { + $str = self::$dbh->qstr("bar"); + $this->assertEquals("'bar'", $str); + } + + /** + * Test getResult() if the sql fails + * + * @return void + */ + public function testGetResultSqlFail() + { + $ret = self::$dbh->getResult("UPDATE FOO SET `name`='foo'"); + $this->assertFalse($ret); + $errmsg = self::$dbh->getErrorMsg(); + $this->assertStringContainsString('no such table: FOO', $errmsg); + } + + /** + * Test getResultArray() if the sql fails + * + * @return void + */ + public function testGetResultArraySqlFail() + { + $ret = self::$dbh->getResultArray("SELECT * FROM FOO"); + $this->assertFalse($ret); + $errmsg = self::$dbh->getErrorMsg(); + $this->assertStringContainsString('no such table: FOO', $errmsg); + } + + /** + * Test logging into file + * + * @return void + */ + public function testLogging() + { + $fp = fopen('php://memory', 'r+'); + self::$dbh->setLogFp($fp); + $sql = "SELECT * FROM `tblUsers`"; + $ret = self::$dbh->getResultArray($sql); + $this->assertIsArray($ret); + fseek($fp, 0); + $contents = fread($fp, 200); + /* Check if sql statement was logged into file */ + $this->assertStringContainsString($sql, $contents); + fclose($fp); + } + + /** + * Test createDump() + * + * @return void + */ + public function testCreateDump() + { + $fp = fopen('php://memory', 'r+'); + $ret = self::$dbh->createDump($fp); + $this->assertTrue($ret); + $stat = fstat($fp); + $this->assertIsArray($stat); + $dms = new \SeedDMS_Core_DMS(null, ''); + if($dms->version[0] == '5') + $this->assertEquals(1724, $stat['size']); + else + $this->assertEquals(2272, $stat['size']); +// fseek($fp, 0); +// echo fread($fp, 200); + fclose($fp); + } +} + diff --git a/SeedDMS_Core/tests/DmsTest.php b/SeedDMS_Core/tests/DmsTest.php new file mode 100644 index 000000000..a6811c5d6 --- /dev/null +++ b/SeedDMS_Core/tests/DmsTest.php @@ -0,0 +1,2956 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * DMS test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DmsTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + self::$dbversion = self::$dms->getDBVersion(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Create a mock admin role object (only used for SeedDMS 6) + * + * @return SeedDMS_Core_User + */ + protected function getAdminRole() + { + $role = new SeedDMS_Core_Role(1, 'admin', SeedDMS_Core_Role::role_admin); + return $role; + } + + /** + * Test checkIfEqual() + * + * @return void + */ + public function testCheckIfEqual() + { + $user1 = new SeedDMS_Core_User(1, 'user 1', '', '', '', '', '', '', 1); + $group1 = new SeedDMS_Core_Group(1, 'group 1', ''); + $group1n = new SeedDMS_Core_Group(1, 'group 1n', ''); + $group1c = clone $group1; + $group2 = new SeedDMS_Core_Group(2, 'group 1', ''); + $dms = new SeedDMS_Core_DMS(null, ''); + $this->assertFalse($dms->checkIfEqual($group1, $user1)); // different classes + $this->assertFalse($dms->checkIfEqual($group1, $group2)); // different id + $this->assertTrue($dms->checkIfEqual($group1, $group1c)); // a clone is always equal + $this->assertTrue($dms->checkIfEqual($group1, $group1n)); // different instances but same id is sufficient to be equal + } /* }}} */ + + /** + * Test checkDate() + * + * @return void + */ + public function testCheckDate() + { + $dms = new SeedDMS_Core_DMS(null, ''); + $this->assertTrue($dms->checkDate('2020-02-28 10:12:34')); + $this->assertTrue($dms->checkDate('2020-02-29 10:12:34')); // a leap year + $this->assertFalse($dms->checkDate('2020-02-30 10:12:34')); // feb has never 30 days + $this->assertFalse($dms->checkDate('2021-02-29 10:12:34')); // not a leap year + $this->assertFalse($dms->checkDate('2020-02-28 24:12:34')); // hour is out of range + $this->assertFalse($dms->checkDate('2020-02-28 23:60:34')); // minute is out of range + $this->assertFalse($dms->checkDate('2020-02-28 23:59:60')); // second is out of range + $this->assertFalse($dms->checkDate('2020-02-28 23:59:')); // second is missing + $this->assertTrue($dms->checkDate('2020-02-28', 'Y-m-d')); // just checking the date + $this->assertFalse($dms->checkDate('28.2.2020', 'd.m.Y')); // month must be 01-12 + $this->assertTrue($dms->checkDate('28.2.2020', 'd.n.Y')); // month must be 1-12 + $this->assertFalse($dms->checkDate('28.02.2020', 'd.n.Y')); // month must be 1-12 + } /* }}} */ + + /** + * Test getClassname() + * + * @return void + */ + public function testGetClassName() + { + /* Do not mess up the global instance self::$dms, but create my own */ + $dms = new SeedDMS_Core_DMS(null, ''); + $this->assertEquals('SeedDMS_Core_Folder', $dms->getClassname('folder')); + $this->assertEquals('SeedDMS_Core_Document', $dms->getClassname('document')); + $this->assertEquals('SeedDMS_Core_DocumentContent', $dms->getClassname('documentcontent')); + $this->assertEquals('SeedDMS_Core_User', $dms->getClassname('user')); + $this->assertEquals('SeedDMS_Core_Group', $dms->getClassname('group')); + $this->assertFalse($dms->getClassname('foo')); + } + + /** + * Test setClassname() + * + * @return void + */ + public function testSetClassName() + { + /* Do not mess up the global instance self::$dms, but create my own */ + $dms = new SeedDMS_Core_DMS(null, ''); + $this->assertEquals('SeedDMS_Core_Folder', $dms->setClassname('folder', 'MyNewFolderClass')); + $this->assertEquals('MyNewFolderClass', $dms->getClassname('folder')); + $this->assertEquals('MyNewFolderClass', $dms->setClassname('folder', 'MySuperNewFolderClass')); + $this->assertFalse($dms->setClassname('foo', 'MyNewFolderClass')); + } + + /** + * Test addCallback() + * + * @return void + */ + public function testAddCallback() + { + /* Do not mess up the global instance self::$dms, but create my own */ + $dms = new SeedDMS_Core_DMS(null, ''); + /* Add a closure as a callback is just fine */ + $this->assertTrue( + $dms->addCallback( + 'onPostSomething', function () { + } + ) + ); + /* An empty callback will make addCallback() fail */ + $this->assertFalse( + $dms->addCallback( + '', function () { + } + ) + ); + /* Passing a class method is ok */ + $this->assertTrue($dms->addCallback('onPostSomething', 'DmsTest::testAddCallback')); + /* Passing a none existing class mehtod makes addCallback() fail */ + $this->assertFalse($dms->addCallback('onPostSomething', 'DmsTest::thisMethodDoesNotExist')); + } + + /** + * Test for hasCallback + * + * @return void + */ + public function testHasCallback() + { + /* Do not mess up the global instance self::$dms, but create my own */ + $dms = new SeedDMS_Core_DMS(null, ''); + /* Add a closure as a callback is just fine */ + $this->assertTrue( + $dms->addCallback( + 'onPostSomething', function () { + } + ) + ); + $this->assertTrue($dms->hasCallback('onPostSomething')); + $this->assertFalse($dms->hasCallback('thisOneDoesNotExist')); + } + + /** + * Test for getDecorators + * + * @return void + */ + public function testGetDecorators() + { + /* Do not mess up the global instance self::$dms, but create my own */ + $dms = new SeedDMS_Core_DMS(null, ''); + $this->assertFalse($dms->getDecorators('folder')); + } + + /** + * Test for addDecorator + * + * @return void + */ + public function testaddDecorator() + { + /* Do not mess up the global instance self::$dms, but create my own */ + $dms = new SeedDMS_Core_DMS(null, ''); + $this->assertTrue($dms->addDecorator('folder', 'MyNewDecorator')); + $decorators = $dms->getDecorators('folder'); + $this->assertIsArray($decorators); + $this->assertCount(1, $decorators); + } + + /** + * Test getDb() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDb() + { + $this->assertEquals(self::$dbh, self::$dms->getDb()); + } + + /** + * Test getDBVersion() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDbVersion() + { + $version = self::$dms->getDBVersion(); + $this->assertCount(4, $version); + $this->assertGreaterThanOrEqual(5, $version['major']); + $this->assertGreaterThanOrEqual(0, $version['minor']); + } + + /** + * Test getDBVersionFailMissingTable() + * + * This method checks if getDBVersion() returns false if the table + * list of the database does not contain the table 'tblVersion' + * + * @return void + */ + public function testGetDbVersionFailMissingTable() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('TableList') + ->willReturn(['tblFolders', 'tblDocuments']); + $dms = new SeedDMS_Core_DMS($db, ''); + $version = $dms->getDBVersion(); + $this->assertFalse($version); + } + + /** + * Test getDBVersionSqlFail() + * + * This method checks if getDBVersion() returns false if the sql + * for selecting the records in table 'tblVersion' fail + * + * @return void + */ + public function testGetDbVersionSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblVersion` ORDER BY `major`,`minor`,`subminor` LIMIT 1") + ->willReturn(false); + $db->expects($this->once()) + ->method('TableList') + ->willReturn(['tblVersion', 'tblFolders', 'tblDocuments']); + $dms = new SeedDMS_Core_DMS($db, ''); + $version = $dms->getDBVersion(); + $this->assertFalse($version); + } + + /** + * Test getDBVersionNoRecord() + * + * This method checks if getDBVersion() returns false a table 'tblVersion' + * exists but has no record + * + * @return void + */ + public function testGetDbVersionNoRecord() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblVersion` ORDER BY `major`,`minor`,`subminor` LIMIT 1") + ->willReturn(array()); + $db->expects($this->once()) + ->method('TableList') + ->willReturn(['tblVersion', 'tblFolders', 'tblDocuments']); + $dms = new SeedDMS_Core_DMS($db, ''); + $version = $dms->getDBVersion(); + $this->assertFalse($version); + } + + /** + * Test checkVersion() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCheckVersion() + { + $this->assertTrue(self::$dms->checkVersion()); + } + + /** + * Test checkVersionFail() + * + * This method checks if checkVersion() returns false if the version + * in table 'tblVersion' does not match the version in the class variable + * $version. To make this method independant of version changes, the + * current version is taken from SeedDMS_Core_DMS::version and modified + * in order to differ from the version stored in the database. + * + * @return void + */ + public function testcheckVersionFail() + { + $verstr = (new SeedDMS_Core_DMS(null, ''))->version; + $verarr = explode('.', $verstr); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblVersion` ORDER BY `major`,`minor`,`subminor` LIMIT 1") + ->willReturn([['major'=>$verarr[0], 'minor'=>$verarr[1]+1]]); + $db->expects($this->once()) + ->method('TableList') + ->willReturn(['tblVersion', 'tblFolders', 'tblDocuments']); + $dms = new SeedDMS_Core_DMS($db, ''); + $version = $dms->checkVersion(); + $this->assertFalse($version); + } + + /** + * Test checkVersionSqlFail() + * + * This method checks if checkVersion() returns false if the sql + * for selecting the records in table 'tblVersion' fail + * + * @return void + */ + public function testcheckVersionSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblVersion` ORDER BY `major`,`minor`,`subminor` LIMIT 1") + ->willReturn(false); + $db->expects($this->once()) + ->method('TableList') + ->willReturn(['tblVersion', 'tblFolders', 'tblDocuments']); + $dms = new SeedDMS_Core_DMS($db, ''); + $version = $dms->checkVersion(); + $this->assertFalse($version); + } + + /** + * Test checkVersionFailMissingTable() + * + * This method checks if checkVersion() returns false if the table + * list of the database does not contain the table 'tblVersion' + * + * @return void + */ + public function testCheckVersionFailMissingTable() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('TableList') + ->willReturn(['tblFolders', 'tblDocuments']); + $dms = new SeedDMS_Core_DMS($db, ''); + $version = $dms->checkVersion(); + $this->assertTrue($version); // A missing table tblVersion returns true! + } + + /** + * Test checkVersionNoRecord() + * + * This method checks if checkVersion() returns false a table 'tblVersion' + * exists but has no record + * + * @return void + */ + public function testCheckVersionNoRecord() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblVersion` ORDER BY `major`,`minor`,`subminor` LIMIT 1") + ->willReturn(array()); + $db->expects($this->once()) + ->method('TableList') + ->willReturn(['tblVersion', 'tblFolders', 'tblDocuments']); + $dms = new SeedDMS_Core_DMS($db, ''); + $version = $dms->checkVersion(); + $this->assertFalse($version); + } + + /** + * Test setRootFolderID() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetRootFolderID() + { + /* Setting the same root folder is ok */ + $oldid = self::$dms->setRootFolderID(1); + $this->assertEquals(1, $oldid); + /* Setting a none existing root folder id will not change the root folder */ + $oldid = self::$dms->setRootFolderID(2); + $this->assertFalse($oldid); + /* Make sure the old root folder is still set */ + $rootfolder = self::$dms->getRootFolder(); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $this->assertEquals(1, $rootfolder->getId()); + } + + /** + * Test getRootFolder() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetRootFolder() + { + $rootfolder = self::$dms->getRootFolder(); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $this->assertEquals(1, $rootfolder->getId()); + } + + /** + * Test setUser() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetUser() + { + $user = self::$dms->getUser(1); + $olduser = self::$dms->setUser($user); // returns null because there is no old user + $this->assertNull($olduser); + $olduser = self::$dms->setUser($user); // second call will return the user set before + $this->assertIsObject($olduser); + $olduser = self::$dms->setUser(null); // old user is still an object + $this->assertIsObject($olduser); + $olduser = self::$dms->setUser(8); // invalid user + $this->assertFalse($olduser); + } + + /** + * Test getLoggedInUser() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetLoggedInUser() + { + $olduser = self::$dms->getLoggedInUser(); // initially this is set to null + $this->assertNull($olduser); + $user = self::$dms->getUser(1); + self::$dms->setUser($user); + $olduser = self::$dms->getLoggedInUser(); + $this->assertEquals($olduser->getId(), $user->getId()); + } + + /** + * Test getDocument() + * + * As there is currently no document, getDocument() must return null. + * If false was returned it would indicated an sql error. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocument() + { + $document = self::$dms->getDocument(1); + $this->assertNull($document); + } + + /** + * Test getDocumentsByUser() + * + * As there is currently no document, getDocumentsByUser() must return + * an empty array. + * If false was returned it would indicated an sql error. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentsByUser() + { + $documents = self::$dms->getDocumentsByUser(self::$dms->getUser(1)); + $this->assertIsArray($documents); + $this->assertCount(0, $documents); + } + + /** + * Test getDocumentsLockedByUser() + * + * As there is currently no document, getDocumentsLockedByUser() must return + * an empty array. + * If false was returned it would indicated an sql error. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentsLockedByUser() + { + $documents = self::$dms->getDocumentsLockedByUser(self::$dms->getUser(1)); + $this->assertIsArray($documents); + $this->assertCount(0, $documents); + } + + /** + * Test makeTimeStamp() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testMakeTimeStamp() + { + /* Assert correct date */ + $this->assertEquals(0, self::$dms->makeTimeStamp(1, 0, 0, 1970, 1, 1)); + $this->assertEquals(68166000, self::$dms->makeTimeStamp(0, 0, 0, 1972, 2, 29)); + /* Assert incorrect dates */ + $this->assertFalse(self::$dms->makeTimeStamp(0, 0, 0, 1970, 13, 1), 'Incorrect month not recognized'); + $this->assertFalse(self::$dms->makeTimeStamp(0, 0, 0, 1970, 1, 32), 'Incorrect day in january not recognized'); + $this->assertFalse(self::$dms->makeTimeStamp(0, 0, 0, 1970, 4, 31), 'Incorrect day in april not recognized'); + $this->assertFalse(self::$dms->makeTimeStamp(0, 0, 0, 1970, 2, 29), 'Incorrect day in february not recognized'); + $this->assertFalse(self::$dms->makeTimeStamp(24, 0, 0, 1970, 1, 1), 'Incorrect hour not recognized'); + $this->assertFalse(self::$dms->makeTimeStamp(0, 60, 0, 1970, 1, 1), 'Incorrect minute not recognized'); + $this->assertFalse(self::$dms->makeTimeStamp(0, 0, 60, 1970, 1, 1), 'Incorrect second not recognized'); + } + + /** + * Test search() + * + * Just search the root folder in different ways. Because the initial database + * does not have any documents, this method will test various ways to + * find the root folder 'DMS' with id=1 + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSearchRootFolder() + { + /* searching for folders/documents in any field */ + $result = self::$dms->search( + array( + 'query'=>'DMS' + ) + ); + $this->assertEquals(1, $result['totalFolders']); + $this->assertCount(1, $result['folders']); + $this->assertEquals(0, $result['totalDocs']); + $this->assertCount(0, $result['docs']); + + /* searching for folders in any field */ + $result = self::$dms->search( + array( + 'query'=>'DMS', + 'mode'=>0x2 + ) + ); + $this->assertEquals(1, $result['totalFolders']); + $this->assertCount(1, $result['folders']); + $this->assertEquals(0, $result['totalDocs']); + $this->assertCount(0, $result['docs']); + + /* searching for documents in any field will not return any folders*/ + $result = self::$dms->search( + array( + 'query'=>'DMS', + 'mode'=>0x1 + ) + ); + $this->assertEquals(0, $result['totalFolders']); + $this->assertCount(0, $result['folders']); + $this->assertEquals(0, $result['totalDocs']); + $this->assertCount(0, $result['docs']); + + /* searching for folders with a bogus name may not return any folders */ + $result = self::$dms->search( + array( + 'query'=>'foo', + 'mode'=>0x2 + ) + ); + $this->assertEquals(0, $result['totalFolders']); + $this->assertCount(0, $result['folders']); + + /* searching for folders by its id */ + $result = self::$dms->search( + array( + 'query'=>'1', + 'mode'=>0x2 + ) + ); + $this->assertEquals(1, $result['totalFolders']); + $this->assertCount(1, $result['folders']); + + /* searching for folders by an unknown id */ + $result = self::$dms->search( + array( + 'query'=>'2', + 'mode'=>0x2 + ) + ); + $this->assertEquals(0, $result['totalFolders']); + $this->assertCount(0, $result['folders']); + + /* searching for folders with two terms ANDed, but only one matches */ + $result = self::$dms->search( + array( + 'query'=>'DMS foo', + 'mode'=>0x2, + 'logicalmode'=>'AND', + ) + ); + $this->assertEquals(0, $result['totalFolders']); + $this->assertCount(0, $result['folders']); + + /* searching for folders with two terms ORed, but only one matches */ + $result = self::$dms->search( + array( + 'query'=>'DMS foo', + 'mode'=>0x2, + 'logicalmode'=>'OR', + ) + ); + $this->assertEquals(1, $result['totalFolders']); + $this->assertCount(1, $result['folders']); + + /* searching for folders with two terms ANDed, both match, but in different fields (name and id) */ + $result = self::$dms->search( + array( + 'query'=>'DMS 1', + 'mode'=>0x2, + 'logicalmode'=>'AND', + ) + ); + $this->assertEquals(1, $result['totalFolders']); + $this->assertCount(1, $result['folders']); + + /* searching for folders with two terms ANDed, both match, but in different fields (name and id). But only one field is searched. */ + $result = self::$dms->search( + array( + 'query'=>'DMS 1', + 'mode'=>0x2, + 'logicalmode'=>'AND', + 'searchin'=>array(2,3), // name, comment + ) + ); + $this->assertEquals(0, $result['totalFolders']); + $this->assertCount(0, $result['folders']); + + /* searching for folders below a start folder will not find the folder 'DMS' + * anymore, because the start folder itself will not be found. + */ + $result = self::$dms->search( + array( + 'query'=>'DMS', + 'mode'=>0x2, + 'startFolder'=>self::$dms->getRootFolder() + ) + ); + $this->assertEquals(0, $result['totalFolders']); + $this->assertCount(0, $result['folders']); + + /* Restrict search to the owner of the folder 'DMS' + */ + $result = self::$dms->search( + array( + 'query'=>'DMS', + 'mode'=>0x2, + 'owner'=>self::$dms->getUser(1) + ) + ); + $this->assertEquals(1, $result['totalFolders']); + $this->assertCount(1, $result['folders']); + + /* Restrict search to user who does not own a document + */ + $result = self::$dms->search( + array( + 'query'=>'DMS', + 'mode'=>0x2, + 'owner'=>self::$dms->getUser(2) + ) + ); + $this->assertEquals(0, $result['totalFolders']); + $this->assertCount(0, $result['folders']); + + /* Restrict search to a list of owners (in this case all users) + */ + $result = self::$dms->search( + array( + 'query'=>'DMS', + 'mode'=>0x2, + 'owner'=>self::$dms->getAllUsers() + ) + ); + $this->assertEquals(1, $result['totalFolders']); + $this->assertCount(1, $result['folders']); + + } + + /** + * Test getFolder() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetFolder() + { + $folder = self::$dms->getFolder(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $folder); + $this->assertEquals(1, $folder->getId()); + } + + /** + * Test getFolderByName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetFolderByName() + { + $folder = self::$dms->getFolderByName('DMS'); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $folder); + $this->assertEquals(1, $folder->getId()); + $folder = self::$dms->getFolderByName('FOO'); + $this->assertNull($folder); + } + + /** + * Test checkFolders() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCheckFolders() + { + $errors = self::$dms->checkFolders(); + $this->assertIsArray($errors); + $this->assertCount(0, $errors); + } + + /** + * Test checkFoldersSqlFail() + * + * This test catches the case when the sql statement for getting all + * folders fails. + * + * @return void + */ + public function testCheckFoldersSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblFolders`") + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->checkFolders()); + } + + /** + * Test checkFoldersFailNoParent() + * + * This test catches the case when a folder's parent is not present + * + * @return void + */ + public function testCheckFoldersFailNoParent() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblFolders`") + ->willReturn( + array( + array('id'=>1, 'name'=>'DMS', 'parent'=>0, 'folderList'=>''), + array('id'=>5, 'name'=>'Subfolder', 'parent'=>3, 'folderList'=>':1:'), + ) + ); + $dms = new SeedDMS_Core_DMS($db, ''); + $errors = $dms->checkFolders(); + $this->assertIsArray($errors); + $this->assertCount(1, $errors); // there should be 1 error + $this->assertArrayHasKey(5, $errors); // folder with id=5 has the wrong parent + $this->assertEquals('Missing parent', $errors[5]['msg']); + } + + /** + * Test checkFoldersFailWrongFolderList() + * + * This test catches the case when a folder's parent is not present + * + * @return void + */ + public function testCheckFoldersFailWrongFolderList() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblFolders`") + ->willReturn( + array( + array('id'=>1, 'name'=>'DMS', 'parent'=>0, 'folderList'=>''), + array('id'=>5, 'name'=>'Subfolder', 'parent'=>1, 'folderList'=>':1:2:'), + ) + ); + $dms = new SeedDMS_Core_DMS($db, ''); + $errors = $dms->checkFolders(); + $this->assertIsArray($errors); + $this->assertCount(1, $errors); // there should be 1 error + $this->assertArrayHasKey(5, $errors); // folder with id=5 has the wrong parent + $this->assertStringContainsString('Wrong folder list', $errors[5]['msg']); + } + + /** + /** + * Test checkDocuments() + * + * The intitial database does not have any documents which makes this + * test less usefull. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCheckDocuments() + { + $errors = self::$dms->checkDocuments(); + $this->assertIsArray($errors); + $this->assertCount(0, $errors); + } + + /** + * Test checkDocumentsSqlFoldersFail() + * + * This test catches the case when the sql statement for getting all + * folders fails. + * + * @return void + */ + public function testCheckDocumentsSqlFoldersFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblFolders`") + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->checkDocuments()); + } + + /** + * Test checkDocumentsSqlDocumentsFail() + * + * This test catches the case when the sql statement for getting all + * documents fails, after getting all folders succeeded. + * + * @return void + */ + public function testCheckDocumentsSqlDocumentsFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->exactly(2)) + ->method('getResultArray') + ->will( + $this->returnValueMap( + array( + array("SELECT * FROM `tblFolders`", true, array( + array('id'=>1, 'name'=>'DMS', 'parent'=>0, 'folderList'=>'') + )), + array("SELECT * FROM `tblDocuments`", true, false) + ) + ) + ); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->checkDocuments()); + } + + /** + * Test checkDocumentsFailNoParent() + * + * This test catches the case when a documents's parent is not present + * + * @return void + */ + public function testCheckDocumentsFailNoParent() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->exactly(2)) + ->method('getResultArray') + ->will( + $this->returnValueMap( + array( + array("SELECT * FROM `tblFolders`", true, array( + array('id'=>1, 'name'=>'DMS', 'parent'=>0, 'folderList'=>''), + array('id'=>5, 'name'=>'Subfolder', 'parent'=>1, 'folderList'=>':1:'), + )), + array("SELECT * FROM `tblDocuments`", true, array( + array('id'=>1, 'name'=>'Document 1', 'folder'=>1, 'folderList'=>':1:'), + array('id'=>2, 'name'=>'Document 2', 'folder'=>2, 'folderList'=>':1:5:'), + )) + ) + ) + ); + $dms = new SeedDMS_Core_DMS($db, ''); + $errors = $dms->checkDocuments(); + $this->assertIsArray($errors); + $this->assertCount(1, $errors); // there should be 1 error + $this->assertArrayHasKey(2, $errors); // document with id=2 has the wrong parent + $this->assertEquals('Missing parent', $errors[2]['msg']); + } + + /** + * Test checkDocumentsFailWrongFolderList() + * + * This test catches the case when a documents's parent is not present + * + * @return void + */ + public function testCheckDocumentsFailWrongFolderList() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->exactly(2)) + ->method('getResultArray') + ->will( + $this->returnValueMap( + array( + array("SELECT * FROM `tblFolders`", true, array( + array('id'=>1, 'name'=>'DMS', 'parent'=>0, 'folderList'=>''), + array('id'=>5, 'name'=>'Subfolder', 'parent'=>1, 'folderList'=>':1:'), + )), + array("SELECT * FROM `tblDocuments`", true, array( + array('id'=>1, 'name'=>'Document 1', 'folder'=>1, 'folderList'=>':1:'), + array('id'=>2, 'name'=>'Document 2', 'folder'=>5, 'folderList'=>':1:2:'), + )) + ) + ) + ); + $dms = new SeedDMS_Core_DMS($db, ''); + $errors = $dms->checkDocuments(); + $this->assertIsArray($errors); + $this->assertCount(1, $errors); // there should be 1 error + $this->assertArrayHasKey(2, $errors); // document with id=2 has the wrong parent + $this->assertStringContainsString('Wrong folder list', $errors[2]['msg']); + } + + /** + * Test getUser() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetUser() + { + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_User::class, $user); + $this->assertEquals(1, $user->getId()); + } + + /** + * Test getUserByLogin() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetUserByLogin() + { + $user = self::$dms->getUserByLogin('admin'); + $this->assertInstanceOf(SeedDMS_Core_User::class, $user); + $this->assertEquals('admin', $user->getLogin()); + } + + /** + * Test getUserByEmail() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetUserByEmail() + { + $user = self::$dms->getUserByEmail('info@seeddms.org'); + $this->assertInstanceOf(SeedDMS_Core_User::class, $user); + $this->assertEquals('admin', $user->getLogin()); + } + + /** + * Test getAllUsers() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAllUsers() + { + $users = self::$dms->getAllUsers(); + $this->assertIsArray($users); + $this->assertCount(2, $users); + } + + /** + * Test addUser() + * + * Add a new user and retrieve it afterwards. Also check if the number + * of users has increased by one. Add a user with the same name a + * second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddUser() + { + /* Adding a new user */ + $user = self::$dms->addUser('new user', 'pwd', 'Full Name', 'newuser@seeddms.org', 'en_GB', 'bootstrap', 'with comment'); + $this->assertIsObject($user); + $this->assertEquals('new user', $user->getLogin()); + $this->assertEquals('with comment', $user->getComment()); + + /* Adding a user with the same login must fail */ + $user = self::$dms->addUser('new user', 'pwd', 'Full Name', 'newuser@seeddms.org', 'en_GB', 'bootstrap', 'with comment'); + $this->assertFalse($user); + + /* There should be 3 users now */ + $users = self::$dms->getAllUsers(); + $this->assertIsArray($users); + $this->assertCount(3, $users); + + /* Check if setting the password expiration to 'now' works */ + $now = date('Y-m-d H:i:s'); + $user = self::$dms->addUser('new user pwdexpiration 1', 'pwd', 'Full Name', 'newuser@seeddms.org', 'en_GB', 'bootstrap', 'with comment', '', false, false, 'now'); + $this->assertEquals($now, $user->getPwdExpiration()); + $now = date('Y-m-d H:i:s'); + $user = self::$dms->addUser('new user pwdexpiration 2', 'pwd', 'Full Name', 'newuser@seeddms.org', 'en_GB', 'bootstrap', 'with comment', '', false, false, $now); + $this->assertEquals($now, $user->getPwdExpiration()); + } + + /** + * Test addUserWithPostAddHook() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddUserWithPostAddHook() + { + /* Add the 'onPostAddUser' callback */ + $ret = 0; + $callback = function ($param, $user) use (&$ret) { + $ret = 1; + }; + self::$dms->addCallback('onPostAddUser', $callback, 1); + /* Adding a new user */ + $user = self::$dms->addUser('new user', 'pwd', 'Full Name', 'newuser@seeddms.org', 'en_GB', 'bootstrap', 'with comment'); + $this->assertIsObject($user); + $this->assertEquals('new user', $user->getLogin()); + $this->assertEquals(1, $ret); + } + + /** + * Test addUser() with sql failure + * + * @return void + */ + public function testAddUserSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("INSERT INTO `tblUsers`")) + ->willReturn(false); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblUsers` WHERE `login` = ") + ->willReturn([]); + $dms = new SeedDMS_Core_DMS($db, ''); + if(self::$dbversion['major'] < 6) + $role = 1; + else + $role = $this->getAdminRole(); + $user = $dms->addUser('new user', 'pwd', 'Full Name', 'newuser@seeddms.org', 'en_GB', 'bootstrap', 'with comment', $role); + $this->assertFalse($user); + } + + /** + * Test getGroup() + * + * Get a group by its id + * + * @return void + */ + public function testGetGroup() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblGroups` WHERE `id` = 1") + ->willReturn([['id'=>1, 'name'=>'foo', 'comment'=>'']]); + $dms = new SeedDMS_Core_DMS($db, ''); + $group = $dms->getGroup(1); + $this->assertIsObject($group); + $this->assertEquals(1, $group->getId()); + } + + /** + * Test getGroupByName() + * + * Get a group by its name + * + * qstr must be mocked because it is used in the sql statement to quote + * the name. + * + * @return void + */ + public function testGetGroupByName() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblGroups` WHERE `name` = 'foo'") + ->willReturn([['id'=>1, 'name'=>'foo', 'comment'=>'']]); + $db->expects($this->once()) + ->method('qstr') + ->will( + $this->returnCallback( + function ($a) { + return "'".$a."'"; + } + ) + ); + $dms = new SeedDMS_Core_DMS($db, ''); + $group = $dms->getGroupByName('foo'); + $this->assertIsObject($group); + $this->assertEquals('foo', $group->getName()); + } + + /** + * Test getAllGroups() + * + * The intitial database does not have any groups + * + * @return void + */ + public function testGetAllGroups() + { + $groups = self::$dms->getAllGroups(); + $this->assertIsArray($groups); + $this->assertCount(0, $groups); + } + + /** + * Test addGroup() + * + * Add a new group and retrieve it afterwards. Also check if the number + * of groups has increased by one. Add a group with the same name a + * second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddGroup() + { + /* Adding a new group */ + $group = self::$dms->addGroup('new group', 'with comment'); + $this->assertIsObject($group); + $this->assertEquals('new group', $group->getName()); + /* Adding a group with the same name must fail */ + $group = self::$dms->addGroup('new group', 'with comment'); + $this->assertFalse($group); + /* There should be one group now */ + $groups = self::$dms->getAllGroups(); + $this->assertIsArray($groups); + $this->assertCount(1, $groups); + } + + /** + * Test addGroupWithPostAddHook() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddGroupWithPostAddHook() + { + /* Add the 'onPostAddGroup' callback */ + $ret = 0; + $callback = function ($param, $group) use (&$ret) { + $ret = 1; + }; + self::$dms->addCallback('onPostAddGroup', $callback, 1); + /* Adding a new group */ + $group = self::$dms->addGroup('new group', 'with comment'); + $this->assertIsObject($group); + $this->assertEquals('new group', $group->getName()); + $this->assertEquals(1, $ret); + } + + /** + * Test addGroup() with sql failure + * + * @return void + */ + public function testAddGroupSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("INSERT INTO `tblGroups`")) + ->willReturn(false); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblGroups` WHERE `name` = ") + ->willReturn([]); + $dms = new SeedDMS_Core_DMS($db, ''); + $group = $dms->addGroup('new group', 'with comment'); + $this->assertFalse($group); + } + + /** + * Test getAllKeywordCategories() + * + * The intitial database does not have any keyword categories + * + * @return void + */ + public function testGetAllKeywordCategories() + { + $cats = self::$dms->getAllKeywordCategories(); + $this->assertIsArray($cats); + $this->assertCount(0, $cats); + /* Even passing bogus ids is handled propperly */ + $cats = self::$dms->getAllKeywordCategories(['kk', '0', 3, true]); + $this->assertIsArray($cats); + $this->assertCount(0, $cats); + } + + /** + * Test getAllUserKeywordCategories() + * + * Method getAllUserKeywordCategories() actually uses + * getAllKeywordCategories() + * + * The intitial database does not have any keyword categories + * + * @return void + */ + public function testGetAllUserKeywordCategories() + { + $cats = self::$dms->getAllUserKeywordCategories(1); + $this->assertIsArray($cats); + $this->assertCount(0, $cats); + /* Passing a none existing user id will return an empty array */ + $cats = self::$dms->getAllUserKeywordCategories(3); + $this->assertIsArray($cats); + $this->assertCount(0, $cats); + /* Passing an invalid user id will return false */ + $cats = self::$dms->getAllUserKeywordCategories(0); + $this->assertFalse($cats); + } + + /** + * Test getAllKeywordCategories() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAllKeywordCategoriesSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblKeywordCategories`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $cats = $dms->getAllKeywordCategories(); + $this->assertFalse($cats); + } + + /** + * Test addKeywordCategory() + * + * Add a new keyword category and retrieve it afterwards. Also check if the + * number of keyword categories has increased by one. Add a keyword category + * with the same name a second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddKeywordCategory() + { + /* Adding a new keyword category */ + $cat = self::$dms->addKeywordCategory(1, 'new category'); + $this->assertIsObject($cat); + $this->assertEquals('new category', $cat->getName()); + + /* Adding a keyword category for the same user and with the same name must fail */ + $cat = self::$dms->addKeywordCategory(1, 'new category'); + $this->assertFalse($cat); + + /* Adding a keyword category with a non existing user id must fail */ + $cat = self::$dms->addKeywordCategory(0, 'new category'); + $this->assertFalse($cat); + + /* Adding a keyword category with an empty name must fail */ + $cat = self::$dms->addKeywordCategory(1, ' '); + $this->assertFalse($cat); + + /* Adding a keyword category with a non existing user id must fail */ + // $cat = self::$dms->addKeywordCategory(3, 'new category'); + // $this->assertFalse($cat); + + /* There should be 1 keyword category now */ + $cats = self::$dms->getAllKeywordCategories(); + $this->assertIsArray($cats); + $this->assertCount(1, $cats); + } + + /** + * Test addKeywordCategoryWithPostAddHook() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddKeywordCategoryWithPostAddHook() + { + /* Add the 'onPostAddKeywordCategory' callback */ + $ret = 0; + $callback = function ($param, $cat) use (&$ret) { + $ret = 1; + }; + self::$dms->addCallback('onPostAddKeywordCategory', $callback, 1); + /* Adding a new keyword category */ + $cat = self::$dms->addKeywordCategory(1, 'new category'); + $this->assertIsObject($cat); + $this->assertEquals('new category', $cat->getName()); + $this->assertEquals(1, $ret); + } + + /** + * Test addKeywordCategory() with sql failure + * + * @return void + */ + public function testAddKeywordCategorySqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("INSERT INTO `tblKeywordCategories`")) + ->willReturn(false); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblKeywordCategories` WHERE `name` =")) + ->willReturn([]); + $dms = new SeedDMS_Core_DMS($db, ''); + $cat = $dms->addKeywordCategory(1, 'new category'); + $this->assertFalse($cat); + } + + /** + * Test getKeywordCategory() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetKeywordCategory() + { + $cat = self::$dms->addKeywordCategory(1, 'new category'); + $cat = self::$dms->getKeywordCategory(1); + $this->assertInstanceOf(SeedDMS_Core_Keywordcategory::class, $cat); + $this->assertEquals(1, $cat->getId()); + /* Return false if the id is invalid */ + $cat = self::$dms->getKeywordCategory(0); + $this->assertFalse($cat); + /* Return null if the keyword category with the id does not exist */ + $cat = self::$dms->getKeywordCategory(2); + $this->assertNull($cat); + } + + /** + * Test getKeywordCategory() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetKeywordCategorySqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblKeywordCategories` WHERE `id` = 1") + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $cat = $dms->getKeywordCategory(1); + $this->assertFalse($cat); + } + + /** + * Test getKeywordCategoryByName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetKeywordCategoryByName() + { + $cat = self::$dms->addKeywordCategory(1, 'new category'); + $cat = self::$dms->getKeywordCategory(1); + $this->assertInstanceOf(SeedDMS_Core_Keywordcategory::class, $cat); + $this->assertEquals(1, $cat->getId()); + /* Return false if the user id is invalid */ + $cat = self::$dms->getKeywordCategoryByName('new category', 0); + $this->assertFalse($cat); + /* Return null if the keyword category with the passed name does not exist */ + $cat = self::$dms->getKeywordCategoryByName('foo', 1); + $this->assertNull($cat); + /* Return category if the keyword category with the passed name exists */ + $cat = self::$dms->getKeywordCategoryByName('new category', 1); + $this->assertIsObject($cat); + $this->assertInstanceOf(SeedDMS_Core_Keywordcategory::class, $cat); + } + + /** + * Test getKeywordCategoryByName() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetKeywordCategoryByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblKeywordCategories` WHERE `name` =")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $cat = $dms->getKeywordCategoryByName('foo', 1); + $this->assertFalse($cat); + } + + /** + * Test getDocumentCategories() + * + * The intitial database does not have any document categories + * + * @return void + */ + public function testGetDocumentCategories() + { + $cats = self::$dms->getDocumentCategories(); + $this->assertIsArray($cats); + $this->assertCount(0, $cats); + } + + /** + * Test getDocumentCategories() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentCategoriesNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblCategory`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $cats = $dms->getDocumentCategories(); + $this->assertFalse($cats); + } + + /** + * Test getDocumentCategory() + * + * The intitial database does not have any document categories + * + * @return void + */ + public function testGetDocumentCategory() + { + /* Adding a new keyword category */ + $cat = self::$dms->addDocumentCategory('new category'); + $this->assertIsObject($cat); + $this->assertEquals('new category', $cat->getName()); + + $cat = self::$dms->getDocumentCategory($cat->getId()); + $this->assertIsObject($cat); + + /* Return false if the id is out of range */ + $cat = self::$dms->getDocumentCategory(0); + $this->assertFalse($cat); + + /* Return null if the keyword category with the id does not exist */ + $cat = self::$dms->getDocumentCategory(2); + $this->assertNull($cat); + } + + /** + * Test getDocumentCategory() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentCategorySqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with("SELECT * FROM `tblCategory` WHERE `id` = 1") + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $cat = $dms->getDocumentCategory(1); + $this->assertFalse($cat); + } + + /** + * Test getDocumentCategoryByName() + * + * The intitial database does not have any document categories + * + * @return void + */ + public function testGetDocumentCategoryByName() + { + /* Adding a new keyword category with leading and trailing spaces*/ + $cat = self::$dms->addDocumentCategory(' new category '); + $this->assertIsObject($cat); + $this->assertEquals('new category', $cat->getName()); + + $cat = self::$dms->getDocumentCategoryByName($cat->getName()); + $this->assertIsObject($cat); + + $cat = self::$dms->getDocumentCategoryByName(' '); + $this->assertFalse($cat); + } + + /** + * Test getDocumentCategoryByName() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentCategoryByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblCategory` WHERE `name`=")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $cat = $dms->getDocumentCategoryByName('foo'); + $this->assertFalse($cat); + } + + /** + * Test addDocumentCategory() + * + * Add a new keyword category and retrieve it afterwards. Also check if the + * number of keyword categories has increased by one. Add a keyword category + * with the same name a second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddDocumentCategory() + { + /* Adding a new keyword category */ + $cat = self::$dms->addDocumentCategory('new category'); + $this->assertIsObject($cat); + $this->assertEquals('new category', $cat->getName()); + + /* Adding a document category with the same name must fail */ + $cat = self::$dms->addDocumentCategory('new category'); + $this->assertFalse($cat); + + /* Adding a document category with an empty name must fail */ + $cat = self::$dms->addDocumentCategory(' '); + $this->assertFalse($cat); + + /* There should be 1 document category now */ + $cats = self::$dms->getDocumentCategories(); + $this->assertIsArray($cats); + $this->assertCount(1, $cats); + } + + /** + * Test addDocumentCategoryWithPostAddHook() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddDocumentCategoryWithPostAddHook() + { + /* Add the 'onPostAddDocumentCategory' callback */ + $ret = 0; + $callback = function ($param, $group) use (&$ret) { + $ret = 1; + }; + self::$dms->addCallback('onPostAddDocumentCategory', $callback, 1); + /* Adding a new group */ + $cat = self::$dms->addDocumentCategory('new category'); + $this->assertIsObject($cat); + $this->assertEquals('new category', $cat->getName()); + $this->assertEquals(1, $ret); + } + + /** + * Test addDocumentCategory() with sql failure + * + * @return void + */ + public function testAddDocumentCategorySqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("INSERT INTO `tblCategory`")) + ->willReturn(false); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblCategory` WHERE `name`=")) + ->willReturn([]); + $dms = new SeedDMS_Core_DMS($db, ''); + $cat = $dms->addDocumentCategory('new category'); + $this->assertFalse($cat); + } + + /** + * Test getAttributeDefinition() with a none existing workflow + * + * The intitial database does not have any workflows + * + * @return void + */ + public function testGetAttributeDefinitionNoExists() + { + $workflow = self::$dms->getAttributeDefinition(1); + $this->assertNull($workflow); + /* Passing an id not a numeric value returns false */ + $workflow = self::$dms->getAttributeDefinition('foo'); + $this->assertFalse($workflow); + /* Passing an id out of range returns false */ + $workflow = self::$dms->getAttributeDefinition(0); + $this->assertFalse($workflow); + } + + /** + * Test getAttributeDefinition() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAttributeDefinitionSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblAttributeDefinitions` WHERE `id` =")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $attrdef = $dms->getAttributeDefinition(1); + $this->assertFalse($attrdef); + } + + /** + * Test getAttributeDefinitionByName() with a none existing workflow + * + * The intitial database does not have any workflows + * + * @return void + */ + public function testGetAttributeDefinitionByNameNoExists() + { + $workflow = self::$dms->getAttributeDefinitionByName('foo'); + $this->assertNull($workflow); + } + + /** + * Test getAttributeDefinitionByName() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAttributeDefinitionByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblAttributeDefinitions` WHERE `name` =")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $attrdef = $dms->getAttributeDefinitionByName('foo'); + $this->assertFalse($attrdef); + } + + /** + * Test getAllAttributeDefinitions() + * + * The intitial database does not have any attribute definitions + * + * @return void + */ + public function testGetAllAttributeDefinitions() + { + $attrdefs = self::$dms->getAllAttributeDefinitions(); + $this->assertIsArray($attrdefs); + $this->assertCount(0, $attrdefs); + } + + /** + * Test getAllAttributeDefinitions() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAllAttributeDefinitionsSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblAttributeDefinitions`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $attrdef = $dms->getAllAttributeDefinitions(); + $this->assertFalse($attrdef); + } + + /** + * Test addAttributeDefinition() + * + * Add a new group and retrieve it afterwards. Also check if the number + * of groups has increased by one. Add a group with the same name a + * second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddAttributeDefinition() + { + /* Adding a new attribute definition */ + $attrdef = self::$dms->addAttributeDefinition('new attribute definition', SeedDMS_Core_AttributeDefinition::objtype_folder, SeedDMS_Core_AttributeDefinition::type_int, false, 0, 0, '', ''); + $this->assertIsObject($attrdef); + $this->assertEquals('new attribute definition', $attrdef->getName()); + /* Get the new attribute definition by its id */ + $newattrdef = self::$dms->getAttributeDefinition($attrdef->getId()); + $this->assertIsObject($newattrdef); + $this->assertEquals($attrdef->getId(), $newattrdef->getId()); + /* Get the new attribute definition by its name */ + $newattrdef = self::$dms->getAttributeDefinitionByName('new attribute definition'); + $this->assertIsObject($newattrdef); + $this->assertEquals($attrdef->getId(), $newattrdef->getId()); + /* Adding an attribute definition with the same name must fail */ + $attrdef = self::$dms->addAttributeDefinition('new attribute definition', SeedDMS_Core_AttributeDefinition::objtype_folder, SeedDMS_Core_AttributeDefinition::type_int, false, 0, 0, '', ''); + $this->assertFalse($attrdef); + /* Adding an attribute definition with an empty name must fail */ + $attrdef = self::$dms->addAttributeDefinition(' ', SeedDMS_Core_AttributeDefinition::objtype_folder, SeedDMS_Core_AttributeDefinition::type_int, false, 0, 0, '', ''); + $this->assertFalse($attrdef); + /* Adding an attribute definition with an invalid object type must fail */ + $attrdef = self::$dms->addAttributeDefinition('no object type', -1, SeedDMS_Core_AttributeDefinition::type_int, false, 0, 0, '', ''); + $this->assertFalse($attrdef); + /* Adding an attribute definition without a type must fail */ + $attrdef = self::$dms->addAttributeDefinition('no type', SeedDMS_Core_AttributeDefinition::objtype_folder, 0, 0, '', ''); + $this->assertFalse($attrdef); + /* There should be one attribute definition now */ + $attrdefs = self::$dms->getAllAttributeDefinitions(); + $this->assertIsArray($attrdefs); + $this->assertCount(1, $attrdefs); + /* There should be one attribute definition of object type folder now */ + $attrdefs = self::$dms->getAllAttributeDefinitions(SeedDMS_Core_AttributeDefinition::objtype_folder); + $this->assertIsArray($attrdefs); + $this->assertCount(1, $attrdefs); + /* The object type can also be passed as an array */ + $attrdefs = self::$dms->getAllAttributeDefinitions([SeedDMS_Core_AttributeDefinition::objtype_folder]); + $this->assertIsArray($attrdefs); + $this->assertCount(1, $attrdefs); + /* Adding more attribute definitions of different object type */ + $attrdef = self::$dms->addAttributeDefinition('new attribute definition all', SeedDMS_Core_AttributeDefinition::objtype_all, SeedDMS_Core_AttributeDefinition::type_int, false, 0, 0, '', ''); + $this->assertIsObject($attrdef); + $this->assertEquals('new attribute definition all', $attrdef->getName()); + $attrdef = self::$dms->addAttributeDefinition('new attribute definition document', SeedDMS_Core_AttributeDefinition::objtype_all, SeedDMS_Core_AttributeDefinition::type_int, false, 0, 0, '', ''); + $this->assertIsObject($attrdef); + $this->assertEquals('new attribute definition document', $attrdef->getName()); + $attrdef = self::$dms->addAttributeDefinition('new attribute definition documentcontent', SeedDMS_Core_AttributeDefinition::objtype_all, SeedDMS_Core_AttributeDefinition::type_int, false, 0, 0, '', ''); + $this->assertIsObject($attrdef); + $this->assertEquals('new attribute definition documentcontent', $attrdef->getName()); + /* There should be four attribute definitions now */ + $attrdefs = self::$dms->getAllAttributeDefinitions(); + $this->assertIsArray($attrdefs); + $this->assertCount(4, $attrdefs); + } + + /** + * Test getAllWorkflows() + * + * The intitial database does not have any workflows + * + * @return void + */ + public function testGetAllWorkflows() + { + $workflows = self::$dms->getAllWorkflows(); + $this->assertIsArray($workflows); + $this->assertCount(0, $workflows); + } + + /** + * Test getAllWorkflows() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAllWorkflowsSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflows`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $workflows = $dms->getAllWorkflows(); + $this->assertFalse($workflows); + } + + /** + * Test getWorkflow() with a none existing workflow + * + * The intitial database does not have any workflows + * + * @return void + */ + public function testGetWorkflowNoExists() + { + $workflow = self::$dms->getWorkflow(1); + $this->assertNull($workflow); + /* Passing an id not a numeric value returns false */ + $workflow = self::$dms->getWorkflow('foo'); + $this->assertFalse($workflow); + /* Passing an id out of range returns false */ + $workflow = self::$dms->getWorkflow(0); + $this->assertFalse($workflow); + } + + /** + * Test getWorkflow() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflows` WHERE `id`=")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $workflow = $dms->getWorkflow(1); + $this->assertFalse($workflow); + } + + /** + * Test getWorkflowByName() with a none existing workflow + * + * The intitial database does not have any workflows + * + * @return void + */ + public function testGetWorkflowByNameNoExists() + { + $workflow = self::$dms->getWorkflowByName('foo'); + $this->assertNull($workflow); + } + + /** + * Test getWorkflowByName() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflows` WHERE `name`=")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $workflow = $dms->getWorkflowByName('foo'); + $this->assertFalse($workflow); + } + + /** + * Test addWorkflow() + * + * Add a new workflow and retrieve it afterwards. Also check if the number + * of workflows has increased by one. Add a workflow with the same name a + * second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddWorkflow() + { + /* Adding a new workflow */ + $workflowstate = self::$dms->addWorkflowState('new workflow state', S_RELEASED); + $workflow = self::$dms->addWorkflow('new workflow', $workflowstate); + $this->assertIsObject($workflow); + $this->assertEquals('new workflow', $workflow->getName()); + /* Adding a workflow with the same name must fail */ + $workflow = self::$dms->addWorkflow('new workflow', $workflowstate); + $this->assertFalse($workflow); + /* Adding a workflow with an empty name must fail */ + $workflow = self::$dms->addWorkflow(' ', $workflowstate); + $this->assertFalse($workflow); + /* There should be one workflow now */ + $workflows = self::$dms->getAllWorkflows(); + $this->assertIsArray($workflows); + $this->assertCount(1, $workflows); + } + + /** + * Test getAllWorkflowStates() + * + * The intitial database does not have any workflow states + * + * @return void + */ + public function testGetAllWorkflowStates() + { + $states = self::$dms->getAllWorkflowStates(); + $this->assertIsArray($states); + $this->assertCount(0, $states); + } + + /** + * Test getAllWorkflowStates() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAllWorkflowStatesSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflowStates`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $states = $dms->getAllWorkflowStates(); + $this->assertFalse($states); + } + + /** + * Test getWorkflowState() with a none existing workflow state + * + * The intitial database does not have any workflow states + * + * @return void + */ + public function testGetWorkflowStateNoExists() + { + $workflowstate = self::$dms->getWorkflowState(1); + $this->assertNull($workflowstate); + /* Passing an id not a numeric value returns false */ + $workflowstate = self::$dms->getWorkflowState('foo'); + $this->assertFalse($workflowstate); + /* Passing an id out of range returns false */ + $workflowstate = self::$dms->getWorkflowState(0); + $this->assertFalse($workflowstate); + } + + /** + * Test getWorkflowState() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowStateSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflowStates` WHERE `id` =")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $state = $dms->getWorkflowState(1); + $this->assertFalse($state); + } + + /** + * Test getWorkflowStateByName() with a none existing workflow state + * + * The intitial database does not have any workflow states + * + * @return void + */ + public function testGetWorkflowStateByNameNoExists() + { + $workflowstate = self::$dms->getWorkflowStateByName('foo'); + $this->assertNull($workflowstate); + } + + /** + * Test getWorkflowStateByName() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowStateByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflowStates` WHERE `name`=")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $state = $dms->getWorkflowStateByName('foo'); + $this->assertFalse($state); + } + + /** + * Test addWorkflowState() + * + * Add a new workflow state and retrieve it afterwards. Also check if the number + * of workflow states has increased by one. Add a workflow state with the same name a + * second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddWorkflowState() + { + /* Adding a new workflow state */ + $workflowstate = self::$dms->addWorkflowState('new workflow state', S_RELEASED); + $this->assertIsObject($workflowstate); + $this->assertEquals('new workflow state', $workflowstate->getName()); + /* Adding a workflow state with the same name must fail */ + $workflowstate = self::$dms->addWorkflowState('new workflow state', S_RELEASED); + $this->assertFalse($workflowstate); + /* Adding a workflow state with an empty name must fail */ + $workflowstate = self::$dms->addWorkflowState(' ', S_RELEASED); + $this->assertFalse($workflowstate); + /* There should be one workflow state now */ + $workflowstates = self::$dms->getAllWorkflowStates(); + $this->assertIsArray($workflowstates); + $this->assertCount(1, $workflowstates); + } + + /** + * Test getAllWorkflowActions() + * + * The intitial database does not have any workflow actions + * + * @return void + */ + public function testGetAllWorkflowActions() + { + $actions = self::$dms->getAllWorkflowActions(); + $this->assertIsArray($actions); + $this->assertCount(0, $actions); + } + + /** + * Test getAllWorkflowActions() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAllWorkflowActionsSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflowActions`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $actions = $dms->getAllWorkflowActions(); + $this->assertFalse($actions); + } + + /** + * Test getWorkflowAction() with a none existing workflow + * + * The intitial database does not have any workflow actions + * + * @return void + */ + public function testGetWorkflowActionNoExists() + { + $workflowaction = self::$dms->getWorkflowAction(1); + $this->assertNull($workflowaction); + /* Passing an id not a numeric value returns false */ + $workflowaction = self::$dms->getWorkflowAction('foo'); + $this->assertFalse($workflowaction); + /* Passing an id out of range returns false */ + $workflowaction = self::$dms->getWorkflowAction(0); + $this->assertFalse($workflowaction); + } + + /** + * Test getWorkflowAction() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowActionSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflowActions` WHERE `id` =")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $action = $dms->getWorkflowAction(1); + $this->assertFalse($action); + } + + /** + * Test getWorkflowActionByName() with a none existing workflow action + * + * The intitial database does not have any workflow actions + * + * @return void + */ + public function testGetWorkflowActionByNameNoExists() + { + $workflowaction = self::$dms->getWorkflowActionByName('foo'); + $this->assertNull($workflowaction); + } + + /** + * Test getWorkflowActionByName() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowActionByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblWorkflowActions` WHERE `name` =")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $action = $dms->getWorkflowActionByName('foo'); + $this->assertFalse($action); + } + + /** + * Test addWorkflowAction() + * + * Add a new workflow state and retrieve it afterwards. Also check if the number + * of workflow states has increased by one. Add a workflow state with the same name a + * second time and check if it returns false. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddWorkflowAction() + { + /* Adding a new workflow action */ + $workflowaction = self::$dms->addWorkflowAction('new workflow action', S_RELEASED); + $this->assertIsObject($workflowaction); + $this->assertEquals('new workflow action', $workflowaction->getName()); + /* Adding a workflow action with the same name must fail */ + $workflowaction = self::$dms->addWorkflowAction('new workflow action', S_RELEASED); + $this->assertFalse($workflowaction); + /* Adding a workflow action with an empty name must fail */ + $workflowaction = self::$dms->addWorkflowAction(' ', S_RELEASED); + $this->assertFalse($workflowaction); + /* There should be one workflow action now */ + $workflowactions = self::$dms->getAllWorkflowActions(); + $this->assertIsArray($workflowactions); + $this->assertCount(1, $workflowactions); + } + + /** + * Test getStatisticalData() + * + * @return void + */ + public function testGetStatisticalData() + { + /* There should one folder (root folder) */ + $data = self::$dms->getStatisticalData('foldersperuser'); + $this->assertIsArray($data); + $this->assertEquals(1, $data[0]['total']); + /* There should be no documents */ + foreach (array('docsperuser', 'docspermimetype', 'docspercategory', 'docspermonth', 'docsperstatus', 'docsaccumulated', 'sizeperuser') as $type) { + $data = self::$dms->getStatisticalData($type); + $this->assertIsArray($data); + $this->assertCount(0, $data); + } + /* Passing an unknown name returns an empty array */ + $data = self::$dms->getStatisticalData('foo'); + $this->assertIsArray($data); + $this->assertCount(0, $data); + } + + /** + * Test getStatisticalDataFail() + * + * Check if getStatisticalData() fails if the sql statements fail + * + * @return void + */ + public function testGetStatisticalDataFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->any()) + ->method('getResultArray') + ->with($this->stringContains("SELECT ")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + foreach (array('foldersperuser', 'docsperuser', 'docspermimetype', 'docspercategory', 'docspermonth', 'docsperstatus', 'docsaccumulated', 'sizeperuser') as $type) { + $data = $dms->getStatisticalData($type); + $this->assertFalse($data); + } + } + + /** + * Test createPasswordRequest() and checkPasswordRequest() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCreateAndCheckAndDeletePasswordRequest() + { + $user = self::$dms->getUser(1); + $hash = self::$dms->createPasswordRequest($user); + $this->assertIsString($hash); + $user = self::$dms->checkPasswordRequest($hash); + $this->assertIsObject($user); + $this->assertEquals(1, $user->getId()); + /* Check a non existing hash */ + $user = self::$dms->checkPasswordRequest('foo'); + $this->assertFalse($user); + /* Delete the hash */ + $ret = self::$dms->deletePasswordRequest($hash); + $this->assertTrue($ret); + /* Checking the hash again must return false, because it was deleted */ + $user = self::$dms->checkPasswordRequest($hash); + $this->assertFalse($user); + } + + /** + * Test method checkPasswordRequest() with sql failure + * + * @return void + */ + public function testCheckPasswordRequestSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblUserPasswordRequest`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->checkPasswordRequest('foo')); + } + + /** + * Test method deletePasswordRequest() with sql failure + * + * @return void + */ + public function testDeletePasswordRequestSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("DELETE FROM `tblUserPasswordRequest`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->deletePasswordRequest('foo')); + } + + /** + * Test getTimeline() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetTimeline() + { + $timeline = self::$dms->getTimeline(); + $this->assertIsArray($timeline); + } + + /** + * Test getUnlinkedDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetUnlinkedDocumentContent() + { + $contents = self::$dms->getUnlinkedDocumentContent(); + $this->assertIsArray($contents); + $this->assertCount(0, $contents); + } + + /** + * Test getNoFileSizeDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetNoFileSizeDocumentContent() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $contents = self::$dms->getNoFileSizeDocumentContent(); + $this->assertIsArray($contents); + $this->assertCount(0, $contents); + /* Manipulate the file size right in the database */ + $dbh = self::$dms->getDB(); + $ret = $dbh->getResult("UPDATE `tblDocumentContent` SET `fileSize` = 0"); + $this->assertTrue($ret); + $contents = self::$dms->getNoFileSizeDocumentContent(); + $this->assertIsArray($contents); + $this->assertCount(1, $contents); + } + + /** + * Test getNoFileSizeDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetNoFileSizeDocumentContentSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblDocumentContent` WHERE `fileSize`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->getNoFileSizeDocumentContent()); + } + + /** + * Test getNoChecksumDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetNoChecksumDocumentContent() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $contents = self::$dms->getNoChecksumDocumentContent(); + $this->assertIsArray($contents); + $this->assertCount(0, $contents); + /* Manipulate the checksum right in the database */ + $dbh = self::$dms->getDB(); + $ret = $dbh->getResult("UPDATE `tblDocumentContent` SET `checksum` = null"); + $this->assertTrue($ret); + $contents = self::$dms->getNoChecksumDocumentContent(); + $this->assertIsArray($contents); + $this->assertCount(1, $contents); + } + + /** + * Test getNoChecksumDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetNoChecksumDocumentContentSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblDocumentContent` WHERE `checksum`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->getNoChecksumDocumentContent()); + } + + /** + * Test getDuplicateDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDuplicateDocumentContent() + { + $contents = self::$dms->getDuplicateDocumentContent(); + $this->assertIsArray($contents); + $this->assertCount(0, $contents); + } + + /** + * Test getDuplicateDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDuplicateDocumentContentWithDuplicates() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $document1 = self::createDocument($rootfolder, $user, 'Document 1'); + + $filename = self::createTempFile(200); + list($document2, $res) = $rootfolder->addDocument( + 'Documet 2', // name + '', // comment + null, // no expiration + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + list($document3, $res) = $rootfolder->addDocument( + 'Documet 3', // name + '', // comment + null, // no expiration + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + + $contents = self::$dms->getDuplicateDocumentContent(); + $this->assertIsArray($contents); + $this->assertCount(1, $contents); + } + + /** + * Test method getDuplicateDocumentContent() with sql failure + * + * @return void + */ + public function testGetDuplicateDocumentContentSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT a.*, b.`id` as dupid FROM `tblDocumentContent`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->getDuplicateDocumentContent()); + } + + /** + * Test getNotificationsByUser() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetNotificationsByUser() + { + $user = self::$dms->getUser(1); + $notifications = self::$dms->getNotificationsByUser($user, 0); + $this->assertIsArray($notifications); + $this->assertCount(0, $notifications); + $notifications = self::$dms->getNotificationsByUser($user, 1); + $this->assertIsArray($notifications); + $this->assertCount(0, $notifications); + } + + /** + * Test getNotificationsByGroup() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetNotificationsByGroup() + { + $group = self::$dms->addGroup('new group', 'with comment'); + $this->assertIsObject($group); + $notifications = self::$dms->getNotificationsByGroup($group, 0); + $this->assertIsArray($notifications); + $this->assertCount(0, $notifications); + $notifications = self::$dms->getNotificationsByGroup($group, 1); + $this->assertIsArray($notifications); + $this->assertCount(0, $notifications); + } + + /** + * Test getDocumentsExpired() + * + * Check if getDocumentsExpired() fails if the parameters are wrong + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentsExpiredFail() + { + $documents = self::$dms->getDocumentsExpired(false); + $this->assertFalse($documents); + $documents = self::$dms->getDocumentsExpired('2021-04'); + $this->assertFalse($documents); + $documents = self::$dms->getDocumentsExpired('2021-01-32'); + $this->assertFalse($documents); + $documents = self::$dms->getDocumentsExpired('2021-01-31'); // valid date + $this->assertIsArray($documents); + } + + /** + * Test method getDocumentsExpired() with sql failure + * + * @return void + */ + public function testGetDocumentsExpiredSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('createTemporaryTable') + ->with('ttstatid') + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->getDocumentsExpired(1)); + } + + /** + * Test getDocumentByName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentByName() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $subfolder); + + /* Add a new document */ + $filename = self::createTempFile(200); + list($document, $res) = $subfolder->addDocument( + 'Document 1', // name + '', // comment + null, // no expiration + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertIsObject($document); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + /* Search without a parent folder restriction */ + $document = self::$dms->getDocumentByName('Document 1'); + $this->assertInstanceOf(SeedDMS_Core_Document::class, $document); + /* Searching in the root folder will return no document */ + $document = self::$dms->getDocumentByName('Document 1', $rootfolder); + $this->assertNull($document); + /* Searching in the sub folder will return the document */ + $document = self::$dms->getDocumentByName('Document 1', $subfolder); + $this->assertInstanceOf(SeedDMS_Core_Document::class, $document); + /* Searching for an empty name returns false */ + $document = self::$dms->getDocumentByName(' '); + $this->assertFalse($document); + } + + /** + * Test getDocumentByName() with sql failure + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lockUser`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document = $dms->getDocumentByName('foo'); + $this->assertFalse($document); + } + + /** + * Test getDocumentByOriginalFilename() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentByOriginalFilename() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $subfolder); + + /* Add a new document */ + $filename = self::createTempFile(200); + list($document, $res) = $subfolder->addDocument( + 'Document 1', // name + '', // comment + null, // no expiration + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertIsObject($document); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + /* Search without a parent folder restriction */ + $document = self::$dms->getDocumentByOriginalFilename('file1.txt'); + $this->assertInstanceOf(SeedDMS_Core_Document::class, $document); + /* Searching in the root folder will return no document */ + $document = self::$dms->getDocumentByOriginalFilename('file1.txt', $rootfolder); + $this->assertNull($document); + /* Searching in the sub folder will return the document */ + $document = self::$dms->getDocumentByOriginalFilename('file1.txt', $subfolder); + $this->assertInstanceOf(SeedDMS_Core_Document::class, $document); + /* Searching for an empty name returns false */ + $document = self::$dms->getDocumentByOriginalFilename(' '); + $this->assertFalse($document); + } + + /** + * Test method getDocumentByOriginalFilename() with sql failure + * + * @return void + */ + public function testGetDocumentByOriginalFilenameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('createTemporaryTable') + ->with('ttcontentid') + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->getDocumentByOriginalFilename(1)); + } + + /** + * Test getDocumentList() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentList() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + + /* Add a new document */ + $filename = self::createTempFile(200); + list($document, $res) = $rootfolder->addDocument( + 'Document 1', // name + '', // comment + null, // no expiration + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($document); + + /* Add a second new document */ + $filename = self::createTempFile(200); + list($document, $res) = $rootfolder->addDocument( + 'Document 2', // name + '', // comment + mktime(0, 0, 0), // expires today + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file2.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($document); + + $documents = self::$dms->getDocumentList('MyDocs', $user); + $this->assertIsArray($documents); + $this->assertCount(2, $documents); + /* All documents expiring from 1 year ago till today */ + $documents = self::$dms->getDocumentList('ExpiredOwner', $user); + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + /* All documents expiring today */ + $documents = self::$dms->getDocumentList('ExpiredOwner', $user, 0); + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + /* All documents expiring tomorrow */ + $documents = self::$dms->getDocumentList('ExpiredOwner', $user, date("Y-m-d", time()+86400)); + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + /* Get unknown list */ + $documents = self::$dms->getDocumentList('foo', $user); + $this->assertFalse($documents); + } + + /** + * Test search() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSearch() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $subfolder); + + /* Add a new document to the subfolder*/ + $filename = self::createTempFile(200); + list($document, $res) = $subfolder->addDocument( + 'Document 1', // name + '', // comment + null, // no expiration + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + /* Add a new document to the root folder. All documents expire. The first + * expires today, the second expires tomorrow, etc. + */ + for ($i=2; $i<=30; $i++) { + $filename = self::createTempFile(200); + list($document, $res) = $rootfolder->addDocument( + 'Document '.$i, // name + '', // comment + mktime(0, 0, 0)+($i-2)*86400, // expires in $i-2 days + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file'.$i.'.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0+$i // sequence + ); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + } + $hits = self::$dms->search(['query'=>'Document']); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(30, $hits['totalDocs']); + $this->assertCount(30, $hits['docs']); + /* Limit number of documents to 10 */ + $hits = self::$dms->search(['query'=>'Document', 'limit'=>10]); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(30, $hits['totalDocs']); + $this->assertCount(10, $hits['docs']); + /* Same number of documents if startFolder is the root folder */ + $hits = self::$dms->search(['query'=>'Document', 'startFolder'=>$rootfolder]); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(30, $hits['totalDocs']); + $this->assertCount(30, $hits['docs']); + /* There is just one document below the sub folder */ + $hits = self::$dms->search(['query'=>'Document', 'startFolder'=>$subfolder]); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(1, $hits['totalDocs']); + /* Get documents with a given expiration date in the future + * All documents in subfolder, but not the one in the root folder + */ + $expts = mktime(0, 0, 0); + $expstart = [ + 'year'=>date('Y', $expts), + 'month'=>date('m', $expts), + 'day'=>date('d', $expts), + 'hour'=>date('H', $expts), + 'minute'=>date('i', $expts), + 'second'=>date('s', $expts) + ]; + $hits = self::$dms->search(['query'=>'Document', 'expirationstartdate'=>$expstart]); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(29, $hits['totalDocs']); + /* Get documents with a given expiration date in the future, starting tomorrow + * All documents in subfolder - 1 + */ + $expts = mktime(0, 0, 0)+86400; + $expstart = [ + 'year'=>date('Y', $expts), + 'month'=>date('m', $expts), + 'day'=>date('d', $expts), + 'hour'=>date('H', $expts), + 'minute'=>date('i', $expts), + 'second'=>date('s', $expts) + ]; + $hits = self::$dms->search(['query'=>'Document', 'expirationstartdate'=>$expstart]); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(28, $hits['totalDocs']); + /* Get documents expire today or tomorrow + * 2 documents in subfolder + */ + $expts = mktime(0, 0, 0); + $expstart = [ + 'year'=>date('Y', $expts), + 'month'=>date('m', $expts), + 'day'=>date('d', $expts), + 'hour'=>date('H', $expts), + 'minute'=>date('i', $expts), + 'second'=>date('s', $expts) + ]; + $expts += 1*86400; + $expstop = [ + 'year'=>date('Y', $expts), + 'month'=>date('m', $expts), + 'day'=>date('d', $expts), + 'hour'=>date('H', $expts), + 'minute'=>date('i', $expts), + 'second'=>date('s', $expts) + ]; + $hits = self::$dms->search(['query'=>'Document', 'expirationstartdate'=>$expstart, 'expirationenddate'=>$expstop]); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(2, $hits['totalDocs']); + /* Get documents expire before and tomorrow + * 2 documents in subfolder + */ + $expts = mktime(0, 0, 0); // Start of today + $expts += 1*86400; // Start of tomorrow + $expstop = [ + 'year'=>date('Y', $expts), + 'month'=>date('m', $expts), + 'day'=>date('d', $expts), + 'hour'=>date('H', $expts), + 'minute'=>date('i', $expts), + 'second'=>date('s', $expts) + ]; + $hits = self::$dms->search(['query'=>'Document', 'expirationenddate'=>$expstop]); + $this->assertIsArray($hits); + $this->assertCount(5, $hits); + $this->assertEquals(2, $hits['totalDocs']); + } + +} + diff --git a/SeedDMS_Core/tests/DmsWithDataTest.php b/SeedDMS_Core/tests/DmsWithDataTest.php new file mode 100644 index 000000000..17fde09e7 --- /dev/null +++ b/SeedDMS_Core/tests/DmsWithDataTest.php @@ -0,0 +1,290 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * DMS test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DmsWithDataTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test getFoldersMinMax() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetFoldersMinMax() + { + self::createSimpleFolderStructure(); + $rootfolder = self::$dms->getRootFolder(); + $minmax = $rootfolder->getFoldersMinMax(); + $this->assertIsArray($minmax); + $this->assertCount(2, $minmax); + $this->assertEquals(0.5, $minmax['min']); + $this->assertEquals(2.0, $minmax['max']); + } + + /** + * Test method getFoldersMinMax() + * + * @return void + */ + public function testGetFoldersMinMaxSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT min(`sequence`) AS `min`, max(`sequence`) AS `max` FROM `tblFolders`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->getFoldersMinMax()); + } + + /** + * Test getDocumentsMinMax() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentsMinMax() + { + self::createSimpleFolderStructureWithDocuments(); + $subfolder = self::$dms->getFolderByName('Subfolder 1'); + $this->assertIsObject($subfolder); + $minmax = $subfolder->getDocumentsMinMax(); + $this->assertIsArray($minmax); + $this->assertCount(2, $minmax); + $this->assertEquals(2.0, $minmax['min']); + $this->assertEquals(16.0, $minmax['max']); + } + + /** + * Test method getDocumentsMinMax() + * + * @return void + */ + public function testGetDocumentsMinMaxSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT min(`sequence`) AS `min`, max(`sequence`) AS `max` FROM `tblDocuments`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->getDocumentsMinMax()); + } + + /** + * Test addDocument() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddDocument() + { + self::createSimpleFolderStructure(); + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $this->assertEquals(1, $rootfolder->getId()); + /* Add a new document */ + $filename = self::createTempFile(200); + list($document, $res) = $rootfolder->addDocument( + 'Document 1', // name + '', // comment + null, // expiration + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($document); + $this->assertInstanceOf(SeedDMS_Core_Document::class, $document); + $this->assertEquals('Document 1', $document->getName()); + } + + /** + * Test getDocumentsExpired() + * + * Create two documents which will expired today and tomorrow + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentsExpiredFuture() + { + self::createSimpleFolderStructure(); + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $this->assertEquals(1, $rootfolder->getId()); + /* Add a new document */ + $filename = self::createTempFile(200); + list($document, $res) = $rootfolder->addDocument( + 'Document 1', // name + '', // comment + mktime(23,59,59), // expiration is still today at 23:59:59 + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertIsObject($document); + list($document, $res) = $rootfolder->addDocument( + 'Document 2', // name + '', // comment + mktime(23,59,59)+1, // expiration is tomorrow today at 0:00:00 + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertIsObject($document); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $documents = self::$dms->getDocumentsExpired(0); /* Docs expire today */ + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $documents = self::$dms->getDocumentsExpired(date('Y-m-d')); /* Docs expire today */ + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $documents = self::$dms->getDocumentsExpired(1); /* Docs expire till tomorrow 23:59:59 */ + $this->assertIsArray($documents); + $this->assertCount(2, $documents); + $documents = self::$dms->getDocumentsExpired(date('Y-m-d', time()+86400)); /* Docs expire till tomorrow 23:59:59 */ + $this->assertIsArray($documents); + $this->assertCount(2, $documents); + $documents = self::$dms->getDocumentsExpired(date('Y-m-d', time()+86400), $user); /* Docs expire till tomorrow 23:59:59 owned by $user */ + $this->assertIsArray($documents); + $this->assertCount(2, $documents); + } + + /** + * Test getDocumentsExpired() + * + * Create two documents which have expired yesterday and the day before + * yesterday + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentsExpiredPast() + { + self::createSimpleFolderStructure(); + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $rootfolder); + $this->assertEquals(1, $rootfolder->getId()); + /* Add a new document */ + $filename = self::createTempFile(200); + list($document, $res) = $rootfolder->addDocument( + 'Document 1', // name + '', // comment + mktime(0,0,0)-1, // expiration was yesterday + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertIsObject($document); + list($document, $res) = $rootfolder->addDocument( + 'Document 2', // name + '', // comment + mktime(0,0,0)-1-86400, // expiration the day before yesterday + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($document); + $documents = self::$dms->getDocumentsExpired(0); /* No Docs expire today */ + $this->assertIsArray($documents); + $this->assertCount(0, $documents); + $documents = self::$dms->getDocumentsExpired(-1); /* Docs expired yesterday */ + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $documents = self::$dms->getDocumentsExpired(-2); /* Docs expired since the day before yesterday */ + $this->assertIsArray($documents); + $this->assertCount(2, $documents); + } + + +} diff --git a/SeedDMS_Core/tests/DocumentCategoryTest.php b/SeedDMS_Core/tests/DocumentCategoryTest.php new file mode 100644 index 000000000..37309d9a1 --- /dev/null +++ b/SeedDMS_Core/tests/DocumentCategoryTest.php @@ -0,0 +1,295 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * User test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DocumentCategoryTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetName() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $cat = self::$dms->addDocumentCategory('Category 1'); + $name = $cat->getName(); + $ret = $cat->setName('foo'); + $this->assertTrue($ret); + $name = $cat->getName(); + $this->assertEquals('foo', $name); + $ret = $cat->setName(' '); + $this->assertFalse($ret); + } + + /** + * Test method addCategories(), hasCategory(), setCategory() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddCategoryToDocument() + { + $rootfolder = self::$dms->getRootFolder(); + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + + /* Add a new document and two categories */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $cat1 = self::$dms->addDocumentCategory('Category 1'); + $cat2 = self::$dms->addDocumentCategory('Category 2'); + + /* There are no categories yet */ + $ret = $document->hasCategory($cat1); + $this->assertFalse($ret); + + /* Not passing a category yields on error */ + $ret = $document->hasCategory(null); + $this->assertFalse($ret); + + /* Adding a category ... */ + $ret = $document->addCategories([$cat1]); + $this->assertTrue($ret); + + /* ... and check if it is there */ + $ret = $document->hasCategory($cat1); + $this->assertTrue($ret); + + /* There should be one category now */ + $cats = $document->getCategories(); + $this->assertIsArray($cats); + $this->assertCount(1, $cats); + $this->assertEquals($cat1->getName(), $cats[0]->getName()); + + /* Adding the same category shouldn't change anything */ + $ret = $document->addCategories([$cat1]); + $this->assertTrue($ret); + + /* Check if category is used */ + $ret = $cat1->isUsed(); + $this->assertTrue($ret); + $ret = $cat2->isUsed(); + $this->assertFalse($ret); + + /* There is one document with cat 1 but none with cat 2 */ + $docs = $cat1->getDocumentsByCategory(); + $this->assertIsArray($docs); + $this->assertCount(1, $docs); + $num = $cat1->countDocumentsByCategory(); + $this->assertEquals(1, $num); + $docs = $cat2->getDocumentsByCategory(); + $this->assertIsArray($docs); + $this->assertCount(0, $docs); + $num = $cat2->countDocumentsByCategory(); + $this->assertEquals(0, $num); + + /* Still only one category */ + $cats = $document->getCategories(); + $this->assertIsArray($cats); + $this->assertCount(1, $cats); + + /* Setting new categories will replace the old ones */ + $ret = $document->setCategories([$cat1, $cat2]); + $this->assertTrue($ret); + + /* Now we have two categories */ + $cats = $document->getCategories(); + $this->assertIsArray($cats); + $this->assertCount(2, $cats); + + /* Remove a category */ + $ret = $document->removeCategories([$cat1]); + $this->assertTrue($ret); + + /* Removing the same category again does not harm*/ + $ret = $document->removeCategories([$cat1]); + $this->assertTrue($ret); + + /* We are back to one category */ + $cats = $document->getCategories(); + $this->assertIsArray($cats); + $this->assertCount(1, $cats); + + /* Remove the remaining category from the document */ + $ret = $document->removeCategories($cats); + $this->assertTrue($ret); + + /* No category left */ + $cats = $document->getCategories(); + $this->assertIsArray($cats); + $this->assertCount(0, $cats); + + /* Remove the category itself */ + $cats = self::$dms->getDocumentCategories(); + $this->assertIsArray($cats); + $this->assertCount(2, $cats); + $ret = $cat1->remove(); + $cats = self::$dms->getDocumentCategories(); + $this->assertIsArray($cats); + $this->assertCount(1, $cats); + } + + /** + * Test method getCategories() with sql fail + * + * @return void + */ + public function testGetCategoriesSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblCategory` WHERE")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->getCategories()); + } + + /** + * Test method addCategories() with sql fail + * + * @return void + */ + public function testAddCategoriesSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + /* mock sql statement in getCategories() which is called in addCategories() */ + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblCategory` WHERE")) + ->willReturn([]); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("INSERT INTO `tblDocumentCategory`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document = $this->getMockedDocument(); + $document->setDMS($dms); + $cat = new SeedDMS_Core_DocumentCategory(1, 'Category'); + $cat->setDMS($dms); + $this->assertFalse($document->addCategories([$cat])); + } + + /** + * Test method removeCategories() with sql fail + * + * @return void + */ + public function testRemoveCategoriesSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("DELETE FROM `tblDocumentCategory` WHERE")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document = $this->getMockedDocument(); + $document->setDMS($dms); + $cat = new SeedDMS_Core_DocumentCategory(1, 'Category'); + $cat->setDMS($dms); + $this->assertFalse($document->removeCategories([$cat])); + } + + /** + * Test method setCategories() with sql fail when deleting categories + * + * @return void + */ + public function testSetCategoriesSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("DELETE FROM `tblDocumentCategory` WHERE")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document = $this->getMockedDocument(); + $document->setDMS($dms); + $cat = new SeedDMS_Core_DocumentCategory(1, 'Category'); + $cat->setDMS($dms); + $this->assertFalse($document->setCategories([$cat])); + } + + /** + * Test method setCategories() with sql fail when inserting new categories + * + * @return void + */ + public function testSetCategoriesSqlFail2() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->exactly(2)) + ->method('getResult') + ->will( + $this->returnValueMap( + array( + array("DELETE FROM `tblDocumentCategory` WHERE `documentID` = 1", true, true), + array("INSERT INTO `tblDocumentCategory`", true, false) + ) + ) + ); + $dms = new SeedDMS_Core_DMS($db, ''); + $document = $this->getMockedDocument(); + $document->setDMS($dms); + $cat = new SeedDMS_Core_DocumentCategory(1, 'Category'); + $cat->setDMS($dms); + $this->assertFalse($document->setCategories([$cat])); + } + +} + diff --git a/SeedDMS_Core/tests/DocumentContentTest.php b/SeedDMS_Core/tests/DocumentContentTest.php new file mode 100644 index 000000000..9a8e9a14b --- /dev/null +++ b/SeedDMS_Core/tests/DocumentContentTest.php @@ -0,0 +1,593 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DocumentContentTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getContent(), getContentByVersion(), getLatestContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetContent() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $version = $document->getContentByVersion(1); + $this->assertIsObject($version); + $this->assertEquals($version->getId(), $lcontent->getId()); + $content = $document->getContent(); + $this->assertIsArray($content); + $this->assertCount(1, $content); + $this->assertEquals($version->getId(), $content[0]->getId()); + } + + /** + * Test method getContent() mit sql fail + * + * @return void + */ + public function testGetContentSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblDocumentContent` WHERE `document` ")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->getContent()); + } + + /** + * Test method getContentByVersion() mit sql fail + * + * @return void + */ + public function testGetContentByVersionSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblDocumentContent` WHERE `document` ")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->getContentByVersion(1)); + } + + /** + * Test method getLatestContent() mit sql fail + * + * @return void + */ + public function testGetLatestContentSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblDocumentContent` WHERE `document` ")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->getLatestContent()); + } + + /** + * Test method removeContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testRemoveContent() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + + /* Removing the only version will fail */ + $ret = $document->removeContent($lcontent); + $this->assertFalse($ret); + + /* Add a new version */ + $filename = self::createTempFile(300); + $result = $document->addContent('', $user, $filename, 'file2.txt', '.txt', 'text/plain'); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($result); + $this->assertIsObject($result->getContent()); + + /* Second trial to remove a version. Now it succeeds because it is not + * the last version anymore. + */ + $ret = $document->removeContent($lcontent); + $this->assertTrue($ret); + + /* The latest version is now version 2 */ + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $this->assertEquals(2, $lcontent->getVersion()); + + /* There is only 1 version left */ + $contents = $document->getContent(); + $this->assertIsArray($contents); + $this->assertCount(1, $contents); + } + + /** + * Test method isType() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testIsType() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $ret = $lcontent->isType('documentcontent'); + $this->assertTrue($ret); + } + + /** + * Test method getUser(), getDocument() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testVarious() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $ret = $lcontent->isType('documentcontent'); + $this->assertTrue($ret); + $doc = $lcontent->getDocument(); + $this->assertEquals($document->getId(), $doc->getId()); + $u = $lcontent->getUser(); + $this->assertEquals($user->getId(), $u->getId()); + $filetype = $lcontent->getFileType(); + $this->assertEquals('.txt', $filetype); + $origfilename = $lcontent->getOriginalFileName(); + $this->assertEquals('file1.txt', $origfilename); + } + + /** + * Test method getComment(), setComment() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetComment() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $comment = $lcontent->getComment(); + $this->assertEquals('', $comment); + $ret = $lcontent->setComment('Document content comment'); + $this->assertTrue($ret); + /* Retrieve the document content from the database again */ + $content = self::$dms->getDocumentContent($lcontent->getId()); + $comment = $content->getComment(); + $this->assertEquals('Document content comment', $comment); + } + + /** + * Test method getDate(), setDate() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetDate() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $date = $lcontent->getDate(); + $this->assertIsInt($date); + $this->assertGreaterThanOrEqual(time(), $date); + + /* Set date as timestamp */ + $ret = $lcontent->setDate($date-1000); + $this->assertTrue($ret); + /* Retrieve the document content from the database again */ + $content = self::$dms->getDocumentContent($lcontent->getId()); + $newdate = $content->getDate(); + $this->assertEquals($date-1000, $newdate); + + /* Set date in Y-m-d H:i:s format */ + $date = time()-500; + $ret = $lcontent->setDate(date('Y-m-d H:i:s', $date)); + $this->assertTrue($ret); + /* Retrieve the document content from the database again */ + $content = self::$dms->getDocumentContent($lcontent->getId()); + $newdate = $content->getDate(); + $this->assertEquals($date, $newdate); + + /* Not passing a date will set the current date/time */ + $date = time(); + $ret = $lcontent->setDate(); + $this->assertTrue($ret); + /* Retrieve the document content from the database again */ + $content = self::$dms->getDocumentContent($lcontent->getId()); + $newdate = $content->getDate(); + $this->assertEquals($date, $newdate); + } + + /** + * Test method getFileSize(), setFileSize() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetFileSize() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1', 200); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $filesize = $lcontent->getFileSize(); + $this->assertEquals(200, $filesize); + + /* Intentially corrupt the file size */ + $db = self::$dms->getDb(); + $ret = $db->getResult("UPDATE `tblDocumentContent` SET `fileSize` = 300 WHERE `document` = " . $document->getID() . " AND `version` = " . $lcontent->getVersion()); + $this->assertTrue($ret); + + $corcontent = self::$dms->getDocumentContent($lcontent->getId()); + $filesize = $corcontent->getFileSize(); + $this->assertEquals(300, $filesize); + + /* Repair filesize by calling setFileSize() */ + $ret = $corcontent->setFileSize(); + $this->assertTrue($ret); + $filesize = $corcontent->getFileSize(); + $this->assertEquals(200, $filesize); + } + + /** + * Test method getChecksum(), setChecksum() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetChecksum() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1', 200); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + $orgchecksum = $lcontent->getChecksum(); + $this->assertIsString($orgchecksum); + $this->assertEquals(32, strlen($orgchecksum)); + + /* Intentially corrupt the checksum */ + $db = self::$dms->getDb(); + $ret = $db->getResult("UPDATE `tblDocumentContent` SET `checksum` = 'foobar' WHERE `document` = " . $document->getID() . " AND `version` = " . $lcontent->getVersion()); + $this->assertTrue($ret); + + $corcontent = self::$dms->getDocumentContent($lcontent->getId()); + $checksum = $corcontent->getChecksum(); + $this->assertEquals('foobar', $checksum); + + /* Repair filesize by calling setChecksum() */ + $ret = $corcontent->setChecksum(); + $this->assertTrue($ret); + $checksum = $corcontent->getChecksum(); + $this->assertEquals($orgchecksum, $checksum); + } + + /** + * Test method getStatus(), setStatus(), getStatusLog() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetStatus() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1', 200); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + + $status = $lcontent->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + $statuslog = $lcontent->getStatusLog(); + $this->assertIsArray($statuslog); + $this->assertCount(1, $statuslog); + + /* Missing update user returns false */ + $ret = $lcontent->setStatus(S_OBSOLETE, '', null); + $this->assertFalse($ret); + + /* A status out of range returns false */ + $ret = $lcontent->setStatus(9, '', $user); + $this->assertFalse($ret); + + /* A wrong date returns false */ + $ret = $lcontent->setStatus(S_OBSOLETE, '', $user, '2021-02-29 10:10:10'); + $this->assertFalse($ret); + + $ret = $lcontent->setStatus(S_OBSOLETE, 'No longer valid', $user, date('Y-m-d H:i:s')); + $status = $lcontent->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_OBSOLETE, $status['status']); + + /* Status log has now 2 entries */ + $statuslog = $lcontent->getStatusLog(); + $this->assertIsArray($statuslog); + $this->assertCount(2, $statuslog); + + /* Add the 'onSetStatus' callback */ + $callret = ''; + $callback = function ($param, $content, $updateuser, $oldstatus, $newstatus) use (&$callret) { + $callret = $oldstatus.' to '.$newstatus; + return $param; + }; + /* Because the callback will return false, the status will not be set */ + self::$dms->setCallback('onSetStatus', $callback, false); + /* Trying to go back to status released with a callback returning false */ + $ret = $lcontent->setStatus(S_RELEASED, 'Valid again', $user); + $status = $lcontent->getStatus(); + $this->assertIsArray($status); + /* Status is still S_OBSOLETE because the callback returned false */ + $this->assertEquals(S_OBSOLETE, $status['status']); + $this->assertEquals(S_OBSOLETE.' to '.S_RELEASED, $callret); + + /* Do it again, but this time the callback returns true */ + self::$dms->setCallback('onSetStatus', $callback, true); + /* Trying to go back to status released with a callback returning true */ + $ret = $lcontent->setStatus(S_RELEASED, 'Valid again', $user); + $status = $lcontent->getStatus(); + $this->assertIsArray($status); + /* Status updated to S_RELEASED because the callback returned true */ + $this->assertEquals(S_RELEASED, $status['status']); + $this->assertEquals(S_OBSOLETE.' to '.S_RELEASED, $callret); + + /* Status log has now 3 entries */ + $statuslog = $lcontent->getStatusLog(); + $this->assertIsArray($statuslog); + $this->assertCount(3, $statuslog); + + /* Get just the last entry */ + $statuslog = $lcontent->getStatusLog(1); + $this->assertIsArray($statuslog); + $this->assertCount(1, $statuslog); + $this->assertEquals('Valid again', $statuslog[0]['comment']); + } + + /** + * Test method getMimeType(), setMimeType() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetMimeType() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1', 200); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + + $ret = $lcontent->setMimeType('text/csv'); + $this->assertTrue($ret); + + /* Retrieve the document content from the database again */ + $content = self::$dms->getDocumentContent($lcontent->getId()); + $this->assertIsObject($content); + $this->assertEquals('text/csv', $content->getMimeType()); + } + + /** + * Test method getFileType(), setFileType() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetFileType() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1', 200); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + + $ret = $lcontent->setMimeType('text/css'); + $this->assertTrue($ret); + + $ret = $lcontent->setFileType(); + $this->assertTrue($ret); + + /* Retrieve the document content from the database again */ + $content = self::$dms->getDocumentContent($lcontent->getId()); + $this->assertIsObject($content); + $this->assertEquals('.css', $content->getFileType()); + + /* Also get the file content to ensure the renaming of the file + * on disc has succeeded. + */ + $c = file_get_contents(self::$dms->contentDir.$lcontent->getPath()); + $this->assertEquals(200, strlen($c)); + } + + /** + * Test method replaceContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testReplaceContent() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $guest = self::$dms->getUser(2); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1', 200); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + + $filename = self::createTempFile(300); + /* Not using the same user yields an error */ + $ret = $document->replaceContent(1, $guest, $filename, 'file1.txt', '.txt', 'text/plain'); + $this->assertFalse($ret); + /* Not using the same orig. file name yields an error */ + $ret = $document->replaceContent(1, $user, $filename, 'file2.txt', '.txt', 'text/plain'); + $this->assertFalse($ret); + /* Not using the same file type yields an error */ + $ret = $document->replaceContent(1, $user, $filename, 'file1.txt', '.csv', 'text/plain'); + $this->assertFalse($ret); + /* Not using the same mime type yields an error */ + $ret = $document->replaceContent(1, $user, $filename, 'file1.txt', '.txt', 'text/csv'); + $this->assertFalse($ret); + + /* Setting version to 0 will replace the latest version */ + $ret = $document->replaceContent(0, $user, $filename, 'file1.txt', '.txt', 'text/plain'); + $this->assertTrue($ret); + + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + + /* Retrieve the document content from the database again */ + $newcontent = $document->getLatestContent(); + $this->assertIsObject($newcontent); + $this->assertEquals('text/plain', $newcontent->getMimeType()); + /* File size has grown from 200 to 300 bytes */ + $filesize = $newcontent->getFileSize(); + $this->assertEquals(300, $filesize); + /* Still version 1 */ + $version = $newcontent->getVersion(); + $this->assertEquals(1, $version); + } + + /** + * Test method replaceContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAccessMode() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $guest = self::$dms->getUser(2); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1', 200); + $this->assertIsObject($document); + $lcontent = $document->getLatestContent(); + $this->assertIsObject($lcontent); + + /* Access rights on a document content are always M_READ unless the callback + * onCheckAccessDocumentContent is implemented */ + $mode = $lcontent->getAccessMode($user); + $this->assertEquals(M_READ, $mode); + } +} diff --git a/SeedDMS_Core/tests/DocumentFileTest.php b/SeedDMS_Core/tests/DocumentFileTest.php new file mode 100644 index 000000000..95d758c56 --- /dev/null +++ b/SeedDMS_Core/tests/DocumentFileTest.php @@ -0,0 +1,290 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DocumentFileTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + self::$dbversion = self::$dms->getDBVersion(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getInstance() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetMockedDocumentFile() + { + $user = self::getMockedUser(); + $document1 = self::getMockedDocument(1, 'Document 1'); + $file = new SeedDMS_Core_DocumentFile(1, $document1, $user->getId(), 'comment', time(), '', '.txt', 'text/plain', 'test.txt', 'name', 1, true); + $this->assertIsObject($file); + $this->assertTrue($file->isType('documentfile')); + + $document = $file->getDocument(); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $this->assertEquals('Document 1', $document->getName()); + + $ispublic = $file->isPublic(); + $this->assertTrue($ispublic); + + $comment = $file->getComment(); + $this->assertEquals('comment', $comment); + + $filetype = $file->getFileType(); + $this->assertEquals('.txt', $filetype); + + $mimetype = $file->getMimeType(); + $this->assertEquals('text/plain', $mimetype); + + $name = $file->getName(); + $this->assertEquals('name', $name); + + $origfilename = $file->getOriginalFileName(); + $this->assertEquals('test.txt', $origfilename); + + $version = $file->getVersion(); + $this->assertEquals(1, $version); + + $accessmode = $file->getAccessMode($user); + $this->assertEquals(M_READ, $accessmode); + } + + /** + * Test method setComment() mit sql fail + * + * @return void + */ + public function testSetCommentSqlFail() + { + $user = self::getMockedUser(); + $document = $this->getMockedDocument(); + $file = new SeedDMS_Core_DocumentFile(1, $document, $user->getId(), 'comment', time(), '', '.txt', 'text/plain', 'test.txt', 'name', 1, true); + $this->assertIsObject($file); + $this->assertTrue($file->isType('documentfile')); + + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocumentFiles` SET `comment`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($file->setComment('my comment')); + } + + /** + * Test method setName() mit sql fail + * + * @return void + */ + public function testSetNameSqlFail() + { + $user = self::getMockedUser(); + $document = $this->getMockedDocument(); + $file = new SeedDMS_Core_DocumentFile(1, $document, $user->getId(), 'comment', time(), '', '.txt', 'text/plain', 'test.txt', 'name', 1, true); + $this->assertIsObject($file); + $this->assertTrue($file->isType('documentfile')); + + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocumentFiles` SET `name`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($file->setName('my name')); + } + + /** + * Test method setDate() mit sql fail + * + * @return void + */ + public function testSetDateSqlFail() + { + $user = self::getMockedUser(); + $document = $this->getMockedDocument(); + $file = new SeedDMS_Core_DocumentFile(1, $document, $user->getId(), 'comment', time(), '', '.txt', 'text/plain', 'test.txt', 'name', 1, true); + $this->assertIsObject($file); + $this->assertTrue($file->isType('documentfile')); + + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocumentFiles` SET `date`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($file->setDate()); + } + + /** + * Test method setVersion() mit sql fail + * + * @return void + */ + public function testSetVersionSqlFail() + { + $user = self::getMockedUser(); + $document = $this->getMockedDocument(); + $file = new SeedDMS_Core_DocumentFile(1, $document, $user->getId(), 'comment', time(), '', '.txt', 'text/plain', 'test.txt', 'name', 1, true); + $this->assertIsObject($file); + $this->assertTrue($file->isType('documentfile')); + + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocumentFiles` SET `version`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($file->setVersion(1)); + } + + /** + * Test method setPublic() mit sql fail + * + * @return void + */ + public function testSetPublicnSqlFail() + { + $user = self::getMockedUser(); + $document = $this->getMockedDocument(); + $file = new SeedDMS_Core_DocumentFile(1, $document, $user->getId(), 'comment', time(), '', '.txt', 'text/plain', 'test.txt', 'name', 1, true); + $this->assertIsObject($file); + $this->assertTrue($file->isType('documentfile')); + + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocumentFiles` SET `public`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($file->setPublic(true)); + } + + /** + * Test method addDocumentFile(), getDocumentFile() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddDocumentFile() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $tmpfile = self::createTempFile(); + $file = $document->addDocumentFile('attachment.txt', 'comment', $user, $tmpfile, 'attachment.txt', '.txt', 'text/plain', 0, true); + $this->assertTrue(SeedDMS_Core_File::removeFile($tmpfile)); + $this->assertIsObject($file); + $this->assertTrue($file->isType('documentfile')); + + $files = $document->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(1, $files); + + $file = $files[0]; + + $document = $file->getDocument(); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $this->assertEquals('Document 1', $document->getName()); + + $ispublic = $file->isPublic(); + $this->assertTrue($ispublic); + + $luser = $file->getUser(); + $this->assertIsObject($luser); + $this->assertTrue($luser->isType('user')); + + $ret = $file->setComment('new comment'); + $this->assertTrue($ret); + $comment = $file->getComment(); + $this->assertEquals('new comment', $comment); + + $ret = $file->setName('new name'); + $this->assertTrue($ret); + $name = $file->getName(); + $this->assertEquals('new name', $name); + + $now = time(); + $ret = $file->setDate($now); + $this->assertTrue($ret); + $date = $file->getDate(); + $this->assertEquals($now, $date); + + $ret = $file->setDate('fail'); + $this->assertFalse($ret); + + $ret = $file->setVersion(2); + $this->assertTrue($ret); + $version = $file->getVersion(); + $this->assertEquals(2, $version); + + $ret = $file->setVersion('fail'); + $this->assertFalse($ret); + + $ret = $file->setPublic(true); + $this->assertTrue($ret); + $ispublic = $file->isPublic(); + $this->assertEquals(1, $ispublic); + + + } +} diff --git a/SeedDMS_Core/tests/DocumentLinkTest.php b/SeedDMS_Core/tests/DocumentLinkTest.php new file mode 100644 index 000000000..d646607e9 --- /dev/null +++ b/SeedDMS_Core/tests/DocumentLinkTest.php @@ -0,0 +1,125 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DocumentLinkTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + self::$dbversion = self::$dms->getDBVersion(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getInstance() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetMockedDocumentLink() + { + $user = self::getMockedUser(); + $document1 = self::getMockedDocument(1, 'Document 1'); + $document2 = self::getMockedDocument(2, 'Document 2'); + $link = new SeedDMS_Core_DocumentLink(1, $document1, $document2, $user, true); + $this->assertIsObject($link); + $this->assertTrue($link->isType('documentlink')); + + $document = $link->getDocument(); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $this->assertEquals('Document 1', $document->getName()); + + $document = $link->getTarget(); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $this->assertEquals('Document 2', $document->getName()); + + $ispublic = $link->isPublic(); + $this->assertTrue($ispublic); + } + + /** + * Test method addDocumentLink(), getDocumentLink() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddDocumentLink() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $document1 = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document1); + $document2 = self::createDocument($rootfolder, $user, 'Document 2'); + $this->assertIsObject($document2); + $link = $document1->addDocumentLink($document2->getId(), $user->getId(), true); + $this->assertIsObject($link); + $this->assertTrue($link->isType('documentlink')); + + $document = $link->getDocument(); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $this->assertEquals('Document 1', $document->getName()); + + $document = $link->getTarget(); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $this->assertEquals('Document 2', $document->getName()); + + $ispublic = $link->isPublic(); + $this->assertTrue($ispublic); + + $luser = $link->getUser(); + $this->assertIsObject($luser); + $this->assertTrue($luser->isType('user')); + } +} diff --git a/SeedDMS_Core/tests/DocumentTest.php b/SeedDMS_Core/tests/DocumentTest.php new file mode 100644 index 000000000..5331f367d --- /dev/null +++ b/SeedDMS_Core/tests/DocumentTest.php @@ -0,0 +1,1323 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class DocumentTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + self::$dbversion = self::$dms->getDBVersion(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getInstance() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetInstance() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + /* Get the document with id 1, which must be 'Document 1' */ + $document = SeedDMS_Core_Document::getInstance(1, self::$dms); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $this->assertEquals('Document 1', $document->getName()); + /* Get a none existing document */ + $document = SeedDMS_Core_Document::getInstance(2, self::$dms); + $this->assertNull($document); + } + + /** + * Test method getInstance() within a root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetInstanceWithinRoot() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* The simple folder structure will create to subfolders, each + * with 15 documents. + */ + self::createSimpleFolderStructureWithDocuments(); + $subfolder = self::$dms->getFolderByName('Subfolder 1'); + /* Get a document in Subfolder 1 */ + $document1 = self::$dms->getDocumentByName('Document 1-1'); + /* Get a document in Subfolder 2 */ + $document2 = self::$dms->getDocumentByName('Document 2-1'); + + /* Getting a document in subfolder 1 without any restrictions must succeed */ + $document = SeedDMS_Core_Document::getInstance($document1->getId(), self::$dms); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + + /* Make Subfolder 1 the root folder */ + self::$dms->checkWithinRootDir = true; + self::$dms->setRootFolderID($subfolder->getId()); + + /* Getting a document by id in subfolder 1 still must succeed */ + $document = SeedDMS_Core_Document::getInstance($document1->getId(), self::$dms); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + + /* Getting a document by id in subfolder 2 must fail */ + $document = SeedDMS_Core_Document::getInstance($document2->getId(), self::$dms); + $this->assertNull($document); + + /* Get a document in Subfolder 1 */ + $document = self::$dms->getDocumentByName('Document 1-1'); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + + /* Get a document in Subfolder 2 */ + $document = self::$dms->getDocumentByName('Document 2-1'); + $this->assertNull($document); + } + + /** + * Test method getInstance() + * + * @return void + */ + public function testGetInstanceSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lock` FROM `tblDocuments`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse(SeedDMS_Core_Document::getInstance(1, $dms)); + } + + /** + * Test method getDir() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDir() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $this->assertIsObject($document); + $this->assertEquals('1/', $document->getDir()); + } + + /** + * Test method getComment() and setComment() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetComment() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $document = self::createDocument($folder, $user, 'Document 1'); + $this->assertIsObject($document); + $comment = $document->getComment(); + $this->assertEquals('', $comment); + $ret = $document->setComment('foo'); + $this->assertTrue($ret); + $comment = $document->getComment(); + $this->assertEquals('foo', $comment); + } + + /** + * Test method setComment() mit sql fail + * + * @return void + */ + public function testSetCommentSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `comment`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setComment('my comment')); + } + + /** + * Test method getKeywords() and setKeywords() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetKeywords() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $document = self::createDocument($folder, $user, 'Document 1'); + $this->assertIsObject($document); + $keywords = $document->getKeywords(); + $this->assertEquals('', $keywords); + $ret = $document->setKeywords('foo bar'); + $this->assertTrue($ret); + $keywords = $document->getKeywords(); + $this->assertEquals('foo bar', $keywords); + } + + /** + * Test method setKeywords() mit sql fail + * + * @return void + */ + public function testSetKeywordsSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `keywords`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setKeywords('keywords')); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetName() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $document = self::createDocument($folder, $user, 'Document 1'); + $this->assertIsObject($document); + $name = $document->getName(); + $this->assertEquals('Document 1', $name); + $ret = $document->setName('foo'); + $this->assertTrue($ret); + $name = $document->getName(); + $this->assertEquals('foo', $name); + } + + /** + * Test method setName() mit sql fail + * + * @return void + */ + public function testSetNameSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `name`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setName('my name')); + } + + /** + * Test method getDate() and setDate() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetDate() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $document = self::createDocument($folder, $user, 'Document 1'); + $now = time(); + /* Passing false as a time stamp will take current time stamp */ + $ret = $document->setDate(false); + $this->assertTrue($ret); + $date = $document->getDate(); + $this->assertEquals($now, $date); + /* Setting a time stamp */ + $now -= 1000; + $ret = $document->setDate($now); + $this->assertTrue($ret); + $date = $document->getDate(); + $this->assertEquals($now, $date); + /* Setting a none numeric value will fail */ + $ret = $document->setDate('foo'); + $this->assertFalse($ret); + } + + /** + * Test method setDate() with sql fail + * + * @return void + */ + public function testSetDateSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `date` = ")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setDate(null)); + } + + /** + * Test method getDefaultAccess() and setDefaultAccess() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetDefaultAccess() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $document = self::createDocument($folder, $user, 'Document 1'); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $defaultaccess = $document->getDefaultAccess(); + $this->assertEquals(M_READ, $defaultaccess); + + /* Setting a default access out of range yields an error */ + $ret = $document->setDefaultAccess(0, true); + $this->assertFalse($ret); + + /* Setting a default access out of range yields an error */ + $ret = $document->setDefaultAccess(M_ALL+1, true); + $this->assertFalse($ret); + + /* Setting the default access will have no effect as long as access + * rights are inherited. */ + $ret = $document->setDefaultAccess(M_READWRITE, true); + $this->assertTrue($ret); + $defaultaccess = $document->getDefaultAccess(); + $this->assertEquals(M_READ, $defaultaccess); + + /* Once inheritance of access rights is turned off, the previously + * set default access right will take effect. */ + $ret = $document->setInheritAccess(false, true); + $this->assertTrue($ret); + $defaultaccess = $document->getDefaultAccess(); + $this->assertEquals(M_READWRITE, $defaultaccess); + + /* Also check if inherited access was turned off */ + $ret = $document->getInheritAccess(); + $this->assertFalse($ret); + } + + /** + * Test method setDefaultAccess() mit sql fail + * + * @return void + */ + public function testSetDefaultAccessSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `defaultAccess`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setDefaultAccess(M_READ)); + } + + /** + * Test method setInheritAccess() mit sql fail + * + * @return void + */ + public function testSetInheritAccessSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `inheritAccess`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setInheritAccess(0)); + } + + /** + * Test method addAccess(), removeAccess(), changeAccess(), getAccessList() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetAccess() + { + self::createGroupsAndUsers(); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $adminuser = self::$dms->getUser(1); + $guestuser = self::$dms->getUser(2); + $user = self::$dms->getUserByLogin('user-1-1'); + $this->assertIsObject($user); + $this->assertTrue($user->isType('user')); + $group = self::$dms->getGroupByName('Group 1'); + $this->assertIsObject($group); + $this->assertTrue($group->isType('group')); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + $defaultaccess = $document->getDefaultAccess(); + $this->assertEquals(M_READ, $defaultaccess); + + /* Turn off inheritance, otherwise the access rights have no effect */ + $ret = $document->setInheritAccess(false, true); + $this->assertTrue($ret); + + /* Retrieving an access mode without a valid user will always return M_NONE */ + $mode = $document->getAccessMode(null); + $this->assertEquals(M_NONE, $mode); + + /* The admin user has always unlimited access */ + $mode = $document->getAccessMode($adminuser); + $this->assertEquals(M_ALL, $mode); + + /* Without setting any specific access, the document has a default mode M_READ */ + $mode = $document->getAccessMode($user); + $this->assertEquals(M_READ, $mode); + + /* Access mode for group is also the default access */ + $mode = $document->getGroupAccessMode($group); + $this->assertEquals(M_READ, $mode); + + /* Set unlimited access rights for everybody */ + $ret = $document->setDefaultAccess(M_ALL); + $this->assertTrue($ret); + $mode = $document->getAccessMode($user); + $this->assertEquals(M_ALL, $mode); + $mode = $document->getGroupAccessMode($group); + $this->assertEquals(M_ALL, $mode); + + /* Guest still have just read access */ + $mode = $document->getAccessMode($guestuser); + $this->assertEquals(M_READ, $mode); + + /* Add wrong access type returns false */ + $ret = $document->addAccess(M_ALL+1, $user->getId(), true); + $this->assertFalse($ret); + + /* Add read/write access on the document for user */ + $ret = $document->addAccess(M_READWRITE, $user->getId(), true); + $this->assertTrue($ret); + /* Adding another access right (not matter which one) for the + * same user yields an error + */ + $ret = $document->addAccess(M_READ, $user->getId(), true); + $this->assertFalse($ret); + + /* Passing an invalid second parameter will return false */ + $accesslist = $document->getAccessList(M_ANY, 5); + $this->assertFalse($accesslist); + + /* Searching for mode == M_READ will return neither a group nor + * the user, because the user has read&write access + */ + $accesslist = $document->getAccessList(M_READ); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['groups']); + $this->assertCount(0, $accesslist['users']); + + $accesslist = $document->getAccessList(M_READWRITE); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['groups']); + $this->assertCount(1, $accesslist['users']); + + $accesslist = $document->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['groups']); + $this->assertCount(1, $accesslist['users']); + + /* Access mode is just read/write for the user thought the default is unlimited */ + $mode = $document->getAccessMode($user); + $this->assertEquals(M_READWRITE, $mode); + /* Access mode for the group is still unlimited */ + $mode = $document->getGroupAccessMode($group); + $this->assertEquals(M_ALL, $mode); + + /* Setting default access to M_READ + * is just a precaution to ensure the unlimeted access rights is not + * derived from the default access which was set to M_ALL above. + */ + $ret = $document->setDefaultAccess(M_READ); + $this->assertTrue($ret); + $mode = $document->getGroupAccessMode($group); + $this->assertEquals(M_READ, $mode); + + /* Add unlimeted access on the document for group */ + $ret = $document->addAccess(M_ALL, $group->getId(), false); + $this->assertTrue($ret); + /* Adding another access right (not matter which one) for the + * same group yields an error + */ + $ret = $document->addAccess(M_READ, $group->getId(), false); + $this->assertFalse($ret); + + $accesslist = $document->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(1, $accesslist['groups']); + $this->assertCount(1, $accesslist['users']); + + /* The group has now unlimited access rights */ + $mode = $document->getGroupAccessMode($group); + $this->assertEquals(M_ALL, $mode); + + /* The user still has just read/write access, though the group he belongs + * to has unlimeted rights. The specific user rights has higher priority. + */ + $mode = $document->getAccessMode($user); + $this->assertEquals(M_READWRITE, $mode); + + /* Remove all specific access rights for the user */ + $ret = $document->removeAccess($user->getId(), true); + $this->assertTrue($ret); + + /* Now the group rights apply for the user, because there are no + * specific user rights anymore. + */ + $mode = $document->getAccessMode($user); + $this->assertEquals(M_ALL, $mode); + + /* change unlimeted access on the document for group to none */ + $ret = $document->changeAccess(M_NONE, $group->getId(), false); + $this->assertTrue($ret); + $mode = $document->getAccessMode($user); + $this->assertEquals(M_NONE, $mode); + + /* clear all access rights */ + $ret = $document->clearAccessList(); + $this->assertTrue($ret); + $accesslist = $document->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['groups']); + $this->assertCount(0, $accesslist['users']); + + /* We are back to the default access rights */ + $mode = $document->getAccessMode($user); + $this->assertEquals(M_READ, $mode); + + } + + /** + * Test method addNotify(), removeNotify(), getNotifyList(), cleanNotifyList() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetNotify() + { + self::createGroupsAndUsers(); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $adminuser = self::$dms->getUser(1); + $guestuser = self::$dms->getUser(2); + $user = self::$dms->getUserByLogin('user-1-1'); + $this->assertIsObject($user); + $this->assertTrue($user->isType('user')); + $group = self::$dms->getGroupByName('Group 1'); + $this->assertIsObject($group); + $this->assertTrue($group->isType('group')); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + $this->assertIsObject($document); + $this->assertTrue($document->isType('document')); + + $notifylist = $document->getNotifyList(); + $this->assertIsArray($notifylist); + $this->assertCount(0, $notifylist['groups']); + $this->assertCount(0, $notifylist['users']); + + /* Add notify on the document for user */ + $ret = $document->addNotify($user->getId(), true); + $this->assertEquals(0, $ret); + + /* Add notify on the document for group */ + $ret = $document->addNotify($group->getId(), false); + $this->assertEquals(0, $ret); + + /* Add notify on the document for a user that does not exists */ + $ret = $document->addNotify(15, true); + $this->assertEquals(-1, $ret); + + $notifylist = $document->getNotifyList(); + $this->assertIsArray($notifylist); + $this->assertCount(1, $notifylist['groups']); + $this->assertCount(1, $notifylist['users']); + + /* Setting the default access to M_NONE and turning off inheritance + * will clean the notification list, because the notifiers have no + * longer read access on the document and therefore will be removed + * from the notification list. + */ + $ret = $document->setInheritAccess(false); + $this->assertTrue($ret); + $ret = $document->setDefaultAccess(M_NONE); + $this->assertTrue($ret); + + $notifylist = $document->getNotifyList(); + $this->assertIsArray($notifylist); + $this->assertCount(0, $notifylist['groups']); + $this->assertCount(0, $notifylist['users']); + } + + /** + * Test method isDescendant() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testIsDescendant() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder1 = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subfolder2 = $rootfolder->addSubFolder('Subfolder 2', '', $user, 1.0); + $document = self::createDocument($subfolder1, $user, 'Document 1'); + /* document is a descendant of root folder and subfolder 1 */ + $this->assertTrue($document->isDescendant($rootfolder)); + $this->assertTrue($document->isDescendant($subfolder1)); + /* subfolder is not a descendant of subfolder 2 */ + $this->assertFalse($document->isDescendant($subfolder2)); + } + + /** + * Test method getParent() + * + * Create a new document below root folder and check if parent + * of the document is the root folder. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetParent() + { + $user = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $parent = $document->getParent(); + $this->assertIsObject($parent); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $parent); + $this->assertEquals(1, $parent->getId()); + } + + /** + * Test method setParent() + * + * Create a new document below root folder, move it to a subfolder + * and check if parent of the document is the sub folder. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetParent() + { + $user = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 0); + $document = self::createDocument($rootfolder, $user, 'Document 1'); + /* Setting a null folder is not allowed */ + $ret = $document->setParent(null); + $this->assertFalse($ret); + + /* Passed object must be a folder */ + $ret = $document->setParent($user); + $this->assertFalse($ret); + + $ret = $document->setParent($subfolder); + $this->assertTrue($ret); + $parent = $document->getParent(); + $this->assertIsObject($parent); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $parent); + $this->assertEquals(2, $parent->getId()); + } + + /** + * Test method setParent() mit sql fail + * + * @return void + */ + public function testSetParentSqlFail() + { + $document = $this->getMockedDocument(); + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `folder`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setParent($rootfolder)); + } + + /** + * Test method setOwner() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetOwner() + { + $adminuser = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $document = self::createDocument($rootfolder, $adminuser, 'Document 1'); + /* Setting a null user is not allowed */ + $ret = $document->setOwner(null); + $this->assertFalse($ret); + + /* Passed object must be a folder */ + $ret = $document->setOwner($rootfolder); + $this->assertFalse($ret); + + $res = $document->setOwner($user); + $this->assertTrue($res); + $owner = $document->getOwner(); + $this->assertIsObject($owner); + $this->assertInstanceOf(SeedDMS_Core_User::class, $owner); + $this->assertEquals($user->getId(), $owner->getId()); + } + + /** + * Test method setOwner() mit sql fail + * + * @return void + */ + public function testSetOwnerSqlFail() + { + $document = $this->getMockedDocument(); + $user = $this->getMockedUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `owner`")) + ->willReturn(false); + // SeedDMS 6 will fetch the old owner in setOwner() before setting the + // new owner + if(self::$dbversion['major'] == 6) { + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblUsers` WHERE `id` = ")) + ->willReturn([]); + } + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setOwner($user)); + } + + /** + * Test method expires(), setExpires(), getExpires() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetExpires() + { + $adminuser = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $document = self::createDocument($rootfolder, $adminuser, 'Document 1'); + $expires = $document->expires(); + $this->assertFalse($expires); + $expires = $document->getExpires(); + $this->assertFalse($expires); + $now = time(); + $res = $document->setExpires($now); + $this->assertTrue($res); + /* Setting it again will return true */ + $res = $document->setExpires($now); + $this->assertTrue($res); + $expires = $document->expires(); + $this->assertTrue($res); + $expirets = $document->getExpires(); + $this->assertEquals($now, $expirets); + } + + /** + * Test method setExpires() mit sql fail + * + * @return void + */ + public function testSetExpiresSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `expires`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setExpires(time())); + } + + /** + * Test method setLocked(), isLocked() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetAndIsLocked() + { + $adminuser = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $document = self::createDocument($rootfolder, $adminuser, 'Document 1'); + $res = $document->isLocked(); + $this->assertFalse($res); + $res = $document->setLocked($user); + $this->assertTrue($res); + $res = $document->isLocked(); + $this->assertTrue($res); + $lockuser = $document->getLockingUser(); + $this->assertIsObject($lockuser); + $this->assertInstanceOf(SeedDMS_Core_User::class, $lockuser); + $this->assertEquals($user->getId(), $lockuser->getId()); + /* parameter passed to setLocked must be false or a user */ + $res = $document->setLocked(null); + /* document is still locked and locking user is unchanged */ + $res = $document->isLocked(); + $this->assertTrue($res); + $lockuser = $document->getLockingUser(); + $this->assertIsObject($lockuser); + $this->assertInstanceOf(SeedDMS_Core_User::class, $lockuser); + $this->assertEquals($user->getId(), $lockuser->getId()); + /* Unlock the document */ + $res = $document->setLocked(false); + $this->assertTrue($res); + $res = $document->isLocked(); + $this->assertFalse($res); + $lockuser = $document->getLockingUser(); + $this->assertFalse($lockuser); + } + + /** + * Test method setLocked() with sql fail + * + * @return void + */ + public function testSetLockedSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("DELETE FROM `tblDocumentLocks`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setLocked(false)); + } + + /** + * Test method getSequence() and setSequence() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetSequence() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + /* The document still has sequence = 1.0 */ + $sequence = $document->getSequence(); + $this->assertEquals(1.0, $sequence); + $ret = $document->setSequence(1.5); + $this->assertTrue($ret); + $sequence = $document->getSequence(); + $this->assertEquals(1.5, $sequence); + } + + /** + * Test method setSequence() mit sql fail + * + * @return void + */ + public function testSetSequenceSqlFail() + { + $document = $this->getMockedDocument(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblDocuments` SET `sequence`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $document->setDMS($dms); + $this->assertFalse($document->setSequence(1.1)); + } + + /** + * Test method getContentByVersion(), isLatestContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetContentByVersion() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + /* Get version 1 */ + $content = $document->getContentByVersion(1); + $this->assertIsObject($content); + $this->assertInstanceOf(SeedDMS_Core_DocumentContent::class, $content); + /* There is no version 2 */ + $content = $document->getContentByVersion(2); + $this->assertNull($content); + /* version must be numeric */ + $content = $document->getContentByVersion('foo'); + $this->assertFalse($content); + /* Check if 1 is the latest version number */ + $ret = $document->isLatestContent(1); + $this->assertTrue($ret); + $ret = $document->isLatestContent(2); + $this->assertFalse($ret); + } + + /** + * Test method getDocumentContent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentContent() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + /* Get version 1 */ + $content = $document->getContentByVersion(1); + $this->assertIsObject($content); + $this->assertInstanceOf(SeedDMS_Core_DocumentContent::class, $content); + $again = self::$dms->getDocumentContent($content->getId()); + $this->assertIsObject($again); + $this->assertInstanceOf(SeedDMS_Core_DocumentContent::class, $again); + $this->assertEquals($content->getId(), $again->getId()); + $none = self::$dms->getDocumentContent(2); + $this->assertNull($none); + } + + /** + * Test method getDocumentContent() with sql failure + * + * @return void + */ + public function testGetDocumentContentSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblDocumentContent` WHERE `id`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse($dms->getDocumentContent(1)); + } + + /** + * Test method addDocumentLink(), getDocumentLinks(), getReverseDocumentLinks() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddAndGetDocumentLinks() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $document1 = self::createDocument($folder, $adminuser, 'Document 1'); + $document2 = self::createDocument($folder, $adminuser, 'Document 2'); + + /* document 1 has no links */ + $links = $document1->getDocumentLinks(); + $this->assertIsArray($links); + $this->assertCount(0, $links); + $links = $document1->getReverseDocumentLinks(); + $this->assertIsArray($links); + $this->assertCount(0, $links); + + /* Adding a link to none existing target or by a none existing user fails */ + $ret = $document1->addDocumentLink(3, $user->getId(), false); + $this->assertFalse($ret); + $ret = $document1->addDocumentLink($document2->getId(), 4, false); + $this->assertFalse($ret); + + /* Adding a link with a bogus target or user must fail */ + $ret = $document1->addDocumentLink('foo', 1, false); + $this->assertFalse($ret); + $ret = $document1->addDocumentLink(3, 'foo', false); + $this->assertFalse($ret); + + /* Adding a link to myself must fail */ + $ret = $document1->addDocumentLink($document1->getId(), $user->getId(), false); + $this->assertFalse($ret); + + /* Add a non public link to document 2 by user */ + $link = $document1->addDocumentLink($document2->getId(), $user->getId(), false); + $this->assertIsObject($link); + $this->assertInstanceOf(SeedDMS_Core_DocumentLink::class, $link); + $links = $document1->getDocumentLinks(); + $this->assertIsArray($links); + $this->assertCount(1, $links); + $links = $document2->getReverseDocumentLinks(); + $this->assertIsArray($links); + $this->assertCount(1, $links); + /* There is one reverse link of a user */ + $links = $document2->getReverseDocumentLinks(false, $user); + $this->assertIsArray($links); + $this->assertCount(1, $links); + /* There are no public reverse links */ + $links = $document2->getReverseDocumentLinks(true); + $this->assertIsArray($links); + $this->assertCount(0, $links); + + /* There are no public links of document 1 */ + $document1->clearCache(); + $links = $document1->getDocumentLinks(true); + $this->assertIsArray($links); + $this->assertCount(0, $links); + + /* There are no links by adminuser of document 1 */ + $document1->clearCache(); + $links = $document1->getDocumentLinks(false, $adminuser); + $this->assertIsArray($links); + $this->assertCount(0, $links); + + /* There are links by user of document 1 */ + $document1->clearCache(); + $links = $document1->getDocumentLinks(false, $user); + $this->assertIsArray($links); + $this->assertCount(1, $links); + + $link = $document1->getDocumentLink($links[0]->getId()); + $this->assertIsObject($link); + $this->assertTrue($link->isType('documentlink')); + } + + /** + * Test method addDocumentLink(), removeDocumentLinks() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddAndRemoveDocumentLink() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $document1 = self::createDocument($folder, $adminuser, 'Document 1'); + $document2 = self::createDocument($folder, $adminuser, 'Document 2'); + + /* Add a non public link to document 2 by user */ + $link = $document1->addDocumentLink($document2->getId(), $user->getId(), false); + $this->assertIsObject($link); + $this->assertInstanceOf(SeedDMS_Core_DocumentLink::class, $link); + $links = $document1->getDocumentLinks(); + $this->assertIsArray($links); + $this->assertCount(1, $links); + + /* Remove the link again */ + $link = $links[0]; + $ret = $document1->removeDocumentLink($link->getId()); + $this->assertTrue($ret); + $links = $document1->getDocumentLinks(); + $this->assertIsArray($links); + $this->assertCount(0, $links); + } + + /** + * Test method addDocumentFile(), getDocumentFiles() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddAndGetDocumentFiles() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + + /* document has no files */ + $files = $document->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(0, $files); + + $filename = self::createTempFile(100); + $file1 = $document->addDocumentFile('Attachment 1', '', $user, $filename, 'attachment1.txt', '.txt', 'plain/text'); + unlink($filename); + $this->assertIsObject($file1); + $this->assertInstanceOf(SeedDMS_Core_DocumentFile::class, $file1); + + $filename = self::createTempFile(100); + $file2 = $document->addDocumentFile('Attachment 2', '', $user, $filename, 'attachment2.txt', '.txt', 'plain/text', 1); + unlink($filename); + $this->assertIsObject($file2); + $this->assertInstanceOf(SeedDMS_Core_DocumentFile::class, $file2); + + /* Get all attachments */ + $files = $document->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(2, $files); + + /* Get attachments for version 1 only */ + $files = $document->getDocumentFiles(1, false); + $this->assertIsArray($files); + $this->assertCount(1, $files); + + /* Get attachments for version 1 and version independed */ + $files = $document->getDocumentFiles(1, true); + $this->assertIsArray($files); + $this->assertCount(2, $files); + } + + /** + * Test method addDocumentFile(), removeDocumentFile() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddAndRemoveDocumentFiles() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + + /* document has no files */ + $files = $document->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(0, $files); + + $filename = self::createTempFile(100); + $file1 = $document->addDocumentFile('Attachment 1', '', $user, $filename, 'attachment1.txt', '.txt', 'plain/text'); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($file1); + $this->assertInstanceOf(SeedDMS_Core_DocumentFile::class, $file1); + + /* document has now 1 file */ + $files = $document->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(1, $files); + + /* Removing a file with a none exiting or bogus id must fail */ + $ret = $document->removeDocumentFile(2); + $this->assertFalse($ret); + $ret = $document->removeDocumentFile('foo'); + $this->assertFalse($ret); + + $ret = $document->removeDocumentFile($files[0]->getId()); + $this->assertTrue($ret); + + $files = $document->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(0, $files); + } + + /** + * Test method addDocument(), removeDocument() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddAndRemoveDocument() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $document = self::createDocument($folder, $adminuser, 'Document 1'); + $docid = $document->getId(); + + $filename = self::createTempFile(100); + $file1 = $document->addDocumentFile('Attachment 1', '', $user, $filename, 'attachment1.txt', '.txt', 'plain/text'); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($file1); + $this->assertInstanceOf(SeedDMS_Core_DocumentFile::class, $file1); + + $ret = $document->remove(); + $this->assertTrue($ret); + $document = self::$dms->getDocument($docid); + $this->assertNull($document); + } + + /** + * Test method getUsedDiskSpace() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetUsedDiskSpace() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + /* Create a document with 1234 Bytes */ + $document = self::createDocument($folder, $adminuser, 'Document 1', 1234); + $size = $document->getUsedDiskSpace(); + $this->assertEquals(1234, $size); + } + + /** + * Test method getTimeline() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetTimeline() + { + $adminuser = self::$dms->getUser(1); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + /* Create a document */ + $document = self::createDocument($folder, $adminuser, 'Document 1'); + /* Attach a file */ + $filename = self::createTempFile(100); + $file1 = $document->addDocumentFile('Attachment 1', '', $adminuser, $filename, 'attachment1.txt', '.txt', 'plain/text'); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($file1); + $this->assertInstanceOf(SeedDMS_Core_DocumentFile::class, $file1); + + /* Get the timeline. It must contain two entries + * - the initial release of the document + * - adding the attachment + */ + $timeline = $document->getTimeLine(); + $this->assertIsArray($timeline); + $this->assertCount(2, $timeline); + } + + /** + * Test method transferToUser() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testTransferToUser() + { + $adminuser = self::$dms->getUser(1); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($user); + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + /* Create two documents */ + $document1 = self::createDocument($folder, $adminuser, 'Document 1'); + $document2 = self::createDocument($folder, $adminuser, 'Document 2'); + + /* Attach a file */ + $filename = self::createTempFile(100); + $file1 = $document1->addDocumentFile('Attachment 1', '', $adminuser, $filename, 'attachment1.txt', '.txt', 'plain/text'); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + $this->assertIsObject($file1); + $this->assertInstanceOf(SeedDMS_Core_DocumentFile::class, $file1); + + /* Add a non public link to document 2 */ + $link = $document1->addDocumentLink($document2->getId(), $adminuser->getId(), false); + $this->assertIsObject($link); + $this->assertInstanceOf(SeedDMS_Core_DocumentLink::class, $link); + + /* Transfer document to $user */ + $this->assertEquals('admin', $document1->getOwner()->getLogin()); + $links = $document1->getDocumentLinks(false, $adminuser); + $this->assertIsArray($links); + $this->assertCount(1, $links); + + $ret = $document1->transferToUser($user); + $this->assertTrue($ret); + $this->assertEquals('user1', $document1->getOwner()->getLogin()); + $links = $document1->getDocumentLinks(false, $user); + $this->assertIsArray($links); + $this->assertCount(1, $links); + $files = $document1->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(1, $files); + $this->assertEquals($files[0]->getUserID(), $user->getId()); + } +} diff --git a/SeedDMS_Core/tests/FileUtilsTest.php b/SeedDMS_Core/tests/FileUtilsTest.php new file mode 100644 index 000000000..7e1b87cfb --- /dev/null +++ b/SeedDMS_Core/tests/FileUtilsTest.php @@ -0,0 +1,219 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class FileUtilsTest extends SeedDmsTest +{ + /** + * Create temporary directory + * + * @return void + */ + protected function setUp(): void + { + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method format_filesize() + * + * @return void + */ + public function testFormatFileSize() + { + $this->assertEquals('1 Byte', SeedDMS_Core_File::format_filesize(1)); + $this->assertEquals('0 Bytes', SeedDMS_Core_File::format_filesize(0)); + $this->assertEquals('1000 Bytes', SeedDMS_Core_File::format_filesize(1000)); + $this->assertEquals('1 KiB', SeedDMS_Core_File::format_filesize(1024)); + $this->assertEquals('1 KiB', SeedDMS_Core_File::format_filesize(1025)); + $this->assertEquals('2 KiB', SeedDMS_Core_File::format_filesize(2047)); + $this->assertEquals('1 MiB', SeedDMS_Core_File::format_filesize(1024*1024)); + $this->assertEquals('1 GiB', SeedDMS_Core_File::format_filesize(1024*1024*1024)); + } + + /** + * Test method format_filesize() + * + * @return void + */ + public function testParseFileSize() + { + $this->assertEquals(200, SeedDMS_Core_File::parse_filesize('200B')); + $this->assertEquals(200, SeedDMS_Core_File::parse_filesize('200 B')); + $this->assertEquals(200, SeedDMS_Core_File::parse_filesize('200')); + $this->assertEquals(1024, SeedDMS_Core_File::parse_filesize('1K')); + $this->assertEquals(2*1024*1024, SeedDMS_Core_File::parse_filesize('2M')); + $this->assertEquals(3*1024*1024*1024, SeedDMS_Core_File::parse_filesize('3 G')); + $this->assertEquals(4*1024*1024*1024*1024, SeedDMS_Core_File::parse_filesize('4 T')); + $this->assertFalse(SeedDMS_Core_File::parse_filesize('4 t')); + $this->assertFalse(SeedDMS_Core_File::parse_filesize('-4T')); + } + + /** + * Test method fileSize() + * + * @return void + */ + public function testFileSize() + { + $filename = self::createTempFile(200, self::$contentdir); + $this->assertEquals(200, SeedDMS_Core_File::fileSize($filename)); + /* Getting the size of a none existing file returns false */ + $this->assertFalse(SeedDMS_Core_File::fileSize('foobar')); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + } + + /** + * Test method file_exists() + * + * @return void + */ + public function testFileExists() + { + $filename = self::createTempFile(200, self::$contentdir); + $this->assertTrue(SeedDMS_Core_File::file_exists($filename)); + $this->assertFalse(SeedDMS_Core_File::file_exists($filename.'bla')); + $this->assertTrue(SeedDMS_Core_File::removeFile($filename)); + } + + /** + * Test method fileExtension() + * + * @return void + */ + public function testFileExtension() + { + $this->assertEquals('png', SeedDMS_Core_File::fileExtension('image/png')); + $this->assertEquals('', SeedDMS_Core_File::fileExtension('image/kpng')); + $this->assertEquals('txt', SeedDMS_Core_File::fileExtension('text/plain')); + $this->assertEquals('md', SeedDMS_Core_File::fileExtension('text/markdown')); + } + + /** + * Test method moveFile() + * + * @return void + */ + public function testMoveFile() + { + $filename = self::createTempFile(200, self::$contentdir); + $this->assertEquals(200, SeedDMS_Core_File::fileSize($filename)); + $ret = SeedDMS_Core_File::moveFile($filename, self::$contentdir.DIRECTORY_SEPARATOR."foobar"); + $this->assertTrue($ret); + /* Getting the file size of the old doc must fail now */ + $this->assertFalse(SeedDMS_Core_File::fileSize($filename)); + /* Getting the file size of the new doc succeds */ + $this->assertEquals(200, SeedDMS_Core_File::fileSize(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + $this->assertTrue(SeedDMS_Core_File::removeFile(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + } + + /** + * Test method makeDir(), renameDir(), removeDir() + * + * @return void + */ + public function testMakeRenameAndRemoveDir() + { + /* Create a directory and put a file into it */ + $ret = SeedDMS_Core_File::makeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar"); + system('touch '.self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."tt"); + /* Rename the directory */ + $ret = SeedDMS_Core_File::renameDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar", self::$contentdir.DIRECTORY_SEPARATOR."bazfoo"); + $this->assertTrue($ret); + /* The new must exist and the old one is gone */ + $this->assertTrue(is_dir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + $this->assertFalse(is_dir(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + $this->assertTrue(SeedDMS_Core_File::removeDir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + $this->assertFalse(SeedDMS_Core_File::removeDir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + $this->assertFalse(SeedDMS_Core_File::removeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + + /* Create a directory, a sub directory and a file */ + $ret = SeedDMS_Core_File::makeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar"); + $this->assertTrue($ret); + $ret = SeedDMS_Core_File::makeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."bazfoo"); + $this->assertTrue($ret); + system('touch '.self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."bazfoo".DIRECTORY_SEPARATOR."tt"); + $this->assertTrue(SeedDMS_Core_File::file_exists(self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."bazfoo".DIRECTORY_SEPARATOR."tt")); + + $ret = SeedDMS_Core_File::removeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar"); + $this->assertTrue($ret); + $this->assertFalse(SeedDMS_Core_File::file_exists(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + $this->assertFalse(SeedDMS_Core_File::file_exists(self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."bazfoo")); + $this->assertFalse(SeedDMS_Core_File::file_exists(self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."bazfoo".DIRECTORY_SEPARATOR."tt")); + } + + /** + * Test method makeDir(), copyDir(), removeDir() + * + * @return void + */ + public function testMakeCopyAndRemoveDir() + { + /* Create a directory and put a file into it */ + $ret = SeedDMS_Core_File::makeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar"); + system('touch '.self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."tt"); + /* Rename the directory */ + $ret = SeedDMS_Core_File::copyDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar", self::$contentdir.DIRECTORY_SEPARATOR."bazfoo"); + $this->assertTrue($ret); + /* The new and the old dir must exist */ + $this->assertTrue(is_dir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + $this->assertTrue(is_dir(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + $this->assertTrue(SeedDMS_Core_File::removeDir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + $this->assertTrue(SeedDMS_Core_File::removeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + } + + /** + * Test method moveDir() + * + * @return void + */ + public function testMakeAndMoveDir() + { + /* Create a directory and put a file into it */ + $ret = SeedDMS_Core_File::makeDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar"); + system('touch '.self::$contentdir.DIRECTORY_SEPARATOR."foobar".DIRECTORY_SEPARATOR."tt"); + /* Rename the directory */ + $ret = SeedDMS_Core_File::moveDir(self::$contentdir.DIRECTORY_SEPARATOR."foobar", self::$contentdir.DIRECTORY_SEPARATOR."bazfoo"); + $this->assertTrue($ret); + /* The new must exist and the old dir must be disappeared */ + $this->assertTrue(is_dir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + $this->assertFalse(is_dir(self::$contentdir.DIRECTORY_SEPARATOR."foobar")); + $this->assertTrue(SeedDMS_Core_File::removeDir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + $this->assertFalse(is_dir(self::$contentdir.DIRECTORY_SEPARATOR."bazfoo")); + } +} diff --git a/SeedDMS_Core/tests/FolderTest.php b/SeedDMS_Core/tests/FolderTest.php new file mode 100644 index 000000000..ee05f0b8c --- /dev/null +++ b/SeedDMS_Core/tests/FolderTest.php @@ -0,0 +1,1221 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class FolderTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getInstance() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetInstanceRootFolder() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $this->assertIsObject($folder); + $this->assertEquals('DMS', $folder->getName()); + /* get instance of none existing folder */ + $folder = SeedDMS_Core_Folder::getInstance(2, self::$dms); + $this->assertNull($folder); + } + + /** + * Test method isType() + * + * @return void + */ + public function testIsType() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $this->assertTrue($folder->isType('folder')); + } + + /** + * Test method getInstance() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetInstance() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subsubfolder = $subfolder->addSubFolder('Subsubfolder 1', '', $user, 1.0); + /* Get the folder with id 2, which must be 'Subfolder 1' */ + $folder = SeedDMS_Core_Folder::getInstance(2, self::$dms); + $this->assertIsObject($folder); + $this->assertEquals('Subfolder 1', $folder->getName()); + /* Get a none existing folder */ + $folder = SeedDMS_Core_Folder::getInstance(4, self::$dms); + $this->assertNull($folder); + } + + /** + * Test method getInstance() + * + * @return void + */ + public function testGetInstanceSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblFolders`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse(SeedDMS_Core_Folder::getInstance(1, $dms)); + } + + /** + * Test method getInstanceByName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetInstanceByName() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subsubfolder = $subfolder->addSubFolder('Subsubfolder 1', '', $user, 1.0); + /* Search for it anywhere in the folder hierarchy */ + $folder = SeedDMS_Core_Folder::getInstanceByName('Subsubfolder 1', null, self::$dms); + $this->assertIsObject($folder); + $this->assertEquals('Subsubfolder 1', $folder->getName()); + /* Search for it within 'Subfolder 1' will find it */ + $folder = SeedDMS_Core_Folder::getInstanceByName('Subsubfolder 1', $subfolder, self::$dms); + $this->assertIsObject($folder); + $this->assertEquals('Subsubfolder 1', $folder->getName()); + /* Search for it within root folder will not find it */ + $folder = SeedDMS_Core_Folder::getInstanceByName('Subsubfolder 1', $rootfolder, self::$dms); + $this->assertNull($folder); + } + + /** + * Test method getInstanceByName() + * + * @return void + */ + public function testGetInstanceByNameSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblFolders`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $this->assertFalse(SeedDMS_Core_Folder::getInstanceByName('foo', null, $dms)); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetName() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $name = $folder->getName(); + $this->assertEquals('DMS', $name); + $ret = $folder->setName('foo'); + $this->assertTrue($ret); + $name = $folder->getName(); + $this->assertEquals('foo', $name); + } + + /** + * Test method setName() + * + * @return void + */ + public function testSetNameSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblFolders` SET `name`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->setName('foo')); + } + + /** + * Test method getComment() and setComment() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetComment() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $comment = $folder->getComment(); + $this->assertEquals('DMS root', $comment); + $ret = $folder->setComment('foo'); + $this->assertTrue($ret); + $comment = $folder->getComment(); + $this->assertEquals('foo', $comment); + } + + /** + * Test method setComment() + * + * @return void + */ + public function testSetCommentSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblFolders` SET `comment`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->setComment('foo')); + } + + /** + * Test method getSequence() and setSequence() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetSequence() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + /* The root folder's sequence in the initial database is 0.0 */ + $sequence = $folder->getSequence(); + $this->assertEquals(0.0, $sequence); + $ret = $folder->setSequence(1.5); + $this->assertTrue($ret); + $sequence = $folder->getSequence(); + $this->assertEquals(1.5, $sequence); + } + + /** + * Test method setSequence() + * + * @return void + */ + public function testSetSequenceSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblFolders` SET `sequence`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->setSequence(0.0)); + } + + /** + * Test method getDate() and setDate() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetDate() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $now = time(); + /* Passing false as a time stamp will take current time stamp */ + $ret = $folder->setDate(false); + $this->assertTrue($ret); + $date = $folder->getDate(); + $this->assertEquals($now, $date); + /* Setting a time stamp */ + $now -= 1000; + $ret = $folder->setDate($now); + $this->assertTrue($ret); + $date = $folder->getDate(); + $this->assertEquals($now, $date); + /* Setting a none numeric value will fail */ + $ret = $folder->setDate('foo'); + $this->assertFalse($ret); + } + + /** + * Test method setDate() + * + * @return void + */ + public function testSetDateSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblFolders` SET `date`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->setDate(time())); + } + + /** + * Test method getParent() + * + * Get parent of root folder which is always null. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetParentRootFolder() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $parent = $folder->getParent(); + $this->assertNull($parent); + } + + /** + * Test method getParent() + * + * Create a new subfolder below root folder and check if parent + * of the folder is the root folder. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetParent() + { + $adminuser = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $adminuser, 0); + $parent = $subfolder->getParent(); + $this->assertIsObject($parent); + $this->assertInstanceOf(SeedDMS_Core_Folder::class, $parent); + $this->assertEquals(1, $parent->getId()); + } + + /** + * Test method setParent() on root folder + * + * Moving the root folder will always fail + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetParentRootFolder() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $ret = $folder->setParent(1); + $this->assertFalse($ret); + } + + /** + * Test method getOwner() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetOwner() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $owner = $folder->getOwner(); + $this->assertIsObject($owner); + $this->assertInstanceOf(SeedDMS_Core_User::class, $owner); + $this->assertEquals(1, $owner->getId()); + } + + /** + * Test method setOwner() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetOwner() + { + $adminuser = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->addUser('user1', 'user1', 'User One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $adminuser, 0); + $res = $subfolder->setOwner($user); + $this->assertTrue($res); + $owner = $subfolder->getOwner(); + $this->assertIsObject($owner); + $this->assertInstanceOf(SeedDMS_Core_User::class, $owner); + $this->assertEquals($user->getId(), $owner->getId()); + } + + /** + * Test method setOwner() + * + * @return void + */ + public function testSetOwnerSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $user = new SeedDMS_Core_User(1, 'admin', 'pass', 'Joe Foo', 'baz@foo.de', 'en_GB', 'bootstrap', 'My comment', SeedDMS_Core_User::role_admin); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblFolders` SET `owner`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->setOwner($user)); + } + + /** + * Test method getDefaultAccess() + * + * The default access is always M_READ unless it was set differently + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDefaultAccess() + { + $adminuser = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $accessmode = $rootfolder->getDefaultAccess(); + $this->assertEquals(M_READ, $accessmode); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $adminuser, 0); + $accessmode = $subfolder->getDefaultAccess(); + $this->assertEquals(M_READ, $accessmode); + } + + /** + * Test method setDefaultAccess() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetDefaultAccess() + { + $adminuser = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $adminuser, 0); + /* Setting the default access to something != M_READ will not have + * any effect as long as inheritage of access rights is turned on. + */ + $subfolder->setDefaultAccess(M_READWRITE, true); + $accessmode = $subfolder->getDefaultAccess(); + $this->assertEquals(M_READ, $accessmode); + /* Turning inheritage off will use the default access */ + $subfolder->setInheritAccess(false, true); + $accessmode = $subfolder->getDefaultAccess(); + $this->assertEquals(M_READWRITE, $accessmode); + } + + /** + * Test method setDefaultAccess() + * + * @return void + */ + public function testSetDefaultAccessSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblFolders` SET `defaultAccess`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->setDefaultAccess(M_NONE)); + } + + /** + * Test method setInheritAccess() + * + * @return void + */ + public function testSetInheritAccessSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblFolders` SET `inheritAccess`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->setInheritAccess(true)); + } + + /** + * Test method hasSubFolders() on root folder and after adding + * new subfolders. + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testHasSubFolders() + { + $user = self::$dms->getUser(1); + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $ret = $rootfolder->hasSubFolders(); + $this->assertIsInt($ret); + $this->assertEquals(0, $ret); + $subfolder1 = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subfolder2 = $rootfolder->addSubFolder('Subfolder 2', '', $user, 1.0); + $ret = $rootfolder->hasSubFolders(); + $this->assertIsInt($ret); + $this->assertEquals(2, $ret); + /* hasSubFolderByName() just returns true or false */ + $ret = $rootfolder->hasSubFolderByName('Subfolder 1'); + $this->assertTrue($ret); + $ret = $rootfolder->hasSubFolderByName('Subfolder 3'); + $this->assertFalse($ret); + } + + /** + * Test method hasSubFolders with sql fail() + * + * @return void + */ + public function testHasSubFoldersSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT count(*) as c FROM `tblFolders`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->hasSubFolders()); + } + + /** + * Test method hasSubFolderByName with sql fail() + * + * @return void + */ + public function testHasSubFolderByNameSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT count(*) as c FROM `tblFolders`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->hasSubFolderByName('foo')); + } + + /** + * Test method getSubFolders() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetSubFoldersRootOnly() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $folders = $folder->getSubFolders(); + $this->assertIsArray($folders); + $this->assertCount(0, $folders); + } + + /** + * Test method getSubFolders() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetSubFolders() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder1 = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subfolder2 = $rootfolder->addSubFolder('Subfolder 2', '', $user, 1.0); + $folders = $rootfolder->getSubFolders(); + $this->assertIsArray($folders); + $this->assertCount(2, $folders); + + /* Get sub folders order by name descending */ + $rootfolder->clearCache(); // Force retrieving sub folders from database + $folders = $rootfolder->getSubFolders('n', 'desc', 1, 0); + $this->assertIsArray($folders); + $this->assertCount(1, $folders); + $this->assertEquals('Subfolder 2', $folders[0]->getName()); + + /* Get sub folders order by name descending with an offset of 1 */ + $rootfolder->clearCache(); // Force retrieving sub folders from database + $folders = $rootfolder->getSubFolders('n', 'desc', 1, 1); + $this->assertIsArray($folders); + $this->assertCount(1, $folders); + $this->assertEquals('Subfolder 1', $folders[0]->getName()); + + /* Get sub folders order by sequence ascending */ + $rootfolder->clearCache(); // Force retrieving sub folders from database + $folders = $rootfolder->getSubFolders('s', 'asc', 1, 0); + $this->assertIsArray($folders); + $this->assertCount(1, $folders); + $this->assertEquals('Subfolder 2', $folders[0]->getName()); + + /* Get sub folders order by sequence ascending with a bogus offset */ + $rootfolder->clearCache(); // Force retrieving sub folders from database + $folders = $rootfolder->getSubFolders('s', 'asc', 0, 4); + $this->assertIsArray($folders); + $this->assertCount(2, $folders); + $this->assertEquals('Subfolder 2', $folders[0]->getName()); + } + + /** + * Test method getSubFolders() + * + * @return void + */ + public function testGetSubFoldersSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblFolders`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->getSubFolders()); + } + + /** + * Test method isDescendant() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testIsDescendant() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subsubfolder = $subfolder->addSubFolder('Subsubfolder 1', '', $user, 1.0); + /* subsubfolder is a descendant of root folder */ + $this->assertTrue($subsubfolder->isDescendant($rootfolder)); + /* subfolder is not a descendant of subsubfolder */ + $this->assertFalse($subfolder->isDescendant($subsubfolder)); + } + + /** + * Test method isSubFolder() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testIsSubFolder() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subsubfolder = $subfolder->addSubFolder('Subsubfolder 1', '', $user, 1.0); + $this->assertTrue($rootfolder->isSubFolder($subsubfolder)); + $this->assertFalse($subsubfolder->isSubFolder($subfolder)); + } + + /** + * Test method setParent() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetParent() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subsubfolder = $subfolder->addSubFolder('Subsubfolder 1', '', $user, 1.0); + /* Add a new document for folderList checking afterwards */ + $document = self::createDocument($subsubfolder, $user, 'Document 1'); + $folderlist = $subsubfolder->getFolderList(); + $this->assertEquals(':1:2:', $folderlist); + $folderlist = $document->getFolderList(); + $this->assertEquals(':1:2:3:', $folderlist); + /* Making $subsubfolder parent of $subfolder will fail, because + * $subfolder is a parent of $subsubfolder + */ + $this->assertFalse($subfolder->setParent($subsubfolder)); + /* Moving $subsubfolder into rool folder is possible */ + $this->assertTrue($subsubfolder->setParent($rootfolder)); + /* Root folder has now two children */ + $children = $rootfolder->getSubFolders(); + $this->assertIsArray($children); + $this->assertCount(2, $children); + /* Move the folder will have changed the folder list. Check it */ + $errors = self::$dms->checkFolders(); + $this->assertIsArray($errors); + $this->assertCount(0, $errors); + $errors = self::$dms->checkDocuments(); + $this->assertIsArray($errors); + $this->assertCount(0, $errors); + } + + /** + * Test method getPath() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetPathRootOnly() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $path = $folder->getPath(); + $this->assertIsArray($path); + $this->assertCount(1, $path); + /* The only folder in the path is the root folder itself */ + $this->assertEquals(1, $path[0]->getId()); + } + + /** + * Test method getPath() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetPath() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subsubfolder = $subfolder->addSubFolder('Subsubfolder 1', '', $user, 1.0); + $path = $subsubfolder->getPath(); + $this->assertIsArray($path); + $this->assertCount(3, $path); + } + + /** + * Test method getFolderPathPlain() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetFolderPathPlain() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $path = $folder->getFolderPathPlain(); + $this->assertIsString($path); + $this->assertEquals('/ DMS', $path); + } + + /** + * Test method hasDocuments() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testHasDocuments() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $documents = $folder->hasDocuments(); + $this->assertIsInt($documents); + $this->assertEquals(0, $documents); + /* Add a new document for calling hasDocuments() afterwards */ + $document = self::createDocument($folder, $user, 'Document 1'); + $documents = $folder->hasDocuments(); + $this->assertIsInt($documents); + $this->assertEquals(1, $documents); + } + + /** + * Test method hasDocuments() + * + * @return void + */ + public function testHasDokumentsSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT count(*) as c FROM `tblDocuments`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->hasDocuments()); + } + + /** + * Test method hasDocumentByName() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testHasDocumentByName() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $res = $folder->hasDocumentByName('foo'); + $this->assertFalse($res); + /* Add a new document for calling hasDocumentByName() afterwards */ + $document = self::createDocument($folder, $user, 'Document 1'); + $res = $folder->hasDocumentByName('Document 1'); + $this->assertTrue($res); + } + + /** + * Test method hasDocumentByName() + * + * @return void + */ + public function testHasDokumentByNameSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT count(*) as c FROM `tblDocuments`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->hasDocumentByName('foo')); + } + + /** + * Test method getDocuments() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocuments() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $documents = $folder->getDocuments(); + $this->assertIsArray($documents); + $this->assertCount(0, $documents); + /* Add a new document for calling getDocuments() afterwards */ + $folder->clearCache(); + $document = self::createDocument($folder, $user, 'Document 1'); + $document = self::createDocument($folder, $user, 'Document 2'); + $documents = $folder->getDocuments(); + $this->assertIsArray($documents); + $this->assertCount(2, $documents); + $folder->clearCache(); + /* sort by name asc, limit 1, offset 0 */ + $documents = $folder->getDocuments('n', 'asc', 1); + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $this->assertEquals('Document 1', $documents[0]->getName()); + $folder->clearCache(); + /* sort by name desc, limit 1, offset 0 */ + $documents = $folder->getDocuments('n', 'desc', 1); + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $this->assertEquals('Document 2', $documents[0]->getName()); + $folder->clearCache(); + /* sort by name asc, limit 1, offset 1 */ + $documents = $folder->getDocuments('n', 'asc', 1, 1); + $this->assertIsArray($documents); + $this->assertCount(1, $documents); + $this->assertEquals('Document 2', $documents[0]->getName()); + } + + /** + * Test method getDocuments() + * + * @return void + */ + public function testGetDokumentsSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lock` FROM `tblDocuments`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->getDocuments()); + } + + /** + * Test method countChildren() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCountChildren() + { + $folder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $count = $folder->countChildren($user, 0); + $this->assertIsArray($count); + $this->assertCount(4, $count); + $this->assertEquals(0, $count['folder_count']); + $this->assertEquals(0, $count['document_count']); + /* Add some folders and documents */ + $this->createSimpleFolderStructure(); + $document = self::createDocument($folder, $user, 'Document 1'); + $count = $folder->countChildren($user, 6); + $this->assertIsArray($count); + $this->assertCount(4, $count); + $this->assertEquals(5, $count['folder_count']); + $this->assertEquals(1, $count['document_count']); + } + + /** + * Test method emptyFolder() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testEmptyFolder() + { + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + /* Add some folders and documents */ + $this->createSimpleFolderStructure(); + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $res = $rootfolder->emptyFolder(); + $this->assertTrue($res); + } + + /** + * Test method emptyFolder() on root folder + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testEmptyFolderWithCallback() + { + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + /* Add some folders and documents */ + $this->createSimpleFolderStructure(); + $document = self::createDocument($rootfolder, $user, 'Document 1'); + + /* Add the 'onPostAddUser' callback */ + $msgs = []; + $callback = function ($param, $object) use (&$msgs) { + $msgs[] = $param." ".$object->getName(). " (".$object->getId().")"; + }; + self::$dms->addCallback('onPreRemoveFolder', $callback, 'onPreRemoveFolder'); + self::$dms->addCallback('onPostRemoveFolder', $callback, 'onPostRemoveFolder'); + self::$dms->addCallback('onPreRemoveDocument', $callback, 'onPreRemoveDocument'); + self::$dms->addCallback('onPostRemoveDocument', $callback, 'onPostRemoveDocument'); + self::$dms->addCallback('onPreEmptyFolder', $callback, 'onPreEmptyFolder'); + self::$dms->addCallback('onPostEmptyFolder', $callback, 'onPostEmptyFolder'); + + $res = $rootfolder->emptyFolder(); + $this->assertTrue($res); + $this->assertIsArray($msgs); + $this->assertCount(14, $msgs); // 5 folders x 2 callbacks + 1 document x 2 callbacks + 2 emptyFolder callbacks + } + + /** + * Test method getAccessList() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAccessList() + { + /* Add some folders and documents */ + $this->createSimpleFolderStructure(); + $this->createGroupsAndUsers(); + + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + $group = self::$dms->getGroup(1); + $this->assertIsObject($group); + $subfolder = self::$dms->getFolderByName('Subfolder 1'); + $this->assertIsObject($subfolder); + $subsubfolder = self::$dms->getFolderByName('Subsubfolder 1'); + $this->assertIsObject($subsubfolder); + + /* Adding an access rule will have no effect until the inheritance + * is turned off. + */ + $subfolder->addAccess(M_NONE, $user->getId(), true); + $subfolder->addAccess(M_READWRITE, $group->getId(), false); + $accesslist = $subfolder->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['users']); + $this->assertCount(0, $accesslist['groups']); + /* Turn inheritance off */ + $res = $subfolder->setInheritAccess(false); + $this->assertTrue($res); + /* Now the access rules on $subfolder take effect */ + $accesslist = $subfolder->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(1, $accesslist['users']); + $this->assertCount(1, $accesslist['groups']); + /* get list of users/groups which no access */ + $accesslist = $subfolder->getAccessList(M_NONE, O_EQ); + $this->assertIsArray($accesslist); + $this->assertCount(1, $accesslist['users']); + $this->assertCount(0, $accesslist['groups']); + /* get list of users/groups which read+write access */ + $accesslist = $subfolder->getAccessList(M_READWRITE, O_EQ); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['users']); + $this->assertCount(1, $accesslist['groups']); + /* get list of users/groups which have at least read access */ + $accesslist = $subfolder->getAccessList(M_READ, O_GTEQ); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['users']); + $this->assertCount(1, $accesslist['groups']); + /* get list of users/groups which have at least unlimited access */ + $accesslist = $subfolder->getAccessList(M_ALL, O_GTEQ); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['users']); + $this->assertCount(0, $accesslist['groups']); + /* Subsubfolder 1 inherits from Subfolder 1 */ + $accesslist = $subsubfolder->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(1, $accesslist['users']); + $this->assertCount(1, $accesslist['groups']); + /* clear the access list */ + $res = $subfolder->clearAccessList(); + $this->assertTrue($res); + $accesslist = $subfolder->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['users']); + $this->assertCount(0, $accesslist['groups']); + /* calling getAccessList() on the $subsubfolder will still return + * the user and group, because the getParent() call in getAccessList() + * will not return the same instance like $subfolder. Hence calling + * $subfolder->clearAccessList() won't clear the accesslist of $subsubfolder's + * parent. You would have to explicitly + * clear acceslist of $subsubfolder's parent. + $res = $subsubfolder->getParent()->clearAccessList(); + $this->assertTrue($res); + $accesslist = $subsubfolder->getAccessList(); + $this->assertIsArray($accesslist); + $this->assertCount(0, $accesslist['users']); + $this->assertCount(0, $accesslist['groups']); + */ + } + + /** + * Test method addAccess() + * + * @return void + */ + public function testAddAccessWrongMode() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->addAccess(M_ANY, 1, true)); + } + + /** + * Test method addAccess() + * + * @return void + */ + public function testAddAccessSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("INSERT INTO `tblACLs`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->addAccess(M_NONE, 1, true)); + } + + /** + * Test method getAccessMode() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAccessMode() + { + /* Add some folders and documents */ + $this->createSimpleFolderStructureWithDocuments(); + $this->createGroupsAndUsers(); + + $rootfolder = SeedDMS_Core_Folder::getInstance(1, self::$dms); + $admin = self::$dms->getUser(1); + $this->assertIsObject($admin); + $this->assertTrue($admin->isAdmin()); + $guest = self::$dms->getUser(2); + $this->assertTrue($guest->isGuest()); + $user = self::$dms->getUser(3); + $this->assertIsObject($user); + if(self::$dms->version[0] == '5') + $this->assertTrue($user->getRole() == SeedDMS_Core_User::role_user); + else + $this->assertTrue($user->getRole()->getRole() == SeedDMS_Core_Role::role_user); + $joe = self::$dms->getUser(4); + $this->assertIsObject($joe); + if(self::$dms->version[0] == '5') + $this->assertTrue($joe->getRole() == SeedDMS_Core_User::role_user); + else + $this->assertTrue($joe->getRole()->getRole() == SeedDMS_Core_Role::role_user); + $sally = self::$dms->getUser(6); + $this->assertIsObject($sally); + if(self::$dms->version[0] == '5') + $this->assertTrue($sally->getRole() == SeedDMS_Core_User::role_user); + else + $this->assertTrue($sally->getRole()->getRole() == SeedDMS_Core_Role::role_user); + $group = self::$dms->getGroup(1); + $this->assertIsObject($group); + /* add guest and joe to group */ + if(!$group->isMember($guest)) { + $res = $guest->joinGroup($group); + $this->assertTrue($res); + } + if(!$group->isMember($joe)) { + $res = $joe->joinGroup($group); + $this->assertTrue($res); + } + + $subfolder1 = self::$dms->getFolderByName('Subfolder 1'); + $this->assertIsObject($subfolder1); + $subsubfolder = self::$dms->getFolderByName('Subsubfolder 1'); + $this->assertIsObject($subsubfolder); + $subfolder2 = self::$dms->getFolderByName('Subfolder 2'); + $this->assertIsObject($subfolder2); + $subfolder3 = self::$dms->getFolderByName('Subfolder 3'); + $this->assertIsObject($subfolder3); + $res = $subfolder3->setOwner($sally); + $this->assertTrue($res); + + /* Setup Subfolder 1: + * no inheritance, user has read-write access, group has unlimited access, + * default is no access + */ + $res = $subfolder1->setInheritAccess(false); + $this->assertTrue($res); + $res = $subfolder1->setDefaultAccess(M_NONE); + $this->assertTrue($res); + $res = $subfolder1->addAccess(M_READWRITE, $user->getId(), true); + $this->assertTrue($res); + $res = $subfolder1->addAccess(M_ALL, $group->getId(), false); + $this->assertTrue($res); + + /* Admin has always access mode M_ALL */ + $mode = $subfolder1->getAccessMode($admin); + $this->assertEquals(M_ALL, $mode); + /* Guest has max read access, though it's group has any access */ + $mode = $subfolder1->getAccessMode($guest); + $this->assertEquals(M_READ, $mode); + /* Joe has any access, because it's group has any access */ + $mode = $subfolder1->getAccessMode($joe); + $this->assertEquals(M_ALL, $mode); + /* Sally has no access, because it has no explicit access right and the + * default access is M_NONE. + */ + $mode = $subfolder1->getAccessMode($sally); + $this->assertEquals(M_NONE, $mode); + + /* Subfolder 3 inherits from the root folder, but sally is the owner */ + $mode = $subfolder3->getAccessMode($sally); + $this->assertEquals(M_ALL, $mode); + /* joe has just read access which is the default inherited from root */ + $mode = $subfolder3->getAccessMode($joe); + $this->assertEquals(M_READ, $mode); + + } + + /** + * Test method getFolderList() + * + * @return void + */ + public function testGetFolderListSqlFail() + { + $rootfolder = $this->getMockedRootFolder(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT `folderList` FROM `tblFolders`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $rootfolder->setDMS($dms); + $this->assertFalse($rootfolder->getFolderList()); + } + +} diff --git a/SeedDMS_Core/tests/GroupTest.php b/SeedDMS_Core/tests/GroupTest.php new file mode 100644 index 000000000..df14a13f5 --- /dev/null +++ b/SeedDMS_Core/tests/GroupTest.php @@ -0,0 +1,410 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class GroupTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + self::$dbversion = self::$dms->getDBVersion(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Create a mock group object + * + * @return SeedDMS_Core_Group + */ + protected function getMockGroup() + { + $user = $this->getMockBuilder(SeedDMS_Core_Group::class) + ->onlyMethods([]) + ->disableOriginalConstructor()->getMock(); + return $user; + } + + /** + * Create a mock group object + * + * @return SeedDMS_Core_Group + */ + protected function getGroup() + { + $group = new SeedDMS_Core_Group(1, 'foogroup', 'My comment'); + return $group; + } + + /** + * Create a mock regular user object + * + * @return SeedDMS_Core_User + */ + protected function getUser() + { + $user = new SeedDMS_Core_User(2, 'user', 'pass', 'Joe Baz', 'joe@foo.de', 'en_GB', 'bootstrap', 'My comment', SeedDMS_Core_User::role_user); + return $user; + } + + /** + * Test method isType() + * + * @return void + */ + public function testIsType() + { + $group = $this->getGroup(); + $this->assertTrue($group->isType('group')); + } + + /** + * Test method getName() + * + * @return void + */ + public function testGetName() + { + $group = $this->getGroup(); + $this->assertEquals('foogroup', $group->getName()); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetName() + { + $group = self::$dms->addGroup('Group', ''); + $ret = $group->setName('foo'); + $this->assertTrue($ret); + $name = $group->getName(); + $this->assertEquals('foo', $name); + /* Setting an empty name must fail */ + $ret = $group->setName(' '); + $this->assertFalse($ret); + } + + /** + * Test method setName() + * + * @return void + */ + public function testSetNameSqlFail() + { + $group = $this->getGroup(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblGroups` SET `name`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $group->setDMS($dms); + $this->assertFalse($group->setName('my name')); + } + + /** + * Test method getComment() + * + * @return void + */ + public function testGetComment() + { + $group = $this->getGroup(); + $this->assertEquals('My comment', $group->getComment()); + } + + /** + * Test method getComment() and setComment() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetComment() + { + $group = self::$dms->addGroup('Group', ''); + $ret = $group->setComment('foo'); + $this->assertTrue($ret); + $comment = $group->getComment(); + $this->assertEquals('foo', $comment); + } + + /** + * Test method setComment() + * + * @return void + */ + public function testSetCommentSqlFail() + { + $group = $this->getGroup(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblGroups` SET `comment`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $group->setDMS($dms); + $this->assertFalse($group->setComment('my comment')); + } + + /** + * Test method getUsers() + * + * @return void + */ + public function testGetUsersSqlFail() + { + $group = $this->getGroup(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT `tblUsers`.* FROM `tblUsers`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $group->setDMS($dms); + $this->assertFalse($group->getUsers()); + } + + /** + * Test method addUser(), isMember(), and removeUser() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAddAndRemoveUser() + { + $group = self::$dms->addGroup('Group', ''); + if(self::$dms->version[0] == '5') + $role = SeedDMS_Core_User::role_user; + else { + $role = SeedDMS_Core_Role::getInstance(3, self::$dms); + $this->assertIsObject($role); + $this->assertEquals($role->getRole(), SeedDMS_Core_Role::role_user); + } + $user1 = self::$dms->addUser('joe', 'pass', 'Joe Foo', 'joe@foo.de', 'en_GB', 'bootstrap', 'My comment', $role); + $user2 = self::$dms->addUser('sally', 'pass', 'Sally Foo', 'sally@foo.de', 'en_GB', 'bootstrap', 'My comment', $role); + + /* Add user1 and user2. user2 is also a manager */ + $ret = $group->addUser($user1); + $this->assertTrue($ret); + $ret = $group->addUser($user2, true); + $this->assertTrue($ret); + + $users = $group->getUsers(); + $this->assertIsArray($users); + $this->assertCount(2, $users); + + $ret = $group->removeUser($user1); + $this->assertTrue($ret); + $users = $group->getUsers(); + $this->assertIsArray($users); + $this->assertCount(1, $users); + } + + /** + * Test method isMember(), toggleManager() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testIsMember() + { + $group = self::$dms->addGroup('Group', ''); + $user1 = self::$dms->addUser('joe', 'pass', 'Joe Foo', 'joe@foo.de', 'en_GB', 'bootstrap', 'My comment'); + $user2 = self::$dms->addUser('sally', 'pass', 'Sally Foo', 'sally@foo.de', 'en_GB', 'bootstrap', 'My comment'); + + /* Add user1 and user2. user2 is also a manager */ + $ret = $group->addUser($user1); + $this->assertTrue($ret); + $ret = $group->addUser($user2, true); + $this->assertTrue($ret); + + /* user1 is a member but not a manager */ + $ret = $group->isMember($user1); + $this->assertTrue($ret); + $ret = $group->isMember($user1, true); + $this->assertFalse($ret); + + /* user2 is a member and a manager */ + $ret = $group->isMember($user2, true); + $this->assertTrue($ret); + } + + /** + * Test method toggleManager() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testToggleManager() + { + $group = self::$dms->addGroup('Group', ''); + $user1 = self::$dms->addUser('joe', 'pass', 'Joe Foo', 'joe@foo.de', 'en_GB', 'bootstrap', 'My comment'); + + /* Add user1 */ + $ret = $group->addUser($user1); + $this->assertTrue($ret); + + /* user1 is a member but not a manager */ + $ret = $group->isMember($user1); + $this->assertTrue($ret); + $ret = $group->isMember($user1, true); + $this->assertFalse($ret); + + /* Toggle manager mode of user 1 and check again */ + $ret = $group->toggleManager($user1); + $ret = $group->isMember($user1, true); + $this->assertTrue($ret); + } + + /** + * Test method getUsers() + * + * @return void + */ + public function testGetUsers() + { + $group = $this->getGroup(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + if(self::$dbversion['major'] == 6) { + $db->expects($this->exactly(2)) + ->method('getResultArray') + ->withConsecutive([$this->stringContains("`tblGroupMembers`.`groupID` = '".$group->getId()."'")], [$this->stringContains("SELECT * FROM `tblRoles` WHERE `id` =")]) + ->willReturnOnConsecutiveCalls(array(array('id'=>2, 'login'=>'user', 'pwd'=>'pass', 'fullName'=>'Joe Baz', 'email'=>'joe@foo.de', 'language'=>'en_GB', 'theme'=>'bootstrap', 'comment'=>'', 'role'=>SeedDMS_Core_User::role_user, 'hidden'=>0, 'role'=>1)), array('id'=>1, 'name'=>'role', 'role'=>1, 'noaccess'=>'')); + } else { + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("`tblGroupMembers`.`groupID` = '".$group->getId()."'")) + ->willReturn(array(array('id'=>2, 'login'=>'user', 'pwd'=>'pass', 'fullName'=>'Joe Baz', 'email'=>'joe@foo.de', 'language'=>'en_GB', 'theme'=>'bootstrap', 'comment'=>'', 'role'=>SeedDMS_Core_User::role_user, 'hidden'=>0, 'role'=>1))); + } + $dms = new SeedDMS_Core_DMS($db, ''); + + $group->setDMS($dms); + $users = $group->getUsers(); + $this->assertIsArray($users); + $this->assertCount(1, $users); + } + + /** + * Test method getManagers() + * + * @return void + */ + public function testGetManagers() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + if(self::$dbversion['major'] == 6) { + $db->expects($this->exactly(2)) + ->method('getResultArray') + ->withConsecutive([$this->stringContains("`manager` = 1")], [$this->stringContains("SELECT * FROM `tblRoles` WHERE `id` =")]) + ->willReturnOnConsecutiveCalls(array(array('id'=>2, 'login'=>'user', 'pwd'=>'pass', 'fullName'=>'Joe Baz', 'email'=>'joe@foo.de', 'language'=>'en_GB', 'theme'=>'bootstrap', 'comment'=>'', 'role'=>SeedDMS_Core_User::role_user, 'hidden'=>0, 'role'=>1)), array('id'=>1, 'name'=>'role', 'role'=>1, 'noaccess'=>'')); + } else { + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains('`manager` = 1')) + ->willReturn(array(array('id'=>2, 'login'=>'user', 'pwd'=>'pass', 'fullName'=>'Joe Baz', 'email'=>'joe@foo.de', 'language'=>'en_GB', 'theme'=>'bootstrap', 'comment'=>'', 'role'=>SeedDMS_Core_User::role_user, 'hidden'=>0))); + } + $dms = new SeedDMS_Core_DMS($db, ''); + + $group = $this->getGroup(); + $group->setDMS($dms); + $managers = $group->getManagers(); + $this->assertIsArray($managers); + $this->assertCount(1, $managers); + } + + /** + * Test method getNotifications() + * + * @return void + */ + public function testGetNotifications() + { + $group = $this->getGroup(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("WHERE `tblNotify`.`groupID` = ".$group->getId())) + ->willReturn(array(array('target'=>2, 'targetType'=>'0', 'userID'=>0, 'groupID'=>$group->getId()))); + $dms = new SeedDMS_Core_DMS($db, ''); + $group->setDMS($dms); + $notifications = $group->getNotifications(); + $this->assertIsArray($notifications); + $this->assertCount(1, $notifications); + $this->assertInstanceOf(SeedDMS_Core_Notification::class, $notifications[0]); + } + + /** + * Test method getNotifications() with target type + * + * @return void + */ + public function testGetNotificationsWithTargetType() + { + $group = $this->getGroup(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("WHERE `tblNotify`.`groupID` = ".$group->getId()." AND `tblNotify`.`targetType` = 1")) + ->willReturn(array(array('target'=>2, 'targetType'=>'1', 'userID'=>0, 'groupID'=>$group->getId()))); + $dms = new SeedDMS_Core_DMS($db, ''); + $group->setDMS($dms); + $notifications = $group->getNotifications(1); + $this->assertIsArray($notifications); + $this->assertCount(1, $notifications); + $this->assertInstanceOf(SeedDMS_Core_Notification::class, $notifications[0]); + } + + +} diff --git a/SeedDMS_Core/tests/KeywordCategoryTest.php b/SeedDMS_Core/tests/KeywordCategoryTest.php new file mode 100644 index 000000000..e22d138ed --- /dev/null +++ b/SeedDMS_Core/tests/KeywordCategoryTest.php @@ -0,0 +1,147 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * User test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class KeywordCategoryTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetName() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $cat = self::$dms->addKeywordCategory($user->getId(), 'Category 1'); + $name = $cat->getName(); + $ret = $cat->setName('foo'); + $this->assertTrue($ret); + $name = $cat->getName(); + $this->assertEquals('foo', $name); + $ret = $cat->setName(' '); + $this->assertFalse($ret); + } + + /** + * Test method getOwner() and setOwner() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetOwner() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $guest = SeedDMS_Core_User::getInstance(2, self::$dms); + $cat = self::$dms->addKeywordCategory($user->getId(), 'Category 1'); + $this->assertIsObject($cat); + $ret = $cat->setOwner($guest); + $this->assertTrue($ret); + $owner = $cat->getOwner(); + $this->assertEquals(2, $owner->getId()); + $ret = $cat->setOwner(null); + $this->assertFalse($ret); + } + + /** + * Test method addKeywordList() and editKeywordList(), getKeywordLists(), removeKeywordList() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetSetEditAndRemoveKeywordList() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $cat = self::$dms->addKeywordCategory($user->getId(), 'Category 1'); + $this->assertIsObject($cat); + $ret = $cat->addKeywordList('foo'); + $this->assertTrue($ret); + $ret = $cat->addKeywordList('bar'); + $this->assertTrue($ret); + $list = $cat->getKeywordLists(); + $this->assertIsArray($list); + $this->assertCount(2, $list); + $ret = $cat->editKeywordList(1, 'baz'); + $this->assertTrue($ret); + + $ret = $cat->removeKeywordList(1); + $this->assertTrue($ret); + $list = $cat->getKeywordLists(); + $this->assertIsArray($list); + $this->assertCount(1, $list); + } + + /** + * Test method addKeywordCategory() and remove() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAndAndRemoveKeywordCategory() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $cat = self::$dms->addKeywordCategory($user->getId(), 'Category 1'); + $this->assertIsObject($cat); + $ret = $cat->addKeywordList('foo'); + $this->assertTrue($ret); + $ret = $cat->addKeywordList('bar'); + $this->assertTrue($ret); + $ret = $cat->remove(); + $this->assertTrue($ret); + } +} diff --git a/SeedDMS_Core/tests/ReviewApprovalTest.php b/SeedDMS_Core/tests/ReviewApprovalTest.php new file mode 100644 index 000000000..aea040e39 --- /dev/null +++ b/SeedDMS_Core/tests/ReviewApprovalTest.php @@ -0,0 +1,477 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class ReviewApprovalTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method addIndReviewer(), addGrpReviewer(), verifyStatus(), + * getReviewStatus(), removeReview(), delIndReviewer() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testReviewDocumentByUserAndGroup() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + + /* Add a new user who will be the reviewer */ + $reviewer = self::$dms->addUser('reviewer', 'reviewer', 'Reviewer One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($reviewer); + + /* Add a new group which will be the reviewer */ + $reviewergrp = self::$dms->addGroup('reviewer', ''); + $this->assertIsObject($reviewergrp); + + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $content = $document->getLatestContent(); + $this->assertIsObject($content); + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + /* A missing reviewer or user causes an error */ + $ret = $content->addIndReviewer($reviewer, null); + $this->assertEquals(-1, $ret); + + /* A missing reviewer or user causes an error */ + $ret = $content->addIndReviewer(null, $user); + $this->assertEquals(-1, $ret); + + /* Adding a group instead of a user causes an error */ + $ret = $content->addIndReviewer($reviewergrp, $user); + $this->assertEquals(-1, $ret); + + /* Finally add the reviewer */ + $ret = $content->addIndReviewer($reviewer, $user); + $this->assertGreaterThan(0, $ret); + + /* Adding the user again will yield in an error */ + $ret = $content->addIndReviewer($reviewer, $user); + $this->assertEquals(-3, $ret); + + /* Needs to call verifyStatus() in order to recalc the status */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_DRAFT_REV, $newstatus); + + /* Get all reviews */ + $reviewstatus = $content->getReviewStatus(); + $this->assertIsArray($reviewstatus); + $this->assertCount(1, $reviewstatus); + + /* Get list of individual und group reviewers */ + $reviewers = $content->getReviewers(); + $this->assertIsArray($reviewers); + $this->assertCount(2, $reviewers); + $this->assertCount(1, $reviewers['i']); + $this->assertCount(0, $reviewers['g']); +/* + $db = self::$dms->getDB(); + $db->createTemporaryTable("ttreviewid", true); + $queryStr = "SELECT * FROM ttreviewid"; + $recs = $db->getResultArray($queryStr); + echo $db->getErrorMsg(); + var_dump($recs); +*/ + + /* A missing reviewer or user causes an error */ + $ret = $content->addGrpReviewer($reviewergrp, null); + $this->assertEquals(-1, $ret); + + /* A missing reviewer or user causes an error */ + $ret = $content->addGrpReviewer(null, $user); + $this->assertEquals(-1, $ret); + + /* Adding a user instead of a group causes an error */ + $ret = $content->addGrpReviewer($reviewer, $user); + $this->assertEquals(-1, $ret); + + /* Finally add the reviewer */ + $ret = $content->addGrpReviewer($reviewergrp, $user); + $this->assertGreaterThan(0, $ret); + $groupstatus = $reviewergrp->getReviewStatus(); + + /* Adding the group again will yield in an error */ + $ret = $content->addGrpReviewer($reviewergrp, $user); + $this->assertEquals(-3, $ret); + + /* Get all reviews */ + $reviewstatus = $content->getReviewStatus(); + $this->assertIsArray($reviewstatus); + $this->assertCount(2, $reviewstatus); + + /* Get list of individual und group reviewers */ + $reviewers = $content->getReviewers(); + $this->assertIsArray($reviewers); + $this->assertCount(2, $reviewers); + $this->assertCount(1, $reviewers['i']); + $this->assertCount(1, $reviewers['g']); + + $userstatus = $reviewer->getReviewStatus(); + $groupstatus = $reviewergrp->getReviewStatus(); + + /* There should be two log entries, one for each reviewer */ + $reviewlog = $content->getReviewLog(5); + $this->assertIsArray($reviewlog); + $this->assertCount(2, $reviewlog); + + /* Adding a review without a user of reviewer causes an error */ + $ret = $content->setReviewByInd($reviewer, null, S_LOG_ACCEPTED, 'Comment of individual reviewer'); + $this->assertEquals(-1, $ret); + $ret = $content->setReviewByInd(null, $user, S_LOG_ACCEPTED, 'Comment of individual reviewer'); + $this->assertEquals(-1, $ret); + + /* Adding a review as an individual but passing a group causes an error */ + $ret = $content->setReviewByInd($reviewergrp, $user, S_LOG_ACCEPTED, 'Comment of individual reviewer'); + $this->assertEquals(-1, $ret); + + /* Individual reviewer reviews document */ + $ret = $content->setReviewByInd($reviewer, $user, S_LOG_ACCEPTED, 'Comment of individual reviewer'); + $this->assertIsInt(0, $ret); + $this->assertGreaterThan(0, $ret); + + /* Get the last 5 review log entries (actually there are just 3 now) */ + $reviewlog = $content->getReviewLog(5); + $this->assertIsArray($reviewlog); + $this->assertCount(3, $reviewlog); + $this->assertEquals('Comment of individual reviewer', $reviewlog[0]['comment']); + $this->assertEquals(1, $reviewlog[0]['status']); + + /* Needs to call verifyStatus() in order to recalc the status. + * It must not be changed because the group reviewer has not done the + * review. + */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_DRAFT_REV, $newstatus); + + /* Adding a review without a user of reviewer causes an error */ + $ret = $content->setReviewByGrp($reviewergrp, null, S_LOG_ACCEPTED, 'Comment of group reviewer'); + $this->assertEquals(-1, $ret); + $ret = $content->setReviewByGrp(null, $user, S_LOG_ACCEPTED, 'Comment of group reviewer'); + $this->assertEquals(-1, $ret); + + /* Adding a review as an group but passing a user causes an error */ + $ret = $content->setReviewByGrp($reviewer, $user, S_LOG_ACCEPTED, 'Comment of group reviewer'); + $this->assertEquals(-1, $ret); + + /* Group reviewer reviews document */ + $ret = $content->setReviewByGrp($reviewergrp, $user, S_LOG_ACCEPTED, 'Comment of group reviewer'); + $this->assertIsInt(0, $ret); + $this->assertGreaterThan(0, $ret); + + /* Get the last 5 review log entries (actually there are just 4 now) */ + $reviewlog = $content->getReviewLog(5); + $this->assertIsArray($reviewlog); + $this->assertCount(4, $reviewlog); + $this->assertEquals('Comment of group reviewer', $reviewlog[0]['comment']); + $this->assertEquals(1, $reviewlog[0]['status']); + + /* Now the document has received all reviews */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_RELEASED, $newstatus); + + /* Remove the last review of the user */ + $userstatus = $reviewer->getReviewStatus($document->getId(), $content->getVersion()); + $this->assertIsArray($userstatus); + $this->assertCount(2, $userstatus); + $this->assertCount(1, $userstatus['indstatus']); + $ret = $content->removeReview($userstatus['indstatus'][$document->getId()]['reviewID'], $user, 'Undo review'); + $this->assertTrue($ret); + + /* Get the last 8 review log entries (actually there are just 5 now) */ + $reviewlog = $content->getReviewLog(8); + $this->assertIsArray($reviewlog); + $this->assertCount(5, $reviewlog); + $this->assertEquals('Undo review', $reviewlog[0]['comment']); + $this->assertEquals(0, $reviewlog[0]['status']); + + /* Now the document must be back in draft mode */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_DRAFT_REV, $newstatus); + + /* Removing the user as a reviewer completly will release the + * document again, because the group reviewer became the only + * reviewer and has done the review already. + */ + $ret = $content->delIndReviewer($reviewer, $user, 'Reviewer removed'); + $this->assertIsInt($ret); + $this->assertEquals(0, $ret); + + /* Get the last 8 review log entries (actually there are just 6 now) */ + $reviewlog = $content->getReviewLog(8); + $this->assertIsArray($reviewlog); + $this->assertCount(6, $reviewlog); + $this->assertEquals('Reviewer removed', $reviewlog[0]['comment']); + $this->assertEquals(-2, $reviewlog[0]['status']); + + /* Now the document will be released again */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_RELEASED, $newstatus); + } + + /** + * Test method addIndApprover(), addGrpApprover(), verifyStatus(), + * getApprovalStatus(), removeApproval(), delIndApprover() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testApproveDocumentByUserAndGroup() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + + /* Add a new user who will be the approver */ + $approver = self::$dms->addUser('approver', 'approver', 'Approver One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($approver); + + /* Add a new group which will be the approver */ + $approvergrp = self::$dms->addGroup('approver', ''); + $this->assertIsObject($approvergrp); + + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $content = $document->getLatestContent(); + $this->assertIsObject($content); + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + /* A missing approver or user causes an error */ + $ret = $content->addIndApprover($approver, null); + $this->assertEquals(-1, $ret); + + /* A missing approver or user causes an error */ + $ret = $content->addIndApprover(null, $user); + $this->assertEquals(-1, $ret); + + /* Adding a group instead of a user causes an error */ + $ret = $content->addIndApprover($approvergrp, $user); + $this->assertEquals(-1, $ret); + + /* Finally add the reviewer */ + $ret = $content->addIndApprover($approver, $user); + $this->assertGreaterThan(0, $ret); + + /* Adding the user again will yield in an error */ + $ret = $content->addIndApprover($approver, $user); + $this->assertEquals(-3, $ret); + + /* Needs to call verifyStatus() in order to recalc the status */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_DRAFT_APP, $newstatus); + + /* Get all approvals */ + $approvalstatus = $content->getApprovalStatus(); + $this->assertIsArray($approvalstatus); + $this->assertCount(1, $approvalstatus); + + /* Get list of individual und group approvers */ + $approvers = $content->getApprovers(); + $this->assertIsArray($approvers); + $this->assertCount(2, $approvers); + $this->assertCount(1, $approvers['i']); + $this->assertCount(0, $approvers['g']); + + /* A missing approver or user causes an error */ + $ret = $content->addGrpApprover($approvergrp, null); + $this->assertEquals(-1, $ret); + + /* A missing approver or user causes an error */ + $ret = $content->addGrpApprover(null, $user); + $this->assertEquals(-1, $ret); + + /* Adding a user instead of a group causes an error */ + $ret = $content->addGrpApprover($approver, $user); + $this->assertEquals(-1, $ret); + + /* Finally add the reviewer */ + $ret = $content->addGrpApprover($approvergrp, $user); + $this->assertGreaterThan(0, $ret); + $groupstatus = $approvergrp->getApprovalStatus(); + + /* Adding the group again will yield in an error */ + $ret = $content->addGrpApprover($approvergrp, $user); + $this->assertEquals(-3, $ret); + + /* Get all approvals */ + $approvalstatus = $content->getApprovalStatus(); + $this->assertIsArray($approvalstatus); + $this->assertCount(2, $approvalstatus); + + /* Get list of individual und group approvers */ + $approvers = $content->getApprovers(); + $this->assertIsArray($approvers); + $this->assertCount(2, $approvers); + $this->assertCount(1, $approvers['i']); + $this->assertCount(1, $approvers['g']); + + $userstatus = $approver->getApprovalStatus(); + $groupstatus = $approvergrp->getApprovalStatus(); + + /* There should be two log entries, one for each approver */ + $approvallog = $content->getApproveLog(5); + $this->assertIsArray($approvallog); + $this->assertCount(2, $approvallog); + + /* Adding a approval without a user of approver causes an error */ + $ret = $content->setApprovalByInd($approver, null, S_LOG_ACCEPTED, 'Comment of individual approver'); + $this->assertEquals(-1, $ret); + $ret = $content->setApprovalByInd(null, $user, S_LOG_ACCEPTED, 'Comment of individual approver'); + $this->assertEquals(-1, $ret); + + /* Adding a approval as an individual but passing a group causes an error */ + $ret = $content->setApprovalByInd($approvergrp, $user, S_LOG_ACCEPTED, 'Comment of individual approver'); + $this->assertEquals(-1, $ret); + + /* Individual approver approvals document */ + $ret = $content->setApprovalByInd($approver, $user, S_LOG_ACCEPTED, 'Comment of individual approver'); + $this->assertIsInt(0, $ret); + $this->assertGreaterThan(0, $ret); + + /* Get the last 5 approval log entries (actually there are just 3 now) */ + $approvallog = $content->getApproveLog(5); + $this->assertIsArray($approvallog); + $this->assertCount(3, $approvallog); + $this->assertEquals('Comment of individual approver', $approvallog[0]['comment']); + $this->assertEquals(1, $approvallog[0]['status']); + + /* Needs to call verifyStatus() in order to recalc the status. + * It must not be changed because the group approver has not done the + * approval. + */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_DRAFT_APP, $newstatus); + + /* Adding a approval without a user of approver causes an error */ + $ret = $content->setApprovalByGrp($approvergrp, null, S_LOG_ACCEPTED, 'Comment of group approver'); + $this->assertEquals(-1, $ret); + $ret = $content->setApprovalByGrp(null, $user, S_LOG_ACCEPTED, 'Comment of group approver'); + $this->assertEquals(-1, $ret); + + /* Adding a approval as an group but passing a user causes an error */ + $ret = $content->setApprovalByGrp($approver, $user, S_LOG_ACCEPTED, 'Comment of group approver'); + $this->assertEquals(-1, $ret); + + /* Group approver approvals document */ + $ret = $content->setApprovalByGrp($approvergrp, $user, S_LOG_ACCEPTED, 'Comment of group approver'); + $this->assertIsInt(0, $ret); + $this->assertGreaterThan(0, $ret); + + /* Get the last 5 approval log entries (actually there are just 4 now) */ + $approvallog = $content->getApproveLog(5); + $this->assertIsArray($approvallog); + $this->assertCount(4, $approvallog); + $this->assertEquals('Comment of group approver', $approvallog[0]['comment']); + $this->assertEquals(1, $approvallog[0]['status']); + + /* Now the document has received all approvals */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_RELEASED, $newstatus); + + /* Remove the last approval of the user */ + $userstatus = $approver->getApprovalStatus($document->getId(), $content->getVersion()); + $this->assertIsArray($userstatus); + $this->assertCount(2, $userstatus); + $this->assertCount(1, $userstatus['indstatus']); + $ret = $content->removeApproval($userstatus['indstatus'][$document->getId()]['approveID'], $user, 'Undo approval'); + $this->assertTrue($ret); + + /* Get the last 8 approval log entries (actually there are just 5 now) */ + $approvallog = $content->getApproveLog(8); + $this->assertIsArray($approvallog); + $this->assertCount(5, $approvallog); + $this->assertEquals('Undo approval', $approvallog[0]['comment']); + $this->assertEquals(0, $approvallog[0]['status']); + + /* Now the document must be back in draft mode */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_DRAFT_APP, $newstatus); + + /* Removing the user as a approver completly will release the + * document again, because the group approver became the only + * approver and has done the approval already. + */ + $ret = $content->delIndApprover($approver, $user, 'Approver removed'); + $this->assertIsInt($ret); + $this->assertEquals(0, $ret); + + /* Get the last 8 approval log entries (actually there are just 6 now) */ + $approvallog = $content->getApproveLog(8); + $this->assertIsArray($approvallog); + $this->assertCount(6, $approvallog); + $this->assertEquals('Approver removed', $approvallog[0]['comment']); + $this->assertEquals(-2, $approvallog[0]['status']); + + /* Now the document will be released again */ + $newstatus = $content->verifyStatus(false, $user); + $this->assertIsInt($newstatus); + $this->assertEquals(S_RELEASED, $newstatus); + } +} diff --git a/SeedDMS_Core/tests/SeedDmsBase.php b/SeedDMS_Core/tests/SeedDmsBase.php new file mode 100644 index 000000000..dc8f7e920 --- /dev/null +++ b/SeedDMS_Core/tests/SeedDmsBase.php @@ -0,0 +1,365 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +namespace PHPUnit\Framework; + +use PHPUnit\Framework\TestCase; + +/** + * Database test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class SeedDmsTest extends TestCase +{ + + public static $dbh; + + public static $dms; + + public static $contentdir; + + public static $dbversion; + + /** + * Create a sqlite database in memory + * + * @return void + */ + public static function createInMemoryDatabase(): object + { + $dbh = new \SeedDMS_Core_DatabaseAccess('sqlite', '', '', '', ':memory:'); + $dbh->connect(); + $queries = file_get_contents(getenv("SEEDDMS_CORE_SQL")); + // generate SQL query + $queries = explode(";", $queries); + + // execute queries + $errorMsg = ''; + foreach ($queries as $query) { + //echo $query; + $query = trim($query); + if (!empty($query)) { + $dbh->getResult($query); + + if ($dbh->getErrorNo() != 0) { + //echo $dbh->getErrorMsg()."\n"; + $errorMsg .= $dbh->getErrorMsg()."\n"; + } + } + } + return $dbh; + } + + /** + * Create a mocked root folder object + * + * @return \SeedDMS_Core_Folder + */ + protected function getMockedRootFolder($id=1, $name='DMS') + { + $folder = new \SeedDMS_Core_Folder($id, $name, 0, 'DMS root', time(), 1, 0, 0, 0.0); + return $folder; + } + + /** + * Create a mocked document object + * + * @return \SeedDMS_Core_Document + */ + protected function getMockedDocument($id=1, $name='Document') + { + $document = new \SeedDMS_Core_Document($id, $name, '', time(), null, 1, 1, 1, M_READ, 0, '', 1.0); + return $document; + } + + /** + * Create a mocked user object + * + * @return \SeedDMS_Core_User + */ + protected function getMockedUser() + { + $user = new \SeedDMS_Core_User(1, 'login', '', 'New User', 'email@seeddms.org', 'de_DE', 'bootstrap', '', null); + return $user; + } + + /** + * Create a temporary file with random content and the given length. + * + * @param integer $length length of file + * + * @return string name of temporary file + */ + protected static function createTempFile($length=200, $dir='') + { + if($tmpfname = @tempnam($dir ? $dir : sys_get_temp_dir(), 'foo')) { + file_put_contents($tmpfname, substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', (int) ceil($length/strlen($x)) )),1,$length)); + return $tmpfname; + } else + return false; + } + + /** + * Create a temporary directory with random name in systems temp dir. + * + * @param integer $mode access mode of new directory + * + * @return string name of temporary directory + */ + protected static function createTempDir(string $dir = null, int $mode = 0700): string { + /* Use the system temp dir by default. */ + if (is_null($dir)) { + $dir = sys_get_temp_dir(); + } + + do { $tmp = $dir . '/' . mt_rand(); } + while (!@mkdir($tmp, $mode)); + return $tmp; + } + + /** + * Create a simple document. + * + * @param \SeedDMS_Core_Folder $parent parent folder + * @param \SeedDMS_Core_User $owner owner of document + * @param string $name name of document + * @param integer $length length of file + * + * @return string name of temporary file + */ + protected static function createDocument($parent, $owner, $name, $length=200) + { + $filename = self::createTempFile($length); + list($document, $res) = $parent->addDocument( + $name, // name + '', // comment + null, // no expiration + $owner, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file1.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0 // sequence + ); + unlink($filename); + return $document; + } + + /** + * Create a simple folder structure without documents + * + * DMS root -+- Subfolder 1 -+- Subsubfolder 1 -+- Subsubsubfolder 1 + * | + * +- Subfolder 2 + * | + * +- Subfolder 3 + * + * The sequence field of Subfolder x is: + * Subfolder 1: 2.0 + * Subfolder 2: 1.0 + * Subfolder 1: 0.5 + * + * @return void + */ + protected static function createSimpleFolderStructure() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + /* Set up a folder structure */ + $subfolder = $rootfolder->addSubFolder('Subfolder 1', '', $user, 2.0); + $subsubfolder = $subfolder->addSubFolder('Subsubfolder 1', '', $user, 1.0); + $subsubsubfolder = $subsubfolder->addSubFolder('Subsubsubfolder 1', '', $user, 1.0); + $rootfolder->addSubFolder('Subfolder 2', '', $user, 1.0); + $rootfolder->addSubFolder('Subfolder 3', '', $user, 0.5); + } + + /** + * Create a simple folder structure with documents + * + * Creates the same folder structure like createSimpleFolderStructure() + * but adds 30 documents to 'Subfolder 1'. They are named 'Document 1' + * to 'Document 30'. + * + * @return void + */ + protected static function createSimpleFolderStructureWithDocuments() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + self::createSimpleFolderStructure(); + /* Add documents to 'Subfolder 1' */ + $subfolder = self::$dms->getFolderByName('Subfolder 1'); + for ($i=1; $i<=15; $i++) { + $filename = self::createTempFile(200); + list($document, $res) = $subfolder->addDocument( + 'Document 1-'.$i, // name + '', // comment + null, + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file-1-'.$i.'.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0+$i // sequence + ); + unlink($filename); + } + /* Add documents to 'Subfolder 2' */ + $subfolder = self::$dms->getFolderByName('Subfolder 2'); + for ($i=1; $i<=15; $i++) { + $filename = self::createTempFile(200); + list($document, $res) = $subfolder->addDocument( + 'Document 2-'.$i, // name + '', // comment + null, + $user, // owner + '', // keywords + [], // categories + $filename, // name of file + 'file-2-'.$i.'.txt', // original file name + '.txt', // file type + 'text/plain', // mime type + 1.0+$i // sequence + ); + unlink($filename); + } + } + + /** + * Create two groups with 3 users each + * The groups are named 'Group 1' and 'Group 2'. The users in Group 1 + * are named 'User-1-1', 'User-1-2', 'User-1-3'. The users in Group 2 + * are named 'User-2-1', 'User-2-2', 'User-2-3'. + * The login name is the lower case of the name. + * + * @return void + */ + protected static function createGroupsAndUsers() + { + for($i=1; $i<=2; $i++) { + $group = self::$dms->addGroup('Group '.$i, ''); + for($j=1; $j<=3; $j++) { + $user = self::$dms->addUser('user-'.$i.'-'.$j, '', 'User '.$j.' in group '.$i, 'user@seeddms.org', 'en_GB', 'bootstrap', ''); + $user->joinGroup($group); + } + } + } + + /** + * Creates a workflow with two transitions identical to the traditional + * workflow + * + * NR --- review --> NA -+- approve --> RL + * +- reject --> RJ | + * +- reject ---> RJ + * + * States: + * NR = needs review + * NA = needs approval + * RL = released + * RJ = rejected + * + * Actions: + * review + * approve + * reject + * + * Transitions: + * NR -- review -> NA maybe done by reviewer + * NR -- reject -> RJ maybe done by reviewer + * NA -- approve -> RL maybe done by approver + * NA -- reject -> RJ maybe done by approver + */ + protected function createWorkflow(\SeedDMS_Core_User $reviewer, \SeedDMS_Core_User $approver): \SeedDMS_Core_Workflow + { + /* Create workflow states */ + $ws_nr = self::$dms->addWorkflowState('needs review', S_IN_WORKFLOW); + $ws_na = self::$dms->addWorkflowState('needs approval', S_IN_WORKFLOW); + $ws_rl = self::$dms->addWorkflowState('released', S_RELEASED); + $ws_rj = self::$dms->addWorkflowState('rejected', S_REJECTED); + + /* Create workflow actions */ + $wa_rv = self::$dms->addWorkflowAction('review', S_IN_WORKFLOW); + $wa_rj = self::$dms->addWorkflowAction('reject', S_REJECTED); + $wa_ap = self::$dms->addWorkflowAction('approve', S_RELEASED); + + /* Create a workflow which starts in state 'needs review' */ + $workflow = self::$dms->addWorkflow('traditional workflow', $ws_nr); + /* Add transition NR -- review -> NA */ + $wt_nr_na = $workflow->addTransition($ws_nr, $wa_rv, $ws_na, [$reviewer], []); + /* Add transition NR -- review -> RJ */ + $wt_nr_rj = $workflow->addTransition($ws_nr, $wa_rj, $ws_rj, [$reviewer], []); + /* Add transition NA -- approve -> RL */ + $wt_na_rl = $workflow->addTransition($ws_na, $wa_ap, $ws_rl, [$approver], []); + /* Add transition NA -- reject -> RJ */ + $wt_na_rj = $workflow->addTransition($ws_na, $wa_rj, $ws_rj, [$approver], []); + + return $workflow; + } + + /** + * Creates a workflow with one transitions for approving a document + * + * NA -+- approve --> RL + * | + * +- reject ---> RJ + * + * States: + * NA = needs approval + * RL = released + * RJ = rejected + * + * Actions: + * approve + * reject + * + * Transitions: + * NA -- approve -> RL maybe done by approver + * NA -- reject -> RJ maybe done by approver + */ + protected function createSimpleWorkflow(\SeedDMS_Core_User $approver): \SeedDMS_Core_Workflow + { + /* Create workflow states */ + $ws_na = self::$dms->addWorkflowState('simple needs approval', S_IN_WORKFLOW); + $ws_rl = self::$dms->addWorkflowState('simple released', S_RELEASED); + $ws_rj = self::$dms->addWorkflowState('simple rejected', S_REJECTED); + + /* Create workflow actions */ + $wa_rj = self::$dms->addWorkflowAction('simple reject', S_REJECTED); + $wa_ap = self::$dms->addWorkflowAction('simple approve', S_RELEASED); + + /* Create a workflow which starts in state 'needs approval' */ + $workflow = self::$dms->addWorkflow('simple workflow', $ws_na); + /* Add transition NA -- approve -> RL */ + $wt_na_rl = $workflow->addTransition($ws_na, $wa_ap, $ws_rl, [$approver], []); + /* Add transition NA -- reject -> RJ */ + $wt_na_rj = $workflow->addTransition($ws_na, $wa_rj, $ws_rj, [$approver], []); + + return $workflow; + } + +} + diff --git a/SeedDMS_Core/tests/UserTest.php b/SeedDMS_Core/tests/UserTest.php new file mode 100644 index 000000000..6d2e6f208 --- /dev/null +++ b/SeedDMS_Core/tests/UserTest.php @@ -0,0 +1,1679 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * User test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class UserTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + self::$dbversion = self::$dms->getDBVersion(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Create a mock admin user object + * + * @return SeedDMS_Core_User + */ + protected function getAdminUser() + { + $user = new SeedDMS_Core_User(1, 'admin', 'pass', 'Joe Foo', 'baz@foo.de', 'en_GB', 'bootstrap', 'My comment', SeedDMS_Core_User::role_admin); + return $user; + } + + /** + * Create a mock admin role object (only used for SeedDMS 6) + * + * @return SeedDMS_Core_User + */ + protected function getAdminRole() + { + $role = new SeedDMS_Core_Role(1, 'admin', SeedDMS_Core_Role::role_admin); + return $role; + } + + /** + * Create a mock regular user object + * + * @return SeedDMS_Core_User + */ + protected function getUser() + { + $user = new SeedDMS_Core_User(2, 'user', 'pass', 'Joe Baz', 'joe@foo.de', 'en_GB', 'bootstrap', 'My comment', SeedDMS_Core_User::role_user); + return $user; + } + + /** + * Test method setDMS() and getDMS() + * + * @return void + */ + public function testSetAndGetDMS() + { + $user = $this->getAdminUser(); + $user->setDMS(self::$dms); + $this->assertInstanceOf(SeedDMS_Core_DMS::class, $user->getDMS()); + } + + /** + * Test method isType() + * + * @return void + */ + public function testIsType() + { + $user = $this->getAdminUser(); + $this->assertTrue($user->isType('user')); + } + + /** + * Test method getPwd() + * + * @return void + */ + public function testGetPwd() + { + $user = $this->getAdminUser(); + $this->assertEquals('pass', $user->getPwd()); + } + + /** + * Test method getEmail() + * + * @return void + */ + public function testGetEmail() + { + $user = $this->getAdminUser(); + $this->assertEquals('baz@foo.de', $user->getEmail()); + } + + /** + * Test method getLanguage() + * + * @return void + */ + public function testGetLanguage() + { + $user = $this->getAdminUser(); + $this->assertEquals('en_GB', $user->getLanguage()); + } + + /** + * Test method getTheme() + * + * @return void + */ + public function testGetTheme() + { + $user = $this->getAdminUser(); + $this->assertEquals('bootstrap', $user->getTheme()); + } + + /** + * Test method getComment() + * + * @return void + */ + public function testGetComment() + { + $user = $this->getAdminUser(); + $this->assertEquals('My comment', $user->getComment()); + } + + /** + * Test method getRole() + * + * @return void + */ + public function testGetRole() + { + $user = $this->getAdminUser(); + $this->assertEquals(1, $user->getRole()); + } + + /** + * Test method isAdmin() + * + * @return void + */ + public function testIsAdmin() + { + $user = $this->getAdminUser(); + $this->assertTrue($user->isAdmin()); + $this->assertFalse($user->isGuest()); + } + + /** + * Test method isGuest() + * + * @return void + */ + public function testIsGuest() + { + $user = $this->getAdminUser(); + $this->assertFalse($user->isGuest()); + } + + /** + * Test method isHidden() + * + * @return void + */ + public function testIsHidden() + { + $user = $this->getAdminUser(); + $this->assertFalse($user->isHidden()); + } + + /** + * Test method getQuota() + * + * @return void + */ + public function testGetQuota() + { + $user = $this->getAdminUser(); + $this->assertEquals(0, $user->getQuota()); + } + + /** + * Test method getSecret() + * + * @return void + */ + public function testGetSecret() + { + if(self::$dbversion['major'] < 6) { + $this->markTestSkipped( + 'This test is not applicable for SeedDMS 5.' + ); + } else { + $user = $this->getAdminUser(); + $this->assertEquals('', $user->getSecret()); + } + } + + /** + * Test method getInstance() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetInstance() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $this->assertIsObject($user); + $this->assertEquals('admin', $user->getLogin()); + $user = SeedDMS_Core_User::getInstance('admin', self::$dms, 'name'); + $this->assertIsObject($user); + $this->assertEquals('admin', $user->getLogin()); + $user = SeedDMS_Core_User::getInstance('admin', self::$dms, 'name', 'info@seeddms.org'); + $this->assertIsObject($user); + $this->assertEquals('admin', $user->getLogin()); + /* get instance of none existing user */ + $user = SeedDMS_Core_User::getInstance('foo', self::$dms, 'name'); + $this->assertNull($user); + } + + /** + * Test method getAllInstances() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAllInstancesSqlFail() + { + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->exactly(2)) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblUsers` ORDER BY")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + /* Order by login */ + $users = SeedDMS_Core_User::getAllInstances('', $dms); + $this->assertFalse($users); + /* Order by fullname */ + $users = SeedDMS_Core_User::getAllInstances('fullname', $dms); + $this->assertFalse($users); + } + + /** + * Test method getLogin() + * + * @return void + */ + public function testGetLogin() + { + $user = $this->getAdminUser(); + $this->assertEquals('admin', $user->getLogin()); + } + + /** + * Test method setLogin() + * + * @return void + */ + public function testSetLoginSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `login`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setLogin('foo')); + } + + /** + * Test method getLogin() and setLogin() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetLogin() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $login = $user->getLogin(); + $ret = $user->setLogin('foo'); + $this->assertTrue($ret); + $login = $user->getLogin(); + $this->assertEquals('foo', $login); + $ret = $user->setLogin(' '); + $this->assertFalse($ret); + } + + /** + * Test method getFullName() + * + * @return void + */ + public function testGetFullName() + { + $user = $this->getAdminUser(); + $this->assertEquals('Joe Foo', $user->getFullName()); + } + + /** + * Test method setFullName() + * + * @return void + */ + public function testSetFullNameSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `fullName`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setFullName('foo')); + } + + /** + * Test method getFullName() and setFullName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetFullName() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $fullname = $user->getFullName(); + $ret = $user->setFullName('foo'); + $this->assertTrue($ret); + $fullname = $user->getFullName(); + $this->assertEquals('foo', $fullname); + } + + /** + * Test method getPwd() and setPwd() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetPwd() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $pwd = $user->getPwd(); + $ret = $user->setPwd('foo'); + $this->assertTrue($ret); + $pwd = $user->getPwd(); + $this->assertEquals('foo', $pwd); + } + + /** + * Test method setPwd() + * + * @return void + */ + public function testSetPwdSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `pwd`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setPwd('foo')); + } + + /** + * Test method getPwdExpiration() and setPwdExpiration() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetPwdExpiration() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $pwdexp = $user->getPwdExpiration(); + /* Set password expiration to 'never' */ + $ret = $user->setPwdExpiration('never'); + $this->assertTrue($ret); + $pwdexp = $user->getPwdExpiration(); + $this->assertNull($pwdexp); + + /* Set password expiration to 'now' */ + $now = date('Y-m-d H:i:s'); + $ret = $user->setPwdExpiration('now'); + $this->assertTrue($ret); + $pwdexp = $user->getPwdExpiration(); + $this->assertEquals($now, $pwdexp); + } + + /** + * Test method setPwdExpiration() + * + * @return void + */ + public function testSetPwdExpirationSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `pwdExpiration`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setPwdExpiration('foo')); + } + + /** + * Test method getEmail() and setEmail() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetEmail() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $email = $user->getEmail(); + $ret = $user->setEmail('new@seeddms.org'); + $this->assertTrue($ret); + $email = $user->getEmail(); + $this->assertEquals('new@seeddms.org', $email); + } + + /** + * Test method setEmail() + * + * @return void + */ + public function testSetEmailSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `email`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setEmail('foo')); + } + + /** + * Test method getLanguage() and setLanguage() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetLanguage() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $language = $user->getLanguage(); + $ret = $user->setLanguage('de_DE'); + $this->assertTrue($ret); + $language = $user->getLanguage(); + $this->assertEquals('de_DE', $language); + } + + /** + * Test method setLanguage() + * + * @return void + */ + public function testSetLanguageSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `language`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setLanguage('de_DE')); + } + + /** + * Test method getTheme() and setTheme() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetTheme() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $theme = $user->getTheme(); + $ret = $user->setTheme('bootstrap4'); + $this->assertTrue($ret); + $theme = $user->getTheme(); + $this->assertEquals('bootstrap4', $theme); + } + + /** + * Test method setTheme() + * + * @return void + */ + public function testSetThemeSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `theme`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setTheme('bootstrap')); + } + + /** + * Test method getComment() and setComment() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetComment() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $comment = $user->getComment(); + $ret = $user->setComment('my comment'); + $this->assertTrue($ret); + $comment = $user->getComment(); + $this->assertEquals('my comment', $comment); + } + + /** + * Test method setComment() + * + * @return void + */ + public function testSetCommentSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `comment`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setComment('my comment')); + } + + /** + * Test method getRole() and setRole() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetRole() + { + if(self::$dbversion['major'] < 6) { + // SeedDMS 5 use integers for roles: 0=user, 1=admin, 2=guest + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + // User with id=1 is the admin user in the initial database + $role = $user->getRole(); + $this->assertEquals(SeedDMS_Core_User::role_admin, $role); + $ret = $user->setRole(SeedDMS_Core_User::role_guest); + $this->assertTrue($ret); + $role = $user->getRole(); + $this->assertEquals(SeedDMS_Core_User::role_guest, $role); + $ret = $user->setRole(''); + $this->assertFalse($ret); + } else { + // Starting with SeedDMS 6 a role is an object + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + // User with id=1 is the admin user in the initial database + $role = $user->getRole(); + $this->assertTrue($role->isAdmin()); + // SeedDMS_Core_User has an isAdmin() method too, which internally + // uses SeedDMS_Core_Role::isAdmin() + $this->assertTrue($user->isAdmin()); + // Get the guest role, which is supposed to have id=2 in the + // initial database + $guestrole = SeedDMS_Core_Role::getInstance(2, self::$dms); + $this->assertTrue($guestrole->isGuest()); + // Assign guest role and check if the user is a guest + $ret = $user->setRole($guestrole); + $this->assertTrue($ret); + $this->assertTrue($user->isGuest()); + } + } + + /** + * Test method setRole() + * + * @return void + */ + public function testSetRoleSqlFail() + { + if(self::$dbversion['major'] > 5) { + $role = $this->getAdminRole(); + } + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `role`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + if(self::$dbversion['major'] > 5) { + $this->assertFalse($user->setRole($role)); + } else { + $this->assertFalse($user->setRole(SeedDMS_Core_User::role_admin)); + } + } + + /** + * Test method setGuest() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetGuest() + { + if(self::$dbversion['major'] == '5') { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $role = $user->getRole(); + $ret = $user->setGuest(); + $this->assertTrue($ret); + $role = $user->getRole(); + $this->assertEquals(SeedDMS_Core_User::role_guest, $role); + } else { + $this->markTestSkipped( + 'This test is not applicable for SeedDMS 6.' + ); + } + } + + /** + * Test method setGuest() + * + * @return void + */ + public function testSetGuestSqlFail() + { + $dms = new SeedDMS_Core_DMS(null, ''); + if(self::$dbversion['major'] == '5') { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `role`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setGuest()); + } else { + $this->markTestSkipped( + 'This test is not applicable for SeedDMS 6.' + ); + } + } + + /** + * Test method setAdmin() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetAdmin() + { + if(self::$dbversion['major'] == '5') { + $user = SeedDMS_Core_User::getInstance(2, self::$dms); + $role = $user->getRole(); + $ret = $user->setAdmin(); + $this->assertTrue($ret); + $role = $user->getRole(); + $this->assertEquals(SeedDMS_Core_User::role_admin, $role); + } else { + $this->markTestSkipped( + 'This test is not applicable for SeedDMS 6.' + ); + } + } + + /** + * Test method setAdmin() + * + * @return void + */ + public function testSetAdminSqlFail() + { + $dms = new SeedDMS_Core_DMS(null, ''); + if(self::$dbversion['major'] == '5') { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `role`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setAdmin()); + } else { + $this->markTestSkipped( + 'This test is not applicable for SeedDMS 6.' + ); + } + } + + /** + * Test method getQuota() and setQuota() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetQuota() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $quota = $user->getQuota(); + $ret = $user->setQuota(100000); + $this->assertTrue($ret); + $quota = $user->getQuota(); + $this->assertEquals(100000, $quota); + /* Setting a non numeric or negative value will fail */ + $ret = $user->setQuota('foo'); + $this->assertFalse($ret); + $ret = $user->setQuota(-100); + $this->assertFalse($ret); + } + + /** + * Test method setQuota() + * + * @return void + */ + public function testSetQuotaSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `quota`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setQuota(10000)); + } + + /** + * Test method getSecret() and setSecret() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetSecret() + { + if(self::$dbversion['major'] < 6) { + $this->markTestSkipped( + 'This test is not applicable for SeedDMS 5.' + ); + } else { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $secret = $user->getSecret(); + $ret = $user->setSecret('secret'); + $this->assertTrue($ret); + $secret = $user->getSecret(); + $this->assertEquals('secret', $secret); + } + } + + /** + * Test method setSecret() + * + * @return void + */ + public function testSetSecretSqlFail() + { + if(self::$dbversion['major'] < 6) { + $this->markTestSkipped( + 'This test is not applicable for SeedDMS 5.' + ); + } else { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `secret`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setSecret('secret')); + } + } + + /** + * Test method isHidden() and setHidden() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testIsAndSetHidden() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $ishidden = $user->isHidden(); + /* set hidden to true */ + $ret = $user->setHidden(true); + $this->assertTrue($ret); + $ishidden = $user->isHidden(); + $this->assertTrue($ishidden); + /* set hidden to false */ + $ret = $user->setHidden(false); + $this->assertTrue($ret); + $ishidden = $user->isHidden(); + $this->assertFalse($ishidden); + } + + /** + * Test method setHidden() + * + * @return void + */ + public function testSetHiddentSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `hidden`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setHidden(true)); + } + + /** + * Test method isDisabled() and setDisabled() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testIsAndSetDisabled() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $isdisabled = $user->isDisabled(); + /* set disabled to true */ + $ret = $user->setDisabled(true); + $this->assertTrue($ret); + $isdisabled = $user->isDisabled(); + $this->assertTrue($isdisabled); + /* set disabled to false */ + $ret = $user->setDisabled(false); + $this->assertTrue($ret); + $isdisabled = $user->isDisabled(); + $this->assertFalse($isdisabled); + } + + /** + * Test method setDisabled() + * + * @return void + */ + public function testSetDisabledtSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `disabled`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setDisabled(true)); + } + + /** + * Test method addLoginFailure() + * + * @return void + */ + public function testAddLoginFailure() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->exactly(2)) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `loginfailures`")) + ->willReturn(true); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertEquals(1, $user->addLoginFailure()); + $this->assertEquals(2, $user->addLoginFailure()); + } + + /** + * Test method addLoginFailure() + * + * @return void + */ + public function testAddLoginFailureSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `loginfailures`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->addLoginFailure()); + } + + /** + * Test method clearLoginFailure() + * + * @return void + */ + public function testClearLoginFailure() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->exactly(2)) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `loginfailures`")) + ->willReturn(true); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertEquals(1, $user->addLoginFailure()); + $this->assertEquals(true, $user->clearLoginFailures()); + } + + /** + * Test method clearLoginFailure() + * + * @return void + */ + public function testClearLoginFailureSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `loginfailures`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->clearLoginFailures()); + } + + /** + * Test method setHomeFolder() and getHomeFolder() + * + * @return void + */ + public function testSetAndGetHomeFolder() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `homefolder`")) + ->willReturn(true); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertTrue($user->setHomeFolder(1)); + $this->assertEquals(1, $user->getHomeFolder()); + } + + /** + * Test method setHomeFolder() + * + * @return void + */ + public function testSetHomeFolderSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResult') + ->with($this->stringContains("UPDATE `tblUsers` SET `homefolder`")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->setHomeFolder(1)); + } + + /** + * Test method getUsedDiskSpace() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetUsedDiskSpace() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $size = $user->getUsedDiskSpace(); + $this->assertEquals(0, $size); + } + + /** + * Test method getUsedDiskSpace() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetUsedDiskSpaceSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT SUM(`fileSize`) sum")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->getUsedDiskSpace()); + } + + /** + * Test method removeFromProcesses() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testRemoveFromProcesses() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $ret = $user->removeFromProcesses($user); + $this->assertTrue($ret); + } + + /** + * Test method transferDocumentsFolders() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testTransferDocumentsFolders() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + self::createSimpleFolderStructureWithDocuments(); + $newuser = self::$dms->addUser('newuser', '', 'New User', 'newuser@seeddms.org', 'en_GB', 'bootstrap', ''); + /* Transfering documents and folders to the same user returns true */ + $ret = $user->transferDocumentsFolders($user); + $this->assertTrue($ret); + /* A subfolder still belongs to $user */ + $subfolder = self::$dms->getFolder(2); + $this->assertEquals($user->getId(), $subfolder->getOwner()->getId()); + /* A document still belongs to $user */ + $document = self::$dms->getDocument(1); + $this->assertEquals($user->getId(), $document->getOwner()->getId()); + /* Transfer the documents and folders to $newuser */ + $ret = $user->transferDocumentsFolders($newuser); + $this->assertTrue($ret); + /* Get the folder again, because the owner has changed */ + $subfolder = self::$dms->getFolder(2); + $this->assertEquals($newuser->getId(), $subfolder->getOwner()->getId()); + /* Get the document again, because the owner has changed */ + $document = self::$dms->getDocument(1); + $this->assertEquals($newuser->getId(), $document->getOwner()->getId()); + } + + /** + * Test method remove() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testRemove() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + self::createSimpleFolderStructureWithDocuments(); + $newuser = self::$dms->addUser('newuser', '', 'New User', 'newuser@seeddms.org', 'en_GB', 'bootstrap', ''); + /* removing a user without passed a new user for docs and folders will fail */ + $ret = $user->remove($newuser, null); + $this->assertFalse($ret); + + $ret = $user->remove($newuser, $newuser); + $this->assertTrue($ret); + + /* all documents and folders now belong to $newuser */ + $document = self::$dms->getDocument(1); + $this->assertEquals($newuser->getId(), $document->getOwner()->getId()); + $subfolder = self::$dms->getFolder(1); + $this->assertEquals($newuser->getId(), $subfolder->getOwner()->getId()); + } + + /** + * Test method getDocuments() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocuments() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $documents = $user->getDocuments(); + $this->assertIsArray($documents); + $this->assertCount(0, $documents); + } + + /** + * Test method getDocumentsLocked() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentsLocked() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $documents = $user->getDocumentsLocked(); + $this->assertIsArray($documents); + $this->assertCount(0, $documents); + } + + /** + * Test method getDocumentLinks() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentLinks() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $links = $user->getDocumentLinks(); + $this->assertIsArray($links); + $this->assertCount(0, $links); + } + + /** + * Test method getDocumentFiles() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentFiles() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $files = $user->getDocumentFiles(); + $this->assertIsArray($files); + $this->assertCount(0, $files); + } + + /** + * Test method getDocumentContents() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetDocumentContents() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $contents = $user->getDocumentContents(); + $this->assertIsArray($contents); + $this->assertCount(0, $contents); + } + + /** + * Test method getFolders() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetFolders() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $folders = $user->getFolders(); + $this->assertIsArray($folders); + $this->assertCount(1, $folders); + } + + /** + * Test method getReviewStatus() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetReviewStatus() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $status = $user->getReviewStatus(); + $this->assertIsArray($status); + $this->assertCount(2, $status); + $this->assertCount(0, $status['indstatus']); + $this->assertCount(0, $status['grpstatus']); + } + + /** + * Test method getApprovalStatus() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetApprovalStatus() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $status = $user->getApprovalStatus(); + $this->assertIsArray($status); + $this->assertCount(2, $status); + $this->assertCount(0, $status['indstatus']); + $this->assertCount(0, $status['grpstatus']); + } + + /** + * Test method getWorkflowStatus() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowStatus() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $status = $user->getWorkflowStatus(); + $this->assertIsArray($status); + $this->assertCount(2, $status); + $this->assertCount(0, $status['u']); + $this->assertCount(0, $status['g']); + } + + /** + * Test method getWorkflowsInvolved() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetWorkflowsInvolved() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $workflows = $user->getWorkflowsInvolved(); + $this->assertIsArray($workflows); + $this->assertCount(0, $workflows); + } + + /** + * Test method getMandatoryReviewers() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetMandatoryReviewers() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $reviewers = $user->getMandatoryReviewers(); + $this->assertIsArray($reviewers); + $this->assertCount(0, $reviewers); + } + + /** + * Test method setMandatoryReviewer() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetMandatoryReviewer() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $newuser = self::$dms->addUser('newuser', '', 'New User', 'newuser@seeddms.org', 'en_GB', 'bootstrap', ''); + $ret = $user->setMandatoryReviewer($newuser->getId(), false); + $this->assertTrue($ret); + $reviewers = $user->getMandatoryReviewers(); + $this->assertIsArray($reviewers); + $this->assertCount(1, $reviewers); + /* $newuser is now a mandatory user of $user */ + $mandatoryreviewers = $newuser->isMandatoryReviewerOf(); + $this->assertIsArray($mandatoryreviewers); + $this->assertCount(1, $mandatoryreviewers); + $this->assertEquals($user->getId(), $mandatoryreviewers[0]->getId()); + + $group = self::$dms->addGroup('Group', ''); + $ret = $user->setMandatoryReviewer($group->getId(), true); + $this->assertTrue($ret); + $reviewers = $user->getMandatoryReviewers(); + $this->assertIsArray($reviewers); + $this->assertCount(2, $reviewers); + /* FIXME: there is not isMandatoryReviewerOf() for groups */ + } + + /** + * Test method getMandatoryApprovers() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetMandatoryApprovers() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $approvers = $user->getMandatoryApprovers(); + $this->assertIsArray($approvers); + $this->assertCount(0, $approvers); + } + + /** + * Test method setMandatoryApprover() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetMandatoryApprover() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $newuser = self::$dms->addUser('newuser', '', 'New User', 'newuser@seeddms.org', 'en_GB', 'bootstrap', ''); + $ret = $user->setMandatoryApprover($newuser->getId(), false); + $this->assertTrue($ret); + $approvers = $user->getMandatoryApprovers(); + $this->assertIsArray($approvers); + $this->assertCount(1, $approvers); + /* $newuser is now a mandatory user of $user */ + $mandatoryapprovers = $newuser->isMandatoryApproverOf(); + $this->assertIsArray($mandatoryapprovers); + $this->assertCount(1, $mandatoryapprovers); + $this->assertEquals($user->getId(), $mandatoryapprovers[0]->getId()); + + $group = self::$dms->addGroup('Group', ''); + $ret = $user->setMandatoryApprover($group->getId(), true); + $this->assertTrue($ret); + $approvers = $user->getMandatoryApprovers(); + $this->assertIsArray($approvers); + $this->assertCount(2, $approvers); + /* FIXME: there is not isMandatoryApproverOf() for groups */ + } + + /** + * Test method setMandatoryWorkflow() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetMandatoryWorkflow() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $approver = self::$dms->addUser('approver', '', 'Approver', 'newuser@seeddms.org', 'en_GB', 'bootstrap', ''); + $reviewer = self::$dms->addUser('reviewer', '', 'Reviewer', 'newuser@seeddms.org', 'en_GB', 'bootstrap', ''); + $simpleworkflow = self::createSimpleWorkflow($approver); + $traditionalworkflow = self::createWorkflow($reviewer, $approver); + $newuser = self::$dms->addUser('newuser', '', 'New User', 'newuser@seeddms.org', 'en_GB', 'bootstrap', ''); + /* Set a single mandatory workflow */ + $ret = $newuser->setMandatoryWorkflow($simpleworkflow); + $this->assertTrue($ret); + $workflows = $newuser->getMandatoryWorkflows(); + $this->assertIsArray($workflows); + $this->assertCount(1, $workflows); + + /* Set a single mandatory workflow will add it to the list of workflows */ + $ret = $newuser->setMandatoryWorkflow($traditionalworkflow); + $this->assertTrue($ret); + $workflows = $newuser->getMandatoryWorkflows(); + $this->assertIsArray($workflows); + $this->assertCount(2, $workflows); + + /* Set a single mandatory workflow with setMandatoryWorkflows() will delete + * all existing workflows and set a new list of workflows + */ + $ret = $newuser->setMandatoryWorkflows([$simpleworkflow]); + $this->assertTrue($ret); + $workflows = $newuser->getMandatoryWorkflows(); + $this->assertIsArray($workflows); + $this->assertCount(1, $workflows); + + /* Set several mandatory workflows will delete all existing workflows + * and set new workflows. + */ + $ret = $newuser->setMandatoryWorkflows([$simpleworkflow, $traditionalworkflow]); + $this->assertTrue($ret); + $workflows = $newuser->getMandatoryWorkflows(); + $this->assertIsArray($workflows); + $this->assertCount(2, $workflows); + + /* Setting an empty list will delete all mandatory workflows */ + $ret = $newuser->setMandatoryWorkflows([]); + $this->assertTrue($ret); + $workflows = $newuser->getMandatoryWorkflows(); + $this->assertNull($workflows); + } + + /** + * Test method getMandatoryWorkflow() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetMandatoryWorkflow() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $workflow = $user->getMandatoryWorkflow(); + $this->assertNull($workflow); + } + + /** + * Test method getMandatoryWorkflows() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetMandatoryWorkflows() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $workflow = $user->getMandatoryWorkflows(); + $this->assertNull($workflow); + } + + /** + * Test method getGroups() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetGroups() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $groups = $user->getGroups(); + $this->assertIsArray($groups); + $this->assertCount(0, $groups); + $group = self::$dms->addGroup('Group', ''); + $ret = $user->joinGroup($group); + $this->assertTrue($ret); + /* Adding the user a twice to a group will fail */ + $ret = $user->joinGroup($group); + $this->assertFalse($ret); + /* user now belongs to two groups */ + $groups = $user->getGroups(); + $this->assertIsArray($groups); + $this->assertCount(1, $groups); + /* Leave the group */ + $ret = $user->leaveGroup($group); + $this->assertTrue($ret); + /* Leave the group again will fail */ + $ret = $user->leaveGroup($group); + $this->assertFalse($ret); + /* the user is no longer in any group */ + $groups = $user->getGroups(); + $this->assertIsArray($groups); + $this->assertCount(0, $groups); + } + + /** + * Test method hasImage() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testHasImage() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $image = $user->hasImage(); + $this->assertFalse($image); + } + + /** + * Test method hasImage() + * + * @return void + */ + public function testHasImageSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT COUNT(*) AS num FROM `tblUserImages` WHERE")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->hasImage()); + } + + /** + * Test method getImage() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetImage() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $image = $user->getImage(); + $this->assertNull($image); + } + + /** + * Test method getImage() + * + * @return void + */ + public function testGetImageSqlFail() + { + $user = $this->getAdminUser(); + $db = $this->createMock(SeedDMS_Core_DatabaseAccess::class); + $db->expects($this->once()) + ->method('getResultArray') + ->with($this->stringContains("SELECT * FROM `tblUserImages` WHERE")) + ->willReturn(false); + $dms = new SeedDMS_Core_DMS($db, ''); + $user->setDMS($dms); + $this->assertFalse($user->getImage()); + } + + /** + * Test method setImage() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testSetImage() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $file = self::createTempFile(200); + $ret = $user->setImage($file, 'text/plain'); + $this->assertTrue(SeedDMS_Core_File::removeFile($file)); + $this->assertTrue($ret); + $ret = $user->hasImage(); + $this->assertTrue($ret); + $image = $user->getImage(); + $this->assertIsArray($image); + $this->assertEquals('text/plain', $image['mimeType']); + } + + /** + * Test method delMandatoryReviewers() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testDelMandatoryReviewers() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $ret = $user->delMandatoryReviewers(); + $this->assertTrue($ret); + } + + /** + * Test method delMandatoryApprovers() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testDelMandatoryApprovers() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $ret = $user->delMandatoryApprovers(); + $this->assertTrue($ret); + } + + /** + * Test method delMandatoryWorkflow() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testDelMandatoryWorkflow() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $ret = $user->delMandatoryWorkflow(); + $this->assertTrue($ret); + } + + /** + * Test method getNotifications() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetNotifications() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $notifications = $user->getNotifications(); + $this->assertIsArray($notifications); + $this->assertCount(0, $notifications); + $notifications = $user->getNotifications(0); + $this->assertIsArray($notifications); + $this->assertCount(0, $notifications); + $notifications = $user->getNotifications(1); + $this->assertIsArray($notifications); + $this->assertCount(0, $notifications); + } + + /** + * Test method getKeywordCategories() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetKeywordCategories() + { + $user = SeedDMS_Core_User::getInstance(1, self::$dms); + $cats = $user->getKeywordCategories(); + $this->assertIsArray($cats); + $this->assertCount(0, $cats); + } +} + diff --git a/SeedDMS_Core/tests/WorkflowTest.php b/SeedDMS_Core/tests/WorkflowTest.php new file mode 100644 index 000000000..3abe1c26d --- /dev/null +++ b/SeedDMS_Core/tests/WorkflowTest.php @@ -0,0 +1,638 @@ + + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version @package_version@ + * @link https://www.seeddms.org + */ + +use PHPUnit\Framework\SeedDmsTest; + +/** + * Group test class + * + * @category SeedDMS + * @package Tests + * @author Uwe Steinmann + * @copyright 2021 Uwe Steinmann + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @version Release: @package_version@ + * @link https://www.seeddms.org + */ +class WorkflowTest extends SeedDmsTest +{ + + /** + * Create a real sqlite database in memory + * + * @return void + */ + protected function setUp(): void + { + self::$dbh = self::createInMemoryDatabase(); + self::$contentdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'phpunit-'.time(); + mkdir(self::$contentdir); + // echo "Creating temp content dir: ".self::$contentdir."\n"; + self::$dms = new \SeedDMS_Core_DMS(self::$dbh, self::$contentdir); + self::$dbversion = self::$dms->getDBVersion(); + } + + /** + * Clean up at tear down + * + * @return void + */ + protected function tearDown(): void + { + self::$dbh = null; + // echo "\nRemoving temp. content dir: ".self::$contentdir."\n"; + exec('rm -rf '.self::$contentdir); + } + + /** + * Test method getInitState() and setInitState() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetInitState() + { + $ws_nr = self::$dms->addWorkflowState('needs review', S_IN_WORKFLOW); + $ws_na = self::$dms->addWorkflowState('needs approval', S_IN_WORKFLOW); + $workflow = self::$dms->addWorkflow('traditional workflow', $ws_nr); + $initstate = $workflow->getInitState(); + $this->assertEquals($ws_nr->getName(), $initstate->getName()); + $ret = $workflow->setInitState($ws_na); + $this->assertTrue($ret); + $initstate = $workflow->getInitState(); + $this->assertEquals($ws_na->getName(), $initstate->getName()); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetStateName() + { + $state = self::$dms->addWorkflowState('needs review', S_IN_WORKFLOW); + $name = $state->getName(); + $this->assertEquals('needs review', $name); + $ret = $state->setName('foobar'); + $this->assertTrue($ret); + $name = $state->getName(); + $this->assertEquals('foobar', $name); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetActionName() + { + $action = self::$dms->addWorkflowAction('action'); + $name = $action->getName(); + $this->assertEquals('action', $name); + $ret = $action->setName('foobar'); + $this->assertTrue($ret); + $name = $action->getName(); + $this->assertEquals('foobar', $name); + } + + /** + * Test method getName() and setName() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetWorkflowName() + { + $ws_nr = self::$dms->addWorkflowState('needs review', S_IN_WORKFLOW); + $workflow = self::$dms->addWorkflow('traditional workflow', $ws_nr); + $name = $workflow->getName(); + $this->assertEquals('traditional workflow', $name); + $ret = $workflow->setName('foo'); + $this->assertTrue($ret); + $name = $workflow->getName(); + $this->assertEquals('foo', $name); + } + + /** + * Test method getDocumentStatus() and setDocumentStatus() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testGetAndSetDocumentStatus() + { + $state = self::$dms->addWorkflowState('some name', S_RELEASED); + $docstatus = $state->getDocumentStatus(); + $this->assertEquals(S_RELEASED, $docstatus); + $ret = $state->setDocumentStatus(S_REJECTED); + $this->assertTrue($ret); + $docstatus = $state->getDocumentStatus(); + $this->assertEquals(S_REJECTED, $docstatus); + } + + /** + * Test method workflow->remove() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCreateAndRemoveWorkflow() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + + /* Add a new user who will be the reviewer */ + $reviewer = self::$dms->addUser('reviewer', 'reviewer', 'Reviewer One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($reviewer); + + /* Add a new user who will be the approver */ + $approver = self::$dms->addUser('approver', 'approver', 'Approver One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($approver); + + $workflow = self::createWorkflow($reviewer, $approver); + $this->assertIsObject($workflow); + + $ret = $workflow->remove(); + $this->assertTrue($ret); + + $states = self::$dms->getAllWorkflowStates(); + $this->assertIsArray($states); + $this->assertCount(4, $states); + foreach($states as $state) + $this->assertFalse($state->isUsed()); + + $actions = self::$dms->getAllWorkflowActions(); + $this->assertIsArray($actions); + $this->assertCount(3, $actions); + foreach($actions as $action) + $this->assertFalse($action->isUsed()); + + } + + /** + * Test method remove() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCreateAndRemoveAction() + { + $action = self::$dms->addWorkflowAction('action'); + $this->assertIsObject($action); + $actions = self::$dms->getAllWorkflowActions(); + $this->assertIsArray($actions); + $this->assertCount(1, $actions); + $ret = $action->remove(); + $this->assertTrue($ret); + $actions = self::$dms->getAllWorkflowActions(); + $this->assertIsArray($actions); + $this->assertCount(0, $actions); + } + + /** + * Test method remove() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testCreateAndRemoveState() + { + $state = self::$dms->addWorkflowState('needs review', S_IN_WORKFLOW); + $this->assertIsObject($state); + $states = self::$dms->getAllWorkflowStates(); + $this->assertIsArray($states); + $this->assertCount(1, $states); + $ret = $state->remove(); + $this->assertTrue($ret); + $states = self::$dms->getAllWorkflowStates(); + $this->assertIsArray($states); + $this->assertCount(0, $states); + } + + /** + * Test method setWorkflow(), getWorkflow(), getWorkflowState() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testAssignWorkflow() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + + /* Add a new user who will be the reviewer */ + $reviewer = self::$dms->addUser('reviewer', 'reviewer', 'Reviewer One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($reviewer); + + /* Add a new user who will be the approver */ + $approver = self::$dms->addUser('approver', 'approver', 'Approver One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($approver); + + $workflow = self::createWorkflow($reviewer, $approver); + $this->assertIsObject($workflow); + + /* Check for cycles */ + $cycles = $workflow->checkForCycles(); + $this->assertFalse($cycles); + + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $content = $document->getLatestContent(); + $this->assertIsObject($content); + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + /* Assign the workflow */ + $ret = $content->setWorkflow($workflow, $user); + $this->assertTrue($ret); + + /* Assign a workflow again causes an error */ + $ret = $content->setWorkflow($workflow, $user); + $this->assertFalse($ret); + + /* Get a fresh copy of the content from the database and get the workflow */ + $again = self::$dms->getDocumentContent($content->getId()); + $this->assertIsObject($again); + $w = $again->getWorkflow(); + $this->assertEquals($workflow->getId(), $w->getId()); + + /* Status of content should be S_IN_WORKFLOW now */ + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_IN_WORKFLOW, $status['status']); + + /* Get current workflow state */ + $state = $content->getWorkflowState(); + $this->assertEquals('needs review', $state->getName()); + + $workflowlog = $content->getWorkflowLog(); + $this->assertIsArray($workflowlog); + $this->assertCount(0, $workflowlog); + + /* The workflow has altogether 4 states */ + $states = $workflow->getStates(); + $this->assertIsArray($states); + $this->assertCount(4, $states); + + /* Check the initial state */ + $initstate = $workflow->getInitState(); + $this->assertEquals('needs review', $initstate->getName()); + + /* init state is definitely used */ + $ret = $initstate->isUsed(); + $this->assertTrue($ret); + + /* init state has two transistions linked to it */ + $transitions = $initstate->getTransitions(); + $this->assertIsArray($transitions); + $this->assertCount(2, $transitions); + + /* Check if workflow is used by any document */ + $isused = $workflow->isUsed(); + $this->assertTrue($isused); + + } + + /** + * Test method setWorkflow(), getWorkflow(), getWorkflowState() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testStepThroughWorkflow() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + + /* Add a new user who will be the reviewer */ + $reviewer = self::$dms->addUser('reviewer', 'reviewer', 'Reviewer One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($reviewer); + + /* Add a new user who will be the approver */ + $approver = self::$dms->addUser('approver', 'approver', 'Approver One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($approver); + + $workflow = self::createWorkflow($reviewer, $approver); + + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $content = $document->getLatestContent(); + $this->assertIsObject($content); + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + /* Assign the workflow */ + $ret = $content->setWorkflow($workflow, $user); + $this->assertTrue($ret); + + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_IN_WORKFLOW, $status['status']); + + /* Remove the workflow */ + $ret = $content->removeWorkflow($user); + $this->assertTrue($ret); + + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + /* Remove the workflow again is just fine */ + $ret = $content->removeWorkflow($user); + $this->assertTrue($ret); + + /* Assign the workflow again */ + $ret = $content->setWorkflow($workflow, $user); + $this->assertTrue($ret); + + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_IN_WORKFLOW, $status['status']); + + + /* Check if workflow needs action by the reviewer/approver */ + $ret = $content->needsWorkflowAction($reviewer); + $this->assertTrue($ret); + $ret = $content->needsWorkflowAction($approver); + $this->assertFalse($ret); + + /* Get current workflow state*/ + $state = $content->getWorkflowState(); + $this->assertEquals('needs review', $state->getName()); + + /* There should be two possible transitions now + * NR -- review -> NA + * NR -- reject -> RJ + */ + $nexttransitions = $workflow->getNextTransitions($state); + $this->assertIsArray($nexttransitions); + $this->assertCount(2, $nexttransitions); + + /* But of course, there were no previous transitions */ + $prevtransitions = $workflow->getPreviousTransitions($state); + $this->assertIsArray($prevtransitions); + $this->assertCount(0, $prevtransitions); + + /* Check if reviewer is allowed to trigger the transition. + * As we are still in the intitial state, the possible transitions + * may both be triggered by the reviewer but not by the approver. + */ + foreach($nexttransitions as $nexttransition) { + if($nexttransition->getNextState()->getDocumentStatus() == S_REJECTED) + $rejecttransition = $nexttransition; + elseif($nexttransition->getNextState()->getDocumentStatus() == S_IN_WORKFLOW) + $reviewtransition = $nexttransition; + $ret = $content->triggerWorkflowTransitionIsAllowed($reviewer, $nexttransition); + $this->assertTrue($ret); + $ret = $content->triggerWorkflowTransitionIsAllowed($approver, $nexttransition); + $this->assertFalse($ret); + } + + /* Trigger the successful review transition. + * As there is only one reviewer the transition will fire and the workflow + * moves forward into the next state. triggerWorkflowTransition() returns the + * next state. + */ + $nextstate = $content->triggerWorkflowTransition($reviewer, $reviewtransition, 'Review succeeded'); + $this->assertIsObject($nextstate); + $this->assertEquals('needs approval', $nextstate->getName()); + + $state = $content->getWorkflowState(); + $this->assertEquals($nextstate->getId(), $state->getId()); + $this->assertEquals('needs approval', $state->getName()); + + /* The workflow log has one entry now */ + $workflowlog = $content->getLastWorkflowLog(); + $this->assertIsObject($workflowlog); + $this->assertEquals('Review succeeded', $workflowlog->getComment()); + + /* There should be two possible transitions now + * NA -- approve -> RL + * NA -- reject -> RJ + */ + $nexttransitions = $workflow->getNextTransitions($state); + $this->assertIsArray($nexttransitions); + $this->assertCount(2, $nexttransitions); + + /* But of course, there is one previous transitions, the one that led to + * the current state of the workflow. + */ + $prevtransitions = $workflow->getPreviousTransitions($state); + $this->assertIsArray($prevtransitions); + $this->assertCount(1, $prevtransitions); + $this->assertEquals($reviewtransition->getId(), $prevtransitions[0]->getId()); + + /* Check if approver is allowed to trigger the transition. + * As we are now in 'needs approval' state, the possible transitions + * may both be triggered by the approver but not by the reviewer. + */ + foreach($nexttransitions as $nexttransition) { + if($nexttransition->getNextState()->getDocumentStatus() == S_REJECTED) + $rejecttransition = $nexttransition; + elseif($nexttransition->getNextState()->getDocumentStatus() == S_RELEASED) + $releasetransition = $nexttransition; + $ret = $content->triggerWorkflowTransitionIsAllowed($approver, $nexttransition); + $this->assertTrue($ret); + $ret = $content->triggerWorkflowTransitionIsAllowed($reviewer, $nexttransition); + $this->assertFalse($ret); + } + + /* Trigger the successful approve transition. + * As there is only one approver the transition will fire and the workflow + * moves forward into the next state. triggerWorkflowTransition() returns the + * next state. + */ + $nextstate = $content->triggerWorkflowTransition($approver, $releasetransition, 'Approval succeeded'); + $this->assertIsObject($nextstate); + $this->assertEquals('released', $nextstate->getName()); + + /* The workflow log has two entries now */ + $workflowlog = $content->getLastWorkflowLog(); + $this->assertIsObject($workflowlog); + $this->assertEquals('Approval succeeded', $workflowlog->getComment()); + + /* Because the workflow has reached a final state, the workflow will no + * longer be attached to the document. + */ + $workflow = $content->getWorkflow(); + $this->assertFalse($workflow); + + /* There is also no way to get the state anymore */ + $state = $content->getWorkflowState(); + $this->assertFalse($state); + + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + /* Even after the workflow has been finished the log can still be retrieved */ + $workflowlog = $content->getLastWorkflowLog(); + $this->assertIsObject($workflowlog); + $this->assertEquals('Approval succeeded', $workflowlog->getComment()); + } + + /** + * Test method rewindWorkflow() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testRewindWorkflow() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + + /* Add a new user who will be the reviewer */ + $reviewer = self::$dms->addUser('reviewer', 'reviewer', 'Reviewer One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($reviewer); + + /* Add a new user who will be the approver */ + $approver = self::$dms->addUser('approver', 'approver', 'Approver One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($approver); + + $workflow = self::createWorkflow($reviewer, $approver); + + /* Add a new document */ + $document = self::createDocument($rootfolder, $user, 'Document 1'); + $content = $document->getLatestContent(); + $this->assertIsObject($content); + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_RELEASED, $status['status']); + + /* Assign the workflow */ + $ret = $content->setWorkflow($workflow, $user); + $this->assertTrue($ret); + + $status = $content->getStatus(); + $this->assertIsArray($status); + $this->assertEquals(S_IN_WORKFLOW, $status['status']); + + /* Check if workflow needs action by the reviewer */ + $ret = $content->needsWorkflowAction($reviewer); + $this->assertTrue($ret); + + /* Get current workflow state*/ + $state = $content->getWorkflowState(); + $this->assertEquals('needs review', $state->getName()); + + /* There should be two possible transitions now + * NR -- review -> NA + * NR -- reject -> RJ + */ + $nexttransitions = $workflow->getNextTransitions($state); + $this->assertIsArray($nexttransitions); + $this->assertCount(2, $nexttransitions); + + /* Check if reviewer is allowed to trigger the transition. + * As we are still in the intitial state, the possible transitions + * may both be triggered by the reviewer but not by the approver. + */ + foreach($nexttransitions as $nexttransition) { + if($nexttransition->getNextState()->getDocumentStatus() == S_IN_WORKFLOW) + $reviewtransition = $nexttransition; + } + + /* Trigger the successful review transition. + * As there is only one reviewer the transition will fire and the workflow + * moves forward into the next state. triggerWorkflowTransition() returns the + * next state. + */ + $nextstate = $content->triggerWorkflowTransition($reviewer, $reviewtransition, 'Review succeeded'); + $this->assertIsObject($nextstate); + $this->assertEquals('needs approval', $nextstate->getName()); + + /* Get current workflow state*/ + $state = $content->getWorkflowState(); + $this->assertEquals('needs approval', $state->getName()); + + /* The workflow log has one entry now */ + $workflowlogs = $content->getWorkflowLog(); + $this->assertIsArray($workflowlogs); + $this->assertCount(1, $workflowlogs); + if(self::$dbversion['major'] > 5) + $this->assertEquals('Review succeeded', $workflowlogs[1][0]->getComment()); + else + $this->assertEquals('Review succeeded', $workflowlogs[0]->getComment()); + + $ret = $content->rewindWorkflow(); + $this->assertTrue($ret); + + /* After rewinding the workflow the initial state is set ... */ + $state = $content->getWorkflowState(); + $this->assertEquals('needs review', $state->getName()); + + /* and the workflow log has been cleared */ + $workflowlogs = $content->getWorkflowLog(); + $this->assertIsArray($workflowlogs); + $this->assertCount(0, $workflowlogs); + } + + /** + * Test method getTransitionsByStates() + * + * This method uses a real in memory sqlite3 database. + * + * @return void + */ + public function testTransitionsByStateWorkflow() + { + $rootfolder = self::$dms->getRootFolder(); + $user = self::$dms->getUser(1); + $this->assertIsObject($user); + + /* Add a new user who will be the reviewer */ + $reviewer = self::$dms->addUser('reviewer', 'reviewer', 'Reviewer One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($reviewer); + + /* Add a new user who will be the approver */ + $approver = self::$dms->addUser('approver', 'approver', 'Approver One', 'user1@seeddms.org', 'en_GB', 'bootstrap', ''); + $this->assertIsObject($approver); + + $workflow = self::createWorkflow($reviewer, $approver); + + /* Check the initial state */ + $initstate = $workflow->getInitState(); + $this->assertEquals('needs review', $initstate->getName()); + + /* init state has two transistions linked to it */ + $transitions = $initstate->getTransitions(); + $this->assertIsArray($transitions); + $this->assertCount(2, $transitions); + + $t = $workflow->getTransitionsByStates($initstate, $transitions[1]->getNextState()); + $this->assertEquals($transitions[1]->getId(), $t[0]->getId()); + } + +} diff --git a/SeedDMS_Lucene/CHANGELOG.md b/SeedDMS_Lucene/CHANGELOG.md new file mode 100644 index 000000000..ce4685461 --- /dev/null +++ b/SeedDMS_Lucene/CHANGELOG.md @@ -0,0 +1,94 @@ +1.1.18 (2023-01-09) +--------------------- +- IndexedDocument() accepts a callable for conversion to text +- SeedDMS_Lucene_Search::open and create return itself but Zend_Search_Lucene + +1.1.17 (2021-05-10) +--------------------- +- close pipes in execWithTimeout(), also return exit code of command + + +1.1.16 (2020-12-12) +--------------------- +- add indexing of folders + +1.1.15 (2020-09-10) +--------------------- +- add searching for document status +- better error handling if opening index fails +- parameters for SeedDMS_Lucene_Search::search() has changed +- SeedDMS_Lucene_Search::search() returns array of hits, count and facets +- pass config array instead of index directory to SeedDMS_Lucene_Indexer::create() + and SeedDMS_Lucene_Indexer::open() + +1.1.14 (2020-09-02) +--------------------- +- Index users with at least read access on the document + +1.1.13 (2018-04-11) +--------------------- +- IndexedDocument() remembers cmd and mimetype + +1.1.12 (2018-01-30) +--------------------- +- execWithTimeout() reads data from stderr and saves it into error msg + +1.1.11 (2017-12-04) +--------------------- +- allow conversion commands for mimetypes with wildcards + +1.1.10 (2017-03-01) +--------------------- +- catch exception in execWithTimeout() + +1.1.9 (2016-04-28) +--------------------- +- pass variables to stream_select() to fullfill strict standards. +- make all functions in Indexer.php static + +1.1.8 (2016-03-29) +--------------------- +- set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 + +1.1.7 (2016-02-01) +--------------------- +- add command for indexing postѕcript files + +1.1.6 (2015-08-05) +--------------------- +- run external commands with a timeout + +1.1.5 (2014-07-30) +--------------------- +- field for original filename is treated as utf-8 +- declare SeeDMS_Lucene_Indexer::open() static + +1.1.4 (2013-08-13) +--------------------- +- class SeedDMS_Lucene_Search::search returns false if query is invalid instead of an empty result record + +1.1.3 (2013-06-27) +--------------------- +- explicitly set encoding to utf-8 when adding fields +- do not check if deleting document from index fails, update it in any case + +1.1.2 (2013-06-17) +--------------------- +- parse query term and catch errors before using it + +1.1.1 (2012-12-03) +--------------------- +- catch exception if index is opened but not available + +1.1.0 (2012-11-06) +--------------------- +- use a configurable list of mime type converters, fixed indexing and searching + of special chars like german umlaute. + +1.0.1 (2011-11-06) +--------------------- +- New Release + +0.0.1 (2009-04-27) +--------------------- + diff --git a/SeedDMS_Lucene/composer.json b/SeedDMS_Lucene/composer.json new file mode 100644 index 000000000..68a37caaf --- /dev/null +++ b/SeedDMS_Lucene/composer.json @@ -0,0 +1,23 @@ +{ + "name": "seeddms/lucene", + "description": "Lucene based fulltext search for SeedDMS ", + "type": "library", + "license": "GPL-2.0-or-later", + "minimum-stability": "dev", + "autoload": { + "psr-4": { + "Seeddms\\Lucene\\": "Lucene/" + }, + "classmap": ["Lucene/"] + }, + "authors": [ + { + "name": "Uwe Steinmann", + "email": "info@seeddms.org" + } + ], + "require-dev": { + "phpunit/phpunit": "^9" + } + +} diff --git a/SeedDMS_Lucene/package.xml b/SeedDMS_Lucene/package.xml index 3ccc53ec9..eeb33269a 100644 --- a/SeedDMS_Lucene/package.xml +++ b/SeedDMS_Lucene/package.xml @@ -59,6 +59,7 @@ + 2009-04-27 0.0.1 0.0.1 @@ -67,7 +68,6 @@ alpha alpha - 2009-04-27 BSD License @@ -101,8 +101,8 @@ GPL License -use a configurable list of mime type converters, fixed indexing and searching -of special chars like german umlaute. +- use a configurable list of mime type converters, fixed indexing and searching + of special chars like german umlaute. @@ -118,7 +118,7 @@ of special chars like german umlaute. GPL License -catch exception if index is opened but not available +- catch exception if index is opened but not available @@ -134,7 +134,7 @@ catch exception if index is opened but not available GPL License -parse query term and catch errors before using it +- parse query term and catch errors before using it @@ -150,8 +150,8 @@ parse query term and catch errors before using it GPL License -explicitly set encoding to utf-8 when adding fields -do not check if deleting document from index fails, update it in any case +- explicitly set encoding to utf-8 when adding fields +- do not check if deleting document from index fails, update it in any case @@ -167,7 +167,7 @@ do not check if deleting document from index fails, update it in any case GPL License -class SeedDMS_Lucene_Search::search returns false if query is invalid instead of an empty result record +- class SeedDMS_Lucene_Search::search returns false if query is invalid instead of an empty result record @@ -183,8 +183,8 @@ class SeedDMS_Lucene_Search::search returns false if query is invalid instead of GPL License -field for original filename is treated as utf-8 -declare SeeDMS_Lucene_Indexer::open() static +- field for original filename is treated as utf-8 +- declare SeeDMS_Lucene_Indexer::open() static @@ -200,7 +200,7 @@ declare SeeDMS_Lucene_Indexer::open() static GPL License -run external commands with a timeout +- run external commands with a timeout @@ -216,7 +216,7 @@ run external commands with a timeout GPL License -add command for indexing postѕcript files +- add command for indexing postѕcript files @@ -232,7 +232,7 @@ add command for indexing postѕcript files GPL License -set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 +- set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 @@ -248,8 +248,8 @@ set last parameter of stream_select() to 200000 micro sec. in case the timeout i GPL License -pass variables to stream_select() to fullfill strict standards. -make all functions in Indexer.php static +- pass variables to stream_select() to fullfill strict standards. +- make all functions in Indexer.php static @@ -265,7 +265,7 @@ make all functions in Indexer.php static GPL License -catch exception in execWithTimeout() +- catch exception in execWithTimeout() @@ -281,7 +281,7 @@ catch exception in execWithTimeout() GPL License -allow conversion commands for mimetypes with wildcards +- allow conversion commands for mimetypes with wildcards @@ -297,7 +297,7 @@ allow conversion commands for mimetypes with wildcards GPL License -execWithTimeout() reads data from stderr and saves it into error msg +- execWithTimeout() reads data from stderr and saves it into error msg @@ -313,7 +313,7 @@ execWithTimeout() reads data from stderr and saves it into error msg GPL License -IndexedDocument() remembers cmd and mimetype +- IndexedDocument() remembers cmd and mimetype @@ -329,7 +329,7 @@ IndexedDocument() remembers cmd and mimetype GPL License -Index users with at least read access on the document +- Index users with at least read access on the document diff --git a/SeedDMS_Preview/CHANGELOG.md b/SeedDMS_Preview/CHANGELOG.md new file mode 100644 index 000000000..76b1546c5 --- /dev/null +++ b/SeedDMS_Preview/CHANGELOG.md @@ -0,0 +1,128 @@ +1.5.0 (2023-01-09) +--------------------- +- add previewer which creates txt + +1.4.0 (2021-10-16) +--------------------- +- use new conversion service if available +- createRawPreview() checks early if a converter exists + + +1.3.3 (2020-12-23) +--------------------- +- close pipes in execWithTimeout(), also return exit code of command +- createPreview() has optional parameter by referenz to return true if a + preview image was actually created + +1.3.2 (2020-12-23) +--------------------- +- set header Content-Length +- update package description + +1.3.1 (2020-03-21) +--------------------- +- add parameter $target to SeedDMS_Preview_pdfPreviewer::hasRawPreview() and SeedDMS_Preview_pdfPreviewer::getRawPreview() + +1.3.0 (2020-02-17) +--------------------- +- add new methode getPreviewFile() + +1.2.10 (2019-02-11) +--------------------- +- new parameter for enabling/disabling xsendfile +- fix creation of pdf preview if document content class is not SeedDMS_Core_DocumentContent + +1.2.9 (2018-07-13) +--------------------- +- make sure list of converters is always an array +- usage of mod_sendfile can be configured + +1.2.8 (2018-03-08) +--------------------- +- preview is also created if SeedDMS_Core_DocumentContent has a child class + +1.2.7 (2018-01-18) +--------------------- +- add SeedDMS_Preview_Base::sendFile() as a replacement for readfile() which uses +- mod_xsendfile if available +- execWithTimeout() reads data from stderr and returns it together with stdout in array + +1.2.6 (2017-12-04) +--------------------- +- SeedDMS_Preview_Base::setConverters() overrides existing converters. +- New method SeedDMS_Preview_Base::addConverters() merges new converters with old ones. + +1.2.5 (2017-10-11) +--------------------- +- SeedDMS_Preview_Base::hasConverter() returns only try if command is set + +1.2.4 (2017-10-11) +--------------------- +- fix typo in converter for tar.gz files + +1.2.3 (2017-09-18) +--------------------- +- createPreview() returns false if running the converter command fails + +1.2.2 (2017-03-02) +--------------------- +- commands can be set for mimetypes 'xxxx/*' and '*' +- pass mimetype as parameter '%m' to converter + +1.2.1 (2016-11-15) +--------------------- +- setConverters() overrides exiting converters + +1.2.0 (2016-11-07) +--------------------- +- add new previewer which converts document to pdf instead of png + +1.1.9 (2016-04-26) +--------------------- +- add more documentation +- finish deletePreview() +- add new method deleteDocumentPreviews() +- fix calculation of timeout (Bug #269) +- check if cache dir exists before deleting it in deleteDocumentPreviews() + +1.1.8 (2016-04-05) +--------------------- +- pass variables to stream_select (required by php7) + +1.1.7 (2016-03-29) +--------------------- +- set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 + +1.1.6 (2016-03-08) +--------------------- +- check if object passed to createPreview(), hasPreview() is not null + +1.1.5 (2016-02-11) +--------------------- +- add method getFilesize() +- timeout for external commands can be passed to contructor of SeedDMS_Preview_Previewer + +1.1.4 (2015-08-08) +--------------------- +- command for creating the preview will be called with a given timeout + +1.1.3 (2015-02-13) +--------------------- +- preview images will also be recreated if the object this image belongs is of newer date than the image itself. This happens if versions are being deleted and than a new version is uploaded. Because the new version will get the version number of the old version, it will also take over the old preview image.Comparing the creation date of the image with the object detects this case. + +1.1.2 (2014-04-10) +--------------------- +- create fixed width image with proportional height + +1.1.1 (2014-03-18) +--------------------- +- add converters for .tar.gz, .ps, .txt + +1.1.0 (2013-04-29) +--------------------- +- preview image can also be created from a document file (SeedDMS_Core_DocumentFile) + +1.0.0 (2012-11-20) +--------------------- +- initial version + diff --git a/SeedDMS_Preview/composer.json b/SeedDMS_Preview/composer.json new file mode 100644 index 000000000..9a4b5a11a --- /dev/null +++ b/SeedDMS_Preview/composer.json @@ -0,0 +1,23 @@ +{ + "name": "seeddms/preview", + "description": "Create Preview images, pdf and txt for for SeedDMS ", + "type": "library", + "license": "GPL-2.0-or-later", + "minimum-stability": "dev", + "autoload": { + "psr-4": { + "Seeddms\\Preview\\": "Preview/" + }, + "classmap": ["Preview/"] + }, + "authors": [ + { + "name": "Uwe Steinmann", + "email": "info@seeddms.org" + } + ], + "require-dev": { + "phpunit/phpunit": "^9" + } + +} diff --git a/SeedDMS_Preview/package.xml b/SeedDMS_Preview/package.xml index cddd83d25..d6dd7bc59 100644 --- a/SeedDMS_Preview/package.xml +++ b/SeedDMS_Preview/package.xml @@ -73,7 +73,7 @@ GPL License - initial version +- initial version @@ -89,7 +89,7 @@ GPL License -preview image can also be created from a document file (SeedDMS_Core_DocumentFile) +- preview image can also be created from a document file (SeedDMS_Core_DocumentFile) @@ -105,7 +105,7 @@ preview image can also be created from a document file (SeedDMS_Core_DocumentFil GPL License -add converters for .tar.gz, .ps, .txt +- add converters for .tar.gz, .ps, .txt @@ -121,7 +121,7 @@ add converters for .tar.gz, .ps, .txt GPL License -create fixed width image with proportional height +- create fixed width image with proportional height @@ -137,7 +137,7 @@ create fixed width image with proportional height GPL License -preview images will also be recreated if the object this image belongs is of newer date than the image itself. This happens if versions are being deleted and than a new version is uploaded. Because the new version will get the version number of the old version, it will also take over the old preview image.Comparing the creation date of the image with the object detects this case. +- preview images will also be recreated if the object this image belongs is of newer date than the image itself. This happens if versions are being deleted and than a new version is uploaded. Because the new version will get the version number of the old version, it will also take over the old preview image.Comparing the creation date of the image with the object detects this case. @@ -153,7 +153,7 @@ preview images will also be recreated if the object this image belongs is of new GPL License -command for creating the preview will be called with a given timeout +- command for creating the preview will be called with a given timeout @@ -169,8 +169,8 @@ command for creating the preview will be called with a given timeout GPL License -add method getFilesize() -timeout for external commands can be passed to contructor of SeedDMS_Preview_Previewer +- add method getFilesize() +- timeout for external commands can be passed to contructor of SeedDMS_Preview_Previewer @@ -186,7 +186,7 @@ timeout for external commands can be passed to contructor of SeedDMS_Preview_Pre GPL License -check if object passed to createPreview(), hasPreview() is not null +- check if object passed to createPreview(), hasPreview() is not null @@ -202,7 +202,7 @@ check if object passed to createPreview(), hasPreview() is not null GPL License -set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 +- set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 @@ -218,7 +218,7 @@ set last parameter of stream_select() to 200000 micro sec. in case the timeout i GPL License -pass variables to stream_select (required by php7) +- pass variables to stream_select (required by php7) @@ -234,11 +234,11 @@ pass variables to stream_select (required by php7) GPL License -add more documentation -finish deletePreview() -add new method deleteDocumentPreviews() -fix calculation of timeout (Bug #269) -check if cache dir exists before deleting it in deleteDocumentPreviews() +- add more documentation +- finish deletePreview() +- add new method deleteDocumentPreviews() +- fix calculation of timeout (Bug #269) +- check if cache dir exists before deleting it in deleteDocumentPreviews() @@ -254,7 +254,7 @@ check if cache dir exists before deleting it in deleteDocumentPreviews() GPL License -add new previewer which converts document to pdf instead of png +- add new previewer which converts document to pdf instead of png @@ -270,7 +270,7 @@ add new previewer which converts document to pdf instead of png GPL License -setConverters() overrides exiting converters +- setConverters() overrides exiting converters @@ -286,8 +286,8 @@ setConverters() overrides exiting converters GPL License -commands can be set for mimetypes 'xxxx/*' and '*' -pass mimetype as parameter '%m' to converter +- commands can be set for mimetypes 'xxxx/*' and '*' +- pass mimetype as parameter '%m' to converter @@ -303,7 +303,7 @@ pass mimetype as parameter '%m' to converter GPL License -createPreview() returns false if running the converter command fails +- createPreview() returns false if running the converter command fails @@ -319,7 +319,7 @@ createPreview() returns false if running the converter command fails GPL License -fix typo in converter for tar.gz files +- fix typo in converter for tar.gz files @@ -335,7 +335,7 @@ fix typo in converter for tar.gz files GPL License -SeedDMS_Preview_Base::hasConverter() returns only try if command is set +- SeedDMS_Preview_Base::hasConverter() returns only try if command is set @@ -351,8 +351,8 @@ SeedDMS_Preview_Base::hasConverter() returns only try if command is set GPL License -SeedDMS_Preview_Base::setConverters() overrides existing converters. -New method SeedDMS_Preview_Base::addConverters() merges new converters with old ones. +- SeedDMS_Preview_Base::setConverters() overrides existing converters. +- New method SeedDMS_Preview_Base::addConverters() merges new converters with old ones. @@ -368,9 +368,9 @@ New method SeedDMS_Preview_Base::addConverters() merges new converters with old GPL License -add SeedDMS_Preview_Base::sendFile() as a replacement for readfile() which uses -mod_xsendfile if available -execWithTimeout() reads data from stderr and returns it together with stdout in array +- add SeedDMS_Preview_Base::sendFile() as a replacement for readfile() which uses +- mod_xsendfile if available +- execWithTimeout() reads data from stderr and returns it together with stdout in array @@ -386,7 +386,7 @@ execWithTimeout() reads data from stderr and returns it together with stdout in GPL License -preview is also created if SeedDMS_Core_DocumentContent has a child class +- preview is also created if SeedDMS_Core_DocumentContent has a child class @@ -402,8 +402,8 @@ preview is also created if SeedDMS_Core_DocumentContent has a child class GPL License -make sure list of converters is always an array -usage of mod_sendfile can be configured +- make sure list of converters is always an array +- usage of mod_sendfile can be configured @@ -419,8 +419,8 @@ usage of mod_sendfile can be configured GPL License -new parameter for enabling/disabling xsendfile -fix creation of pdf preview if document content class is not SeedDMS_Core_DocumentContent +- new parameter for enabling/disabling xsendfile +- fix creation of pdf preview if document content class is not SeedDMS_Core_DocumentContent @@ -436,7 +436,7 @@ fix creation of pdf preview if document content class is not SeedDMS_Core_Docume GPL License -add new methode getPreviewFile() +- add new methode getPreviewFile() @@ -452,7 +452,7 @@ add new methode getPreviewFile() GPL License -add parameter $target to SeedDMS_Preview_pdfPreviewer::hasRawPreview() and SeedDMS_Preview_pdfPreviewer::getRawPreview() +- add parameter $target to SeedDMS_Preview_pdfPreviewer::hasRawPreview() and SeedDMS_Preview_pdfPreviewer::getRawPreview() @@ -468,8 +468,8 @@ add parameter $target to SeedDMS_Preview_pdfPreviewer::hasRawPreview() and SeedD GPL License -set header Content-Length -update package description +- set header Content-Length +- update package description diff --git a/SeedDMS_SQLiteFTS/CHANGELOG.md b/SeedDMS_SQLiteFTS/CHANGELOG.md new file mode 100644 index 000000000..d75aace4c --- /dev/null +++ b/SeedDMS_SQLiteFTS/CHANGELOG.md @@ -0,0 +1,89 @@ +1.0.18 (2023-01-09) +--------------------- +- add optional parameter $order to SeedDMS_SQLiteFTS_Indexer::find() +- add optional parameters $query and $col to SeedDMS_SQLiteFTS_Indexer::terms() +- IndexedDocument() accepts a callable for conversion to text +- remove stop words from content + +1.0.17 (2022-03-04) +--------------------- +- throw exeption in find() instead of returning false +- fix query if rootFolder or startFolder is set + + +1.0.16 (2021-05-10) +--------------------- +- close pipes in execWithTimeout(), also return exit code of command +- add support for fts5 (make it the default) +- add class SeedDMS_SQLiteFTS_Field + +1.0.15 (2020-12-12) +--------------------- +- add indexing folders + +1.0.14 (2020-09-11) +--------------------- +- add searching for document status +- search even if query is empty (will find all documents) +- parameters for SeedDMS_SQLiteFTS_Search::search() has changed +- SeedDMS_Lucene_Search::search() returns array of hits, count and facets +- pass config array instead of index directory to SeedDMS_Lucene_Indexer::create() + and SeedDMS_Lucene_Indexer::open() + +1.0.13 (2020-09-02) +--------------------- +- add user to list of terms + +1.0.12 (2020-09-02) +--------------------- +- Index users with at least read access on a document + +1.0.11 (2019-11-28) +--------------------- +- Set 'created' in index to creation date of indexed content (was set to current +timestamp) + +1.0.10 (2018-04-11) +--------------------- +- IndexedDocument() remembers cmd and mimetype + +1.0.9 (2018-01-30) +--------------------- +- execWithTimeout() reads data from stderr and saves it into error msg + +1.0.8 (2017-12-04) +--------------------- +- allow conversion commands for mimetypes with wildcards + +1.0.7 (2017-03-01) +--------------------- +- catch exception in execWithTimeout() + +1.0.6 (2016-03-29) +--------------------- +- fix calculation of timeout (see bug #269) + +1.0.5 (2016-03-29) +--------------------- +- set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 + +1.0.4 (2016-03-15) +--------------------- +- make it work with sqlite3 < 3.8.0 + +1.0.3 (2016-02-01) +--------------------- +- add command for indexing postѕcript files + +1.0.2 (2016-01-10) +--------------------- +- check if index exists before removing it when creating a new one + +1.0.1 (2015-11-16) +--------------------- +- add __get() to SQLiteFTS_Document because class.IndexInfo.php access class variable title which doesn't exists + +1.0.0 (2015-08-10) +--------------------- +- initial release + diff --git a/SeedDMS_SQLiteFTS/composer.json b/SeedDMS_SQLiteFTS/composer.json new file mode 100644 index 000000000..90a19cd7d --- /dev/null +++ b/SeedDMS_SQLiteFTS/composer.json @@ -0,0 +1,23 @@ +{ + "name": "seeddms/lucene", + "description": "SQLiteFTS based fulltext search for SeedDMS ", + "type": "library", + "license": "GPL-2.0-or-later", + "minimum-stability": "dev", + "autoload": { + "psr-4": { + "Seeddms\\SQLiteFTS\\": "SQLiteFTS/" + }, + "classmap": ["SQLiteFTS/"] + }, + "authors": [ + { + "name": "Uwe Steinmann", + "email": "info@seeddms.org" + } + ], + "require-dev": { + "phpunit/phpunit": "^9" + } + +} diff --git a/SeedDMS_SQLiteFTS/package.xml b/SeedDMS_SQLiteFTS/package.xml index 3813a6252..2b7dfe0b5 100644 --- a/SeedDMS_SQLiteFTS/package.xml +++ b/SeedDMS_SQLiteFTS/package.xml @@ -88,7 +88,7 @@ GPL License -initial release +- initial release @@ -104,7 +104,7 @@ initial release GPL License -add __get() to SQLiteFTS_Document because class.IndexInfo.php access class variable title which doesn't exists +- add __get() to SQLiteFTS_Document because class.IndexInfo.php access class variable title which doesn't exists @@ -120,7 +120,7 @@ add __get() to SQLiteFTS_Document because class.IndexInfo.php access class varia GPL License -check if index exists before removing it when creating a new one +- check if index exists before removing it when creating a new one @@ -136,7 +136,7 @@ check if index exists before removing it when creating a new one GPL License -add command for indexing postѕcript files +- add command for indexing postѕcript files @@ -152,7 +152,7 @@ add command for indexing postѕcript files GPL License -make it work with sqlite3 < 3.8.0 +- make it work with sqlite3 < 3.8.0 @@ -168,7 +168,7 @@ make it work with sqlite3 < 3.8.0 GPL License -set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 +- set last parameter of stream_select() to 200000 micro sec. in case the timeout in sec. is set to 0 @@ -184,7 +184,7 @@ set last parameter of stream_select() to 200000 micro sec. in case the timeout i GPL License -fix calculation of timeout (see bug #269) +- fix calculation of timeout (see bug #269) @@ -200,7 +200,7 @@ fix calculation of timeout (see bug #269) GPL License -catch exception in execWithTimeout() +- catch exception in execWithTimeout() @@ -216,7 +216,7 @@ catch exception in execWithTimeout() GPL License -allow conversion commands for mimetypes with wildcards +- allow conversion commands for mimetypes with wildcards @@ -232,7 +232,7 @@ allow conversion commands for mimetypes with wildcards GPL License -execWithTimeout() reads data from stderr and saves it into error msg +- execWithTimeout() reads data from stderr and saves it into error msg @@ -248,7 +248,7 @@ execWithTimeout() reads data from stderr and saves it into error msg GPL License -IndexedDocument() remembers cmd and mimetype +- IndexedDocument() remembers cmd and mimetype @@ -264,7 +264,7 @@ IndexedDocument() remembers cmd and mimetype GPL License -Set 'created' in index to creation date of indexed content (was set to current +- Set 'created' in index to creation date of indexed content (was set to current timestamp) @@ -281,7 +281,7 @@ timestamp) GPL License -Index users with at least read access on a document +- Index users with at least read access on a document @@ -297,7 +297,7 @@ Index users with at least read access on a document GPL License -add user to list of terms +- add user to list of terms diff --git a/build.xml b/build.xml index b02d9167e..e2f6e0b81 100644 --- a/build.xml +++ b/build.xml @@ -63,7 +63,7 @@ - + @@ -151,7 +151,7 @@ - + + + diff --git a/composer-dist.json b/composer-dist.json index 10fe5da3c..929b06db9 100644 --- a/composer-dist.json +++ b/composer-dist.json @@ -7,7 +7,6 @@ "erusev/parsedown": "*", "erusev/parsedown-extra": "*", "mibe/feedwriter": "^1.1", - "phpoffice/phpexcel": "^1.8", "phpoffice/phpspreadsheet": "*", "pear/log": "*", "pear/mail": "*", @@ -16,8 +15,51 @@ "pear/auth_sasl": "*", "pear/db": "*", "alecrabbit/php-console-colour": "*", - "dragonmantank/cron-expression": "^2.2", + "dragonmantank/cron-expression": "^3", "zf1/zend-search-lucene": "*", - "symfony/http-foundation": "^5.4" - } + "symfony/http-foundation": "^5.4", + "seeddms/core": "dev-master", + "seeddms/lucene": "dev-master", + "seeddms/preview": "dev-master", + "seeddms/sqlitefts": "dev-master", + "seeddms/http_webdav_server": "dev-master" + }, + "repositories": [ + { + "type": "path", + "url": "/home/cvs/seeddms-ext/core", + "options": { + "symlink": false + } + }, + { + "type": "path", + "url": "/home/cvs/seeddms-ext/lucene", + "options": { + "symlink": false + } + }, + { + "type": "path", + "url": "/home/cvs/seeddms-ext/preview", + "options": { + "symlink": false + } + }, + { + "type": "path", + "url": "/home/cvs/seeddms-ext/sqlitefts", + "options": { + "symlink": false + } + }, + { + "type": "path", + "url": "/home/cvs/seeddms-ext/http_webdav_server", + "options": { + "symlink": false + } + } + ] + } diff --git a/controllers/class.EmptyFolder.php b/controllers/class.EmptyFolder.php index eb1c5a629..4fdc7ef62 100644 --- a/controllers/class.EmptyFolder.php +++ b/controllers/class.EmptyFolder.php @@ -77,7 +77,6 @@ class SeedDMS_Controller_EmptyFolder extends SeedDMS_Controller_Common { } /* Register another callback which removes the preview images of the document */ - require_once("SeedDMS/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $dms->addCallback('onPreRemoveDocument', 'SeedDMS_Controller_EmptyFolder::removePreviews', array($previewer)); diff --git a/controllers/class.RemoveDocument.php b/controllers/class.RemoveDocument.php index 56e165e6a..96a5c1bb3 100644 --- a/controllers/class.RemoveDocument.php +++ b/controllers/class.RemoveDocument.php @@ -43,7 +43,6 @@ class SeedDMS_Controller_RemoveDocument extends SeedDMS_Controller_Common { $result = $this->callHook('removeDocument', $document); if($result === null) { - require_once("SeedDMS/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $previewer->deleteDocumentPreviews($document); if (!$document->remove()) { diff --git a/controllers/class.RemoveFolder.php b/controllers/class.RemoveFolder.php index ac64f246a..6b3a8fc73 100644 --- a/controllers/class.RemoveFolder.php +++ b/controllers/class.RemoveFolder.php @@ -77,7 +77,6 @@ class SeedDMS_Controller_RemoveFolder extends SeedDMS_Controller_Common { } /* Register another callback which removes the preview images of the document */ - require_once("SeedDMS/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $dms->addCallback('onPreRemoveDocument', 'SeedDMS_Controller_RemoveFolder::removePreviews', array($previewer)); diff --git a/inc/inc.ClassConversionMgr.php b/inc/inc.ClassConversionMgr.php index a4606327e..557f796c8 100644 --- a/inc/inc.ClassConversionMgr.php +++ b/inc/inc.ClassConversionMgr.php @@ -29,15 +29,22 @@ require_once("inc/inc.ClassConversionServiceTextToImage.php"); */ class SeedDMS_ConversionMgr { /** - * List of services for searching fulltext + * List of services for converting documents */ public $services; + /** + * @var $success set to false if conversion failed + */ + protected $success; + public function __construct() { $this->services = array(); + $this->success = true; } public function addService($service) { + $service->setConversionMgr($this); $this->services[$service->from][$service->to][] = $service; return $service; } @@ -53,22 +60,30 @@ class SeedDMS_ConversionMgr { * Return the service that would be tried first for converting * the document. * - * The conversion may not use this service but choose a different + * The conversion manager may not use this service but choose a different * one when it fails. */ - public function getService($from, $to) { + public function getService($from, $to) { /* {{{ */ if(!empty($this->services[$from][$to])) return end($this->services[$from][$to]); else return null; - } + } /* }}} */ - public function getServices() { + public function getServices() { /* {{{ */ return $this->services; - } + } /* }}} */ + + public function wasSuccessful() { /* {{{ */ + return $this->success; + } /* }}} */ /** - * Convert a file + * Convert a file from one format into another format + * + * This method will try each conversion service until a service + * fails or was successful. If a service succeeds it must not + * return false, null, '' or 0 * * @param string $file name of file to convert * @param string $from mimetype of input file @@ -86,8 +101,10 @@ class SeedDMS_ConversionMgr { for(end($services); key($services)!==null; prev($services)) { $service = current($services); $text = $service->convert($file, $target, $params); - if(!$service->wasSuccessful()) + if(!$service->wasSuccessful()) { + $this->success = false; return false; + } if($text) return $text; } diff --git a/inc/inc.ClassConversionServiceBase.php b/inc/inc.ClassConversionServiceBase.php index 9ea95812e..b90d3e4bc 100644 --- a/inc/inc.ClassConversionServiceBase.php +++ b/inc/inc.ClassConversionServiceBase.php @@ -36,6 +36,11 @@ abstract class SeedDMS_ConversionServiceBase { */ protected $logger; + /** + * conversion manager + */ + protected $conversionmgr; + /** * @var $success set to false if conversion failed */ @@ -45,12 +50,22 @@ abstract class SeedDMS_ConversionServiceBase { $this->from = null; $this->to = null; $this->success = true; + $this->logger = null; + $this->conversionmgr = null; } public function setLogger($logger) { $this->logger = $logger; } + public function setConversionMgr($conversionmgr) { + $this->conversionmgr = $conversionmgr; + } + + public function getConversionMgr() { + return $this->conversionmgr; + } + public function getInfo() { return 'Conversion service'; } diff --git a/inc/inc.ClassSettings.php b/inc/inc.ClassSettings.php index 9a84b393c..91e0a8c64 100644 --- a/inc/inc.ClassSettings.php +++ b/inc/inc.ClassSettings.php @@ -1566,7 +1566,7 @@ class Settings { /* {{{ */ if(!empty($this->_coreDir)) require_once($this->_coreDir.'/Core.php'); else - require_once($this->_rootDir.'../pear/SeedDMS/Core.php'); + require_once($this->_rootDir.'../pear/vendor/seeddms/core/Core.php'); $tmpcore = new SeedDMS_Core_DMS(null, $this->_contentDir); $db = new SeedDMS_Core_DatabaseAccess($this->_dbDriver, $this->_dbHostname, $this->_dbUser, $this->_dbPass, $this->_dbDatabase); if(!$db->connect()) { diff --git a/inc/inc.FulltextInit.php b/inc/inc.FulltextInit.php index 9b0e310fd..e619c104b 100644 --- a/inc/inc.FulltextInit.php +++ b/inc/inc.FulltextInit.php @@ -15,7 +15,7 @@ if($settings->_enableFullSearch) { ); $fulltextservice->addService('sqlitefts', $indexconf); - require_once('SeedDMS/SQLiteFTS.php'); + require_once('vendor/seeddms/sqlitefts/SQLiteFTS.php'); } elseif($settings->_fullSearchEngine == 'lucene') { $indexconf = array( 'Indexer' => 'SeedDMS_Lucene_Indexer', @@ -28,7 +28,7 @@ if($settings->_enableFullSearch) { if(!empty($settings->_luceneClassDir)) require_once($settings->_luceneClassDir.'/Lucene.php'); else - require_once('SeedDMS/Lucene.php'); + require_once('vendor/seeddms/lucene/Lucene.php'); } else { $indexconf = null; if(isset($GLOBALS['SEEDDMS_HOOKS']['initFulltext'])) { @@ -49,7 +49,7 @@ if($settings->_enableFullSearch) { $fulltextservice->setConversionMgr($conversionmgr); $fulltextservice->setMaxSize($settings->_maxSizeForFullText); $fulltextservice->setCmdTimeout($settings->_cmdTimeout); - require_once("SeedDMS/Preview.php"); +// require_once("vendor/seeddms/preview/Preview.php"); $txtpreviewer = new SeedDMS_Preview_TxtPreviewer($settings->_cacheDir, $settings->_cmdTimeout, $settings->_enableXsendfile); if($conversionmgr) $txtpreviewer->setConversionMgr($conversionmgr); diff --git a/inc/inc.Init.php b/inc/inc.Init.php index 69ed0c6a4..1b4aaba5a 100644 --- a/inc/inc.Init.php +++ b/inc/inc.Init.php @@ -23,6 +23,6 @@ use Symfony\Component\HttpFoundation\Request; if(!empty($settings->_coreDir)) require_once($settings->_coreDir.'/Core.php'); else - require_once('SeedDMS/Core.php'); + require_once('vendor/seeddms/core/Core.php'); $request = Request::createFromGlobals(); diff --git a/install/update-1.8.1/update.php b/install/update-1.8.1/update.php index 9e483850b..388b970c8 100644 --- a/install/update-1.8.1/update.php +++ b/install/update-1.8.1/update.php @@ -18,16 +18,12 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -include("../inc/inc.Settings.php"); -include("../inc/inc.AccessUtils.php"); -include("../inc/inc.ClassAccess.php"); -include("../inc/inc.ClassDocument.php"); -include("../inc/inc.ClassFolder.php"); -include("../inc/inc.ClassGroup.php"); -include("../inc/inc.ClassUser.php"); -include("../inc/inc.DBAccess.php"); -include("../inc/inc.FileUtils.php"); -include("../inc/inc.Authentication.php"); +include("../../inc/inc.Settings.php"); +include("../../inc/inc.Utils.php"); +include("../../inc/inc.LogInit.php"); +include("../../inc/inc.Language.php"); +include("../../inc/inc.Init.php"); +include("../../inc/inc.DBInit.php"); print ""; @@ -42,7 +38,7 @@ function update_content() GLOBAL $db,$settings; // create temp folder - if (!makedir($settings->_contentDir."/temp")) return false; + if (!SeedDMS_Core_File::makeDir($settings->_contentDir."/temp")) return false; // for all contents $queryStr = "SELECT * FROM tblDocumentContent"; @@ -53,19 +49,19 @@ function update_content() for ($i=0;$i_contentDir."/temp/".$contents[$i]["document"])) return false; + if (!SeedDMS_Core_File::makeDir($settings->_contentDir."/temp/".$contents[$i]["document"])) return false; // move every content in temp/documentID/version.fileType $source = $settings->_contentDir."/".$contents[$i]["dir"]."/data".$contents[$i]["fileType"]; $target = $settings->_contentDir."/temp/".$contents[$i]["document"]."/".$contents[$i]["version"].$contents[$i]["fileType"]; - if (!copyFile($source, $target)) return false; + if (!SeedDMS_Core_File::copyFile($source, $target)) return false; } // change directory - if (!renameDir($settings->_contentDir."/".$settings->_contentOffsetDir,$settings->_contentDir."/old")) return false; - if (!renameDir($settings->_contentDir."/temp",$settings->_contentDir."/".$settings->_contentOffsetDir)) return false; + if (!SeedDMS_Core_File::renameDir($settings->_contentDir."/".$settings->_contentOffsetDir,$settings->_contentDir."/old")) return false; + if (!SeedDMS_Core_File::renameDir($settings->_contentDir."/temp",$settings->_contentDir."/".$settings->_contentOffsetDir)) return false; return true; } diff --git a/op/op.Ajax.php b/op/op.Ajax.php index c88e67c72..0f4191257 100644 --- a/op/op.Ajax.php +++ b/op/op.Ajax.php @@ -520,7 +520,6 @@ switch($command) { $previewer->deleteDocumentPreviews($document); return null; } - require_once("SeedDMS/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $dms->addCallback('onPreRemoveDocument', 'removePreviews', array($previewer)); diff --git a/op/op.DropFolderPreview.php b/op/op.DropFolderPreview.php index 96f2209d1..b70d01e6a 100644 --- a/op/op.DropFolderPreview.php +++ b/op/op.DropFolderPreview.php @@ -29,11 +29,6 @@ include("../inc/inc.DBInit.php"); include("../inc/inc.ClassUI.php"); include("../inc/inc.Authentication.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - if (!isset($_GET["filename"])) { exit; } diff --git a/op/op.PdfPreview.php b/op/op.PdfPreview.php index 4b963426f..178470706 100644 --- a/op/op.PdfPreview.php +++ b/op/op.PdfPreview.php @@ -30,11 +30,6 @@ include("../inc/inc.ClassUI.php"); include("../inc/inc.ClassController.php"); include("../inc/inc.Authentication.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - $tmp = explode('.', basename($_SERVER['SCRIPT_FILENAME'])); $controller = Controller::factory($tmp[1], array('dms'=>$dms, 'user'=>$user)); diff --git a/op/op.Preview.php b/op/op.Preview.php index 3ded08099..9ceacf462 100644 --- a/op/op.Preview.php +++ b/op/op.Preview.php @@ -30,11 +30,6 @@ include("../inc/inc.ClassUI.php"); include("../inc/inc.ClassController.php"); include("../inc/inc.Authentication.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - $tmp = explode('.', basename($_SERVER['SCRIPT_FILENAME'])); $controller = Controller::factory($tmp[1], array('dms'=>$dms, 'user'=>$user)); diff --git a/op/op.RemoveDocument.php b/op/op.RemoveDocument.php index 591214d33..b4d7920b4 100644 --- a/op/op.RemoveDocument.php +++ b/op/op.RemoveDocument.php @@ -68,7 +68,6 @@ if($document->isLocked()) { $folder = $document->getFolder(); /* Remove all preview images. */ -require_once("SeedDMS/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $previewer->deleteDocumentPreviews($document); diff --git a/op/op.RemoveDocumentFile.php b/op/op.RemoveDocumentFile.php index ea7e88953..e2e23d152 100644 --- a/op/op.RemoveDocumentFile.php +++ b/op/op.RemoveDocumentFile.php @@ -65,7 +65,6 @@ if (($document->getAccessMode($user, 'removeDocumentFile') < M_ALL)&&($user->get } /* Remove preview image. */ -require_once("SeedDMS/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $previewer->deletePreview($file, $settings->_previewWidthDetail); diff --git a/op/op.RemoveFolder.php b/op/op.RemoveFolder.php index 312945ea1..8f5f0c9e7 100644 --- a/op/op.RemoveFolder.php +++ b/op/op.RemoveFolder.php @@ -62,7 +62,7 @@ function removePreviews($arr, $document) { $previewer->deleteDocumentPreviews($document); return null; } -require_once("SeedDMS/Preview.php"); +require_once("vendor/seeddms/preview/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $dms->addCallback('onPreRemoveDocument', 'removePreviews', array($previewer)); */ diff --git a/op/op.RemoveVersion.php b/op/op.RemoveVersion.php index 0d9045a24..8c224209a 100644 --- a/op/op.RemoveVersion.php +++ b/op/op.RemoveVersion.php @@ -62,7 +62,6 @@ if (!is_object($version)) { UI::exitError(getMLText("document_title", array("documentname" => $document->getName())),getMLText("invalid_version")); } -require_once("SeedDMS/Preview.php"); $previewer = new SeedDMS_Preview_Previewer($settings->_cacheDir); $folder = $document->getFolder(); /* Check if there is just one version. In that case remove the document */ diff --git a/op/op.TimelineFeedPreview.php b/op/op.TimelineFeedPreview.php index 183b3d4b7..83a457df5 100644 --- a/op/op.TimelineFeedPreview.php +++ b/op/op.TimelineFeedPreview.php @@ -30,11 +30,6 @@ include("../inc/inc.ClassUI.php"); include("../inc/inc.ClassController.php"); //include("../inc/inc.BasicAuthentication.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - if(empty($_GET['hash'])) exit; diff --git a/out/out.OpensearchDesc.php b/out/out.OpensearchDesc.php index 1055d87dd..18569d6fb 100644 --- a/out/out.OpensearchDesc.php +++ b/out/out.OpensearchDesc.php @@ -32,7 +32,7 @@ require_once("inc/inc.ClassAccessOperation.php"); //require_once("inc/inc.Authentication.php"); $tmp = explode('.', basename($_SERVER['SCRIPT_FILENAME'])); -$view = UI::factory($theme, $tmp[1], array('dms'=>$dms, 'user'=null)); +$view = UI::factory($theme, $tmp[1], array('dms'=>$dms, 'user'=>null)); if($view) { $view($_GET); exit; diff --git a/out/out.TransmittalMgr.php b/out/out.TransmittalMgr.php index 22aac353f..493d8f07a 100644 --- a/out/out.TransmittalMgr.php +++ b/out/out.TransmittalMgr.php @@ -32,11 +32,6 @@ require_once("inc/inc.Authentication.php"); require_once("../views/".$theme."/trait.TransmittalUpdateButton.php"); require_once("../views/".$theme."/trait.TransmittalDeleteButton.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - $isajax = isset($_GET['action']) && ($_GET['action'] == 'items' || $_GET['action'] == 'form'); $tmp = explode('.', basename($_SERVER['SCRIPT_FILENAME'])); $view = UI::factory($theme, $tmp[1], array('dms'=>$dms, 'user'=>$user)); diff --git a/restapi/index.php b/restapi/index.php index 462c3c535..dd4fb21c9 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -91,6 +91,8 @@ class RestapiController { /* {{{ */ 'sequence'=>$document->getSequence(), 'expires'=>$document->getExpires() ? date('Y-m-d H:i:s', $document->getExpires()) : "", 'mimetype'=>$lc->getMimeType(), + 'filetype'=>$lc->getFileType(), + 'origfilename'=>$lc->getOriginalFileName(), 'version'=>$lc->getVersion(), 'version_comment'=>$lc->getComment(), 'version_date'=>date('Y-m-d H:i:s', $lc->getDate()), @@ -627,6 +629,7 @@ class RestapiController { /* {{{ */ $dms = $this->container->dms; $userobj = $this->container->userobj; $settings = $this->container->config; + $notifier = $this->container->notifier; if(!$userobj) { return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); @@ -718,13 +721,12 @@ class RestapiController { /* {{{ */ $fileType = ".".pathinfo($origfilename, PATHINFO_EXTENSION); finfo_close($finfo); $res = $mfolder->addDocument($docname, $comment, $expires, $owner ? $owner : $userobj, $keywords, $cats, $temp, $origfilename ? $origfilename : basename($temp), $fileType, $userfiletype, $sequence, array(), array(), $reqversion, $version_comment, $attributes); - // addDocumentCategories($res, $categories); - // setDocumentAttributes($res, $attributes); - unlink($temp); if($res) { $doc = $res[0]; - // $rec = array('id'=>(int)$doc->getId(), 'name'=>$doc->getName(), 'version'=>$doc->getLatestContent()->getVersion()); + if($notifier) { + $notifier->sendNewDocumentMail($doc, $userobj); + } return $response->withJson(array('success'=>true, 'message'=>'Upload succeded', 'data'=>$this->__getLatestVersionData($doc->getLatestContent())), 201); } else { return $response->withJson(array('success'=>false, 'message'=>'Upload failed', 'data'=>''), 500); @@ -745,6 +747,7 @@ class RestapiController { /* {{{ */ $dms = $this->container->dms; $userobj = $this->container->userobj; $settings = $this->container->config; + $notifier = $this->container->notifier; if(!$userobj) { return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); @@ -801,10 +804,20 @@ class RestapiController { /* {{{ */ $userfiletype = finfo_file($finfo, $temp); $fileType = ".".pathinfo($origfilename, PATHINFO_EXTENSION); finfo_close($finfo); + $oldexpires = $document->getExpires(); $res=$document->addContent($comment, $userobj, $temp, $origfilename, $fileType, $userfiletype, array(), array(), 0, $attributes); unlink($temp); if($res) { + if($notifier) { + $notifier->sendNewDocumentVersionMail($document, $userobj); + + /* Actually there is not need to even try sending this mail + * because the expiration date cannot be set when calling + * this rest api endpoint. + */ + $notifier->sendChangedExpiryMail($document, $userobj, $oldexpires); + } $rec = array('id'=>(int)$document->getId(), 'name'=>$document->getName(), 'version'=>$document->getLatestContent()->getVersion()); return $response->withJson(array('success'=>true, 'message'=>'Upload succeded', 'data'=>$rec), 200); } else { @@ -825,6 +838,7 @@ class RestapiController { /* {{{ */ $dms = $this->container->dms; $userobj = $this->container->userobj; $settings = $this->container->config; + $notifier = $this->container->notifier; if(!$userobj) { return $response->withJson(array('success'=>false, 'message'=>'Not logged in', 'data'=>''), 403); @@ -867,6 +881,9 @@ class RestapiController { /* {{{ */ unlink($temp); if($res) { $doc = $res[0]; + if($notifier) { + $notifier->sendNewDocumentMail($doc, $userobj); + } $rec = array('id'=>(int)$doc->getId(), 'name'=>$doc->getName()); return $response->withJson(array('success'=>true, 'message'=>'Upload succeded', 'data'=>$rec), 200); } else { @@ -1375,8 +1392,6 @@ class RestapiController { /* {{{ */ } /* }}} */ function getDocumentPreview($request, $response, $args) { /* {{{ */ - require_once "SeedDMS/Preview.php"; - $dms = $this->container->dms; $userobj = $this->container->userobj; $settings = $this->container->config; @@ -2594,6 +2609,13 @@ class TestController { /* {{{ */ public function echoData($request, $response, $args) { /* {{{ */ return $response->withJson(array('success'=>true, 'message'=>'This is the result of the echo call.', 'data'=>$args['data']), 200); } /* }}} */ + + public function version($request, $response, $args) { /* {{{ */ + $logger = $this->container->logger; + + $v = new SeedDMS_Version(); + return $response->withJson(array('success'=>true, 'message'=>'This is '.$v->banner(), 'data'=>['major'=>$v->majorVersion(), 'minor'=>$v->minorVersion(), 'subminor'=>$v->subminorVersion()]), 200); + } /* }}} */ } /* }}} */ /* Middleware for authentication */ @@ -2643,7 +2665,7 @@ class RestapiAuth { /* {{{ */ */ if($request->getMethod() == 'OPTIONS') { $logger->log("Received preflight options request", PEAR_LOG_DEBUG); - } elseif(!in_array($request->getUri()->getPath(), array('login')) && substr($request->getUri()->getPath(), 0, 5) != 'echo/') { + } elseif(!in_array($request->getUri()->getPath(), array('login')) && substr($request->getUri()->getPath(), 0, 5) != 'echo/' && $request->getUri()->getPath() != 'version') { $userobj = null; if(!empty($this->container->environment['HTTP_AUTHORIZATION']) && !empty($settings->_apiKey) && !empty($settings->_apiUserId)) { $logger->log("Authorization key: ".$this->container->environment['HTTP_AUTHORIZATION'], PEAR_LOG_DEBUG); @@ -2801,6 +2823,7 @@ $app->put('/categories/{id}/name', \RestapiController::class.':changeCategoryNam $app->get('/attributedefinitions', \RestapiController::class.':getAttributeDefinitions'); $app->put('/attributedefinitions/{id}/name', \RestapiController::class.':changeAttributeDefinitionName'); $app->get('/echo/{data}', \TestController::class.':echoData'); +$app->get('/version', \TestController::class.':version'); $app->get('/statstotal', \RestapiController::class.':getStatsTotal'); if(isset($GLOBALS['SEEDDMS_HOOKS']['initRestAPI'])) { diff --git a/views/bootstrap/class.ApprovalSummary.php b/views/bootstrap/class.ApprovalSummary.php index 1f23ad40e..c742c16fb 100644 --- a/views/bootstrap/class.ApprovalSummary.php +++ b/views/bootstrap/class.ApprovalSummary.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ApprovalSummary view * diff --git a/views/bootstrap/class.AttributeMgr.php b/views/bootstrap/class.AttributeMgr.php index e0b6e70ae..ee30d5986 100644 --- a/views/bootstrap/class.AttributeMgr.php +++ b/views/bootstrap/class.AttributeMgr.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for AttributeMgr view * diff --git a/views/bootstrap/class.Bootstrap.php b/views/bootstrap/class.Bootstrap.php index bd968eb7d..cbe4f3762 100644 --- a/views/bootstrap/class.Bootstrap.php +++ b/views/bootstrap/class.Bootstrap.php @@ -502,7 +502,7 @@ background-image: linear-gradient(to bottom, #882222, #111111);; /* clipboard {{{ */ if($this->params['enableclipboard']) { echo "
"; - echo "
getID() : 0)."\">
"; + echo "
getID() : 0)."\">
"; echo "
"; } /* }}} End of clipboard */ diff --git a/views/bootstrap/class.Calendar.php b/views/bootstrap/class.Calendar.php index 3e09a3d00..48bb4c3ed 100644 --- a/views/bootstrap/class.Calendar.php +++ b/views/bootstrap/class.Calendar.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for Calendar view * diff --git a/views/bootstrap/class.Categories.php b/views/bootstrap/class.Categories.php index f86268025..0f5381674 100644 --- a/views/bootstrap/class.Categories.php +++ b/views/bootstrap/class.Categories.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for Categories view * diff --git a/views/bootstrap/class.Clipboard.php b/views/bootstrap/class.Clipboard.php index cab5ce739..f1c97fa97 100644 --- a/views/bootstrap/class.Clipboard.php +++ b/views/bootstrap/class.Clipboard.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for clipboard view * @@ -53,54 +48,25 @@ class SeedDMS_View_Clipboard extends SeedDMS_Theme_Style { $menuitems = []; - $content = ''; - $content .= "
    \n"; - $content .= "
  • \n"; - $content .= " ".getMLText('clipboard')." (".count($clipboard['folders'])."/".count($clipboard['docs']).") \n"; - $content .= "
      \n"; $subitems = []; foreach($clipboard['folders'] as $folderid) { if($folder = $this->params['dms']->getFolder($folderid)) { - $content .= "
    • params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=".$folder->getID()."\" class=\"table-row-folder droptarget\" data-droptarget=\"folder_".$folder->getID()."\" rel=\"folder_".$folder->getID()."\" data-name=\"".htmlspecialchars($folder->getName(), ENT_QUOTES)."\" data-uploadformtoken=\"".createFormKey('')."\" formtoken=\"".createFormKey('')."\"> ".htmlspecialchars($folder->getName())."
    • \n"; - $subitems[] = array('label'=>' '.$folder->getName(), 'link'=>$this->params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=".$folder->getID(), 'class'=>"table-row-folder droptarget", 'rel'=>"folder_".$folder->getID(), 'attributes'=>array(array('data-droptarget', "folder_".$folder->getID()), array('data-name', htmlspecialchars($folder->getName(), ENT_QUOTES)))); + $subitems[] = array('label'=>' '.$folder->getName(), 'link'=>$this->params['settings']->_httpRoot."out/out.ViewFolder.php?folderid=".$folder->getID(), 'class'=>"table-row-folder droptarget", 'attributes'=>array(array('data-droptarget', "folder_".$folder->getID()), array('rel', "folder_".$folder->getID()), array('data-name', htmlspecialchars($folder->getName(), ENT_QUOTES)))); } } foreach($clipboard['docs'] as $docid) { if($document = $this->params['dms']->getDocument($docid)) - $content .= "
    • params['settings']->_httpRoot."out/out.ViewDocument.php?documentid=".$document->getID()."\" class=\"table-row-document droptarget\" data-droptarget=\"document_".$document->getID()."\" rel=\"document_".$document->getID()."\" data-name=\"".htmlspecialchars($document->getName(), ENT_QUOTES)."\" formtoken=\"".createFormKey('')."\"> ".htmlspecialchars($document->getName())."
    • \n"; - $subitems[] = array('label'=>' '.$document->getName(), 'link'=>$this->params['settings']->_httpRoot."out/out.ViewDocument.php?documentid=".$document->getID(), 'class'=>"table-row-document droptarget", 'rel'=>"document_".$document->getID(), 'attributes'=>array(array('data-droptarget', "document_".$document->getID()), array('data-name', htmlspecialchars($document->getName(), ENT_QUOTES)))); + $subitems[] = array('label'=>' '.$document->getName(), 'link'=>$this->params['settings']->_httpRoot."out/out.ViewDocument.php?documentid=".$document->getID(), 'class'=>"table-row-document droptarget", 'attributes'=>array(array('data-droptarget', "document_".$document->getID()), array('rel', "document_".$document->getID()), array('formtoken', createFormKey('')), array('data-name', htmlspecialchars($document->getName(), ENT_QUOTES)))); } - $content .= "
    • \n"; $subitems[] = array('divider'=>true); if(isset($this->params['folder']) && $this->params['folder']->getAccessMode($this->params['user']) >= M_READWRITE) { - $content .= "
    • params['settings']->_httpRoot."op/op.MoveClipboard.php?targetid=".$this->params['folder']->getID()."&refferer=".urlencode($this->params['settings']->_httpRoot.'out/out.ViewFolder.php?folderid='.$this->params['folder']->getID())."\">".getMLText("move_clipboard")."
    • \n"; $subitems[] = array('label'=>getMLText("move_clipboard"), 'link'=>$this->params['settings']->_httpRoot."op/op.MoveClipboard.php?targetid=".$this->params['folder']->getID()."&refferer=".urlencode($this->params['settings']->_httpRoot.'out/out.ViewFolder.php?folderid='.$this->params['folder']->getID())); } -// $content .= "
    • params['refferer'])."\">".getMLText("clear_clipboard")."kkk
    • \n"; -// $content .= "
    • ".getMLText("clear_clipboard")."
    • \n"; $subitems[] = array('label'=>getMLText('clear_clipboard'), 'class'=>'ajax-click', 'attributes'=>array(array('data-href', $this->params['settings']->_httpRoot.'op/op.Ajax.php'), array('data-param1', 'command=clearclipboard'))); if($this->hasHook('clipboardMenuItems')) $subitems = $this->callHook('clipboardMenuItems', $clipboard, $subitems); - /* - foreach($menuitems as $menuitem) { - $content .= "
    • "; - $content .= "getMLText('clipboard')." (".count($clipboard['folders'])."/". count($clipboard['docs']).")", 'children'=>$subitems); self::showNavigationBar($menuitems, array('right'=>true)); -// echo $content; } /* }}} */ /** diff --git a/views/bootstrap/class.DocumentVersionDetail.php b/views/bootstrap/class.DocumentVersionDetail.php index f52e3938c..a5840e941 100644 --- a/views/bootstrap/class.DocumentVersionDetail.php +++ b/views/bootstrap/class.DocumentVersionDetail.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for DocumentVersionDetail view * diff --git a/views/bootstrap/class.DropFolderChooser.php b/views/bootstrap/class.DropFolderChooser.php index 1fabe5865..065430aea 100644 --- a/views/bootstrap/class.DropFolderChooser.php +++ b/views/bootstrap/class.DropFolderChooser.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for DropFolderChooser view * diff --git a/views/bootstrap/class.ExpiredDocuments.php b/views/bootstrap/class.ExpiredDocuments.php index 4c3aacc61..d16cca9c4 100644 --- a/views/bootstrap/class.ExpiredDocuments.php +++ b/views/bootstrap/class.ExpiredDocuments.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ExpiredDocuments view * diff --git a/views/bootstrap/class.GroupMgr.php b/views/bootstrap/class.GroupMgr.php index 5a60899b2..2898b37c5 100644 --- a/views/bootstrap/class.GroupMgr.php +++ b/views/bootstrap/class.GroupMgr.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for GroupMgr view * diff --git a/views/bootstrap/class.ManageNotify.php b/views/bootstrap/class.ManageNotify.php index c5d69635b..fa77b8083 100644 --- a/views/bootstrap/class.ManageNotify.php +++ b/views/bootstrap/class.ManageNotify.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ManageNotify view * diff --git a/views/bootstrap/class.MyDocuments.php b/views/bootstrap/class.MyDocuments.php index d89a56830..115268d1e 100644 --- a/views/bootstrap/class.MyDocuments.php +++ b/views/bootstrap/class.MyDocuments.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for MyDocuments view * diff --git a/views/bootstrap/class.ObjectCheck.php b/views/bootstrap/class.ObjectCheck.php index c6eb322ca..10c5f2377 100644 --- a/views/bootstrap/class.ObjectCheck.php +++ b/views/bootstrap/class.ObjectCheck.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ObjectCheck view * diff --git a/views/bootstrap/class.ReceiptSummary.php b/views/bootstrap/class.ReceiptSummary.php index 657433a1f..acc2497fe 100644 --- a/views/bootstrap/class.ReceiptSummary.php +++ b/views/bootstrap/class.ReceiptSummary.php @@ -13,16 +13,6 @@ * @version Release: @package_version@ */ -/** - * Include parent class - */ -//require_once("class.Bootstrap.php"); - -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ReceiptSummary view * diff --git a/views/bootstrap/class.RemoveUserFromProcesses.php b/views/bootstrap/class.RemoveUserFromProcesses.php index b56eed221..f343f2fbf 100644 --- a/views/bootstrap/class.RemoveUserFromProcesses.php +++ b/views/bootstrap/class.RemoveUserFromProcesses.php @@ -16,11 +16,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for RemoveUserFromProcesses view * diff --git a/views/bootstrap/class.ReviewSummary.php b/views/bootstrap/class.ReviewSummary.php index 36b8a4f57..873ff7d29 100644 --- a/views/bootstrap/class.ReviewSummary.php +++ b/views/bootstrap/class.ReviewSummary.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ReviewSummary view * diff --git a/views/bootstrap/class.RevisionSummary.php b/views/bootstrap/class.RevisionSummary.php index 323ef6a6f..42df251b9 100644 --- a/views/bootstrap/class.RevisionSummary.php +++ b/views/bootstrap/class.RevisionSummary.php @@ -13,16 +13,6 @@ * @version Release: @package_version@ */ -/** - * Include parent class - */ -//require_once("class.Bootstrap.php"); - -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ReviewSummary view * diff --git a/views/bootstrap/class.Search.php b/views/bootstrap/class.Search.php index bf7d3d808..b11b260d6 100644 --- a/views/bootstrap/class.Search.php +++ b/views/bootstrap/class.Search.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for Search result view * @@ -1071,38 +1066,39 @@ function typeahead() { /* {{{ */ ob_start(); $cats = $dms->getDocumentCategories(); - $options = array(); - $options[] = array("-1", getMLText("choose_category")); - foreach ($cats as $currcat) { - $options[] = array($currcat->getID(), htmlspecialchars($currcat->getName()), false); + if($cats) { + $options = array(); + $options[] = array("-1", getMLText("choose_category")); + foreach ($cats as $currcat) { + $options[] = array($currcat->getID(), htmlspecialchars($currcat->getName()), false); + } + $this->formField( + null, + array( + 'element'=>'select', + 'id'=>'batchcategory', + 'class'=>'chzn-select', + 'options'=>$options, + 'multiple'=>false, + 'placeholder'=>getMLText('select_category'), + 'attributes'=>array(array('style', 'width: 100%;')) + ) + ); + $this->formField( + getMLText("batch_remove_category"), + array( + 'element'=>'input', + 'type'=>'checkbox', + 'id'=>'removecategory', + 'value'=>'1', + ) + ); + + print $this->html_link('Search', array_merge($_GET, array('action'=>'changecategory')), array('class'=>'btn btn-primary change-category-btn mt-4', 'id'=>'changecategory'), " ".getMLText("batch_change_category"), false, true)."\n"; + + $content = ob_get_clean(); + $this->printAccordion(getMLText('batch_change_category'), $content); } - $this->formField( - null, - array( - 'element'=>'select', - 'id'=>'batchcategory', - 'class'=>'chzn-select', - 'options'=>$options, - 'multiple'=>false, - 'placeholder'=>getMLText('select_category'), - 'attributes'=>array(array('style', 'width: 100%;')) - ) - ); - $this->formField( - getMLText("batch_remove_category"), - array( - 'element'=>'input', - 'type'=>'checkbox', - 'id'=>'removecategory', - 'value'=>'1', - ) - ); -// print $this->html_link('Search', array_merge($_GET, array('action'=>'changeowner')), array('class'=>'btn btn-primary', 'id'=>'changeowner'), " ".getMLText("batch_change_owner"), false, true)."\n"; - - print $this->html_link('Search', array_merge($_GET, array('action'=>'changecategory')), array('class'=>'btn btn-primary change-category-btn mt-4', 'id'=>'changecategory'), " ".getMLText("batch_change_category"), false, true)."\n"; - - $content = ob_get_clean(); - $this->printAccordion(getMLText('batch_change_category'), $content); } // }}} diff --git a/views/bootstrap/class.Settings.php b/views/bootstrap/class.Settings.php index ea911c039..7b9a729ec 100644 --- a/views/bootstrap/class.Settings.php +++ b/views/bootstrap/class.Settings.php @@ -243,6 +243,17 @@ class SeedDMS_View_Settings extends SeedDMS_Theme_Style { header('Content-Type: application/javascript; charset=UTF-8'); ?> +function scrollToTargetAdjusted(target){ + var element = document.getElementById(target); + var headerOffset = 60; + var elementPosition = element.getBoundingClientRect().top; + var offsetPosition = elementPosition + window.pageYOffset - headerOffset; + + window.scrollTo({ + top: offsetPosition, + behavior: "smooth" + }); +} $(document).ready( function() { $('#settingstab li a').click(function(event) { $('#currenttab').val($(event.currentTarget).data('target').substring(1)); @@ -269,6 +280,11 @@ class SeedDMS_View_Settings extends SeedDMS_Theme_Style { } }); }); + + $('a.scrollto').click(function(event) { +console.log($(event.currentTarget).data('target').substring(1)); + scrollToTargetAdjusted($(event.currentTarget).data('target').substring(1)); + }); }); callHook('getFullSearchEngine')) && is_array($kkk)) -- SETTINGS - ADVANCED - DISPLAY --> getExtensionConfiguration() as $extname=>$extconf) { + echo ''.$extconf['title']." ● "; + } foreach($extmgr->getExtensionConfiguration() as $extname=>$extconf) { if($this->hasHook('processConfig')) $extconf = $this->callHook('processConfig', $extname, $extconf); if($this->isVisible($extname.'|')) { if($extconf['config']) { - $this->showRawConfigHeadline("".'_extensions[$extname]["__disable__"] ? '1' : '').'" />_extensions[$extname]["__disable__"] ? ' disabled' : ' enabled').'"> '.$extconf['title'].''); + $this->showRawConfigHeadline("".'_extensions[$extname]["__disable__"] ? '1' : '').'" />_extensions[$extname]["__disable__"] ? ' disabled' : ' enabled').'"> '.$extconf['title'].''); foreach($extconf['config'] as $confkey=>$conf) { ob_start(); if($this->isVisible($extname.'|'.$confkey)) { diff --git a/views/bootstrap/class.Tasks.php b/views/bootstrap/class.Tasks.php index ebfa5d857..846466bd0 100644 --- a/views/bootstrap/class.Tasks.php +++ b/views/bootstrap/class.Tasks.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for clipboard view * diff --git a/views/bootstrap/class.Timeline.php b/views/bootstrap/class.Timeline.php index af5323a85..d4529c511 100644 --- a/views/bootstrap/class.Timeline.php +++ b/views/bootstrap/class.Timeline.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for Timeline view * diff --git a/views/bootstrap/class.TimelineFeed.php b/views/bootstrap/class.TimelineFeed.php index 050af1000..854744f91 100644 --- a/views/bootstrap/class.TimelineFeed.php +++ b/views/bootstrap/class.TimelineFeed.php @@ -20,11 +20,6 @@ require_once("vendor/autoload.php"); use \FeedWriter\RSS2; -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for UserList view * diff --git a/views/bootstrap/class.ViewDocument.php b/views/bootstrap/class.ViewDocument.php index 1a71eac9e..a367e01bd 100644 --- a/views/bootstrap/class.ViewDocument.php +++ b/views/bootstrap/class.ViewDocument.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ViewDocument view * diff --git a/views/bootstrap/class.ViewFolder.php b/views/bootstrap/class.ViewFolder.php index bc557963e..ad1bf64ea 100644 --- a/views/bootstrap/class.ViewFolder.php +++ b/views/bootstrap/class.ViewFolder.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for ViewFolder view * diff --git a/views/bootstrap/class.WorkflowSummary.php b/views/bootstrap/class.WorkflowSummary.php index 1d62697a7..a7fbda91d 100644 --- a/views/bootstrap/class.WorkflowSummary.php +++ b/views/bootstrap/class.WorkflowSummary.php @@ -18,11 +18,6 @@ */ //require_once("class.Bootstrap.php"); -/** - * Include class to preview documents - */ -require_once("SeedDMS/Preview.php"); - /** * Class which outputs the html page for WorkflowSummary view * diff --git a/views/bootstrap4/class.Bootstrap4.php b/views/bootstrap4/class.Bootstrap4.php index ad617eb56..69b13a92c 100644 --- a/views/bootstrap4/class.Bootstrap4.php +++ b/views/bootstrap4/class.Bootstrap4.php @@ -397,7 +397,7 @@ background-image: linear-gradient(to bottom, #882222, #111111);; /* clipboard {{{ */ if($this->params['enableclipboard']) { echo "
      "; - echo "
      getID() : 0)."\">
      "; + echo "
      getID() : 0)."\">
      "; echo "
      "; } /* }}} End of clipboard */ diff --git a/webdav/webdav.php b/webdav/webdav.php index a05015dd2..fac3b38ad 100644 --- a/webdav/webdav.php +++ b/webdav/webdav.php @@ -1,6 +1,6 @@