Merge branch 'seeddms-6.0.x'

This commit is contained in:
Uwe Steinmann 2023-05-04 14:24:04 +02:00
commit 199fd57151
459 changed files with 41745 additions and 3862 deletions

305
CHANGELOG
View File

@ -1,3 +1,306 @@
--------------------------------------------------------------------------------
Changes in version 6.0.24
--------------------------------------------------------------------------------
- add task to send list of recent changes by email
- merge changes up to 5.1.31
--------------------------------------------------------------------------------
Changes in version 6.0.23
--------------------------------------------------------------------------------
- fix setting recipients and revisors
- check in of a document is allowed for the user having done the check out
or those users with unlimited access rights on the document
- merge changes up to 5.1.30
--------------------------------------------------------------------------------
Changes in version 6.0.22
--------------------------------------------------------------------------------
- merge changes up to 5.1.29
--------------------------------------------------------------------------------
Changes in version 6.0.21
--------------------------------------------------------------------------------
- merge changes up to 5.1.28
- add new check for documents with identical sequence numbers in a folder
--------------------------------------------------------------------------------
Changes in version 6.0.20
--------------------------------------------------------------------------------
- merge changes up to 5.1.27
- fix triggering workflow (Closes: #542)
- create original file name from new document name when uploading document
from the library folder. Used to be the original file name
--------------------------------------------------------------------------------
Changes in version 6.0.19
--------------------------------------------------------------------------------
- merge changes up to 5.1.26
- fix deletion of tasks when using bootstrap4 theme
- fix deletion of documents when clicking on icon in document list (my documents)
--------------------------------------------------------------------------------
Changes in version 6.0.18
--------------------------------------------------------------------------------
- finish op/op.Cron.php, returns json
- merge changes up to 5.1.25
- fix sending trigger workflow notification (Closes: #522)
- fix updating und deleting items in document lists
- call hook 'filenameDownloadItem' in search export and transmittal download
- fix possible xss attack in UsrMgr (CVE-2022-28479)
--------------------------------------------------------------------------------
Changes in version 6.0.17
--------------------------------------------------------------------------------
- merge changes up to 5.1.24
- send notification when a receiption of a document was submitted
--------------------------------------------------------------------------------
Changes in version 6.0.16
--------------------------------------------------------------------------------
- cancel checkout needs confirmation
- add input field to filter list of recipients if more then 10
- add task for creating missing preview images
- no longer use old PHPExcel classes, use PhpOffice\PhpSpreadsheet\Spreadsheet
instead
--------------------------------------------------------------------------------
Changes in version 6.0.15
--------------------------------------------------------------------------------
- merge changes up to 5.1.22
- add a new task for checking the checksum of all document versions
- add searching for revision date
- list of open tasks will no longer contain expired documents but MyDocuments
page still list them
- fixed downloading approval file (Closes: #503)
- regular users can no longer set owner of document while uploading
--------------------------------------------------------------------------------
Changes in version 6.0.14
--------------------------------------------------------------------------------
- show debug menu only if debug mode is on
- merge changes up to 5.1.21
- document links can be added by regular users again
- add list of checked out documents to tasks
- issue a warning when removing a document which is checked out
- checked out can be discarded if it was changed
--------------------------------------------------------------------------------
Changes in version 6.0.13
--------------------------------------------------------------------------------
- merge changes up to 5.1.20
- create download file for transmittal in system tmp (Closes: #478)
- sync source code of checkin with update document
--------------------------------------------------------------------------------
Changes in version 6.0.12
--------------------------------------------------------------------------------
- merge changes up to 5.1.19
- fix various errors concerning workflows
- show menu tasks even if not admin (Closes: #485)
--------------------------------------------------------------------------------
Changes in version 6.0.11
--------------------------------------------------------------------------------
- merge changes up to 5.1.18
- fix access restriction for roles (content of documents was visible even if the
role and status didn't allow it)
- fix missing Content-Type in UserList (Closes: #480)
--------------------------------------------------------------------------------
Changes in version 6.0.10
--------------------------------------------------------------------------------
- merge changes up to 5.1.17
- fix list of previous document versions (Closes: #471)
- fix uploading files with fine uploader (Closes: #472)
- clear revision date when all revisors have been deleted
- improve scheduler task management, tasks can be deleted, fix setting parameters
- add op.Cron.php for running all scheduled tasks
--------------------------------------------------------------------------------
Changes in version 6.0.9
--------------------------------------------------------------------------------
- merge changes up to 5.1.16
- fix removal of roles (Closes: #465)
- fix password forgotten process
- fix setting role of new user and retrieving role of existing user
- processes of users can be deleted again, instead of only transfered to
another user
- fix export of search results, headers of excel file can be translated
- fix arcordeon for folder filters on search page
- fix upload from dropfolder
- fix adding new calendar event
--------------------------------------------------------------------------------
Changes in version 6.0.8
--------------------------------------------------------------------------------
- merge changes up to 5.1.15
- fix syntax error in op/op.EditComment.php
- fix use of private variable in op/op.SetRecipients.php and op/op.SetRevisors.php
- fix triggering a transition in advanced workflow mode
--------------------------------------------------------------------------------
Changes in version 6.0.7
--------------------------------------------------------------------------------
- fix editing of document attachments
- make receipt summary look like approval/review summary
- merge changes up to 5.1.14
- do not show the updating user in a revision workflow if the status is 0
this is misleading because the user starting the revision workflow is the one
first accessing the document
- rejection of document receipts are turned off by default, but can be turned
on in the settings
- documents in DocumentChooser are sorted by name
- instead of just removing a user from all processes it can be replaced by a new user
--------------------------------------------------------------------------------
Changes in version 6.0.6
--------------------------------------------------------------------------------
- fix setting attributes when checking in a new document version
- setting a document revision to 'needs correction' will no longer set the
documents status to 'needѕ correction' if this was turned off in the settings
- a document will not leave draft status when setting the approver/reviewer
without setting a reviewer/approver
- tasks to be counted in menu can be configured
- add number of documents which need correction to menu
- minor 2 factor auth. fixes when initially setting the secret
- remove ѕome unneeded code from AddDocument which just caused php warnings
- do not set the uploader of new documents to owner if the owner is different from
the uploader
- add scheduler
- add hook showVersionComment in out.ViewDocument.php
- Various minor corrections of database tables tblWorkflowLog and
tblWorkflowDocumentContent
- merge changes up to 5.1.7
--------------------------------------------------------------------------------
Changes in version 6.0.5
--------------------------------------------------------------------------------
- sync form for updating document by upload and checkin
- add list of documents which need correction on MyDocuments page
--------------------------------------------------------------------------------
Changes in version 6.0.4
--------------------------------------------------------------------------------
- merge changes up to 5.1.5
--------------------------------------------------------------------------------
Changes in version 6.0.3
--------------------------------------------------------------------------------
- add list of documents without a receiver on MyDocuments page
- propperly calculate number of documents for each value of value set in attribute mgr
- output of progress bar for reception of a document can be controlled by access list
- recipientof a document version can be set when uploading the file
- fix export of search and display of 2nd, 3rd, ... search page
- speed up creation of document lists if reception progress bar is shown
- status of rejected documents can be overriden
- do not add users from group as recipients if they are the uploader or reviewer
of a document
- add list of documents without a receiver, list of drafts, and list of absolete
documents on MyDocuments page
- add callback onCheckAccessDocument to SeedDMS_Core_Document
- add new document status 'needs correction', revised documents which do not pass
will no longer be in status 'rejected' but 'needs correction'
- better error handling when indexing documents fails
- apache xsendfile module is used for downloading documents when installed
- add view access check for ApprovalSummary, ReviewSummary, ReceiptSummary,
WorkflowSummary, DocumentAccess, GroupView, UsrView, WorkflowSummary
- filter out reviewers and uploader of a document version when setting recipients
by user group
--------------------------------------------------------------------------------
Changes in version 6.0.2
--------------------------------------------------------------------------------
- check if user has access on document and is not disabled if set as
receiver, revisor
- check if group has members if set as reviewer, approver, receiver, revisor
- fix bug in notification of approver after successful review
- add document check for docs in revision and missing access rights of revisor
- add document check for docs requiring receptions but user lacks access right
- fix Acl manager when using pgsql
- list all open tasks of user in user info of user manager
- owner of document may see review/approval/receipt/revision log
- fix sending mails to reviewer/approvers after check in
- downloading of review/approval files works again
- optimizing retrieval of open tasks
- do not show user which has been removed from a process except for admins
- show scheduled revisions in calendar
- merge changes up to 5.1.5
--------------------------------------------------------------------------------
Changes in version 6.0.1
--------------------------------------------------------------------------------
- call hook 'rawcontent' when downloading transmittal list or search content
- speed up list of locked documents on MyDocuments page
- sql queries and execution times can be written to file in database layer
--------------------------------------------------------------------------------
Changes in version 6.0.0
--------------------------------------------------------------------------------
- merge changes up to 5.0.10
- filter documents by status 'draft' on search page
- list of documents to look at now contains documents in revision
- add list of documents waiting for reception on MyDocuments page
- group document lists on MyDocuments page into three sections
- show progressbar and comments for reception of document in documentlist
- restructure page for document/folder check, add check for missing access
on documents by recipient or revisor
- overhaul revision workflow, add hook after revision workflow was finished
- add two factor authentication based on google authenticator
- set timeout for ajax call 'mytasks' from 200ms to 1000ms
- use a similar layout for document list on the ViewDocument page
- add RSS feed of timeline
- put more operations under access control
- add receipent list for documents
- add revision of documents
- add substitute user command for regular users
- add access controll list for many functions
- add document list which can be exported as an archive
- search results can be exported
--------------------------------------------------------------------------------
Changes in version 5.1.31
--------------------------------------------------------------------------------
- rest api returns error msg and not just http status
- comment of document, documentcontent and folder can be rendered as
markdown
- fix preview for file in drop folder
- fix export of search result
- use openssl instead of mcrypt
- search form shows 'from' and 'to' field for integers and floats
- show preview of document in EditAttributes page
- major overhaul of fulltext search, add new search tab for a more
facetted based search
- add initial version of Dashboard
- default public status of attachments can be configured
--------------------------------------------------------------------------------
Changes in version 5.1.30
--------------------------------------------------------------------------------
- conversion from pdf to png replaces alpha channel with white
- add list of conversion services in debug menu of admin tool
- use chosen select for custom attributes
- color category (use first 6 chars of md5(category name) as hex color)
- create missing preview images in category or attribute manager
- support README of extension in different languages
- do not force password change if in substitute user mode
--------------------------------------------------------------------------------
Changes in version 5.1.29
--------------------------------------------------------------------------------
- fix php errors in restapi
- fix 'maximum size' error when uploading a file with drag&drop
- update jquery to 3.6.1 (only bootstrap4 theme)
- introduce authentication service
- new hook in restapi to add middleware
- previews for png, txt, pdf in different directories. This will
enforce a recreation of all cached preview images, pdf and text files!
- 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 SeedDMS/Core, SeedDMS/Lucene, SeedDMS/Preview
are now based on composer and has moved into vendor dir
--------------------------------------------------------------------------------
Changes in version 5.1.28
--------------------------------------------------------------------------------
@ -17,7 +320,7 @@
- rest api returns version attributes as 'version_attributes' (was
'version-attributes'), each attribute also contains the name
- new hook in rest api to add more routes in extensions
- uploaded serveral documents at once by fast upload will assign random
- uploaded several documents at once by fast upload will assign random
sequence number to allow manually sorting the documents afterwards
- fix counting of login failures if both ldap and db authentication is done

View File

@ -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#^ *<date>\([-0-9]*\)</date><release>\([0-9.preRC]*\)</release>#\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

780
SeedDMS_Core/CHANGELOG.md Normal file
View File

@ -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.
<release>4.3.14</release>
- 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

View File

@ -96,7 +96,13 @@ require_once('Core/inc.AccessUtils.php');
*/
require_once('Core/inc.FileUtils.php');
/**
* @uses SeedDMS_Transmittal
*/
require_once('Core/inc.ClassTransmittal.php');
/**
* @uses SeedDMS_File
*/
require_once('Core/inc.ClassIterator.php');
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -778,21 +778,25 @@ class SeedDMS_Core_Folder extends SeedDMS_Core_Object {
/**
* Returns a file system path
*
* This path contains spaces around the slashes for better readability.
* Run str_replace(' / ', '/', $path) on it to get a valid unix
* This path contains by default spaces around the slashes for better readability.
* Run str_replace(' / ', '/', $path) on it or pass '/' as $sep to get a valid unix
* file system path.
*
* @param bool $skiproot skip the name of the root folder and start with $sep
* @param string $sep separator between path elements
* @return string path separated with ' / '
*/
function getFolderPathPlain() { /* {{{ */
$path="";
function getFolderPathPlain($skiproot = false, $sep = ' / ') { /* {{{ */
$path="".$sep;
$folderPath = $this->getPath();
for ($i = 0; $i < count($folderPath); $i++) {
$path .= $folderPath[$i]->getName();
if ($i +1 < count($folderPath))
$path .= " / ";
for ($i = 0; $i < count($folderPath); $i++) {
if($i > 0 || !$skiproot) {
$path .= $folderPath[$i]->getName();
if ($i+1 < count($folderPath))
$path .= $sep;
}
}
return $path;
return trim($path);
} /* }}} */
/**
@ -1021,11 +1025,13 @@ class SeedDMS_Core_Folder extends SeedDMS_Core_Object {
* @param array $version_attributes list of document version attributes.
* The element key must be the id of the attribute definition.
* @param SeedDMS_Core_Workflow $workflow
* @param integer $initstate initial document state (only S_RELEASED and
* S_DRAFT are allowed)
* @return array|bool false in case of error, otherwise an array
* containing two elements. The first one is the new document, the
* second one is the result set returned when inserting the content.
*/
function addDocument($name, $comment, $expires, $owner, $keywords, $categories, $tmpFile, $orgFileName, $fileType, $mimeType, $sequence, $reviewers=array(), $approvers=array(),$reqversion=0,$version_comment="", $attributes=array(), $version_attributes=array(), $workflow=null) { /* {{{ */
function addDocument($name, $comment, $expires, $owner, $keywords, $categories, $tmpFile, $orgFileName, $fileType, $mimeType, $sequence, $reviewers=array(), $approvers=array(),$reqversion=0,$version_comment="", $attributes=array(), $version_attributes=array(), $workflow=null, $initstate=S_RELEASED) { /* {{{ */
$db = $this->_dms->getDB();
$expires = (!$expires) ? 0 : $expires;
@ -1051,7 +1057,8 @@ class SeedDMS_Core_Folder extends SeedDMS_Core_Object {
$document = $this->_dms->getDocument($db->getInsertID('tblDocuments'));
$res = $document->addContent($version_comment, $owner, $tmpFile, $orgFileName, $fileType, $mimeType, $reviewers, $approvers, $reqversion, $version_attributes, $workflow);
$curuser = $this->_dms->getLoggedInUser();
$res = $document->addContent($version_comment, $curuser ? $curuser : $owner, $tmpFile, $orgFileName, $fileType, $mimeType, $reviewers, $approvers, $reqversion, $version_attributes, $workflow, $initstate);
if (is_bool($res) && !$res) {
$db->rollbackTransaction();
@ -1059,7 +1066,11 @@ class SeedDMS_Core_Folder extends SeedDMS_Core_Object {
}
if($categories) {
$document->setCategories($categories);
if(!$document->setCategories($categories)) {
$document->remove();
$db->rollbackTransaction();
return false;
}
}
if($attributes) {
@ -1308,8 +1319,12 @@ class SeedDMS_Core_Folder extends SeedDMS_Core_Object {
* below the actual root folder.
*/
$res = $this->getParent();
if ($res)
return $this->_parent->getAccessList($mode, $op);
if ($res) {
$pacl = $res->getAccessList($mode, $op);
return $pacl;
}
} else {
$pacl = array("groups" => array(), "users" => array());
}
if (!isset($this->_accessList[$mode])) {
@ -1336,6 +1351,7 @@ class SeedDMS_Core_Folder extends SeedDMS_Core_Object {
}
return $this->_accessList[$mode];
return SeedDMS_Core_DMS::mergeAccessLists($pacl, $this->_accessList[$mode]);
} /* }}} */
/**

View File

@ -212,10 +212,12 @@ class SeedDMS_Core_Group { /* {{{ */
$this->_users = array();
$classnamerole = $this->_dms->getClassname('role');
$classname = $this->_dms->getClassname('user');
foreach ($resArr as $row) {
/** @var SeedDMS_Core_User $user */
$user = new $classname($row["id"], $row["login"], $row["pwd"], $row["fullName"], $row["email"], $row["language"], $row["theme"], $row["comment"], $row["role"], $row['hidden']);
$role = $classnamerole::getInstance($row['role'], $this->_dms);
$user = new $classname($row["id"], $row["login"], $row["pwd"], $row["fullName"], $row["email"], $row["language"], $row["theme"], $row["comment"], $role, $row['hidden']);
$user->setDMS($this->_dms);
array_push($this->_users, $user);
}
@ -238,10 +240,12 @@ class SeedDMS_Core_Group { /* {{{ */
$managers = array();
$classnamerole = $this->_dms->getClassname('role');
$classname = $this->_dms->getClassname('user');
foreach ($resArr as $row) {
/** @var SeedDMS_Core_User $user */
$user = new $classname($row["id"], $row["login"], $row["pwd"], $row["fullName"], $row["email"], $row["language"], $row["theme"], $row["comment"], $row["role"], $row['hidden']);
$role = $classnamerole::getInstance($row['role'], $this->_dms);
$user = new $classname($row["id"], $row["login"], $row["pwd"], $row["fullName"], $row["email"], $row["language"], $row["theme"], $row["comment"], $role, $row['hidden']);
$user->setDMS($this->_dms);
array_push($managers, $user);
}
@ -402,6 +406,28 @@ class SeedDMS_Core_Group { /* {{{ */
}
}
$receiptStatus = $this->getReceiptStatus();
foreach ($receiptStatus as $r) {
$queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, `comment`, `date`, `userID`) ".
"VALUES ('". $r["receiptID"] ."', '-2', 'Recipients group removed from process', ".$db->getCurrentDatetime().", '". $user->getID() ."')";
$res=$db->getResult($queryStr);
if(!$res) {
$db->rollbackTransaction();
return false;
}
}
$revisionStatus = $this->getRevisionStatus();
foreach ($revisionStatus as $r) {
$queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, `comment`, `date`, `userID`) ".
"VALUES ('". $r["revisionID"] ."', '-2', 'Revisors group removed from process', ".$db->getCurrentDatetime().", '". $user->getID() ."')";
$res=$db->getResult($queryStr);
if(!$res) {
$db->rollbackTransaction();
return false;
}
}
$db->commitTransaction();
return true;
@ -471,6 +497,79 @@ class SeedDMS_Core_Group { /* {{{ */
return $status;
} /* }}} */
function getReceiptStatus($documentID=null, $version=null) { /* {{{ */
$db = $this->_dms->getDB();
$status = array();
// See if the group is assigned as a recipient.
$queryStr = "SELECT `tblDocumentRecipients`.*, `tblDocumentReceiptLog`.`status`, ".
"`tblDocumentReceiptLog`.`comment`, `tblDocumentReceiptLog`.`date`, ".
"`tblDocumentReceiptLog`.`userID` ".
"FROM `tblDocumentRecipients` ".
"LEFT JOIN `tblDocumentReceiptLog` USING (`receiptID`) ".
"WHERE `tblDocumentRecipients`.`type`='1' ".
($documentID==null ? "" : "AND `tblDocumentRecipients`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentRecipients`.`version` = '". (int) $version ."' ").
"AND `tblDocumentRecipients`.`required`='". $this->_id ."' ";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
if (count($resArr)>0) {
foreach ($resArr as $res) {
if(isset($status["status"][$res['documentID']])) {
if($status["status"][$res['documentID']]['date'] < $res['date']) {
$status["status"][$res['documentID']] = $res;
}
} else {
$status["status"][$res['documentID']] = $res;
}
}
}
return $status;
} /* }}} */
function getRevisionStatus($documentID=null, $version=null) { /* {{{ */
$db = $this->_dms->getDB();
$status = array();
if (!$db->createTemporaryTable("ttcontentid")) {
return false;
}
// See if the group is assigned as a revisor.
$queryStr = "SELECT `tblDocumentRevisors`.*, `tblDocumentRevisionLog`.`status`, ".
"`tblDocumentRevisionLog`.`comment`, `tblDocumentRevisionLog`.`date`, ".
"`tblDocumentRevisionLog`.`userID` ".
"FROM `tblDocumentRevisors` ".
"LEFT JOIN `tblDocumentRevisionLog` USING (`revisionID`) ".
"LEFT JOIN `ttcontentid` ON `ttcontentid`.`maxVersion` = `tblDocumentRevisors`.`version` AND `ttcontentid`.`document` = `tblDocumentRevisors`.`documentID` ".
"WHERE `tblDocumentRevisors`.`type`='1' ".
($documentID==null ? "" : "AND `tblDocumentRevisors`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentRevisors`.`version` = '". (int) $version ."' ").
($documentID==null && $version==null ? "AND `ttcontentid`.`maxVersion` = `tblDocumentRevisors`.`version` " : "").
"AND `tblDocumentRevisors`.`required`='". $this->_id ."' ".
"ORDER BY `tblDocumentRevisionLog`.`revisionLogID` DESC";
$resArr = $db->getResultArray($queryStr);
if ($resArr === false)
return false;
if (count($resArr)>0) {
$status['status'] = array();
foreach ($resArr as $res) {
if($res['date']) {
if(isset($status["status"][$res['documentID']])) {
if($status["status"][$res['documentID']]['date'] < $res['date']) {
$status["status"][$res['documentID']] = $res;
}
} else {
$status["status"][$res['documentID']] = $res;
}
}
}
}
return $status;
} /* }}} */
/**
* Get a list of documents with a workflow
*

View File

@ -8,7 +8,7 @@
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
* 2010 Uwe Steinmann
* 2010-2023 Uwe Steinmann
* @version Release: @package_version@
*/
@ -19,7 +19,7 @@
* @package SeedDMS_Core
* @author Markus Westphal, Malcolm Cowe, Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
* 2010 Uwe Steinmann
* 2010-2023 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_KeywordCategory {
@ -47,53 +47,53 @@ class SeedDMS_Core_KeywordCategory {
*/
protected $_dms;
/**
* SeedDMS_Core_KeywordCategory constructor.
* @param $id
* @param $ownerID
* @param $name
*/
function __construct($id, $ownerID, $name) {
/**
* SeedDMS_Core_KeywordCategory constructor.
* @param $id
* @param $ownerID
* @param $name
*/
function __construct($id, $ownerID, $name) { /* {{{ */
$this->_id = $id;
$this->_name = $name;
$this->_ownerID = $ownerID;
$this->_dms = null;
}
} /* }}} */
/**
* @param SeedDMS_Core_DMS $dms
*/
function setDMS($dms) {
/**
* @param SeedDMS_Core_DMS $dms
*/
function setDMS($dms) { /* {{{ */
$this->_dms = $dms;
}
} /* }}} */
/**
* @return int
*/
/**
* @return int
*/
function getID() { return $this->_id; }
/**
* @return string
*/
/**
* @return string
*/
function getName() { return $this->_name; }
/**
* @return bool|SeedDMS_Core_User
*/
function getOwner() {
/**
* @return bool|SeedDMS_Core_User
*/
function getOwner() { /* {{{ */
if (!isset($this->_owner))
$this->_owner = $this->_dms->getUser($this->_ownerID);
return $this->_owner;
}
} /* }}} */
/**
* @param $newName
* @return bool
*/
function setName($newName) {
$newName = trim($newName);
if(!$newName)
return false;
/**
* @param $newName
* @return bool
*/
function setName($newName) { /* {{{ */
$newName = trim($newName);
if(!$newName)
return false;
$db = $this->_dms->getDB();
@ -103,75 +103,89 @@ class SeedDMS_Core_KeywordCategory {
$this->_name = $newName;
return true;
}
} /* }}} */
/**
* @param SeedDMS_Core_User $user
* @return bool
*/
function setOwner($user) {
if(!$user || !$user->isType('user'))
return false;
/**
* @param SeedDMS_Core_User $user
* @return bool
*/
function setOwner($user) { /* {{{ */
if(!$user || !$user->isType('user'))
return false;
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblKeywordCategories` SET `owner` = " . $user->getID() . " WHERE `id` = " . $this->_id;
$queryStr = "UPDATE `tblKeywordCategories` SET `owner` = " . $user->getID() . " WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr))
return false;
$this->_ownerID = $user->getID();
$this->_owner = $user;
return true;
}
} /* }}} */
/**
* @return array
*/
function getKeywordLists() {
/**
* @return array keywords in this list
*/
function getKeywordLists() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT * FROM `tblKeywords` WHERE `category` = " . $this->_id . " order by `keywords`";
return $db->getResultArray($queryStr);
}
/**
* @param $listID
* @param $keywords
* @return bool
*/
function editKeywordList($listID, $keywords) {
/**
* @return integer number of keywords in this list
*/
function countKeywordLists() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT COUNT(*) as `c` FROM `tblKeywords` where `category`=".$this->_id;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr)
return false;
return $resArr[0]['c'];
} /* }}} */
/**
* @param $listID
* @param $keywords
* @return bool
*/
function editKeywordList($listID, $keywords) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblKeywords` SET `keywords` = ".$db->qstr($keywords)." WHERE `id` = $listID";
return $db->getResult($queryStr);
}
} /* }}} */
/**
* @param $keywords
* @return bool
*/
function addKeywordList($keywords) {
/**
* @param $keywords
* @return bool
*/
function addKeywordList($keywords) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "INSERT INTO `tblKeywords` (`category`, `keywords`) VALUES (" . $this->_id . ", ".$db->qstr($keywords).")";
return $db->getResult($queryStr);
}
} /* }}} */
/**
* @param $listID
* @return bool
*/
function removeKeywordList($listID) {
/**
* @param $listID
* @return bool
*/
function removeKeywordList($listID) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "DELETE FROM `tblKeywords` WHERE `id` = $listID";
return $db->getResult($queryStr);
}
} /* }}} */
/**
* @return bool
*/
function remove() {
/**
* @return bool
*/
function remove() { /* {{{ */
$db = $this->_dms->getDB();
$db->startTransaction();
@ -189,5 +203,5 @@ class SeedDMS_Core_KeywordCategory {
$db->commitTransaction();
return true;
}
} /* }}} */
}

View File

@ -0,0 +1,398 @@
<?php
/**
* Implementation of the transmittal object in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
* 2010 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class to represent a transmittal in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @author Markus Westphal, Malcolm Cowe, Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
* 2010 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_Transmittal {
/**
* @var integer id of transmittal
*
* @access protected
*/
var $_id;
/**
* @var string name of transmittal
*
* @access protected
*/
var $_name;
/**
* @var string comment of transmittal
*
* @access protected
*/
var $_comment;
/**
* @var boolean true if transmittal is public
*
* @access protected
*/
var $_isPublic;
/**
* @var object user this transmittal belongs to
*
* @access protected
*/
var $_user;
/**
* @var object date of creation
*
* @access protected
*/
var $_date;
/**
* @var object items
*
* @access protected
*/
var $_items;
/**
* @var object reference to the dms instance this user belongs to
*
* @access protected
*/
var $_dms;
function __construct($id, $user, $name, $comment, $isPublic=0, $date='') {
$this->_id = $id;
$this->_name = $name;
$this->_comment = $comment;
$this->_user = $user;
$this->_isPublic = $isPublic;
$this->_date = $date;
$this->_items = array();
$this->_dms = null;
}
/**
* Get an instance of a transmittal object
*
* @param string|integer $id id or name of transmittal, depending
* on the 3rd parameter.
* @param object $dms instance of dms
* @param string $by search by [id|name]. If this
* parameter is left empty, the user will be search by its Id.
* @return object instance of class SeedDMS_Core_Transmittal
*/
public static function getInstance($id, $dms, $by='') { /* {{{ */
if(!$dms || get_class($dms) != 'SeedDMS_Core_DMS')
return false;
$db = $dms->getDB();
switch($by) {
case 'name':
$queryStr = "SELECT * FROM `tblTransmittals` WHERE `name` = ".$db->qstr($id);
break;
default:
$queryStr = "SELECT * FROM `tblTransmittals` WHERE id = " . (int) $id;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
if (count($resArr) != 1) return false;
$resArr = $resArr[0];
$uclassname = $dms->getClassname('user');
$user = $uclassname::getInstance($resArr['userID'], $dms);
$transmittal = new self($resArr["id"], $user, $resArr["name"], $resArr["comment"], $resArr["public"], $resArr["date"]);
$transmittal->setDMS($dms);
return $transmittal;
} /* }}} */
/**
* Get all instances of a transmittal object
*
* @param string|integer $id id or name of transmittal, depending
* on the 3rd parameter.
* @param object $dms instance of dms
* @param string $by search by [id|name]. If this
* parameter is left empty, the user will be search by its Id.
* @return object instance of class SeedDMS_Core_Transmittal
*/
public static function getAllInstances($user, $orderby, $dms) { /* {{{ */
$db = $dms->getDB();
$queryStr = "SELECT * FROM `tblTransmittals`";
if($user)
$queryStr .= " WHERE `userID` = " . $user->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
$uclassname = $dms->getClassname('user');
$transmittals = array();
foreach ($resArr as $res) {
$user = $uclassname::getInstance($res['userID'], $dms);
$transmittal = new self($res["id"], $user, $res["name"], $res["comment"], $res["public"], $res["date"]);
$transmittal->setDMS($dms);
$transmittals[] = $transmittal;
}
return $transmittals;
} /* }}} */
function setDMS($dms) {
$this->_dms = $dms;
}
function getID() { return $this->_id; }
function getName() { return $this->_name; }
function setName($newName) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblTransmittals` SET `name` =".$db->qstr($newName)." WHERE `id` = " . $this->_id;
$res = $db->getResult($queryStr);
if (!$res)
return false;
$this->_name = $newName;
return true;
} /* }}} */
function getComment() { return $this->_comment; }
function setComment($newComment) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblTransmittals` SET `comment` =".$db->qstr($newComment)." WHERE `id` = " . $this->_id;
$res = $db->getResult($queryStr);
if (!$res)
return false;
$this->_comment = $newComment;
return true;
} /* }}} */
function getUser() { return $this->_user; }
function getItems() { /* {{{ */
$db = $this->_dms->getDB();
if (!$this->_items) {
$queryStr = "SELECT `tblTransmittalItems`.* FROM `tblTransmittalItems` ".
"LEFT JOIN `tblDocuments` ON `tblTransmittalItems`.`document`=`tblDocuments`.`id` ".
"WHERE `tblTransmittalItems`.`transmittal` = '". $this->_id ."'";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$this->_users = array();
$classname = $this->_dms->getClassname('transmittalitem');
foreach ($resArr as $row) {
$document = $this->_dms->getDocument($row['document']);
$content = $document->getContentByVersion($row['version']);
$item = new $classname($row["id"], $this, $content, $row["date"]);
array_push($this->_items, $item);
}
}
return $this->_items;
} /* }}} */
function getSize() { /* {{{ */
$db = $this->_dms->getDB();
if (!$this->_items) {
self::getItems();
}
$size = 0;
foreach ($this->_items as $item) {
if($content = $item->getContent()) {
$size += $content->getFileSize();
}
}
return $size;
} /* }}} */
/**
* Add an item to the transmittal
*
* @param object $item instance of SeedDMS_Core_DocumentContent
* @return boolean true if item could be added, otherwise false
*/
function addContent($item) { /* {{{ */
$db = $this->_dms->getDB();
if(get_class($item) != $this->_dms->getClassname('documentcontent'))
return false;
$document = $item->getDocument();
$queryStr = "INSERT INTO `tblTransmittalItems` (`transmittal`, `document`, `version`, `date`) ".
"VALUES ('". $this->_id ."', ".$document->getID().", ".$item->getVersion().", ".$db->getCurrentDatetime().")";
$res=$db->getResult($queryStr);
if(!$res) {
return false;
}
$itemID = $db->getInsertID('tblTransmittalItems');
return SeedDMS_Core_TransmittalItem::getInstance($itemID, $this->_dms);
} /* }}} */
function remove() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "DELETE FROM `tblTransmittals` WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr)) {
return false;
}
return true;
} /* }}} */
}
/**
* Class to represent a transmittal in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @author Markus Westphal, Malcolm Cowe, Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
* 2010 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_TransmittalItem {
/**
* @var integer id of transmittal item
*
* @access protected
*/
var $_id;
/**
* @var object document content
*
* @access protected
*/
var $_content;
/**
* @var object transmittal
*
* @access protected
*/
var $_transmittal;
/**
* @var object date of creation
*
* @access protected
*/
var $_date;
function __construct($id, $transmittal, $content, $date='') {
$this->_id = $id;
$this->_transmittal = $transmittal;
$this->_content = $content;
$this->_date = $date;
$this->_dms = null;
}
public static function getInstance($id, $dms) { /* {{{ */
if(!$dms || get_class($dms) != 'SeedDMS_Core_DMS')
return false;
$db = $dms->getDB();
$queryStr = "SELECT * FROM `tblTransmittalItems` WHERE `id` = " . (int) $id;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
if (count($resArr) != 1)
return false;
$resArr = $resArr[0];
$transmittal = SeedDMS_Core_Transmittal::getInstance($resArr['transmittal'], $dms);
$dclassname = $dms->getClassname('document');
$document = $dclassname::getInstance($resArr['document'], $dms);
$content = $document->getContentByVersion((int) $resArr['version']);
$item = new self($resArr["id"], $transmittal, $content, $resArr["date"]);
$item->setDMS($dms);
return $item;
} /* }}} */
function setDMS($dms) {
$this->_dms = $dms;
}
function getID() { return $this->_id; }
function getTransmittal() { return $this->_transmittal; }
function getContent() { return $this->_content; }
function getDate() { return $this->_date; }
function remove() { /* {{{ */
$db = $this->_dms->getDB();
$transmittal = $this->_transmittal;
$queryStr = "DELETE FROM `tblTransmittalItems` WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr)) {
return false;
}
return true;
} /* }}} */
/**
* Check if the content referenzed by the transmittal item is unequal
* to the latest content of the document.
*
* This function updateѕ always to the latest version of the document,
* even if the version in the item is higher. This can happen if a
* version has been removed.
*
* @return boolean/integer false in case of an error, otherwise the new
* version.
*/
function updateContent() { /* {{{ */
$db = $this->_dms->getDB();
$transmittal = $this->_transmittal;
$document = $this->_content->getDocument();
$latestcontent = $document->getLatestContent();
if($latestcontent->getVersion() != $this->_content->getVersion()) {
$queryStr = "UPDATE `tblTransmittalItems` set `version` = ".$latestcontent->getVersion()." WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr)) {
return false;
}
}
return $latestcontent->getVersion();
} /* }}} */
}
?>

View File

@ -12,6 +12,223 @@
* @version Release: @package_version@
*/
/**
* Class to represent a role in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2016 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_Role { /* {{{ */
/**
* @var integer id of role
*
* @access protected
*/
var $_id;
/**
* @var string name of role
*
* @access protected
*/
var $_login;
/**
* @var string role of user. Can be one of SeedDMS_Core_User::role_user,
* SeedDMS_Core_User::role_admin, SeedDMS_Core_User::role_guest
*
* @access protected
*/
var $_role;
/**
* @var array list of status without access
*
* @access protected
*/
var $_noaccess;
/**
* @var object reference to the dms instance this user belongs to
*
* @access protected
*/
var $_dms;
const role_user = '0';
const role_admin = '1';
const role_guest = '2';
function __construct($id, $name, $role, $noaccess=array()) { /* {{{ */
$this->_id = $id;
$this->_name = $name;
$this->_role = $role;
$this->_noaccess = $noaccess;
$this->_dms = null;
} /* }}} */
/**
* Create an instance of a role object
*
* @param string|integer $id Id, login name, or email of user, depending
* on the 3rd parameter.
* @param object $dms instance of dms
* @param string $by search by name. If 'name' is passed, the method
* will search by name instead of id. If this
* parameter is left empty, the role will be searched by its Id.
*
* @return object instance of class SeedDMS_Core_User
*/
public static function getInstance($id, $dms, $by='') { /* {{{ */
$db = $dms->getDB();
switch($by) {
case 'name':
$queryStr = "SELECT * FROM `tblRoles` WHERE `name` = ".$db->qstr($id);
break;
default:
$queryStr = "SELECT * FROM `tblRoles` WHERE `id` = " . (int) $id;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
if (count($resArr) != 1) return false;
$resArr = $resArr[0];
$role = new self($resArr["id"], $resArr["name"], $resArr["role"], $resArr['noaccess'] ? explode(',', $resArr['noaccess']) : array());
$role->setDMS($dms);
return $role;
} /* }}} */
public static function getAllInstances($orderby, $dms) { /* {{{ */
$db = $dms->getDB();
if($orderby == 'name')
$queryStr = "SELECT * FROM `tblRoles` ORDER BY `name`";
else
$queryStr = "SELECT * FROM `tblRoles` ORDER BY `id`";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$roles = array();
for ($i = 0; $i < count($resArr); $i++) {
$role = new self($resArr[$i]["id"], $resArr[$i]["name"], $resArr[$i]["role"], explode(',', $resArr[$i]['noaccess']));
$role->setDMS($dms);
$roles[$i] = $role;
}
return $roles;
} /* }}} */
function setDMS($dms) {
$this->_dms = $dms;
}
function getID() { return $this->_id; }
function getName() { return $this->_name; }
function setName($newName) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblRoles` SET `name` =".$db->qstr($newName)." WHERE `id` = " . $this->_id;
$res = $db->getResult($queryStr);
if (!$res)
return false;
$this->_name = $newName;
return true;
} /* }}} */
function isAdmin() { return ($this->_role == SeedDMS_Core_Role::role_admin); }
function isGuest() { return ($this->_role == SeedDMS_Core_Role::role_guest); }
function getRole() { return $this->_role; }
function setRole($newrole) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblRoles` SET `role` = " . $newrole . " WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr))
return false;
$this->_role = $newrole;
return true;
} /* }}} */
function getNoAccess() { return $this->_noaccess; }
function setNoAccess($noaccess) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblRoles` SET `noaccess` = " . $db->qstr($noaccess ? implode(',',$noaccess) : '') . " WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr))
return false;
$this->_noaccess = $noaccess;
return true;
} /* }}} */
/**
* Delete role
*
* @return boolean true on success or false in case of an error
*/
function remove() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "DELETE FROM `tblRoles` WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr)) {
return false;
}
return true;
} /* }}} */
function isUsed() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT * FROM `tblUsers` WHERE `role`=".$this->_id;
$resArr = $db->getResultArray($queryStr);
if (is_array($resArr) && count($resArr) == 0)
return false;
return true;
} /* }}} */
function getUsers() { /* {{{ */
$db = $this->_dms->getDB();
if (!isset($this->_users)) {
$queryStr = "SELECT * FROM `tblUsers` WHERE `role`=".$this->_id;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$this->_users = array();
$classnamerole = $this->_dms->getClassname('role');
$classname = $this->_dms->getClassname('user');
foreach ($resArr as $row) {
$role = $classnamerole::getInstance($row['role'], $this->_dms);
$user = new $classname($row["id"], $row["login"], $row["pwd"], $row["fullName"], $row["email"], $row["language"], $row["theme"], $row["comment"], $role, $row['hidden']);
$user->setDMS($this->_dms);
array_push($this->_users, $user);
}
}
return $this->_users;
} /* }}} */
} /* }}} */
/**
* Class to represent a user in the document management system
*
@ -44,6 +261,13 @@ class SeedDMS_Core_User { /* {{{ */
*/
protected $_pwd;
/**
* @var string secret of user for 2-factor authentication
*
* @access protected
*/
var $_secret;
/**
* @var string date when password expires
*
@ -123,6 +347,20 @@ class SeedDMS_Core_User { /* {{{ */
*/
protected $_homeFolder;
/**
* @var array list of users this user can substitute
*
* @access protected
*/
var $_substitutes;
/**
* @var array reverse list of users this user can substitute
*
* @access protected
*/
var $_rev_substitutes;
/**
* @var SeedDMS_Core_DMS reference to the dms instance this user belongs to
*
@ -165,8 +403,9 @@ class SeedDMS_Core_User { /* {{{ */
* @param int $loginFailures
* @param int $quota
* @param null $homeFolder
* @param string $secret
*/
function __construct($id, $login, $pwd, $fullName, $email, $language, $theme, $comment, $role, $isHidden=0, $isDisabled=0, $pwdExpiration='', $loginFailures=0, $quota=0, $homeFolder=null) {
function __construct($id, $login, $pwd, $fullName, $email, $language, $theme, $comment, $role, $isHidden=0, $isDisabled=0, $pwdExpiration='', $loginFailures=0, $quota=0, $homeFolder=null, $secret='') {
$this->_id = $id;
$this->_login = $login;
$this->_pwd = $pwd;
@ -182,6 +421,9 @@ class SeedDMS_Core_User { /* {{{ */
$this->_loginFailures = $loginFailures;
$this->_quota = $quota;
$this->_homeFolder = $homeFolder;
$this->_secret = $secret;
$this->_substitutes = null;
$this->_rev_substitutes = null;
$this->_dms = null;
}
@ -220,7 +462,10 @@ class SeedDMS_Core_User { /* {{{ */
$resArr = $resArr[0];
$user = new self((int) $resArr["id"], $resArr["login"], $resArr["pwd"], $resArr["fullName"], $resArr["email"], $resArr["language"], $resArr["theme"], $resArr["comment"], $resArr["role"], $resArr["hidden"], $resArr["disabled"], $resArr["pwdExpiration"], $resArr["loginfailures"], $resArr["quota"], $resArr["homefolder"]);
$classname = $dms->getClassname('role');
$role = $classname::getInstance($resArr['role'], $dms);
$user = new self((int) $resArr["id"], $resArr["login"], $resArr["pwd"], $resArr["fullName"], $resArr["email"], $resArr["language"], $resArr["theme"], $resArr["comment"], $role, $resArr["hidden"], $resArr["disabled"], $resArr["pwdExpiration"], $resArr["loginfailures"], $resArr["quota"], $resArr["homefolder"], $resArr["secret"]);
$user->setDMS($dms);
return $user;
} /* }}} */
@ -244,9 +489,11 @@ class SeedDMS_Core_User { /* {{{ */
$users = array();
$classname = $dms->getClassname('role');
for ($i = 0; $i < count($resArr); $i++) {
/** @var SeedDMS_Core_User $user */
$user = new self($resArr[$i]["id"], $resArr[$i]["login"], $resArr[$i]["pwd"], $resArr[$i]["fullName"], $resArr[$i]["email"], (isset($resArr[$i]["language"])?$resArr[$i]["language"]:NULL), (isset($resArr[$i]["theme"])?$resArr[$i]["theme"]:NULL), $resArr[$i]["comment"], $resArr[$i]["role"], $resArr[$i]["hidden"], $resArr[$i]["disabled"], $resArr[$i]["pwdExpiration"], $resArr[$i]["loginfailures"], $resArr[$i]["quota"], $resArr[$i]["homefolder"]);
$role = $classname::getInstance($resArr[$i]['role'], $dms);
$user = new self($resArr[$i]["id"], $resArr[$i]["login"], $resArr[$i]["pwd"], $resArr[$i]["fullName"], $resArr[$i]["email"], (isset($resArr[$i]["language"])?$resArr[$i]["language"]:NULL), (isset($resArr[$i]["theme"])?$resArr[$i]["theme"]:NULL), $resArr[$i]["comment"], $role, $resArr[$i]["hidden"], $resArr[$i]["disabled"], $resArr[$i]["pwdExpiration"], $resArr[$i]["loginfailures"], $resArr[$i]["quota"], $resArr[$i]["homefolder"]);
$user->setDMS($dms);
$users[$i] = $user;
}
@ -349,6 +596,26 @@ class SeedDMS_Core_User { /* {{{ */
return true;
} /* }}} */
/**
* @return string
*/
function getSecret() { return $this->_secret; }
/**
* @param string $newSecret
*/
function setSecret($newSecret) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblUsers` SET `secret` =".$db->qstr($newSecret)." WHERE `id` = " . $this->_id;
$res = $db->getResult($queryStr);
if (!$res)
return false;
$this->_secret = $newSecret;
return true;
} /* }}} */
/**
* @return string
*/
@ -364,11 +631,11 @@ class SeedDMS_Core_User { /* {{{ */
if(trim($newPwdExpiration) == '' || trim($newPwdExpiration) == 'never') {
$newPwdExpiration = null;
$queryStr = "UPDATE `tblUsers` SET `pwdExpiration` = NULL WHERE `id` = " . $this->_id;
} else {
if(trim($newPwdExpiration) == 'now')
$newPwdExpiration = date('Y-m-d H:i:s');
} else {
if(trim($newPwdExpiration) == 'now')
$newPwdExpiration = date('Y-m-d H:i:s');
$queryStr = "UPDATE `tblUsers` SET `pwdExpiration` =".$db->qstr($newPwdExpiration)." WHERE `id` = " . $this->_id;
}
}
$res = $db->getResult($queryStr);
if (!$res)
return false;
@ -472,10 +739,14 @@ class SeedDMS_Core_User { /* {{{ */
*/
function setRole($newrole) { /* {{{ */
$db = $this->_dms->getDB();
if(!in_array($newrole, array(SeedDMS_Core_User::role_admin, SeedDMS_Core_User::role_guest, SeedDMS_Core_User::role_user), true))
return false;
$queryStr = "UPDATE `tblUsers` SET `role` = " . $newrole . " WHERE `id` = " . $this->_id;
if(!is_object($newrole) || (get_class($newrole) != $this->_dms->getClassname('role')))
return false;
if(is_object($newrole))
$queryStr = "UPDATE `tblUsers` SET `role` = " . $newrole->getID() . " WHERE `id` = " . $this->_id;
else
$queryStr = "UPDATE `tblUsers` SET `role` = " . $newrole . " WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr))
return false;
@ -486,12 +757,12 @@ class SeedDMS_Core_User { /* {{{ */
/**
* @return bool
*/
function isAdmin() { return ($this->_role == SeedDMS_Core_User::role_admin); }
function isAdmin() { return (is_object($this->_role) ? $this->_role->isAdmin() : $this->_role == SeedDMS_Core_User::role_admin); }
/**
* @return bool
* Was never used and is now deprecated
*/
function setAdmin() { /* {{{ */
function _setAdmin($isAdmin) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblUsers` SET `role` = " . SeedDMS_Core_User::role_admin . " WHERE `id` = " . $this->_id;
@ -505,12 +776,12 @@ class SeedDMS_Core_User { /* {{{ */
/**
* @return bool
*/
function isGuest() { return ($this->_role == SeedDMS_Core_User::role_guest); }
function isGuest() { return (is_object($this->_role) ? $this->_role->isGuest() : $this->_role == SeedDMS_Core_User::role_guest); }
/**
* @return bool
* Was never used and is now deprecated
*/
function setGuest() { /* {{{ */
function _setGuest($isGuest) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblUsers` SET `role` = " . SeedDMS_Core_User::role_guest . " WHERE `id` = " . $this->_id;
@ -660,13 +931,24 @@ class SeedDMS_Core_User { /* {{{ */
/**
* Remove user from all processes
*
* This method adds another log entry to the reviews and approvals
* which indicates the user has been deleted from the process. By default it will
* do so for each review/approval regardless of its current state unles
* This method adds another log entry to the reviews, approvals, receptions, revisions,
* which indicates that the user has been deleted from the process. By default it will
* do so for each review/approval/reception/revision regardless of its current state unless
* the user has been removed already (status=-2). So even
* reviews/approvals already processed by the user will be added the log
* reviews/approvals/receptions/revisions already processed by the user will be added the log
* entry. Only, if the last log entry was a removal already, it will not be
* added a second time.
* This behaviour can be changed by passing a list of states in the optional
* argument $states. Only reviews, etc. in the given state will be affected.
* This allows to remove the user only if the review, etc. has not been done
* (state = 0).
*
* The last optional parameter $newuser is for replacing the user currently in
* charge by another user. If this parameter is passed, the old user will
* be deleted first (like described above) and afterwards the new user will
* be addded. The deletion of the old user and adding the new user will only
* happen if the new user has at least read access on the document. If not,
* the document will be skipped and remained unchanged.
*
* This removal from processes will also take place for older versions of a document.
*
@ -773,6 +1055,84 @@ class SeedDMS_Core_User { /* {{{ */
}
$db->commitTransaction();
/* Get a list of all receptions, even those of older document versions */
$receiptStatus = $this->getReceiptStatus();
$db->startTransaction();
foreach ($receiptStatus["indstatus"] as $ri) {
if(!($doc = $this->_dms->getDocument($ri['documentID'])))
continue;
if($docs) {
if(!in_array($doc->getID(), $docs))
continue;
if(!$doc->isLatestContent($ri['version']))
continue;
}
if($newuser && $doc->getAccessMode($newuser) < M_READ)
continue;
if($ri['status'] != -2 && (!isset($states['receipt']) || in_array($ri['status'], $states['receipt']))) {
$queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, `comment`, `date`, `userID`) ".
"VALUES ('". $ri["receiptID"] ."', '-2', '".(($newuser && $ri['status'] == 0) ? 'Recipient replaced by '.$newuser->getLogin() : 'Recipient removed from process')."', ".$db->getCurrentDatetime().", '". $user->getID() ."')";
$res=$db->getResult($queryStr);
if(!$res) {
$db->rollbackTransaction();
return false;
}
/* Only receptions not done already can be transferred to a new user */
if($newuser && $ri['status'] == 0) {
if($doc = $this->_dms->getDocument($ri['documentID'])) {
if($version = $doc->getContentByVersion($ri['version'])) {
$ret = $version->addIndRecipient($newuser, $user);
/* returns -3 if the user is already a recipient */
if($ret === false || ($ret < 0 && $ret != -3)) {
$db->rollbackTransaction();
return false;
}
}
}
}
}
}
$db->commitTransaction();
/* Get a list of all revisions, even those of older document versions */
$revisionStatus = $this->getRevisionStatus();
$db->startTransaction();
foreach ($revisionStatus["indstatus"] as $ri) {
if(!($doc = $this->_dms->getDocument($ri['documentID'])))
continue;
if($docs) {
if(!in_array($doc->getID(), $docs))
continue;
if(!$doc->isLatestContent($ri['version']))
continue;
}
if($newuser && $doc->getAccessMode($newuser) < M_READ)
continue;
if($ri['status'] != -2 && (!isset($states['revision']) || in_array($ri['status'], $states['revision']))) {
$queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, `comment`, `date`, `userID`) ".
"VALUES ('". $ri["revisionID"] ."', '-2', '".(($newuser && in_array($ri['status'], array(S_LOG_WAITING, S_LOG_SLEEPING))) ? 'Revisor replaced by '.$newuser->getLogin() : 'Revisor removed from process')."', ".$db->getCurrentDatetime().", '". $user->getID() ."')";
$res=$db->getResult($queryStr);
if(!$res) {
$db->rollbackTransaction();
return false;
}
/* Only revisions not already done or sleeping can be transferred to a new user */
if($newuser && in_array($ri['status'], array(S_LOG_WAITING, S_LOG_SLEEPING))) {
if($doc = $this->_dms->getDocument($ri['documentID'])) {
if($version = $doc->getContentByVersion($ri['version'])) {
$ret = $version->addIndRevisor($newuser, $user);
/* returns -3 if the user is already a revisor */
if($ret === false || ($ret < 0 && $ret != -3)) {
$db->rollbackTransaction();
return false;
}
}
}
}
}
}
$db->commitTransaction();
return true;
} /* }}} */
@ -1279,6 +1639,34 @@ class SeedDMS_Core_User { /* {{{ */
return $documents;
} /* }}} */
/**
* Returns all documents check out by a given user
*
* @param object $user
* @return array list of documents
*/
function getDocumentsCheckOut() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT `tblDocuments`.* ".
"FROM `tblDocumentCheckOuts` LEFT JOIN `tblDocuments` ON `tblDocuments`.`id` = `tblDocumentCheckOuts`.`document` ".
"WHERE `tblDocumentCheckOuts`.`userID` = '".$this->_id."' ".
"ORDER BY `id` ASC";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr)
return false;
$documents = array();
$classname = $this->_dms->getClassname('document');
foreach ($resArr as $row) {
$document = new $classname($row["id"], $row["name"], $row["comment"], $row["date"], $row["expires"], $row["owner"], $row["folder"], $row["inheritAccess"], $row["defaultAccess"], $row["lockUser"], $row["keywords"], $row["sequence"]);
$document->setDMS($this->_dms);
$documents[] = $document;
}
return $documents;
} /* }}} */
/**
* Returns all document links of a given user
* @return SeedDMS_Core_DocumentLink[]|bool list of document links
@ -1582,6 +1970,185 @@ class SeedDMS_Core_User { /* {{{ */
return $status;
} /* }}} */
/**
* Get a list of receipts
* This function returns a list of all receipts seperated by individual
* and group receipts. If the document id
* is passed, then only this document will be checked for receipts. The
* same is true for the version of a document which limits the list
* further.
*
* For a detaile description of the result array see
* {link SeedDMS_Core_User::getApprovalStatus} which does the same for
* approvals.
*
* @param int $documentID optional document id for which to retrieve the
* receipt
* @param int $version optional version of the document
* @return array list of all receipts
*/
function getReceiptStatus($documentID=null, $version=null) { /* {{{ */
$db = $this->_dms->getDB();
$status = array("indstatus"=>array(), "grpstatus"=>array());
if (!$db->createTemporaryTable("ttcontentid")) {
return false;
}
// See if the user is assigned as an individual recipient.
// left join with ttcontentid to restrict result on latest version of document
// unless a document and version is given
$queryStr = "SELECT `tblDocumentRecipients`.*, `tblDocumentReceiptLog`.`status`, ".
"`tblDocumentReceiptLog`.`comment`, `tblDocumentReceiptLog`.`date`, ".
"`tblDocumentReceiptLog`.`userID` ".
"FROM `tblDocumentRecipients` ".
"LEFT JOIN `tblDocumentReceiptLog` USING (`receiptID`) ".
"LEFT JOIN `ttcontentid` ON `ttcontentid`.`maxVersion` = `tblDocumentRecipients`.`version` AND `ttcontentid`.`document` = `tblDocumentRecipients`.`documentID` ".
"WHERE `tblDocumentRecipients`.`type`='0' ".
($documentID==null ? "" : "AND `tblDocumentRecipients`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentRecipients`.`version` = '". (int) $version ."' ").
($documentID==null && $version==null ? "AND `ttcontentid`.`maxVersion` = `tblDocumentRecipients`.`version` " : "").
"AND `tblDocumentRecipients`.`required`='". $this->_id ."' ".
"ORDER BY `tblDocumentReceiptLog`.`receiptLogID` DESC";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return false;
if (count($resArr)>0) {
foreach ($resArr as $res) {
if(isset($status["indstatus"][$res['documentID']])) {
if($status["indstatus"][$res['documentID']]['date'] < $res['date']) {
$status["indstatus"][$res['documentID']] = $res;
}
} else {
$status["indstatus"][$res['documentID']] = $res;
}
}
}
// See if the user is the member of a group that has been assigned to
// receipt the document version.
$queryStr = "SELECT `tblDocumentRecipients`.*, `tblDocumentReceiptLog`.`status`, ".
"`tblDocumentReceiptLog`.`comment`, `tblDocumentReceiptLog`.`date`, ".
"`tblDocumentReceiptLog`.`userID` ".
"FROM `tblDocumentRecipients` ".
"LEFT JOIN `tblDocumentReceiptLog` USING (`receiptID`) ".
"LEFT JOIN `ttcontentid` ON `ttcontentid`.`maxVersion` = `tblDocumentRecipients`.`version` AND `ttcontentid`.`document` = `tblDocumentRecipients`.`documentID` ".
"LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`groupID` = `tblDocumentRecipients`.`required` ".
"WHERE `tblDocumentRecipients`.`type`='1' ".
($documentID==null ? "" : "AND `tblDocumentRecipients`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentRecipients`.`version` = '". (int) $version ."' ").
($documentID==null && $version==null ? "AND `ttcontentid`.`maxVersion` = `tblDocumentRecipients`.`version` " : "").
"AND `tblGroupMembers`.`userID`='". $this->_id ."' ".
"ORDER BY `tblDocumentReceiptLog`.`receiptLogID` DESC";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return false;
if (count($resArr)>0) {
foreach ($resArr as $res) {
if(isset($status["grpstatus"][$res['documentID']])) {
if($status["grpstatus"][$res['documentID']]['date'] < $res['date']) {
$status["grpstatus"][$res['documentID']] = $res;
}
} else {
$status["grpstatus"][$res['documentID']] = $res;
}
}
}
return $status;
} /* }}} */
/**
* Get a list of revisions
* This function returns a list of all revisions seperated by individual
* and group revisions. If the document id
* is passed, then only this document will be checked for revisions. The
* same is true for the version of a document which limits the list
* further.
*
* For a detaile description of the result array see
* {link SeedDMS_Core_User::getApprovalStatus} which does the same for
* approvals.
*
* @param int $documentID optional document id for which to retrieve the
* revisions
* @param int $version optional version of the document
* @return array list of all revisions. If the result array has no elements,
* then the user was not a revisor. If there elements for 'indstatus' or
* 'grpstatus' then the revision hasn't been started.
*/
function getRevisionStatus($documentID=null, $version=null) { /* {{{ */
$db = $this->_dms->getDB();
$status = array("indstatus"=>array(), "grpstatus"=>array());
if (!$db->createTemporaryTable("ttcontentid")) {
return false;
}
// See if the user is assigned as an individual revisor.
// left join with ttcontentid to restrict result on latest version of document
// unless a document and version is given
$queryStr = "SELECT `tblDocumentRevisors`.*, `tblDocumentRevisionLog`.`status`, ".
"`tblDocumentRevisionLog`.`comment`, `tblDocumentRevisionLog`.`date`, ".
"`tblDocumentRevisionLog`.`userID` ".
"FROM `tblDocumentRevisors` ".
"LEFT JOIN `tblDocumentRevisionLog` USING (`revisionID`) ".
"LEFT JOIN `ttcontentid` ON `ttcontentid`.`maxVersion` = `tblDocumentRevisors`.`version` AND `ttcontentid`.`document` = `tblDocumentRevisors`.`documentID` ".
"WHERE `tblDocumentRevisors`.`type`='0' ".
($documentID==null ? "" : "AND `tblDocumentRevisors`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentRevisors`.`version` = '". (int) $version ."' ").
($documentID==null && $version==null ? "AND `ttcontentid`.`maxVersion` = `tblDocumentRevisors`.`version` " : "").
"AND `tblDocumentRevisors`.`required`='". $this->_id ."' ".
"ORDER BY `tblDocumentRevisionLog`.`revisionLogID` DESC";
$resArr = $db->getResultArray($queryStr);
if ($resArr === false)
return false;
if (count($resArr)>0) {
$status['indstatus'] = array();
foreach ($resArr as $res) {
if($res['date']) {
if(isset($status["indstatus"][$res['documentID']])) {
if($status["indstatus"][$res['documentID']]['date'] < $res['date']) {
$status["indstatus"][$res['documentID']] = $res;
}
} else {
$status["indstatus"][$res['documentID']] = $res;
}
}
}
}
// See if the user is the member of a group that has been assigned to
// revision the document version.
$queryStr = "SELECT `tblDocumentRevisors`.*, `tblDocumentRevisionLog`.`status`, ".
"`tblDocumentRevisionLog`.`comment`, `tblDocumentRevisionLog`.`date`, ".
"`tblDocumentRevisionLog`.`userID` ".
"FROM `tblDocumentRevisors` ".
"LEFT JOIN `tblDocumentRevisionLog` USING (`revisionID`) ".
"LEFT JOIN `ttcontentid` ON `ttcontentid`.`maxVersion` = `tblDocumentRevisors`.`version` AND `ttcontentid`.`document` = `tblDocumentRevisors`.`documentID` ".
"LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`groupID` = `tblDocumentRevisors`.`required` ".
"WHERE `tblDocumentRevisors`.`type`='1' ".
($documentID==null ? "" : "AND `tblDocumentRevisors`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentRevisors`.`version` = '". (int) $version ."' ").
($documentID==null && $version==null ? "AND `ttcontentid`.`maxVersion` = `tblDocumentRevisors`.`version` " : "").
"AND `tblGroupMembers`.`userID`='". $this->_id ."' ".
"ORDER BY `tblDocumentRevisionLog`.`revisionLogID` DESC";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return false;
if (count($resArr)>0) {
foreach ($resArr as $res) {
if(isset($status["grpstatus"][$res['documentID']])) {
if($status["grpstatus"][$res['documentID']]['date'] < $res['date']) {
$status["grpstatus"][$res['documentID']] = $res;
}
} else {
$status["grpstatus"][$res['documentID']] = $res;
}
}
}
return $status;
} /* }}} */
/**
* Get a list of documents with a workflow
*
@ -1939,6 +2506,165 @@ class SeedDMS_Core_User { /* {{{ */
return true;
} /* }}} */
/**
* Get all substitutes of the user
*
* These users are substitutes of the current user
*
* @return array list of users
*/
function getSubstitutes() { /* {{{ */
$db = $this->_dms->getDB();
if (!isset($this->_substitutes)) {
$queryStr = "SELECT `tblUsers`.* FROM `tblUserSubstitutes` ".
"LEFT JOIN `tblUsers` ON `tblUserSubstitutes`.`substitute` = `tblUsers`.`id` ".
"WHERE `tblUserSubstitutes`.`user`='". $this->_id ."'";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$this->_substitutes = array();
$classname = $this->_dms->getClassname('user');
foreach ($resArr as $row) {
$user = new $classname($row["id"], $row["login"], $row["pwd"], $row["fullName"], $row["email"], $row["language"], $row["theme"], $row["comment"], $row["role"], $row["hidden"], $row["disabled"], $row["pwdExpiration"], $row["loginfailures"], $row["quota"], $row["homefolder"]);
$user->setDMS($this->_dms);
array_push($this->_substitutes, $user);
}
}
return $this->_substitutes;
} /* }}} */
/**
* Get all users this user is a substitute of
*
* @return array list of users
*/
function getReverseSubstitutes() { /* {{{ */
$db = $this->_dms->getDB();
if (!isset($this->_rev_substitutes)) {
$queryStr = "SELECT `tblUsers`.* FROM `tblUserSubstitutes` ".
"LEFT JOIN `tblUsers` ON `tblUserSubstitutes`.`user` = `tblUsers`.`id` ".
"LEFT JOIN `tblRoles` ON `tblRoles`.`id`=`tblUsers`.`role` ".
"WHERE `tblUserSubstitutes`.`substitute`='". $this->_id ."'";
/* None admins can only be substitutes for regular users, otherwise
* regular users can become admin
*/
if(!$this->isAdmin())
$queryStr .= " AND `tblRoles`.`role` = ".SeedDMS_Core_User::role_user;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$this->_rev_substitutes = array();
$classnamerole = $this->_dms->getClassname('role');
$classname = $this->_dms->getClassname('user');
foreach ($resArr as $row) {
$role = $classnamerole::getInstance($row['role'], $this->_dms);
$user = new $classname($row["id"], $row["login"], $row["pwd"], $row["fullName"], $row["email"], $row["language"], $row["theme"], $row["comment"], $role, $row["hidden"], $row["disabled"], $row["pwdExpiration"], $row["loginfailures"], $row["quota"], $row["homefolder"]);
$user->setDMS($this->_dms);
array_push($this->_rev_substitutes, $user);
}
}
return $this->_rev_substitutes;
} /* }}} */
/**
* Add a substitute to the user
*
* @return boolean true if successful otherwise false
*/
function addSubstitute($substitute) { /* {{{ */
$db = $this->_dms->getDB();
if(get_class($substitute) != $this->_dms->getClassname('user'))
return false;
$queryStr = "SELECT * FROM `tblUserSubstitutes` WHERE `user`=" . $this->_id . " AND `substitute`=".$substitute->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
if (count($resArr) == 1) return true;
$queryStr = "INSERT INTO `tblUserSubstitutes` (`user`, `substitute`) VALUES (" . $this->_id . ", ".$substitute->getID().")";
if (!$db->getResult($queryStr))
return false;
$this->_substitutes = null;
return true;
} /* }}} */
/**
* Remove a substitute from the user
*
* @return boolean true if successful otherwise false
*/
function removeSubstitute($substitute) { /* {{{ */
$db = $this->_dms->getDB();
if(get_class($substitute) != $this->_dms->getClassname('user'))
return false;
$queryStr = "SELECT * FROM `tblUserSubstitutes` WHERE `user`=" . $this->_id . " AND `substitute`=".$substitute->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
if (count($resArr) == 0) return true;
$queryStr = "DELETE FROM `tblUserSubstitutes` WHERE `user`=" . $this->_id . " AND `substitute`=".$substitute->getID();
if (!$db->getResult($queryStr))
return false;
$this->_substitutes = null;
return true;
} /* }}} */
/**
* Check if user is a substitute of the current user
*
* @return boolean true if successful otherwise false
*/
function isSubstitute($substitute) { /* {{{ */
$db = $this->_dms->getDB();
if(get_class($substitute) != $this->_dms->getClassname('user'))
return false;
$queryStr = "SELECT * FROM `tblUserSubstitutes` WHERE `user`=" . $this->_id . " AND `substitute`=".$substitute->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
if (count($resArr) == 1) return true;
return false;
} /* }}} */
/**
* Check if user may switch to the given user
*
* Switching to the given user is only allowed if the given user
* is a substitute for the current user.
*
* @return boolean true if successful otherwise false
*/
function maySwitchToUser($touser) { /* {{{ */
$db = $this->_dms->getDB();
if(get_class($touser) != $this->_dms->getClassname('user'))
return false;
/* switching to an admin account is always forbitten, unless the
* current user is admin itself
*/
if(!$this->isAdmin() && $touser->isAdmin())
return false;
$queryStr = "SELECT * FROM `tblUserSubstitutes` WHERE `substitute`=" . $this->_id . " AND `user`=".$touser->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
if (count($resArr) == 1) return true;
return false;
} /* }}} */
/**
* Get all notifications of user
*

View File

@ -43,7 +43,14 @@ class SeedDMS_Core_Workflow { /* {{{ */
var $_initstate;
/**
* @var SeedDMS_Core_Workflow_Transition[] name of the workflow state
* @var data for rendering graph
*
* @access protected
*/
var $_layoutdata;
/**
* @var SeedDMS_Core_Workflow_Transition[] list of transitions
*
* @access protected
*/
@ -61,11 +68,13 @@ class SeedDMS_Core_Workflow { /* {{{ */
* @param int $id
* @param string $name
* @param SeedDMS_Core_Workflow_State $initstate
* @param string $layoutdata
*/
function __construct($id, $name, $initstate) { /* {{{ */
function __construct($id, $name, $initstate, $layoutdata) { /* {{{ */
$this->_id = $id;
$this->_name = $name;
$this->_initstate = $initstate;
$this->_layoutdata = $layoutdata;
$this->_transitions = null;
$this->_dms = null;
} /* }}} */
@ -124,6 +133,26 @@ class SeedDMS_Core_Workflow { /* {{{ */
return true;
} /* }}} */
/**
* @return string
*/
function getLayoutData() { return $this->_layoutdata; }
/**
* @param string $layoutdata
*/
function setLayoutData($newdata) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblWorkflows` SET `layoutdata` = ".$db->qstr($newdata)." WHERE `id` = " . $this->_id;
$res = $db->getResult($queryStr);
if (!$res)
return false;
$this->_layoutdata = $newdata;
return true;
} /* }}} */
/**
* @return SeedDMS_Core_Workflow_Transition[]|bool
*/

View File

@ -88,6 +88,16 @@ class SeedDMS_Core_DatabaseAccess {
*/
private $_ttcontentid;
/**
* @var boolean set to true if temp. table for doc reception has been created
*/
private $_ttreceiptid;
/**
* @var boolean set to true if temp. table for doc revision has been created
*/
private $_ttrevisionid;
/**
* @var boolean set to true if in a database transaction
*/
@ -232,6 +242,8 @@ class SeedDMS_Core_DatabaseAccess {
$this->_ttapproveid = false;
$this->_ttstatid = false;
$this->_ttcontentid = false;
$this->_ttreceiptid = false;
$this->_ttrevisionid = false;
$this->_useviews = false; // turn off views, because they are much slower then temp. tables
$this->_debug = false;
} /* }}} */
@ -301,6 +313,7 @@ class SeedDMS_Core_DatabaseAccess {
switch($this->_driver) {
case 'mysql':
$this->_conn->exec('SET NAMES utf8');
// $this->_conn->setAttribute(PDO::ATTR_AUTOCOMMIT, FALSE);
/* Turn this on if you want strict checking of default values, etc. */
/* $this->_conn->exec("SET SESSION sql_mode = 'STRICT_TRANS_TABLES'"); */
/* The following is the default on Ubuntu 16.04 */
@ -315,7 +328,7 @@ class SeedDMS_Core_DatabaseAccess {
}
if($this->_useviews) {
$tmp = $this->ViewList();
foreach(array('ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid') as $viewname) {
foreach(array('ttreviewid', 'ttapproveid', 'ttstatid', 'ttcontentid', 'ttreceiptid', 'ttrevisionid') as $viewname) {
if(in_array($viewname, $tmp)) {
$this->{"_".$viewname} = true;
}
@ -447,9 +460,15 @@ class SeedDMS_Core_DatabaseAccess {
$this->_conn->beginTransaction();
}
$this->_intransaction++;
if($this->_logfp) {
fwrite($this->_logfp, microtime()." START ".$htis->_intransaction."\n");
}
} /* }}} */
function rollbackTransaction() { /* {{{ */
if($this->_logfp) {
fwrite($this->_logfp, microtime()." ROLLBACK ".$htis->_intransaction."\n");
}
if($this->_intransaction == 1) {
$this->_conn->rollBack();
}
@ -457,6 +476,9 @@ class SeedDMS_Core_DatabaseAccess {
} /* }}} */
function commitTransaction() { /* {{{ */
if($this->_logfp) {
fwrite($this->_logfp, microtime()." COMMIT ".$htis->_intransaction."\n");
}
if($this->_intransaction == 1) {
$this->_conn->commit();
}
@ -658,6 +680,88 @@ class SeedDMS_Core_DatabaseAccess {
}
return $this->_ttcontentid;
}
elseif (!strcasecmp($tableName, "ttreceiptid")) {
switch($this->_driver) {
case 'sqlite':
$queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttreceiptid` AS ".
"SELECT `tblDocumentReceiptLog`.`receiptID` AS `receiptID`, ".
"MAX(`tblDocumentReceiptLog`.`receiptLogID`) AS `maxLogID` ".
"FROM `tblDocumentReceiptLog` ".
"GROUP BY `tblDocumentReceiptLog`.`receiptID` ";
// "ORDER BY `maxLogID`";
break;
case 'pgsql':
$queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttreceiptid` (`receiptID` INTEGER, `maxLogID` INTEGER, PRIMARY KEY (`receiptID`));".
"INSERT INTO `ttreceiptid` SELECT `tblDocumentReceiptLog`.`receiptID`, ".
"MAX(`tblDocumentReceiptLog`.`receiptLogID`) AS `maxLogID` ".
"FROM `tblDocumentReceiptLog` ".
"GROUP BY `tblDocumentReceiptLog`.`receiptID` ";
// "ORDER BY `maxLogID`";
break;
default:
$queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttreceiptid` (PRIMARY KEY (`receiptID`), INDEX (`maxLogID`)) ".
"SELECT `tblDocumentReceiptLog`.`receiptID`, ".
"MAX(`tblDocumentReceiptLog`.`receiptLogID`) AS `maxLogID` ".
"FROM `tblDocumentReceiptLog` ".
"GROUP BY `tblDocumentReceiptLog`.`receiptID` ";
// "ORDER BY `maxLogID`";
}
if (!$this->_ttreceiptid) {
if (!$this->getResult($queryStr))
return false;
$this->_ttreceiptid=true;
}
else {
if (is_bool($override) && $override) {
if (!$this->getResult("DELETE FROM `ttreceiptid`"))
return false;
if (!$this->getResult($queryStr))
return false;
}
}
return $this->_ttreceiptid;
}
elseif (!strcasecmp($tableName, "ttrevisionid")) {
switch($this->_driver) {
case 'sqlite':
$queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttrevisionid` AS ".
"SELECT `tblDocumentRevisionLog`.`revisionID` AS `revisionID`, ".
"MAX(`tblDocumentRevisionLog`.`revisionLogID`) AS `maxLogID` ".
"FROM `tblDocumentRevisionLog` ".
"GROUP BY `tblDocumentRevisionLog`.`revisionID` ";
// "ORDER BY `maxLogID`";
break;
case 'pgsql':
$queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttrevisionid` (`revisionID` INTEGER, `maxLogID` INTEGER, PRIMARY KEY (`revisionID`));".
"INSERT INTO `ttrevisionid` SELECT `tblDocumentRevisionLog`.`revisionID`, ".
"MAX(`tblDocumentRevisionLog`.`revisionLogID`) AS `maxLogID` ".
"FROM `tblDocumentRevisionLog` ".
"GROUP BY `tblDocumentRevisionLog`.`revisionID` ";
// "ORDER BY `maxLogID`";
break;
default:
$queryStr = "CREATE TEMPORARY TABLE IF NOT EXISTS `ttrevisionid` (PRIMARY KEY (`revisionID`), INDEX (`maxLogID`)) ".
"SELECT `tblDocumentRevisionLog`.`revisionID`, ".
"MAX(`tblDocumentRevisionLog`.`revisionLogID`) AS `maxLogID` ".
"FROM `tblDocumentRevisionLog` ".
"GROUP BY `tblDocumentRevisionLog`.`revisionID` ";
// "ORDER BY `maxLogID`";
}
if (!$this->_ttrevisionid) {
if (!$this->getResult($queryStr))
return false;
$this->_ttrevisionid=true;
}
else {
if (is_bool($override) && $override) {
if (!$this->getResult("DELETE FROM `ttrevisionid`"))
return false;
if (!$this->getResult($queryStr))
return false;
}
}
return $this->_ttrevisionid;
}
return false;
} /* }}} */
@ -857,6 +961,82 @@ class SeedDMS_Core_DatabaseAccess {
}
return $this->_ttcontentid;
}
elseif (!strcasecmp($tableName, "ttreceiptid")) {
switch($this->_driver) {
case 'sqlite':
$queryStr = "CREATE VIEW `ttreceiptid` AS ".
"SELECT `tblDocumentReceiptLog`.`receiptID` AS `receiptID`, ".
"MAX(`tblDocumentReceiptLog`.`receiptLogID`) AS `maxLogID` ".
"FROM `tblDocumentReceiptLog` ".
"GROUP BY `tblDocumentReceiptLog`.`receiptID` ";
break;
case 'pgsql':
$queryStr = "CREATE VIEW `ttreceiptid` AS ".
"SELECT `tblDocumentReceiptLog`.`receiptID` AS `receiptID`, ".
"MAX(`tblDocumentReceiptLog`.`receiptLogID`) AS `maxLogID` ".
"FROM `tblDocumentReceiptLog` ".
"GROUP BY `tblDocumentReceiptLog`.`receiptID` ";
break;
default:
$queryStr = "CREATE".($override ? " OR REPLACE" : "")." VIEW `ttreceiptid` AS ".
"SELECT `tblDocumentReceiptLog`.`receiptID`, ".
"MAX(`tblDocumentReceiptLog`.`receiptLogID`) AS `maxLogID` ".
"FROM `tblDocumentReceiptLog` ".
"GROUP BY `tblDocumentReceiptLog`.`receiptID` ";
}
if (!$this->_ttreceiptid) {
if (!$this->getResult($queryStr))
return false;
$this->_ttreceiptid=true;
}
else {
if (is_bool($override) && $override) {
if (!$this->getResult("DROP VIEW `ttreceiptid`"))
return false;
if (!$this->getResult($queryStr))
return false;
}
}
return $this->_ttreceiptid;
}
elseif (!strcasecmp($tableName, "ttrevisionid")) {
switch($this->_driver) {
case 'sqlite':
$queryStr = "CREATE VIEW `ttrevisionid` AS ".
"SELECT `tblDocumentRevisionLog`.`revisionID` AS `revisionID`, ".
"MAX(`tblDocumentRevisionLog`.`revisionLogID`) AS `maxLogID` ".
"FROM `tblDocumentRevisionLog` ".
"GROUP BY `tblDocumentRevisionLog`.`revisionID` ";
break;
case 'pgsql':
$queryStr = "CREATE VIEW `ttrevisionid` AS ".
"SELECT `tblDocumentRevisionLog`.`revisionID` AS `revisionID`, ".
"MAX(`tblDocumentRevisionLog`.`revisionLogID`) AS `maxLogID` ".
"FROM `tblDocumentRevisionLog` ".
"GROUP BY `tblDocumentRevisionLog`.`revisionID` ";
break;
default:
$queryStr = "CREATE".($override ? " OR REPLACE" : "")." VIEW `ttrevisionid` AS ".
"SELECT `tblDocumentRevisionLog`.`revisionID`, ".
"MAX(`tblDocumentRevisionLog`.`revisionLogID`) AS `maxLogID` ".
"FROM `tblDocumentRevisionLog` ".
"GROUP BY `tblDocumentRevisionLog`.`revisionID` ";
}
if (!$this->_ttrevisionid) {
if (!$this->getResult($queryStr))
return false;
$this->_ttrevisionid=true;
}
else {
if (is_bool($override) && $override) {
if (!$this->getResult("DROP VIEW `ttrevisionid`"))
return false;
if (!$this->getResult($queryStr))
return false;
}
}
return $this->_ttrevisionid;
}
return false;
} /* }}} */
@ -925,16 +1105,25 @@ class SeedDMS_Core_DatabaseAccess {
*
* @return string sql code
*/
function getCurrentDatetime() { /* {{{ */
function getCurrentDatetime($dayoffset=0) { /* {{{ */
switch($this->_driver) {
case 'mysql':
return "CURRENT_TIMESTAMP";
if($dayoffset)
return "DATE_ADD(CURRENT_TIMESTAMP, INTERVAL ".$dayoffset." DAY)";
else
return "CURRENT_TIMESTAMP";
break;
case 'sqlite':
return "datetime('now', 'localtime')";
if($dayoffset)
return "datetime('now', '".$dayoffset." days', 'localtime')";
else
return "datetime('now', 'localtime')";
break;
case 'pgsql':
return "now()";
if($dayoffset)
return "now() + interval '".$dayoffset." day'";
else
return "now()";
break;
}
return '';

View File

@ -0,0 +1,3 @@
<?php
// Do any kind of bootstraping for major database version 5
$majorversion = 5; // just an example, currently not used

View File

@ -0,0 +1,3 @@
<?php
// Do any kind of bootstraping for major database version 6
$majorversion = 6; // just an example, currently not used

View File

@ -0,0 +1,23 @@
{
"name": "seeddms/core",
"description": "Core classes to access a SeedDMS database",
"type": "library",
"license": "GPL-2.0-or-later",
"minimum-stability": "dev",
"autoload": {
"psr-4": {
"Seeddms\\Core\\": "Core/"
},
"classmap": ["Core/"]
},
"authors": [
{
"name": "Uwe Steinmann",
"email": "info@seeddms.org"
}
],
"require-dev": {
"phpunit/phpunit": "^9"
}
}

View File

@ -12,11 +12,11 @@
<email>uwe@steinmann.cx</email>
<active>yes</active>
</lead>
<date>2022-11-07</date>
<date>2022-12-10</date>
<time>13:44:55</time>
<version>
<release>5.1.28</release>
<api>5.1.28</api>
<release>6.0.22</release>
<api>6.0.22</api>
</version>
<stability>
<release>stable</release>
@ -24,15 +24,7 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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
- all changes from 5.1.29 merged
</notes>
<contents>
<dir baseinstalldir="SeedDMS" name="/">
@ -82,6 +74,9 @@
<file name="inc.ClassWorkflow.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="inc.ClassTransmittal.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="inc.ClassDecorator.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
@ -120,6 +115,7 @@
<phprelease />
<changelog>
<release>
<date>2010-04-27</date>
<version>
<release>3.0.0</release>
<api>3.0.0</api>
@ -128,13 +124,14 @@
<release>stable</release>
<api>stable</api>
</stability>
<date>2010-04-27</date>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
Initial release
</notes>
</release>
<release>
<date>2011-07-23</date>
<time>08:05:38</time>
<version>
<release>3.2.0</release>
<api>3.2.0</api>
@ -143,8 +140,6 @@ Initial release
<release>stable</release>
<api>stable</api>
</stability>
<date>2011-07-23</date>
<time>08:05:38</time>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
New release
@ -704,21 +699,21 @@ no changes
</notes>
</release>
<release>
<date>2014-07-30</date>
<time>09:03:59</time>
<version>
<release>4.3.9</release>
<api>4.3.9</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
<date>2014-07-30</date>
<time>09:03:59</time>
<version>
<release>4.3.9</release>
<api>4.3.9</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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.
</notes>
</notes>
</release>
<release>
<date>2014-10-22</date>
@ -960,7 +955,7 @@ by a group or user right
<notes>
- new method SeedDMS_Core_DMS::createDump()
- minor improvements int SeedDMS_Core_Document::getReadAccessList()
</notes>
</notes>
</release>
<release>
<date>2016-01-22</date>
@ -984,7 +979,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
</notes>
</notes>
</release>
<release>
<date>2016-01-22</date>
@ -1059,7 +1054,7 @@ SeedDMS_Core_DMS::getNotificationsByUser() are deprecated
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add more callbacks
</notes>
</notes>
</release>
<release>
<date>2016-04-26</date>
@ -1280,7 +1275,7 @@ do not sort some temporary tables anymore, because it causes an error in mysql i
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- all changes from 4.3.25 merged
</notes>
</notes>
</release>
<release>
<date>2016-04-04</date>
@ -1440,7 +1435,7 @@ do not sort some temporary tables anymore, because it causes an error in mysql i
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
<notes>
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
</notes>
@ -1506,13 +1501,13 @@ do not sort some temporary tables anymore, because it causes an error in mysql i
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -1595,9 +1590,9 @@ returns just users which are not disabled
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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()
</notes>
</release>
<release>
@ -1613,7 +1608,7 @@ add SeedDMS_Core_AttributeDefinition::removeValue()
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
just bump version
- just bump version
</notes>
</release>
<release>
@ -1629,10 +1624,10 @@ just bump version
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -1648,10 +1643,10 @@ fix possible sql injection in SeedDMS_Core_User
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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()
</notes>
</release>
<release>
@ -1667,8 +1662,8 @@ remove deprecated methods SeedDMS_Core_Document::convert(), SeedDMS_Core_Documen
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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()
</notes>
</release>
<release>
@ -1684,7 +1679,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp()
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
???
- ???
</notes>
</release>
<release>
@ -1709,7 +1704,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp()
</notes>
</release>
<release>
<date>2019-08-07</date>
<date>2019-09-06</date>
<time>07:31:17</time>
<version>
<release>5.1.13</release>
@ -1744,7 +1739,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp()
</release>
<release>
<date>2020-03-02</date>
<time>09:43:12</time>
<time>09:16:48</time>
<version>
<release>5.1.15</release>
<api>5.1.15</api>
@ -1814,7 +1809,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp()
</notes>
</release>
<release>
<date>2020-07-28</date>
<date>2020-07-30</date>
<time>09:43:12</time>
<version>
<release>5.1.19</release>
@ -1911,7 +1906,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp()
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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
@ -1978,7 +1973,7 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp()
</notes>
</release>
<release>
<date>2022-04-25</date>
<date>2022-05-20</date>
<time>13:44:55</time>
<version>
<release>5.1.26</release>
@ -2016,5 +2011,449 @@ add method SeedDMS_Core_DatabaseAccess::setLogFp()
- pass an array as an attribute to search() will OR each element
</notes>
</release>
<release>
<date>2022-11-07</date>
<time>13:44:55</time>
<version>
<release>5.1.28</release>
<api>5.1.28</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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
</notes>
</release>
<release>
<date>2022-11-21</date>
<time>13:44:55</time>
<version>
<release>5.1.29</release>
<api>5.1.29</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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()
</notes>
</release>
<release>
<date>2017-02-28</date>
<time>06:34:50</time>
<version>
<release>6.0.0</release>
<api>6.0.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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()
</notes>
</release>
<release>
<date>2017-05-28</date>
<time>06:34:50</time>
<version>
<release>6.0.1</release>
<api>6.0.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- speed up getting list of locked documents
- setting _logfile in inc.DBAccessPDO.php will log execution times in file
</notes>
</release>
<release>
<date>2017-12-19</date>
<time>09:19:24</time>
<version>
<release>6.0.2</release>
<api>6.0.2</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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
</notes>
</release>
<release>
<date>2018-01-23</date>
<time>09:19:24</time>
<version>
<release>6.0.3</release>
<api>6.0.3</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
<date>2018-02-14</date>
<time>09:19:24</time>
<version>
<release>6.0.4</release>
<api>6.0.4</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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()
</notes>
</release>
<release>
<date>2018-02-27</date>
<time>09:19:24</time>
<version>
<release>6.0.5</release>
<api>6.0.5</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
add list 'NeedsCorrectionOwner' to SeedDMS_Core_DMS::getDocumentList()
</notes>
</release>
<release>
<date>2018-11-13</date>
<time>07:31:17</time>
<version>
<release>6.0.6</release>
<api>6.0.6</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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.
</notes>
</release>
<release>
<date>2020-02-17</date>
<time>09:16:48</time>
<version>
<release>6.0.7</release>
<api>6.0.7</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
<date>2020-03-02</date>
<time>09:43:12</time>
<version>
<release>6.0.8</release>
<api>6.0.8</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- no changes, just keep same version as seeddms application
</notes>
</release>
<release>
<date>2020-05-14</date>
<time>09:43:12</time>
<version>
<release>6.0.9</release>
<api>6.0.9</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- no changes, just keep same version as seeddms application
</notes>
</release>
<release>
<date>2020-05-22</date>
<time>09:43:12</time>
<version>
<release>6.0.10</release>
<api>6.0.10</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
SeedDMS_Core_DocumentContent::delRevisor() returns -4 if user has already made a revision
</notes>
</release>
<release>
<date>2020-06-05</date>
<time>09:43:12</time>
<version>
<release>6.0.11</release>
<api>6.0.11</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
SeedDMS_Core_DMS::filterAccess() properly checks for documents
</notes>
</release>
<release>
<date>2020-06-05</date>
<time>09:43:12</time>
<version>
<release>6.0.12</release>
<api>6.0.12</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
</notes>
</release>
<release>
<date>2020-09-29</date>
<time>13:44:55</time>
<version>
<release>6.0.13</release>
<api>6.0.13</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- SeedDMS_Folder_DMS::getAccessList() and getDefaultAccess() do not return fals anymore if the parent does not exists. They just stop inheritance.
</notes>
</release>
<release>
<date>2021-01-04</date>
<time>13:44:55</time>
<version>
<release>6.0.14</release>
<api>6.0.14</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
better error checking in SeedDMS_Core_Document::cancelCheckOut()
</notes>
</release>
<release>
<date>2021-04-13</date>
<time>13:44:55</time>
<version>
<release>6.0.15</release>
<api>6.0.15</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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
</notes>
</release>
<release>
<date>2021-05-07</date>
<time>13:44:55</time>
<version>
<release>6.0.16</release>
<api>6.0.16</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
</notes>
</release>
<release>
<date>2021-12-11</date>
<time>13:44:55</time>
<version>
<release>6.0.17</release>
<api>6.0.17</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- all changes from 5.1.24
</notes>
</release>
<release>
<date>2022-04-22</date>
<time>13:44:55</time>
<version>
<release>6.0.18</release>
<api>6.0.18</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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
</notes>
</release>
<release>
<date>2022-05-20</date>
<time>13:44:55</time>
<version>
<release>6.0.19</release>
<api>6.0.19</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- all changes from 5.1.26
- removeFromProcesses() will not touch documents for which the new user does not have at least read access
</notes>
</release>
<release>
<date>2022-09-18</date>
<time>13:44:55</time>
<version>
<release>6.0.20</release>
<api>6.0.20</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- all changes from 5.1.27 merged
- SeedDMЅ_Core_DMS::getDocumentsInRevision() returns status from revision log
</notes>
</release>
<release>
<date>2022-11-18</date>
<time>13:44:55</time>
<version>
<release>6.0.21</release>
<api>6.0.21</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- all changes from 5.1.28 merged
</notes>
</release>
</changelog>
</package>

26
SeedDMS_Core/phpunit.xml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
cacheResultFile=".phpunit.cache/test-results"
executionOrder="depends,defects"
forceCoversAnnotation="false"
beStrictAboutCoversAnnotation="false"
beStrictAboutOutputDuringTests="false"
beStrictAboutTodoAnnotatedTests="false"
failOnRisky="false"
failOnWarning="true"
verbose="true">
<testsuites>
<testsuite name="default">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
<coverage cacheDirectory=".phpunit.cache/code-coverage"
includeUncoveredFiles="true"
processUncoveredFiles="true">
<include>
<directory suffix=".php">Core</directory>
</include>
</coverage>
</phpunit>

View File

@ -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;}}}

View File

@ -0,0 +1,574 @@
<?php
/**
* Implementation of the attribute definiton tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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());
}
}

View File

@ -0,0 +1,155 @@
<?php
/**
* Implementation of the attribute tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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());
}
}

View File

@ -0,0 +1,324 @@
<?php
/**
* Implementation of the low level database tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,290 @@
<?php
/**
* Implementation of the complex dms tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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);
}
}

View File

@ -0,0 +1,295 @@
<?php
/**
* Implementation of the category tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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]));
}
}

View File

@ -0,0 +1,593 @@
<?php
/**
* Implementation of the document content tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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);
}
}

View File

@ -0,0 +1,290 @@
<?php
/**
* Implementation of the document file tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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);
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* Implementation of the document link tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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'));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,219 @@
<?php
/**
* Implementation of the file utils tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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"));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,410 @@
<?php
/**
* Implementation of the group tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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]);
}
}

View File

@ -0,0 +1,147 @@
<?php
/**
* Implementation of the keyword tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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);
}
}

View File

@ -0,0 +1,477 @@
<?php
/**
* Implementation of the review and approval tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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);
}
}

View File

@ -0,0 +1,365 @@
<?php declare(strict_types=1);
/**
* Implementation of the database tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,638 @@
<?php
/**
* Implementation of the workflow tests
*
* PHP version 7
*
* @category SeedDMS
* @package Tests
* @author Uwe Steinmann <uwe@steinmann.cx>
* @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 <uwe@steinmann.cx>
* @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());
}
}

View File

@ -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)
---------------------

View File

@ -145,12 +145,14 @@ class SeedDMS_Lucene_IndexedDocument extends Zend_Search_Lucene_Document {
if($document->isType('document')) {
$this->addField(Zend_Search_Lucene_Field::Keyword('document_id', 'D'.$document->getID()));
$this->addField(Zend_Search_Lucene_Field::Keyword('record_type', 'document'));
$version = $document->getLatestContent();
if($version) {
$this->addField(Zend_Search_Lucene_Field::Keyword('mimetype', $version->getMimeType()));
$this->addField(Zend_Search_Lucene_Field::Keyword('origfilename', $version->getOriginalFileName(), 'utf-8'));
$this->addField(Zend_Search_Lucene_Field::UnIndexed('created', $version->getDate()));
if(!$nocontent)
$this->addField(Zend_Search_Lucene_Field::UnIndexed('created', $version->getDate()));
$this->addField(Zend_Search_Lucene_Field::UnIndexed('indexed', time()));
if($attributes = $version->getAttributes()) {
foreach($attributes as $attribute) {
$attrdef = $attribute->getAttributeDefinition();
@ -181,7 +183,15 @@ class SeedDMS_Lucene_IndexedDocument extends Zend_Search_Lucene_Document {
if(file_exists($path)) {
$mimetype = $version->getMimeType();
$this->mimetype = $mimetype;
if(is_object($convcmd) && (get_class($convcmd) == 'SeedDMS_ConversionMgr')) {
if(is_callable($convcmd)) {
$result = $convcmd($document);
if($result['content']) {
self::setContent($result['content']);
} elseif($result['content'] === false) {
$this->errormsg = $result['errormsg'];
}
$this->cmd = $result['cmd'];
} elseif(is_object($convcmd) && (get_class($convcmd) == 'SeedDMS_ConversionMgr')) {
if($service = $convcmd->getService($mimetype, 'text/plain')) {
$content = $convcmd->convert($path, $mimetype, 'text/plain');
if($content) {
@ -223,7 +233,9 @@ class SeedDMS_Lucene_IndexedDocument extends Zend_Search_Lucene_Document {
}
} elseif($document->isType('folder')) {
$this->addField(Zend_Search_Lucene_Field::Keyword('document_id', 'F'.$document->getID()));
$this->addField(Zend_Search_Lucene_Field::Keyword('record_type', 'folder'));
$this->addField(Zend_Search_Lucene_Field::UnIndexed('created', $document->getDate()));
$this->addField(Zend_Search_Lucene_Field::UnIndexed('indexed', time()));
}
} /* }}} */

View File

@ -29,10 +29,23 @@ class SeedDMS_Lucene_Indexer {
*/
protected $indexname;
/**
* @var string $index lucene index
* @access protected
*/
protected $index;
public function __construct($index) {
$this->index = $index;
}
static function open($conf) { /* {{{ */
try {
$index = Zend_Search_Lucene::open($conf['indexdir']);
return($index);
if($index)
return new self($index);
else
return null;
} catch (Exception $e) {
return null;
}
@ -41,7 +54,10 @@ class SeedDMS_Lucene_Indexer {
static function create($conf) { /* {{{ */
try {
$index = Zend_Search_Lucene::create($conf['indexdir']);
return($index);
if($index)
return new self($index);
else
return null;
} catch (Exception $e) {
return null;
}
@ -51,7 +67,7 @@ class SeedDMS_Lucene_Indexer {
* Do some initialization
*
*/
static function init($stopWordsFile='') { /* {{{ */
public function init($stopWordsFile='') { /* {{{ */
$analyzer = new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive();
if($stopWordsFile && file_exists($stopWordsFile)) {
$stopWordsFilter = new Zend_Search_Lucene_Analysis_TokenFilter_StopWords();
@ -62,6 +78,131 @@ class SeedDMS_Lucene_Indexer {
Zend_Search_Lucene_Analysis_Analyzer::setDefault($analyzer);
} /* }}} */
/**
* Add document to index
*
* @param object $doc indexed document of class
* SeedDMS_Lucene_IndexedDocument
* @return boolean false in case of an error, otherwise true
*/
function addDocument($doc) { /* {{{ */
if(!$this->index)
return false;
return $this->index->addDocument($doc);
} /* }}} */
/**
* Remove document from index
*
* @param object $id internal id of document
* @return boolean false in case of an error, otherwise true
*/
public function delete($id) { /* {{{ */
if(!$this->index)
return false;
return $this->index->delete($id);
} /* }}} */
/**
* Check if document was deleted
*
* @param object $id internal id of document
* @return boolean true if document was deleted
*/
public function isDeleted($id) { /* {{{ */
if(!$this->index)
return false;
return $this->index->isDeleted($id);
} /* }}} */
/**
* Search in index
*
* @param string $query
* @return array result
*/
public function find($query) { /* {{{ */
if(!$this->index)
return false;
return $this->index->find($query);
} /* }}} */
/**
* Get a single document from index
*
* @param string $id id of document
* @return boolean false in case of an error, otherwise true
*/
public function findById($id) { /* {{{ */
if(!$this->index)
return false;
return $this->index->findById($id);
} /* }}} */
/**
* Get a single document from index
*
* @param integer $id id of index record
* @return boolean false in case of an error, otherwise true
*/
public function getDocument($id, $content=true) { /* {{{ */
if(!$this->index)
return false;
return $this->index->getDocument($id);
} /* }}} */
/**
* Return list of terms in index
*
* @return array list of Zend_Lucene_Term
*/
public function terms($prefix='', $col='') { /* {{{ */
if(!$this->index)
return false;
return $this->index->terms();
} /* }}} */
/**
* Return number of documents in index
*
* @return interger number of documents
*/
public function count() { /* {{{ */
if(!$this->index)
return false;
return $this->index->count();
} /* }}} */
/**
* Commit changes
*
* This function does nothing!
*/
function commit() { /* {{{ */
if(!$this->index)
return false;
return $this->index->commit();
} /* }}} */
/**
* Optimize index
*
* This function does nothing!
*/
function optimize() { /* {{{ */
if(!$this->index)
return false;
return $this->index->optimize();
} /* }}} */
}
?>

View File

@ -77,10 +77,10 @@ class SeedDMS_Lucene_Search {
$querystr = substr($term, -1) != '*' ? $term.'*' : $term;
}
if(!empty($fields['owner'])) {
if(is_string($owner)) {
if(is_string($fields['owner'])) {
if($querystr)
$querystr .= ' && ';
$querystr .= 'owner:'.$owner;
$querystr .= 'owner:'.$fields['owner'];
} elseif(is_array($fields['owner'])) {
if($querystr)
$querystr .= ' && ';
@ -89,6 +89,13 @@ class SeedDMS_Lucene_Search {
$querystr .= '")';
}
}
if(!empty($fields['record_type'])) {
if($querystr)
$querystr .= ' && ';
$querystr .= '(record_type:';
$querystr .= implode(' || record_type:', $fields['record_type']);
$querystr .= ')';
}
if(!empty($fields['category'])) {
if($querystr)
$querystr .= ' && ';
@ -137,7 +144,7 @@ class SeedDMS_Lucene_Search {
$recs = array();
$c = 0;
foreach($hits as $hit) {
if($c >= $limit['offset'] && ($c-$limit['offset'] < $limit))
if($c >= $limit['offset'] && ($c-$limit['offset'] < $limit['limit']))
$recs[] = array('id'=>$hit->id, 'document_id'=>$hit->document_id);
$c++;
}

View File

@ -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"
}
}

View File

@ -11,11 +11,11 @@
<email>uwe@steinmann.cx</email>
<active>yes</active>
</lead>
<date>2021-05-10</date>
<date>2023-01-09</date>
<time>08:55:43</time>
<version>
<release>1.1.17</release>
<api>1.1.17</api>
<release>1.1.18</release>
<api>1.1.18</api>
</version>
<stability>
<release>stable</release>
@ -23,7 +23,8 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- close pipes in execWithTimeout(), also return exit code of command
- IndexedDocument() accepts a callable for conversion to text
- SeedDMS_Lucene_Search::open and create return itself but Zend_Search_Lucene
</notes>
<contents>
<dir baseinstalldir="SeedDMS" name="/">
@ -58,6 +59,7 @@
<phprelease />
<changelog>
<release>
<date>2009-04-27</date>
<version>
<release>0.0.1</release>
<api>0.0.1</api>
@ -66,7 +68,6 @@
<release>alpha</release>
<api>alpha</api>
</stability>
<date>2009-04-27</date>
<license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
<notes>
</notes>
@ -100,8 +101,8 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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.
</notes>
</release>
<release>
@ -117,7 +118,7 @@ of special chars like german umlaute.
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
catch exception if index is opened but not available
- catch exception if index is opened but not available
</notes>
</release>
<release>
@ -133,7 +134,7 @@ catch exception if index is opened but not available
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
parse query term and catch errors before using it
- parse query term and catch errors before using it
</notes>
</release>
<release>
@ -149,8 +150,8 @@ parse query term and catch errors before using it
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -166,7 +167,7 @@ do not check if deleting document from index fails, update it in any case
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -182,8 +183,8 @@ class SeedDMS_Lucene_Search::search returns false if query is invalid instead of
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -199,7 +200,7 @@ declare SeeDMS_Lucene_Indexer::open() static
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
run external commands with a timeout
- run external commands with a timeout
</notes>
</release>
<release>
@ -215,7 +216,7 @@ run external commands with a timeout
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
add command for indexing postѕcript files
- add command for indexing postѕcript files
</notes>
</release>
<release>
@ -231,7 +232,7 @@ add command for indexing postѕcript files
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -247,8 +248,8 @@ set last parameter of stream_select() to 200000 micro sec. in case the timeout i
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -264,7 +265,7 @@ make all functions in Indexer.php static
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
catch exception in execWithTimeout()
- catch exception in execWithTimeout()
</notes>
</release>
<release>
@ -280,7 +281,7 @@ catch exception in execWithTimeout()
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
allow conversion commands for mimetypes with wildcards
- allow conversion commands for mimetypes with wildcards
</notes>
</release>
<release>
@ -296,7 +297,7 @@ allow conversion commands for mimetypes with wildcards
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
execWithTimeout() reads data from stderr and saves it into error msg
- execWithTimeout() reads data from stderr and saves it into error msg
</notes>
</release>
<release>
@ -312,7 +313,7 @@ execWithTimeout() reads data from stderr and saves it into error msg
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
IndexedDocument() remembers cmd and mimetype
- IndexedDocument() remembers cmd and mimetype
</notes>
</release>
<release>
@ -328,7 +329,7 @@ IndexedDocument() remembers cmd and mimetype
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
Index users with at least read access on the document
- Index users with at least read access on the document
</notes>
</release>
<release>
@ -368,5 +369,21 @@ Index users with at least read access on the document
- add indexing of folders
</notes>
</release>
<release>
<date>2021-05-10</date>
<time>08:55:43</time>
<version>
<release>1.1.17</release>
<api>1.1.17</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- close pipes in execWithTimeout(), also return exit code of command
</notes>
</release>
</changelog>
</package>

View File

@ -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

View File

@ -31,4 +31,9 @@ require_once('Preview/Previewer.php');
*/
require_once('Preview/PdfPreviewer.php');
/**
* @uses Preview/PdfPreviewer.php
*/
require_once('Preview/TxtPreviewer.php');
?>

View File

@ -73,6 +73,7 @@ class SeedDMS_Preview_Base {
$this->previewDir = $previewDir;
}
$this->timeout = intval($timeout);
$this->converters = array();
$this->xsendfile = $xsendfile;
$this->conversionmgr = null;
} /* }}} */
@ -135,6 +136,15 @@ class SeedDMS_Preview_Base {
}
} /* }}} */
/**
* Get preview dir
*
* @return string name of preview directory on disc
*/
public function getPreviewDir() { /* {{{ */
return $this->previewDir;
} /* }}} */
/**
* Set a list of converters
*
@ -145,7 +155,10 @@ class SeedDMS_Preview_Base {
* and the value is the command to be called for creating the preview
*/
function setConverters($arr) { /* {{{ */
$this->converters = $arr;
if(is_array($arr))
$this->converters = $arr;
else
$this->converters = array();
} /* }}} */
/**

View File

@ -25,41 +25,30 @@
class SeedDMS_Preview_PdfPreviewer extends SeedDMS_Preview_Base {
function __construct($previewDir, $timeout=5, $xsendfile=true) { /* {{{ */
parent::__construct($previewDir, $timeout, $xsendfile);
parent::__construct($previewDir.DIRECTORY_SEPARATOR.'pdf', $timeout, $xsendfile);
$this->converters = array(
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'",
'application/vnd.oasis.opendocument.text' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'",
'text/rtf' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'",
'application/msword' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'",
'application/vnd.ms-excel' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'",
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'",
'text/plain' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'",
'application/postscript' => "ps2pdf '%f' - > '%o'",
'image/jpeg' => "convert '%f' pdf:- > '%o'",
'image/png' => "convert '%f' pdf:- > '%o'",
'image/gif' => "convert '%f' pdf:- > '%o'",
'video/mp4' => "convert '%f[1-20]' pdf:- > '%o'",
);
} /* }}} */
/**
* Return the physical filename of the preview image on disk
* Return the physical filename of the preview image on disc
* including the path
*
* @param object $object document content or document file
* @return string file name of preview image
*/
protected function getFileName($object) { /* {{{ */
public function getFileName($object) { /* {{{ */
if(!$object)
return false;
$document = $object->getDocument();
$dms = $document->_dms;
$dir = $this->previewDir.'/'.$document->getDir();
$dir = $this->previewDir.DIRECTORY_SEPARATOR.$document->getDir();
switch(get_class($object)) {
case $dms->getClassname('documentcontent'):
$target = $dir.'p'.$object->getVersion();
break;
case "SeedDMS_Core_DocumentFile":
case $dms->getClassname('documentfile'):
$target = $dir.'f'.$object->getID();
break;
default:
@ -101,8 +90,8 @@ class SeedDMS_Preview_PdfPreviewer extends SeedDMS_Preview_Base {
if(!$this->previewDir)
return false;
if(!is_dir($this->previewDir.'/'.$dir)) {
if (!SeedDMS_Core_File::makeDir($this->previewDir.'/'.$dir)) {
if(!is_dir($this->previewDir.DIRECTORY_SEPARATOR.$dir)) {
if (!SeedDMS_Core_File::makeDir($this->previewDir.DIRECTORY_SEPARATOR.$dir)) {
return false;
}
}
@ -128,9 +117,11 @@ class SeedDMS_Preview_PdfPreviewer extends SeedDMS_Preview_Base {
} elseif(isset($this->converters['*'])) {
$cmd = str_replace(array('%f', '%o', '%m'), array($infile, $target.'.pdf', $mimetype), $this->converters['*']);
}
if($cmd) {
try {
self::execWithTimeout($cmd, $this->timeout);
$new = true;
} catch(Exception $e) {
$this->lastpreviewfile = '';
return false;
@ -139,6 +130,7 @@ class SeedDMS_Preview_PdfPreviewer extends SeedDMS_Preview_Base {
}
return true;
}
$new = false;
return true;
} /* }}} */
@ -305,7 +297,7 @@ class SeedDMS_Preview_PdfPreviewer extends SeedDMS_Preview_Base {
if(!$this->previewDir)
return false;
$dir = $this->previewDir.'/'.$document->getDir();
$dir = $this->previewDir.DIRECTORY_SEPARATOR.$document->getDir();
if(file_exists($dir) && is_dir($dir)) {
return SeedDMS_Preview_Previewer::recurseRmdir($dir);
} else {

View File

@ -29,41 +29,48 @@ class SeedDMS_Preview_Previewer extends SeedDMS_Preview_Base {
*/
protected $width;
/**
* Create instance of image previewer
*
* @param string $previewDir path of base directory where all images are
* stored. This directory will have a subdirectory derived from the object id.
* @param integer $width default width of an image
* @param integer $timeout timeout for shell commands to create a preview image
* @param boolean $xsendfile if set to true the apache module xsendfile will
* be used.
*/
function __construct($previewDir, $width=40, $timeout=5, $xsendfile=true) { /* {{{ */
parent::__construct($previewDir, $timeout, $xsendfile);
parent::__construct($previewDir.DIRECTORY_SEPARATOR.'png', $timeout, $xsendfile);
$this->converters = array(
'image/png' => "convert -resize %wx '%f' '%o'",
'image/gif' => "convert -resize %wx '%f' '%o'",
'image/jpg' => "convert -resize %wx '%f' '%o'",
'image/jpeg' => "convert -resize %wx '%f' '%o'",
'image/svg+xml' => "convert -resize %wx '%f' '%o'",
'text/plain' => "convert -resize %wx '%f' '%o'",
'application/pdf' => "convert -density 100 -resize %wx '%f[0]' '%o'",
'application/postscript' => "convert -density 100 -resize %wx '%f[0]' '%o'",
'application/x-compressed-tar' => "tar tzvf '%f' | convert -density 100 -resize %wx text:-[0] '%o'",
);
$this->width = intval($width);
} /* }}} */
/**
* Return the physical filename of the preview image on disk
* Return the physical filename of the preview image on disc
* including the path
*
* @param object $object document content or document file
* @param integer $width width of preview image
* @return string file name of preview image
*/
public function getFileName($object, $width) { /* {{{ */
public function getFileName($object, $width=0) { /* {{{ */
if(!$object)
return false;
if($width == 0)
$width = $this->width;
else
$width = intval($width);
$document = $object->getDocument();
$dms = $document->_dms;
$dir = $this->previewDir.'/'.$document->getDir();
$dir = $this->previewDir.DIRECTORY_SEPARATOR.$document->getDir();
switch(get_class($object)) {
case $dms->getClassname('documentcontent'):
$target = $dir.'p'.$object->getVersion().'-'.$width;
break;
case "SeedDMS_Core_DocumentFile":
case $dms->getClassname('documentfile'):
$target = $dir.'f'.$object->getID().'-'.$width;
break;
default:
@ -98,6 +105,7 @@ class SeedDMS_Preview_Previewer extends SeedDMS_Preview_Base {
* @param string $mimetype MimeType of input file
* @param integer $width width of generated preview image
* @param string $target optional name of preview image (without extension)
* @param boolean $new will be set to true if the preview images was created
* @return boolean true on success, false on failure
*/
public function createRawPreview($infile, $dir, $mimetype, $width=0, $target='', &$new=false) { /* {{{ */
@ -110,8 +118,8 @@ class SeedDMS_Preview_Previewer extends SeedDMS_Preview_Base {
$width = intval($width);
if(!$this->previewDir)
return false;
if(!is_dir($this->previewDir.'/'.$dir)) {
if (!SeedDMS_Core_File::makeDir($this->previewDir.'/'.$dir)) {
if(!is_dir($this->previewDir.DIRECTORY_SEPARATOR.$dir)) {
if (!SeedDMS_Core_File::makeDir($this->previewDir.DIRECTORY_SEPARATOR.$dir)) {
return false;
}
}
@ -166,6 +174,7 @@ class SeedDMS_Preview_Previewer extends SeedDMS_Preview_Base {
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @param integer $width desired width of preview image
* @param boolean $new will be set to true if the preview images was created
* @return boolean true on success, false on failure
*/
public function createPreview($object, $width=0, &$new=false) { /* {{{ */
@ -350,7 +359,7 @@ class SeedDMS_Preview_Previewer extends SeedDMS_Preview_Base {
if(!$this->previewDir)
return false;
$dir = $this->previewDir.'/'.$document->getDir();
$dir = $this->previewDir.DIRECTORY_SEPARATOR.$document->getDir();
if(file_exists($dir) && is_dir($dir)) {
return SeedDMS_Preview_Previewer::recurseRmdir($dir);
} else {

View File

@ -0,0 +1,306 @@
<?php
/**
* Implementation of text preview documents
*
* @category DMS
* @package SeedDMS_Preview
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing creation of text preview for documents.
*
* @category DMS
* @package SeedDMS_Preview
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Preview_TxtPreviewer extends SeedDMS_Preview_Base {
function __construct($previewDir, $timeout=5, $xsendfile=true) { /* {{{ */
parent::__construct($previewDir.DIRECTORY_SEPARATOR.'txt', $timeout, $xsendfile);
$this->converters = array(
);
} /* }}} */
/**
* Return the physical filename of the preview image on disc
* including the path
*
* @param object $object document content or document file
* @return string file name of preview image
*/
public function getFileName($object) { /* {{{ */
if(!$object)
return false;
$document = $object->getDocument();
$dms = $document->_dms;
$dir = $this->previewDir.DIRECTORY_SEPARATOR.$document->getDir();
switch(get_class($object)) {
case $dms->getClassname('documentcontent'):
$target = $dir.'t'.$object->getVersion();
break;
default:
return false;
}
return $target;
} /* }}} */
/**
* Check if converter for a given mimetype is set
*
* @param string $mimetype from mimetype
*
* @return boolean true if converter exists, otherwise false
*/
function hasConverter($from, $to='') { /* {{{ */
return parent::hasConverter($from, 'text/plain');
} /* }}} */
/**
* Create a text preview for a given file
*
* This method creates a preview in text format for a regular file
* in the file system and stores the result in the directory $dir relative
* to the configured preview directory. The filename of the resulting preview
* image is either $target.text (if set) or md5($infile).text.
* The $mimetype is used to select the propper conversion programm.
* An already existing text preview is replaced.
*
* @param string $infile name of input file including full path
* @param string $dir directory relative to $this->previewDir
* @param string $mimetype MimeType of input file
* @param string $target optional name of preview image (without extension)
* @return boolean true on success, false on failure
*/
public function createRawPreview($infile, $dir, $mimetype, $target='') { /* {{{ */
if(!self::hasConverter($mimetype))
return true;
if(!$this->previewDir)
return false;
if(!is_dir($this->previewDir.DIRECTORY_SEPARATOR.$dir)) {
if (!SeedDMS_Core_File::makeDir($this->previewDir.DIRECTORY_SEPARATOR.$dir)) {
return false;
}
}
if(!file_exists($infile))
return false;
if(!$target)
$target = $this->previewDir.$dir.md5($infile);
$this->lastpreviewfile = $target.'.txt';
if($target != '' && (!file_exists($target.'.txt') || filectime($target.'.txt') < filectime($infile))) {
if($this->conversionmgr) {
if(!$this->conversionmgr->convert($infile, $mimetype, 'text/plain', $target.'.txt')) {
$this->lastpreviewfile = '';
return false;
}
$new = true;
} else {
$cmd = '';
$mimeparts = explode('/', $mimetype, 2);
if(isset($this->converters[$mimetype])) {
$cmd = str_replace(array('%f', '%o', '%m'), array($infile, $target.'.txt', $mimetype), $this->converters[$mimetype]);
} elseif(isset($this->converters[$mimeparts[0].'/*'])) {
$cmd = str_replace(array('%f', '%o', '%m'), array($infile, $target.'.txt', $mimetype), $this->converters[$mimeparts[0].'/*']);
} elseif(isset($this->converters['*'])) {
$cmd = str_replace(array('%f', '%o', '%m'), array($infile, $target.'.txt', $mimetype), $this->converters['*']);
}
if($cmd) {
try {
self::execWithTimeout($cmd, $this->timeout);
$new = true;
} catch(Exception $e) {
$this->lastpreviewfile = '';
return false;
}
}
}
return true;
}
$new = false;
return true;
} /* }}} */
/**
* Create preview image
*
* This function creates a preview image for the given document
* content or document file. It internally uses
* {@link SeedDMS_Preview::createRawPreview()}. The filename of the
* preview image is created by {@link SeedDMS_Preview_Previewer::getFileName()}
*
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @return boolean true on success, false on failure
*/
public function createPreview($object) { /* {{{ */
if(!$object)
return false;
$document = $object->getDocument();
$file = $document->_dms->contentDir.$object->getPath();
$target = $this->getFileName($object);
return $this->createRawPreview($file, $document->getDir(), $object->getMimeType(), $target);
} /* }}} */
/**
* Check if a preview image already exists.
*
* This function is a companion to {@link SeedDMS_Preview_Previewer::createRawPreview()}.
*
* @param string $infile name of input file including full path
* @param string $dir directory relative to $this->previewDir
* @return boolean true if preview exists, otherwise false
*/
public function hasRawPreview($infile, $dir, $target='') { /* {{{ */
if(!$this->previewDir)
return false;
if(!$target)
$target = $this->previewDir.$dir.md5($infile);
if($target !== false && file_exists($target.'.txt') && filectime($target.'.txt') >= filectime($infile)) {
return true;
}
return false;
} /* }}} */
/**
* Check if a preview txt already exists.
*
* This function is a companion to {@link SeedDMS_Preview_Previewer::createPreview()}.
*
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @return boolean true if preview exists, otherwise false
*/
public function hasPreview($object) { /* {{{ */
if(!$object)
return false;
if(!$this->previewDir)
return false;
$target = $this->getFileName($object);
if($target !== false && file_exists($target.'.txt') && filectime($target.'.txt') >= $object->getDate()) {
return true;
}
return false;
} /* }}} */
/**
* Return a preview image.
*
* This function returns the content of a preview image if it exists..
*
* @param string $infile name of input file including full path
* @param string $dir directory relative to $this->previewDir
* @return boolean/string image content if preview exists, otherwise false
*/
public function getRawPreview($infile, $dir, $target='') { /* {{{ */
if(!$this->previewDir)
return false;
if(!$target)
$target = $this->previewDir.$dir.md5($infile);
if($target && file_exists($target.'.txt')) {
$this->sendFile($target.'.txt');
}
} /* }}} */
/**
* Return a preview image.
*
* This function returns the content of a preview image if it exists..
*
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @return boolean/string image content if preview exists, otherwise false
*/
public function getPreview($object) { /* {{{ */
if(!$this->previewDir)
return false;
$target = $this->getFileName($object);
if($target && file_exists($target.'.txt')) {
$this->sendFile($target.'.txt');
}
} /* }}} */
/**
* Return file size preview image.
*
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @return boolean/integer size of preview image or false if image
* does not exist
*/
public function getFilesize($object) { /* {{{ */
$target = $this->getFileName($object);
if($target && file_exists($target.'.txt')) {
return(filesize($target.'.txt'));
} else {
return false;
}
} /* }}} */
/**
* Delete preview image.
*
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @return boolean true if deletion succeded or false if file does not exist
*/
public function deletePreview($object) { /* {{{ */
if(!$this->previewDir)
return false;
$target = $this->getFileName($object);
if($target && file_exists($target.'.txt')) {
return(unlink($target.'.txt'));
} else {
return false;
}
} /* }}} */
static function recurseRmdir($dir) {
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? SeedDMS_Preview_Previewer::recurseRmdir("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
/**
* Delete all preview text belonging to a document
*
* This function removes the preview text of all versions and
* files of a document including the directory. It actually just
* removes the directory for the document in the cache.
*
* @param object $document instance of SeedDMS_Core_Document
* @return boolean true if deletion succeded or false if file does not exist
*/
public function deleteDocumentPreviews($document) { /* {{{ */
if(!$this->previewDir)
return false;
$dir = $this->previewDir.DIRECTORY_SEPARATOR.$document->getDir();
if(file_exists($dir) && is_dir($dir)) {
return SeedDMS_Preview_Previewer::recurseRmdir($dir);
} else {
return false;
}
} /* }}} */
}
?>

View File

@ -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"
}
}

View File

@ -11,11 +11,11 @@
<email>uwe@steinmann.cx</email>
<active>yes</active>
</lead>
<date>2021-10-16</date>
<date>2023-01-09</date>
<time>09:49:39</time>
<version>
<release>1.4.0</release>
<api>1.4.0</api>
<release>1.5.0</release>
<api>1.5.0</api>
</version>
<stability>
<release>stable</release>
@ -23,8 +23,7 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- use new conversion service if available
- createRawPreview() checks early if a converter exists
- add previewer which creates txt
</notes>
<contents>
<dir baseinstalldir="SeedDMS" name="/">
@ -38,6 +37,9 @@
<file name="PdfPreviewer.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="TxtPreviewer.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
</dir> <!-- /Lucene -->
<dir name="tests">
</dir> <!-- /tests -->
@ -49,7 +51,7 @@
<dependencies>
<required>
<php>
<min>4.3.0</min>
<min>7.4.0</min>
</php>
<pearinstaller>
<min>1.5.4</min>
@ -71,7 +73,7 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
initial version
- initial version
</notes>
</release>
<release>
@ -87,7 +89,7 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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)
</notes>
</release>
<release>
@ -103,7 +105,7 @@ preview image can also be created from a document file (SeedDMS_Core_DocumentFil
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
add converters for .tar.gz, .ps, .txt
- add converters for .tar.gz, .ps, .txt
</notes>
</release>
<release>
@ -119,7 +121,7 @@ add converters for .tar.gz, .ps, .txt
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
create fixed width image with proportional height
- create fixed width image with proportional height
</notes>
</release>
<release>
@ -135,7 +137,7 @@ create fixed width image with proportional height
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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.
</notes>
</release>
<release>
@ -151,7 +153,7 @@ preview images will also be recreated if the object this image belongs is of new
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
command for creating the preview will be called with a given timeout
- command for creating the preview will be called with a given timeout
</notes>
</release>
<release>
@ -167,8 +169,8 @@ command for creating the preview will be called with a given timeout
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -184,7 +186,7 @@ timeout for external commands can be passed to contructor of SeedDMS_Preview_Pre
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
check if object passed to createPreview(), hasPreview() is not null
- check if object passed to createPreview(), hasPreview() is not null
</notes>
</release>
<release>
@ -200,7 +202,7 @@ check if object passed to createPreview(), hasPreview() is not null
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -216,7 +218,7 @@ set last parameter of stream_select() to 200000 micro sec. in case the timeout i
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
pass variables to stream_select (required by php7)
- pass variables to stream_select (required by php7)
</notes>
</release>
<release>
@ -232,11 +234,11 @@ pass variables to stream_select (required by php7)
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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()
</notes>
</release>
<release>
@ -252,7 +254,7 @@ check if cache dir exists before deleting it in deleteDocumentPreviews()
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
add new previewer which converts document to pdf instead of png
- add new previewer which converts document to pdf instead of png
</notes>
</release>
<release>
@ -268,7 +270,7 @@ add new previewer which converts document to pdf instead of png
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
setConverters() overrides exiting converters
- setConverters() overrides exiting converters
</notes>
</release>
<release>
@ -284,8 +286,8 @@ setConverters() overrides exiting converters
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -301,7 +303,7 @@ pass mimetype as parameter '%m' to converter
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
createPreview() returns false if running the converter command fails
- createPreview() returns false if running the converter command fails
</notes>
</release>
<release>
@ -317,7 +319,7 @@ createPreview() returns false if running the converter command fails
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
fix typo in converter for tar.gz files
- fix typo in converter for tar.gz files
</notes>
</release>
<release>
@ -333,7 +335,7 @@ fix typo in converter for tar.gz files
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
SeedDMS_Preview_Base::hasConverter() returns only try if command is set
- SeedDMS_Preview_Base::hasConverter() returns only try if command is set
</notes>
</release>
<release>
@ -349,8 +351,8 @@ SeedDMS_Preview_Base::hasConverter() returns only try if command is set
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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.
</notes>
</release>
<release>
@ -366,9 +368,9 @@ New method SeedDMS_Preview_Base::addConverters() merges new converters with old
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -384,7 +386,7 @@ execWithTimeout() reads data from stderr and returns it together with stdout in
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
preview is also created if SeedDMS_Core_DocumentContent has a child class
- preview is also created if SeedDMS_Core_DocumentContent has a child class
</notes>
</release>
<release>
@ -400,8 +402,8 @@ preview is also created if SeedDMS_Core_DocumentContent has a child class
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -417,8 +419,8 @@ usage of mod_sendfile can be configured
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -434,7 +436,7 @@ fix creation of pdf preview if document content class is not SeedDMS_Core_Docume
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
add new methode getPreviewFile()
- add new methode getPreviewFile()
</notes>
</release>
<release>
@ -450,7 +452,7 @@ add new methode getPreviewFile()
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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()
</notes>
</release>
<release>
@ -466,8 +468,8 @@ add parameter $target to SeedDMS_Preview_pdfPreviewer::hasRawPreview() and SeedD
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
set header Content-Length
update package description
- set header Content-Length
- update package description
</notes>
</release>
<release>
@ -488,5 +490,22 @@ update package description
preview image was actually created
</notes>
</release>
<release>
<date>2021-10-16</date>
<time>09:49:39</time>
<version>
<release>1.4.0</release>
<api>1.4.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- use new conversion service if available
- createRawPreview() checks early if a converter exists
</notes>
<release>
</changelog>
</package>

View File

@ -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 &lt; 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

View File

@ -111,7 +111,7 @@ class SeedDMS_SQLiteFTS_Document {
* @return string
*/
public function getFieldValue($fieldName) {
return $this->getField($fieldName)->value;
return $this->getField($fieldName)->value;
}
}
?>

View File

@ -151,8 +151,9 @@ class SeedDMS_SQLiteFTS_IndexedDocument extends SeedDMS_SQLiteFTS_Document {
if($version) {
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('mimetype', $version->getMimeType()));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('origfilename', $version->getOriginalFileName()));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('created', $version->getDate(), 'unindexed'));
if(!$nocontent)
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('created', $version->getDate(), 'unindexed'));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('indexed', time(), 'unindexed'));
if($attributes = $version->getAttributes()) {
foreach($attributes as $attribute) {
$attrdef = $attribute->getAttributeDefinition();
@ -168,7 +169,7 @@ class SeedDMS_SQLiteFTS_IndexedDocument extends SeedDMS_SQLiteFTS_Document {
foreach($categories as $cat) {
$names[] = $cat->getName();
}
$this->addField(SeedDMS_SQLiteFTS_Field::Text('category', implode(' ', $names)));
$this->addField(SeedDMS_SQLiteFTS_Field::Text('category', implode('#', $names)));
}
if($keywords = $document->getKeywords()) {
$this->addField(SeedDMS_SQLiteFTS_Field::Text('keywords', $keywords));
@ -182,7 +183,15 @@ class SeedDMS_SQLiteFTS_IndexedDocument extends SeedDMS_SQLiteFTS_Document {
if(file_exists($path)) {
$mimetype = $version->getMimeType();
$this->mimetype = $mimetype;
if(is_object($convcmd) && (get_class($convcmd) == 'SeedDMS_ConversionMgr')) {
if(is_callable($convcmd)) {
$result = $convcmd($document);
if($result['content']) {
self::setContent($result['content']);
} elseif($result['content'] === false) {
$this->errormsg = $result['errormsg'];
}
$this->cmd = $result['cmd'];
} elseif(is_object($convcmd) && (get_class($convcmd) == 'SeedDMS_ConversionMgr')) {
if($service = $convcmd->getService($mimetype, 'text/plain')) {
$content = $convcmd->convert($path, $mimetype, 'text/plain');
if($content) {
@ -226,6 +235,7 @@ class SeedDMS_SQLiteFTS_IndexedDocument extends SeedDMS_SQLiteFTS_Document {
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('document_id', 'F'.$document->getID()));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('record_type', 'folder'));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('created', $document->getDate(), 'unindexed'));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('indexed', time(), 'unindexed'));
}
} /* }}} */

View File

@ -25,18 +25,48 @@
class SeedDMS_SQLiteFTS_Indexer {
/**
* @var string $ftstype
* @var string $_ftstype
* @access protected
*/
protected $_ftstype;
/**
* @var object $index sqlite index
* @var object $_conn sqlite index
* @access protected
*/
protected $_conn;
/**
* @var array $_stop_words array of stop words
* @access protected
*/
protected $_stop_words;
const ftstype = 'fts5';
/**
* Remove stopwords from string
*/
protected function strip_stopwords($str = "") { /* {{{ */
// 1.) break string into words
// [^-\w\'] matches characters, that are not [0-9a-zA-Z_-']
// if input is unicode/utf-8, the u flag is needed: /pattern/u
$words = preg_split('/[^-\w\']+/u', $str, -1, PREG_SPLIT_NO_EMPTY);
// 2.) if we have at least 2 words, remove stopwords
if(!empty($words)) {
$stopwords = $this->_stop_words;
$words = array_filter($words, function ($w) use (&$stopwords) {
return ((mb_strlen($w, 'utf-8') > 2) && !isset($stopwords[mb_strtolower($w, "utf-8")]));
});
}
// check if not too much was removed such as "the the" would return empty
if(!empty($words))
return implode(" ", $words);
return $str;
} /* }}} */
/**
* Constructor
*
@ -48,6 +78,7 @@ class SeedDMS_SQLiteFTS_Indexer {
$this->_rawid = 'rowid';
else
$this->_rawid = 'docid';
$this->_stop_words = [];
} /* }}} */
/**
@ -59,7 +90,7 @@ class SeedDMS_SQLiteFTS_Indexer {
if(file_exists($conf['indexdir'].'/index.db')) {
return new SeedDMS_SQLiteFTS_Indexer($conf['indexdir']);
} else
return self::create($conf);
return static::create($conf);
} /* }}} */
/**
@ -77,9 +108,9 @@ class SeedDMS_SQLiteFTS_Indexer {
$version = SQLite3::version();
if(self::ftstype == 'fts4') {
if($version['versionNumber'] >= 3008000)
$sql = 'CREATE VIRTUAL TABLE docs USING fts4(documentid, record_type, title, comment, keywords, category, mimetype, origfilename, owner, content, created, users, status, path, notindexed=created, matchinfo=fts3)';
$sql = 'CREATE VIRTUAL TABLE docs USING fts4(documentid, record_type, title, comment, keywords, category, mimetype, origfilename, owner, content, created, indexed, users, status, path, notindexed=created, notindexed=indexed, matchinfo=fts3)';
else
$sql = 'CREATE VIRTUAL TABLE docs USING fts4(documentid, record_type, title, comment, keywords, category, mimetype, origfilename, owner, content, created, users, status, path, matchinfo=fts3)';
$sql = 'CREATE VIRTUAL TABLE docs USING fts4(documentid, record_type, title, comment, keywords, category, mimetype, origfilename, owner, content, created, indexed, users, status, path, matchinfo=fts3)';
$res = $index->_conn->exec($sql);
if($res === false) {
return null;
@ -90,7 +121,7 @@ class SeedDMS_SQLiteFTS_Indexer {
return null;
}
} elseif(self::ftstype == 'fts5') {
$sql = 'CREATE VIRTUAL TABLE docs USING fts5(documentid, record_type, title, comment, keywords, category, mimetype, origfilename, owner, content, created unindexed, users, status, path)';
$sql = 'CREATE VIRTUAL TABLE docs USING fts5(documentid, record_type, title, comment, keywords, category, mimetype, origfilename, owner, content, created unindexed, indexed unindexed, users, status, path)';
$res = $index->_conn->exec($sql);
if($res === false) {
return null;
@ -109,7 +140,9 @@ class SeedDMS_SQLiteFTS_Indexer {
* Do some initialization
*
*/
static function init($stopWordsFile='') { /* {{{ */
public function init($stopWordsFile='') { /* {{{ */
if($stopWordsFile)
$this->_stop_words = array_flip(preg_split("/[\s,]+/", file_get_contents($stopWordsFile)));
} /* }}} */
/**
@ -123,7 +156,7 @@ class SeedDMS_SQLiteFTS_Indexer {
if(!$this->_conn)
return false;
foreach(array('comment', 'keywords', 'category', 'content', 'mimetype', 'origfilename', 'status', 'created') as $kk) {
foreach(array('comment', 'keywords', 'category', 'content', 'mimetype', 'origfilename', 'status', 'created', 'indexed') as $kk) {
try {
${$kk} = $doc->getFieldValue($kk);
} catch (Exception $e) {
@ -135,7 +168,10 @@ class SeedDMS_SQLiteFTS_Indexer {
if($res === false) {
return false;
}
$sql = "INSERT INTO docs (documentid, record_type, title, comment, keywords, category, owner, content, mimetype, origfilename, created, users, status, path) VALUES (".$this->_conn->quote($doc->getFieldValue('document_id')).", ".$this->_conn->quote($doc->getFieldValue('record_type')).", ".$this->_conn->quote($doc->getFieldValue('title')).", ".$this->_conn->quote($comment).", ".$this->_conn->quote($keywords).", ".$this->_conn->quote($category).", ".$this->_conn->quote($doc->getFieldValue('owner')).", ".$this->_conn->quote($content).", ".$this->_conn->quote($mimetype).", ".$this->_conn->quote($origfilename).", ".(int)$created.", ".$this->_conn->quote($doc->getFieldValue('users')).", ".$this->_conn->quote($status).", ".$this->_conn->quote($doc->getFieldValue('path'))/*time()*/.")";
if($this->_stop_words)
$content = $this->strip_stopwords($content);
$sql = "INSERT INTO docs (documentid, record_type, title, comment, keywords, category, owner, content, mimetype, origfilename, created, indexed, users, status, path) VALUES (".$this->_conn->quote($doc->getFieldValue('document_id')).", ".$this->_conn->quote($doc->getFieldValue('record_type')).", ".$this->_conn->quote($doc->getFieldValue('title')).", ".$this->_conn->quote($comment).", ".$this->_conn->quote($keywords).", ".$this->_conn->quote($category).", ".$this->_conn->quote($doc->getFieldValue('owner')).", ".$this->_conn->quote($content).", ".$this->_conn->quote($mimetype).", ".$this->_conn->quote($origfilename).", ".(int)$created.", ".(int)$indexed.", ".$this->_conn->quote($doc->getFieldValue('users')).", ".$this->_conn->quote($status).", ".$this->_conn->quote($doc->getFieldValue('path'))/*time()*/.")";
$res = $this->_conn->exec($sql);
if($res === false) {
return false;
@ -147,8 +183,7 @@ class SeedDMS_SQLiteFTS_Indexer {
/**
* Remove document from index
*
* @param object $doc indexed document of class
* SeedDMS_SQLiteFTS_IndexedDocument
* @param object $id internal id of document
* @return boolean false in case of an error, otherwise true
*/
public function delete($id) { /* {{{ */
@ -179,15 +214,22 @@ class SeedDMS_SQLiteFTS_Indexer {
* @return boolean false in case of an error, otherwise array with elements
* 'count', 'hits', 'facets'. 'hits' is an array of SeedDMS_SQLiteFTS_QueryHit
*/
public function find($query, $limit=array()) { /* {{{ */
public function find($query, $filter='', $limit=array(), $order=array()) { /* {{{ */
if(!$this->_conn)
return false;
/* First count some records for facets */
foreach(array('owner', 'mimetype', 'category') as $facetname) {
foreach(array('owner', 'mimetype', 'category', 'status') as $facetname) {
$sql = "SELECT `".$facetname."`, count(*) AS `c` FROM `docs`";
if($query)
if($query) {
$sql .= " WHERE docs MATCH ".$this->_conn->quote($query);
}
if($filter) {
if($query)
$sql .= " AND ".$filter;
else
$sql .= " WHERE ".$filter;
}
$res = $this->_conn->query($sql." GROUP BY `".$facetname."`");
if(!$res)
throw new SeedDMS_SQLiteFTS_Exception("Counting records in facet \"$facetname\" failed.");
@ -196,7 +238,7 @@ class SeedDMS_SQLiteFTS_Indexer {
foreach($res as $row) {
if($row[$facetname] && $row['c']) {
if($facetname == 'category') {
$tmp = explode(' ', $row[$facetname]);
$tmp = explode('#', $row[$facetname]);
if(count($tmp) > 1) {
foreach($tmp as $t) {
if(!isset($facets[$facetname][$t]))
@ -210,6 +252,8 @@ class SeedDMS_SQLiteFTS_Indexer {
else
$facets[$facetname][$row[$facetname]] += $row['c'];
}
} elseif($facetname == 'status') {
$facets[$facetname][($row[$facetname]-10).''] = $row['c'];
} else
$facets[$facetname][$row[$facetname]] = $row['c'];
}
@ -219,6 +263,12 @@ class SeedDMS_SQLiteFTS_Indexer {
$sql = "SELECT `record_type`, count(*) AS `c` FROM `docs`";
if($query)
$sql .= " WHERE docs MATCH ".$this->_conn->quote($query);
if($filter) {
if($query)
$sql .= " AND ".$filter;
else
$sql .= " WHERE ".$filter;
}
$res = $this->_conn->query($sql." GROUP BY `record_type`");
if(!$res)
throw new SeedDMS_SQLiteFTS_Exception("Counting records in facet \"record_type\" failed.");
@ -232,10 +282,32 @@ class SeedDMS_SQLiteFTS_Indexer {
$sql = "SELECT ".$this->_rawid.", documentid FROM docs";
if($query)
$sql .= " WHERE docs MATCH ".$this->_conn->quote($query);
if($this->_ftstype == 'fts5')
if($filter) {
if($query)
$sql .= " AND ".$filter;
else
$sql .= " WHERE ".$filter;
}
if($this->_ftstype == 'fts5') {
//$sql .= " ORDER BY rank";
// boost documentid, title and comment
$sql .= " ORDER BY bm25(docs, 10.0, 10.0, 10.0)";
// boost documentid, record_type, title, comment, keywords, category, mimetype, origfilename, owner, content, created unindexed, users, status, path
if(!empty($order['by'])) {
switch($order['by']) {
case "title":
$sql .= " ORDER BY title";
break;
case "created":
$sql .= " ORDER BY created";
break;
default:
$sql .= " ORDER BY bm25(docs, 10.0, 0.0, 10.0, 5.0, 5.0, 10.0)";
}
if(!empty($order['dir'])) {
if($order['dir'] == 'desc')
$sql .= " DESC";
}
}
}
if(!empty($limit['limit']))
$sql .= " LIMIT ".(int) $limit['limit'];
if(!empty($limit['offset']))
@ -289,7 +361,7 @@ class SeedDMS_SQLiteFTS_Indexer {
if(!$this->_conn)
return false;
$sql = "SELECT ".$this->_rawid.", documentid, title, comment, owner, keywords, category, mimetype, origfilename, created, users, status, path".($content ? ", content" : "")." FROM docs WHERE ".$this->_rawid."='".$id."'";
$sql = "SELECT ".$this->_rawid.", documentid, title, comment, owner, keywords, category, mimetype, origfilename, created, indexed, users, status, path".($content ? ", content" : "")." FROM docs WHERE ".$this->_rawid."='".$id."'";
$res = $this->_conn->query($sql);
$doc = false;
if($res) {
@ -306,9 +378,10 @@ class SeedDMS_SQLiteFTS_Indexer {
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('origfilename', $rec['origfilename']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Text('owner', $rec['owner']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('created', $rec['created']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('indexed', $rec['indexed']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Text('users', $rec['users']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('status', $rec['status']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('path', $rec['path']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('path', explode('x', substr($rec['path'], 1, -1))));
if($content)
$doc->addField(SeedDMS_SQLiteFTS_Field::UnStored('content', $rec['content']));
}
@ -318,16 +391,33 @@ class SeedDMS_SQLiteFTS_Indexer {
/**
* Return list of terms in index
*
* This function does nothing!
* @return array list of SeedDMS_SQLiteFTS_Term
*/
public function terms() { /* {{{ */
public function terms($prefix='', $col='') { /* {{{ */
if(!$this->_conn)
return false;
if($this->_ftstype == 'fts5')
$sql = "SELECT term, col, doc as occurrences FROM docs_terms WHERE col!='*' ORDER BY col";
else
$sql = "SELECT term, col, occurrences FROM docs_terms WHERE col!='*' ORDER BY col";
if($this->_ftstype == 'fts5') {
$sql = "SELECT term, col, doc as occurrences FROM docs_terms";
if($prefix || $col) {
$sql .= " WHERE";
if($prefix) {
$sql .= " term like '".$prefix."%'";
if($col)
$sql .= " AND";
}
if($col)
$sql .= " col = '".$col."'";
}
$sql .= " ORDER BY col, occurrences desc";
} else {
$sql = "SELECT term, col, occurrences FROM docs_terms WHERE col!='*'";
if($prefix)
$sql .= " AND term like '".$prefix."%'";
if($col)
$sql .= " AND col = '".$col."'";
$sql .= " ORDER BY col, occurrences desc";
}
$res = $this->_conn->query($sql);
$terms = array();
if($res) {
@ -340,8 +430,9 @@ class SeedDMS_SQLiteFTS_Indexer {
} /* }}} */
/**
* Return list of documents in index
* Return number of documents in index
*
* @return interger number of documents
*/
public function count() { /* {{{ */
$sql = "SELECT count(*) c FROM docs";

View File

@ -53,6 +53,7 @@ class SeedDMS_SQLiteFTS_QueryHit {
*/
public function __construct(SeedDMS_SQLiteFTS_Indexer $index) { /* {{{ */
$this->_index = $index;
$this->_document = null;
} /* }}} */
/**

View File

@ -70,7 +70,7 @@ class SeedDMS_SQliteFTS_Search {
* @param object $index SQlite FTS index
* @return object instance of SeedDMS_Lucene_Search
*/
function search($term, $fields=array(), $limit=array()) { /* {{{ */
function search($term, $fields=array(), $limit=array(), $order=array()) { /* {{{ */
$querystr = '';
$term = trim($term);
if($term) {
@ -139,8 +139,20 @@ class SeedDMS_SQliteFTS_Search {
$querystr .= str_replace(':', 'x', $fields['startFolder']->getFolderList().$fields['startFolder']->getID().':');
$querystr .= '*)';
}
$filterstr = '';
if(!empty($fields['created_start'])) {
if($filterstr)
$filterstr .= ' AND ';
$filterstr .= '(created>='.$fields['created_start'].')';
}
if(!empty($fields['created_end'])) {
if($filterstr)
$filterstr .= ' AND ';
$filterstr .= '(created<'.$fields['created_end'].')';
}
try {
$result = $this->index->find($querystr, $limit);
$result = $this->index->find($querystr, $filterstr, $limit, $order);
$recs = array();
foreach($result["hits"] as $hit) {
$recs[] = array('id'=>$hit->id, 'document_id'=>$hit->documentid);

View File

@ -60,7 +60,8 @@ class SeedDMS_SQLiteFTS_Term {
9 => 'created',
10 => 'user',
11 => 'status',
12 => 'path'
12 => 'path',
13 => 'indexed',
);
/* fts5 pass the column name in $col, fts4 uses an integer */
if(is_int($col))

View File

@ -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"
}
}

View File

@ -11,11 +11,11 @@
<email>uwe@steinmann.cx</email>
<active>yes</active>
</lead>
<date>2022-03-04</date>
<date>2023-01-09</date>
<time>08:57:44</time>
<version>
<release>1.0.17</release>
<api>1.0.17</api>
<release>1.0.18</release>
<api>1.0.18</api>
</version>
<stability>
<release>stable</release>
@ -23,8 +23,10 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- throw exeption in find() instead of returning false
- fix query if rootFolder or startFolder is set
- 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
</notes>
<contents>
<dir baseinstalldir="SeedDMS" name="/">
@ -86,7 +88,7 @@
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
initial release
- initial release
</notes>
</release>
<release>
@ -102,7 +104,7 @@ initial release
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -118,7 +120,7 @@ add __get() to SQLiteFTS_Document because class.IndexInfo.php access class varia
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
check if index exists before removing it when creating a new one
- check if index exists before removing it when creating a new one
</notes>
</release>
<release>
@ -134,7 +136,7 @@ check if index exists before removing it when creating a new one
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
add command for indexing postѕcript files
- add command for indexing postѕcript files
</notes>
</release>
<release>
@ -150,7 +152,7 @@ add command for indexing postѕcript files
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
make it work with sqlite3 &lt; 3.8.0
- make it work with sqlite3 &lt; 3.8.0
</notes>
</release>
<release>
@ -166,7 +168,7 @@ make it work with sqlite3 &lt; 3.8.0
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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
</notes>
</release>
<release>
@ -182,7 +184,7 @@ set last parameter of stream_select() to 200000 micro sec. in case the timeout i
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
fix calculation of timeout (see bug #269)
- fix calculation of timeout (see bug #269)
</notes>
</release>
<release>
@ -198,7 +200,7 @@ fix calculation of timeout (see bug #269)
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
catch exception in execWithTimeout()
- catch exception in execWithTimeout()
</notes>
</release>
<release>
@ -214,7 +216,7 @@ catch exception in execWithTimeout()
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
allow conversion commands for mimetypes with wildcards
- allow conversion commands for mimetypes with wildcards
</notes>
</release>
<release>
@ -230,7 +232,7 @@ allow conversion commands for mimetypes with wildcards
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
execWithTimeout() reads data from stderr and saves it into error msg
- execWithTimeout() reads data from stderr and saves it into error msg
</notes>
</release>
<release>
@ -246,7 +248,7 @@ execWithTimeout() reads data from stderr and saves it into error msg
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
IndexedDocument() remembers cmd and mimetype
- IndexedDocument() remembers cmd and mimetype
</notes>
</release>
<release>
@ -262,7 +264,7 @@ IndexedDocument() remembers cmd and mimetype
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
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)
</notes>
</release>
@ -279,7 +281,7 @@ timestamp)
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
Index users with at least read access on a document
- Index users with at least read access on a document
</notes>
</release>
<release>
@ -295,7 +297,7 @@ Index users with at least read access on a document
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
add user to list of terms
- add user to list of terms
</notes>
</release>
<release>
@ -353,5 +355,22 @@ add user to list of terms
- add class SeedDMS_SQLiteFTS_Field
</notes>
</release>
<release>
<date>2022-03-04</date>
<time>08:57:44</time>
<version>
<release>1.0.17</release>
<api>1.0.17</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- throw exeption in find() instead of returning false
- fix query if rootFolder or startFolder is set
</notes>
</release>
</changelog>
</package>

View File

@ -16,7 +16,7 @@
<propertyregex property="shortversion" subject="${version} kk" pattern="([56])\.([0-9]).*" replace="seeddms$1$2x" override="true"/>
<property name="majorversion" value="${version}" />
<propertyregex property="majorversion" subject="${version}" pattern="([56])\..*" replace="$1" override="true"/>
<property name="composer_version" value="2.1.6" />
<property name="composer_version" value="2.1.14" />
<property name="composer_extra_params" value="" />
<property name="composer_env" value="" />
<property name="mink_driver" value="chrome" />
@ -63,7 +63,7 @@
<!-- PHPUnit -->
<target name="phpunitfast" description="Run tests">
<exec dir="${srcdir}/SeedDMS_Core" command="XDEBUG_MODE=coverage ${srcdir}/vendor/bin/phpunit --bootstrap ${srcdir}/SeedDMS_Core/bootstrap-${majorversion}.php --coverage-html ${srcdir}/coverage/" passthru="true" checkreturn="true" />
<exec dir="${srcdir}/SeedDMS_Core" command="XDEBUG_MODE=coverage SEEDDMS_CORE_SQL=../install/create_tables-sqlite3.sql ${srcdir}/vendor/bin/phpunit --bootstrap ${srcdir}/SeedDMS_Core/bootstrap-${majorversion}.php --coverage-html ${srcdir}/coverage/" passthru="true" checkreturn="true" />
<!-- exec dir="${srcdir}/tests" command="SEEDDMS_URL=${seeddmsurl} SEEDDMS_MINK_DRIVER=${mink_driver} SEEDDMS_SNOOZE_MULTIPLIER=${snooze_multiplier} ${srcdir}/vendor/bin/phpunit" passthru="true" checkreturn="true" / -->
</target>
@ -151,7 +151,7 @@
<copy todir="${builddir}/export/${shortversion}/www/ext/example">
<fileset dir="${srcdir}/ext/example" defaultexcludes="false" />
</copy>
<copy todir="${builddir}/export/${shortversion}/pear/SeedDMS">
<!-- copy todir="${builddir}/export/${shortversion}/pear/SeedDMS">
<fileset dir="SeedDMS_Core">
<include name="Core/**" />
<include name="Core.php" />
@ -163,13 +163,13 @@
<fileset dir="SeedDMS_Lucene">
<include name="Lucene/**" />
<include name="Lucene.php" />
</fileset>
<fileset dir="SeedDMS_SQLiteFTS">
</fileset -->
<!-- fileset dir="SeedDMS_SQLiteFTS">
<include name="SQLiteFTS/**" />
<include name="SQLiteFTS.php" />
</fileset>
</copy>
<copy todir="${builddir}/export/${shortversion}/pear">
</copy -->
<!-- copy todir="${builddir}/export/${shortversion}/pear">
<fileset dir="../seeddms-ext/http_webdav_server">
<include name="HTTP/WebDAV/Server/**" />
<include name="HTTP/WebDAV/Server.php" />
@ -179,7 +179,7 @@
<fileset dir="../seeddms-ext/http_webdav_server">
<include name="Tools/**" />
</fileset>
</copy>
</copy -->
<copy tofile="${builddir}/export/${shortversion}/pear/composer.json" file="composer-dist.json">
</copy>
<phingcall target="composer">

View File

@ -1,9 +1,19 @@
{
"config": {
"platform": {
"php": "7.4"
}
},
"require": {
"robthree/twofactorauth": "^1.5",
"sabre/dav": "^3.",
"sabre/xml": "^1.4.",
"slim/slim": "^3.0",
"erusev/parsedown": "*",
"erusev/parsedown-extra": "*",
"mibe/feedwriter": "^1.1",
"phpoffice/phpspreadsheet": "*",
"pear/log": "*",
"pear/mail": "*",
"pear/mail_mime": "*",
@ -11,7 +21,51 @@
"pear/auth_sasl": "*",
"pear/db": "*",
"alecrabbit/php-console-colour": "*",
"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
}
}
]
}

View File

@ -15,7 +15,7 @@
footNote = "SeedDMS free document management system - www.seeddms.org"
printDisclaimer = "true"
language = "en_GB"
theme = "bootstrap"
theme = "bootstrap4"
previewWidthList = "40"
previewWidthDetail = "100"
onePageMode="true"
@ -60,8 +60,8 @@
enableDropUpload = "false"
enableRecursiveCount = "false"
maxRecursiveCount = "0"
enableThemeSelector = "false"
fullSearchEngine = "lucene"
enableThemeSelector = "true"
fullSearchEngine = "sqlitefts"
sortFoldersDefault = "u"
defaultDocPosition = "end"
defaultFolderPosition = "end"
@ -290,7 +290,7 @@
updateNotifyTime = "86400"
extraPath = ""
maxExecutionTime = "30"
cmdTimeout = "1"
cmdTimeout = "10"
/>
<!--
- enableNotificationAppRev: set to true if reviewers and approvers shall be informed about a pending review/approval

View File

@ -52,6 +52,7 @@ class SeedDMS_Controller_AddDocument extends SeedDMS_Controller_Common {
$sequence = $this->getParam('sequence');
$reviewers = $this->getParam('reviewers');
$approvers = $this->getParam('approvers');
$recipients = $this->getParam('recipients');
$reqversion = $this->getParam('reqversion');
$version_comment = $this->getParam('versioncomment');
$attributes = $this->getParam('attributes');
@ -106,6 +107,7 @@ class SeedDMS_Controller_AddDocument extends SeedDMS_Controller_Common {
$workflow = $this->getParam('workflow');
$notificationgroups = $this->getParam('notificationgroups');
$notificationusers = $this->getParam('notificationusers');
$initialdocumentstatus = $this->getParam('initialdocumentstatus');
$maxsizeforfulltext = $this->getParam('maxsizeforfulltext');
$defaultaccessdocs = $this->getParam('defaultaccessdocs');
@ -116,7 +118,7 @@ class SeedDMS_Controller_AddDocument extends SeedDMS_Controller_Common {
$cats, $userfiletmp, utf8_basename($userfilename),
$filetype, $userfiletype, $sequence,
$reviewers, $approvers, $reqversion,
$version_comment, $attributes, $attributes_version, $workflow);
$version_comment, $attributes, $attributes_version, $workflow, $initialdocumentstatus);
if (is_bool($res) && !$res) {
$this->errormsg = "error_occured";
@ -133,6 +135,24 @@ class SeedDMS_Controller_AddDocument extends SeedDMS_Controller_Common {
}
}
$lc = $document->getLatestContent();
if($recipients) {
if($recipients['i']) {
foreach($recipients['i'] as $uid) {
if($u = $dms->getUser($uid)) {
$res = $lc->addIndRecipient($u, $user);
}
}
}
if($recipients['g']) {
foreach($recipients['g'] as $gid) {
if($g = $dms->getGroup($gid)) {
$res = $lc->addGrpRecipient($g, $user);
}
}
}
}
/* Add a default notification for the owner of the document */
if($settings->_enableOwnerNotification) {
$res = $document->addNotify($owner->getID(), true);

View File

@ -0,0 +1,107 @@
<?php
/**
* Implementation of ApproveDocument controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for downloading a document
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_ApproveDocument extends SeedDMS_Controller_Common {
public function run() {
$dms = $this->params['dms'];
$user = $this->params['user'];
$settings = $this->params['settings'];
$document = $this->params['document'];
$content = $this->params['content'];
$approvalstatus = $this->params['approvalstatus'];
$approvaltype = $this->params['approvaltype'];
$group = $this->params['group'];
$comment = $this->params['comment'];
$file = $this->params['file'];
/* Get the document id and name before removing the document */
$docname = $document->getName();
$documentid = $document->getID();
if(!$this->callHook('preApproveDocument', $content)) {
}
$result = $this->callHook('approveDocument', $content);
if($result === null) {
if ($approvaltype == "ind") {
$approvalLogID = $content->setApprovalByInd($user, $user, $approvalstatus, $comment, $file);
if($approvalLogID === false || 0 > $approvalLogID) {
$this->error = 1;
$this->errormsg = "approval_update_failed";
return false;
}
} elseif ($approvaltype == "grp") {
$approvalLogID = $content->setApprovalByGrp($group, $user, $approvalstatus, $comment, $file);
if($approvalLogID === false || 0 > $approvalLogID) {
$this->error = 1;
$this->errormsg = "approval_update_failed";
return false;
}
}
}
/* Check to see if the overall status for the document version needs to be
* updated.
*/
$result = $this->callHook('approveUpdateDocumentStatus', $content);
if($result === null) {
/* If document was rejected, set the document status to S_REJECTED right away */
if ($approvalstatus == -1){
if($content->setStatus(S_REJECTED,$comment,$user)) {
$this->callHook('postApproveDocument', $content, S_REJECTED);
}
} else {
$docApprovalStatus = $content->getApprovalStatus();
if (is_bool($docApprovalStatus) && !$docApprovalStatus) {
$this->error = 1;
$this->errormsg = "cannot_retrieve_approval_snapshot";
return false;
}
$approvalCT = 0;
$approvalTotal = 0;
foreach ($docApprovalStatus as $drstat) {
if ($drstat["status"] == 1) {
$approvalCT++;
}
if ($drstat["status"] != -2) {
$approvalTotal++;
}
}
// If all approvals have been received and there are no rejections, retrieve a
// count of the approvals required for this document.
if ($approvalCT == $approvalTotal) {
// Change the status to released.
if($content->setStatus(S_RELEASED, getMLText("automatic_status_update"), $user)) {
$this->callHook('postApproveDocument', $content, S_RELEASED);
}
}
}
}
if(!$this->callHook('postApproveDocument', $content)) {
}
return true;
}
}

View File

@ -29,8 +29,18 @@ class SeedDMS_Controller_ClearCache extends SeedDMS_Controller_Common {
$post = $this->params['post'];
$ret = '';
if(!empty($post['preview'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/[1-9]*';
if(!empty($post['previewpng'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/png/[1-9]*';
system($cmd, $ret);
}
if(!empty($post['previewpdf'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/pdf/[1-9]*';
system($cmd, $ret);
}
if(!empty($post['previewtxt'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/txt/[1-9]*';
system($cmd, $ret);
}

105
controllers/class.Cron.php Normal file
View File

@ -0,0 +1,105 @@
<?php
/**
* Implementation of Cron controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2020 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for the regular cron job
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2020 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_Cron extends SeedDMS_Controller_Common {
public function run() { /* {{{ */
$dms = $this->params['dms'];
$user = $this->params['user'];
$settings = $this->params['settings'];
$logger = $this->params['logger'];
$mode = $this->params['mode'];
$db = $dms->getDb();
$scheduler = new SeedDMS_Scheduler($db);
$tasks = $scheduler->getTasks();
$jsonarr = [];
foreach($tasks as $task) {
if(isset($GLOBALS['SEEDDMS_SCHEDULER']['tasks'][$task->getExtension()]) && is_object($taskobj = resolveTask($GLOBALS['SEEDDMS_SCHEDULER']['tasks'][$task->getExtension()][$task->getTask()]))) {
$arr = array(
'extension'=>$task->getExtension(),
'name'=>$task->getTask(),
'mode'=>$mode,
'disabled' => (bool) $task->getDisabled(),
'isdue' => $task->isDue(),
);
switch($mode) {
case "run":
case "dryrun":
if(method_exists($taskobj, 'execute')) {
if(!$task->getDisabled() && $task->isDue()) {
if($mode == 'run') {
/* Schedule the next run right away to prevent a second execution
* of the task when the cron job of the scheduler is called before
* the last run was finished. The task itself can still be scheduled
* to fast, but this is up to the admin of seeddms.
*/
$task->updateLastNextRun();
if($taskobj->execute($task)) {
add_log_line("Execution of task ".$task->getExtension()."::".$task->getTask()." successful.");
$arr['success'] = true;
} else {
add_log_line("Execution of task ".$task->getExtension()."::".$task->getTask()." failed, task has been disabled.", PEAR_LOG_ERR);
$arr['success'] = false;
$task->setDisabled(1);
}
} elseif($mode == 'dryrun') {
$arr['success'] = true;
}
}
}
break;
case "check":
$arr['error'] = false;
if(!method_exists($taskobj, 'execute')) {
$arr['error'] = true;
$arr['messages'][] = 'Missing method execute()';
}
if(get_parent_class($taskobj) != 'SeedDMS_SchedulerTaskBase') {
$arr['error'] = true;
$arr['error'][] = "Wrong parent class";
}
break;
case "list":
default:
header("Content-Type: application/json");
$arr['nextrun']=$task->getNextRun();
$arr['frequency']=$task->getFrequency();
$arr['params']=array();
if($params = $task->getParameter()) {
foreach($params as $key=>$value) {
$p = $taskobj->getAdditionalParamByName($key);
$arr['params'][$key] = ($p['type'] == 'password') ? '*******' : $value;
}
}
break;
}
$jsonarr[] = $arr;
}
}
echo json_encode($jsonarr);
return true;
} /* }}} */
}

View File

@ -22,49 +22,192 @@
*/
class SeedDMS_Controller_Download extends SeedDMS_Controller_Common {
public function run() {
public function version() { /* {{{ */
$dms = $this->params['dms'];
$version = $this->params['version'];
$document = $this->params['document'];
if($version < 1) {
$content = $this->callHook('documentLatestContent', $document);
if($content === null)
$content = $document->getLatestContent();
} else {
$content = $this->callHook('documentContent', $document, $version);
if($content === null)
$content = $document->getContentByVersion($version);
}
if (!is_object($content)) {
$this->errormsg = 'invalid_version';
return false;
}
/* set params['content'] for compatiblity with older extensions which
* expect the content in the controller
*/
$this->params['content'] = $content;
if(null === $this->callHook('version')) {
if(file_exists($dms->contentDir . $content->getPath())) {
header("Content-Transfer-Encoding: binary");
$efilename = rawurlencode($content->getOriginalFileName());
header("Content-Disposition: attachment; filename=\"" . $efilename . "\"; filename*=UTF-8''".$efilename);
header("Content-Type: " . $content->getMimeType());
header("Cache-Control: must-revalidate");
header("ETag: ".$content->getChecksum());
sendFile($dms->contentDir . $content->getPath());
}
}
return true;
} /* }}} */
public function file() { /* {{{ */
$dms = $this->params['dms'];
$file = $this->params['file'];
if(null === $this->callHook('file')) {
if(file_exists($dms->contentDir . $file->getPath())) {
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename=\"" . $file->getOriginalFileName() . "\"");
header("Content-Type: " . $file->getMimeType());
header("Cache-Control: must-revalidate");
sendFile($dms->contentDir . $file->getPath());
}
}
return true;
} /* }}} */
public function archive() { /* {{{ */
$dms = $this->params['dms'];
$filename = $this->params['file'];
$basedir = $this->params['basedir'];
if(null === $this->callHook('archive')) {
if(file_exists($basedir . $filename)) {
header('Content-Description: File Transfer');
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: binary");
$efilename = rawurlencode($filename);
header("Content-Disposition: attachment; filename=\"" .$efilename . "\"; filename*=UTF-8''".$efilename);
header("Cache-Control: public");
sendFile($basedir .$filename );
}
}
return true;
} /* }}} */
public function log() { /* {{{ */
$dms = $this->params['dms'];
$filename = $this->params['file'];
$basedir = $this->params['basedir'];
if(null === $this->callHook('log')) {
if(file_exists($basedir . $filename)) {
header("Content-Type: text/plain; name=\"" . $filename . "\"");
header("Content-Transfer-Encoding: binary");
$efilename = rawurlencode($filename);
header("Content-Disposition: attachment; filename=\"" .$efilename . "\"; filename*=UTF-8''".$efilename);
header("Cache-Control: must-revalidate");
sendFile($basedir.$filename);
}
}
return true;
} /* }}} */
public function sqldump() { /* {{{ */
$dms = $this->params['dms'];
$filename = $this->params['file'];
$basedir = $this->params['basedir'];
if(null === $this->callHook('sqldump')) {
if(file_exists($basedir . $filename)) {
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: binary");
$efilename = rawurlencode($filename);
header("Content-Disposition: attachment; filename=\"" .$efilename . "\"; filename*=UTF-8''".$efilename);
header("Cache-Control: must-revalidate");
sendFile($basedir.$filename);
}
}
return true;
} /* }}} */
public function approval() { /* {{{ */
$dms = $this->params['dms'];
$document = $this->params['document'];
$logid = $this->params['approvelogid'];
$filename = $dms->contentDir . $document->getDir().'a'.$logid;
if (!file_exists($filename) ) {
$this->error = 1;
return false;
}
if(null === $this->callHook('approval')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimetype = finfo_file($finfo, $filename);
header("Content-Type: ".$mimetype);
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename=\"approval-" . $document->getID()."-".(int) $_GET['approvelogid'] . get_extension($mimetype) . "\"");
header("Cache-Control: must-revalidate");
sendFile($filename);
}
return true;
} /* }}} */
public function review() { /* {{{ */
$dms = $this->params['dms'];
$document = $this->params['document'];
$logid = $this->params['reviewlogid'];
$filename = $dms->contentDir . $document->getDir().'r'.$logid;
if (!file_exists($filename) ) {
$this->error = 1;
return false;
}
if(null === $this->callHook('review')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimetype = finfo_file($finfo, $filename);
header("Content-Type: ".$mimetype);
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($filename ));
header("Content-Disposition: attachment; filename=\"review-" . $document->getID()."-".(int) $_GET['reviewlogid'] . get_extension($mimetype) . "\"");
header("Cache-Control: must-revalidate");
sendFile($filename);
}
return true;
} /* }}} */
public function run() { /* {{{ */
$dms = $this->params['dms'];
$type = $this->params['type'];
switch($type) {
case "version":
if(empty($this->params['content'])) {
$version = $this->params['version'];
$document = $this->params['document'];
if($version < 1) {
$content = $this->callHook('documentLatestContent', $document);
if($content === null)
$content = $document->getLatestContent();
} else {
$content = $this->callHook('documentContent', $document, $version);
if($content === null)
$content = $document->getContentByVersion($version);
}
if (!is_object($content)) {
$this->errormsg = 'invalid_version';
return false;
}
/* set params['content'] for compatiblity with older extensions which
* expect the content in the controller
*/
$this->params['content'] = $content;
} else {
$content = $this->params['content'];
}
if(null === $this->callHook('version')) {
if(file_exists($dms->contentDir . $content->getPath())) {
header("Content-Transfer-Encoding: binary");
$efilename = rawurlencode($content->getOriginalFileName());
header("Content-Disposition: attachment; filename=\"" . $efilename . "\"; filename*=UTF-8''".$efilename);
header("Content-Type: " . $content->getMimeType());
header("Cache-Control: must-revalidate");
header("ETag: ".$content->getChecksum());
sendFile($dms->contentDir.$content->getPath());
}
}
return $this->version();
break;
case "file":
return $this->file();
break;
case "archive":
return $this->archive();
break;
case "log":
return $this->log();
break;
case "sqldump":
return $this->sqldump();
break;
case "approval":
return $this->approval();
break;
case "review":
return $this->review();
break;
}
return true;
}
} /* }}} */
}

View File

@ -162,6 +162,10 @@ class SeedDMS_Controller_EditDocument extends SeedDMS_Controller_Common {
}
}
/* There are various hooks in inc/inc.FulltextInit.php which will take
* care of reindexing it. They just delete the indexing date which is
* faster then indexing the folder completely
*
if($fulltextservice && ($index = $fulltextservice->Indexer()) && $document) {
$idoc = $fulltextservice->IndexedDocument($document);
if(false !== $this->callHook('preIndexDocument', $document, $idoc)) {
@ -173,6 +177,7 @@ class SeedDMS_Controller_EditDocument extends SeedDMS_Controller_Common {
$index->commit();
}
}
*/
} elseif($result === false) {
if(empty($this->errormsg))

View File

@ -96,6 +96,10 @@ class SeedDMS_Controller_EditFolder extends SeedDMS_Controller_Common {
}
}
/* There are various hooks in inc/inc.FulltextInit.php which will take
* care of reindexing it. They just delete the indexing date which is
* faster then indexing the folder completely
*
if($fulltextservice && ($index = $fulltextservice->Indexer()) && $folder) {
$idoc = $fulltextservice->IndexedDocument($folder);
if(false !== $this->callHook('preIndexFolder', $folder, $idoc)) {
@ -107,6 +111,7 @@ class SeedDMS_Controller_EditFolder extends SeedDMS_Controller_Common {
$index->commit();
}
}
*/
} elseif($result === false) {
if(empty($this->errormsg))

View File

@ -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));

View File

@ -35,9 +35,11 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
$dms = $this->params['dms'];
$settings = $this->params['settings'];
$session = $this->params['session'];
$sesstheme = $this->params['sesstheme'];
$referuri = $this->params['referuri'];
$lang = $this->params['lang'];
$authenticator = $this->params['authenticator'];
$source = isset($this->params['source']) ? $this->params['source'] : '';
$sesstheme = $this->getParam('sesstheme');
$referuri = $this->getParam('referuri');
$lang = $this->getParam('lang');
$login = $this->params['login'];
$pwd = $this->params['pwd'];
@ -75,7 +77,7 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
* return false and if the hook doesn't care at all, if must return null.
*/
if(!$user) {
$user = $this->callHook('authenticate');
$user = $this->callHook('authenticate', $source);
if(false === $user) {
if(empty($this->errormsg))
$this->setErrorMsg("authentication_failed");
@ -97,6 +99,9 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
}
}
$user = $authenticator->authenticate($login, $pwd);
if(0) {
/* Authenticate against LDAP server {{{ */
if (!is_object($user) && isset($settings->_ldapHost) && strlen($settings->_ldapHost)>0) {
require_once("../inc/inc.ClassLdapAuthentication.php");
@ -113,6 +118,7 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
$authobj = new SeedDMS_DbAuthentication($dms, $settings);
$user = $authobj->authenticate($login, $pwd);
} /* }}} */
}
/* If the user is still not authenticated, then exit with an error */
if(!is_object($user)) {
@ -156,6 +162,16 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
return false;
}
if($settings->_enable2FactorAuthentication) {
if($user->getSecret()) {
$tfa = new \RobThree\Auth\TwoFactorAuth('SeedDMS');
if($tfa->verifyCode($user->getSecret(), $_POST['twofactauth']) !== true) {
$this->setErrorMsg("login_error_text");
return false;
}
}
}
/* Run any additional checks which may prevent login */
if(false === $this->callHook('restrictLogin', $user)) {
if(empty($this->errormsg))
@ -166,73 +182,78 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
/* Clear login failures if login was successful */
$user->clearLoginFailures();
// Capture the user's language and theme settings.
if ($lang) {
$user->setLanguage($lang);
} else {
$lang = $user->getLanguage();
if (strlen($lang)==0) {
$lang = $settings->_language;
/* Setting the theme and language and all the cookie handling is
* only done when authentication was requested from a weg page.
*/
if($source == 'web') {
// Capture the user's language and theme settings.
if ($lang) {
$user->setLanguage($lang);
}
}
if ($sesstheme) {
$user->setTheme($sesstheme);
}
else {
$sesstheme = $user->getTheme();
/* Override the theme if the user doesn't have one or the default theme
* shall override it.
*/
if (strlen($sesstheme)==0 || !empty($settings->_overrideTheme)) {
$sesstheme = $settings->_theme;
// $user->setTheme($sesstheme);
}
}
// Delete all sessions that are more than 1 week or the configured
// cookie lifetime old. Probably not the most
// reliable place to put this check -- move to inc.Authentication.php?
if($settings->_cookieLifetime)
$lifetime = intval($settings->_cookieLifetime);
else
$lifetime = 7*86400;
if(!$session->deleteByTime($lifetime)) {
$this->setErrorMsg("error_occured");
return false;
}
if (isset($_COOKIE["mydms_session"])) {
/* This part will never be reached unless the session cookie is kept,
* but op.Logout.php deletes it. Keeping a session could be a good idea
* for retaining the clipboard data, but the user id in the session should
* be set to 0 which is not possible due to foreign key constraints.
* So for now op.Logout.php will delete the cookie as always
*/
/* Load session */
$dms_session = $_COOKIE["mydms_session"];
if(!$resArr = $session->load($dms_session)) {
/* Turn off http only cookies if jumploader is enabled */
setcookie("mydms_session", $dms_session, time()-3600, $settings->_httpRoot, null, false, true); //delete cookie
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$referuri);
exit;
} else {
$session->updateAccess($dms_session);
$session->setUser($userid);
$lang = $user->getLanguage();
if (strlen($lang)==0) {
$lang = $settings->_language;
$user->setLanguage($lang);
}
}
} else {
// Create new session in database
if(!$id = $session->create(array('userid'=>$userid, 'theme'=>$sesstheme, 'lang'=>$lang))) {
if ($sesstheme) {
$user->setTheme($sesstheme);
}
else {
$sesstheme = $user->getTheme();
/* Override the theme if the user doesn't have one or the default theme
* shall override it.
*/
if (strlen($sesstheme)==0 || !empty($settings->_overrideTheme)) {
$sesstheme = $settings->_theme;
// $user->setTheme($sesstheme);
}
}
// Delete all sessions that are more than 1 week or the configured
// cookie lifetime old. Probably not the most
// reliable place to put this check -- move to inc.Authentication.php?
if($settings->_cookieLifetime)
$lifetime = intval($settings->_cookieLifetime);
else
$lifetime = 7*86400;
if(!$session->deleteByTime($lifetime)) {
$this->setErrorMsg("error_occured");
return false;
}
// Set the session cookie.
if($settings->_cookieLifetime)
$lifetime = time() + intval($settings->_cookieLifetime);
else
$lifetime = 0;
setcookie("mydms_session", $id, $lifetime, $settings->_httpRoot, null, false, true);
if (isset($_COOKIE["mydms_session"])) {
/* This part will never be reached unless the session cookie is kept,
* but op.Logout.php deletes it. Keeping a session could be a good idea
* for retaining the clipboard data, but the user id in the session should
* be set to 0 which is not possible due to foreign key constraints.
* So for now op.Logout.php will delete the cookie as always
*/
/* Load session */
$dms_session = $_COOKIE["mydms_session"];
if(!$resArr = $session->load($dms_session)) {
/* Turn off http only cookies if jumploader is enabled */
setcookie("mydms_session", $dms_session, time()-3600, $settings->_httpRoot, null, false, true); //delete cookie
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$referuri);
exit;
} else {
$session->updateAccess($dms_session);
$session->setUser($userid);
}
} else {
// Create new session in database
if(!$id = $session->create(array('userid'=>$userid, 'theme'=>$sesstheme, 'lang'=>$lang))) {
$this->setErrorMsg("error_occured");
return false;
}
// Set the session cookie.
if($settings->_cookieLifetime)
$lifetime = time() + intval($settings->_cookieLifetime);
else
$lifetime = 0;
setcookie("mydms_session", $id, $lifetime, $settings->_httpRoot, null, false, true);
}
}
if($this->callHook('postLogin', $user)) {

View File

@ -0,0 +1,67 @@
<?php
/**
* Implementation of ReceiptDocument controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for downloading a document
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_ReceiptDocument extends SeedDMS_Controller_Common {
public function run() {
$dms = $this->params['dms'];
$user = $this->params['user'];
$settings = $this->params['settings'];
$document = $this->params['document'];
$content = $this->params['content'];
$receiptstatus = $this->params['receiptstatus'];
$receipttype = $this->params['receipttype'];
$group = $this->params['group'];
$comment = $this->params['comment'];
/* Get the document id and name before removing the document */
$docname = $document->getName();
$documentid = $document->getID();
if(!$this->callHook('preReceiptDocument', $content)) {
}
$result = $this->callHook('receiptDocument', $content);
if($result === null) {
if ($receipttype == "ind") {
if(0 > $content->setReceiptByInd($user, $user, $receiptstatus, $comment)) {
$this->error = 1;
$this->errormsg = "receipt_update_failed";
return false;
}
} elseif ($receipttype == "grp") {
if(0 > $content->setReceiptByGrp($group, $user, $receiptstatus, $comment)) {
$this->error = 1;
$this->errormsg = "receipt_update_failed";
return false;
}
}
}
if(!$this->callHook('postReceiptDocument', $content)) {
}
return true;
}
}

View File

@ -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()) {

View File

@ -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));

View File

@ -0,0 +1,131 @@
<?php
/**
* Implementation of ReviewDocument controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for downloading a document
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_ReviewDocument extends SeedDMS_Controller_Common {
public function run() {
$dms = $this->params['dms'];
$user = $this->params['user'];
$settings = $this->params['settings'];
$document = $this->params['document'];
$content = $this->params['content'];
$reviewstatus = $this->params['reviewstatus'];
$reviewtype = $this->params['reviewtype'];
$group = $this->params['group'];
$comment = $this->params['comment'];
$file = $this->params['file'];
/* Get the document id and name before removing the document */
$docname = $document->getName();
$documentid = $document->getID();
if(!$this->callHook('preReviewDocument', $content)) {
}
$result = $this->callHook('reviewDocument', $content);
if($result === null) {
if ($reviewtype == "ind") {
$reviewLogID = $content->setReviewByInd($user, $user, $reviewstatus, $comment, $file);
if($reviewLogID === false || 0 > $reviewLogID) {
$this->error = 1;
$this->errormsg = "review_update_failed";
return false;
}
} elseif ($reviewtype == "grp") {
$reviewLogID = $content->setReviewByGrp($group, $user, $reviewstatus, $comment, $file);
if($reviewLogID === false || 0 > $reviewLogID) {
$this->error = 1;
$this->errormsg = "review_update_failed";
return false;
}
}
}
/* Check to see if the overall status for the document version needs to be
* updated.
*/
$result = $this->callHook('reviewUpdateDocumentStatus', $content);
if($result === null) {
if ($reviewstatus == -1){
if($content->setStatus(S_REJECTED,$comment,$user)) {
}
} else {
$docReviewStatus = $content->getReviewStatus();
if (is_bool($docReviewStatus) && !$docReviewStatus) {
$this->error = 1;
$this->errormsg = "cannot_retrieve_review_snapshot";
return false;
}
$reviewCT = 0;
$reviewTotal = 0;
foreach ($docReviewStatus as $drstat) {
if ($drstat["status"] == 1) {
$reviewCT++;
}
if ($drstat["status"] != -2) {
$reviewTotal++;
}
}
// If all reviews have been received and there are no rejections, retrieve a
// count of the approvals required for this document.
if ($reviewCT == $reviewTotal) {
$docApprovalStatus = $content->getApprovalStatus();
if (is_bool($docApprovalStatus) && !$docApprovalStatus) {
$this->error = 1;
$this->errormsg = "cannot_retrieve_approval_snapshot";
return false;
}
$approvalCT = 0;
$approvalTotal = 0;
foreach ($docApprovalStatus as $dastat) {
if ($dastat["status"] == 1) {
$approvalCT++;
}
if ($dastat["status"] != -2) {
$approvalTotal++;
}
}
// If the approvals received is less than the approvals total, then
// change status to pending approval.
if ($approvalCT<$approvalTotal) {
$newStatus=S_DRAFT_APP;
}
else {
// Otherwise, change the status to released.
$newStatus=S_RELEASED;
}
if ($content->setStatus($newStatus, getMLText("automatic_status_update"), $user)) {
// Send notification to subscribers.
}
}
}
}
if(!$this->callHook('postReviewDocument', $content)) {
}
return true;
}
}

View File

@ -0,0 +1,135 @@
<?php
/**
* Implementation of ReviseDocument controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for downloading a document
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_ReviseDocument extends SeedDMS_Controller_Common {
public function run() {
$dms = $this->params['dms'];
$user = $this->params['user'];
$settings = $this->params['settings'];
$document = $this->params['document'];
$content = $this->params['content'];
$revisionstatus = $this->params['revisionstatus'];
$revisiontype = $this->params['revisiontype'];
$group = $this->params['group'];
$comment = $this->params['comment'];
/* if set to true, a single reject will reject the doc. If set to false
* all revisions will be collected first and afterwards the doc is rejected
* if one has rejected it. So in the very end the doc is rejected, but
* doc remainѕ in S_IN_REVISION until all have revised the doc
*/
$onevotereject = $this->params['onevotereject'];
/* Get the document id and name before removing the document */
$docname = $document->getName();
$documentid = $document->getID();
if(!$this->callHook('preReviseDocument', $content)) {
}
$result = $this->callHook('reviseDocument', $content);
if($result === null) {
if ($revisiontype == "ind") {
if(0 > $content->setRevision($user, $user, $revisionstatus, $comment)) {
$this->error = 1;
$this->errormsg = "revision_update_failed";
return false;
}
} elseif ($revisiontype == "grp") {
if(0 > $content->setRevision($group, $user, $revisionstatus, $comment)) {
$this->error = 1;
$this->errormsg = $ll."revision_update_failed";
return false;
}
}
}
/* Check to see if the overall status for the document version needs to be
* updated.
*/
$result = $this->callHook('reviseUpdateDocumentStatus', $content);
if($result === null) {
if ($onevotereject && $revisionstatus == -1){
if(!$content->setStatus(S_NEEDS_CORRECTION,$comment,$user)) {
$this->error = 1;
$this->errormsg = "revision_update_failed";
return false;
}
} else {
$docRevisionStatus = $content->getRevisionStatus();
if (is_bool($docRevisionStatus) && !$docRevisionStatus) {
$this->error = 1;
$this->errormsg = "cannot_retrieve_revision_snapshot";
return false;
}
$revisionok = 0;
$revisionnotok = 0;
$revisionTotal = 0;
foreach ($docRevisionStatus as $drstat) {
if ($drstat["status"] == 1) {
$revisionok++;
}
if ($drstat["status"] == -1) {
$revisionnotok++;
}
if ($drstat["status"] != -2) {
$revisionTotal++;
}
}
// If all revisions have been done and there are no rejections,
// then release the document. If all revisions have been done but some
// of them were rejections then documents needs correction.
// Otherwise put it back into revision workflow
if ($revisionok == $revisionTotal) {
$newStatus=S_RELEASED;
if ($content->finishRevision($user, $newStatus, 'Finished revision workflow', getMLText("automatic_status_update"))) {
if(!$this->callHook('finishReviseDocument', $content)) {
}
}
} elseif (($revisionok + $revisionnotok) == $revisionTotal) {
$newStatus=S_NEEDS_CORRECTION;
// if ($content->finishRevision($user, $newStatus, 'Finished revision workflow', getMLText("automatic_status_update"))) {
if(!$content->setStatus($newStatus,$comment,$user)) {
$this->error = 1;
$this->errormsg = "revision_update_failed";
return false;
}
} else {
$newStatus=S_IN_REVISION;
if(!$content->setStatus($newStatus,$comment,$user)) {
$this->error = 1;
$this->errormsg = "revision_update_failed";
return false;
}
}
}
}
if(!$this->callHook('postReviseDocument', $content)) {
}
return true;
}
}

View File

@ -0,0 +1,56 @@
<?php
/**
* Implementation of Role manager controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for role manager
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_RoleMgr extends SeedDMS_Controller_Common {
public function run() {
}
public function addrole() {
$dms = $this->params['dms'];
$name = $this->params['name'];
$role = $this->params['role'];
return($dms->addRole($name, $role));
}
public function removerole() {
$roleobj = $this->params['roleobj'];
return $roleobj->remove();
}
public function editrole() {
$dms = $this->params['dms'];
$name = $this->params['name'];
$role = $this->params['role'];
$roleobj = $this->params['roleobj'];
$noaccess = $this->params['noaccess'];
if ($roleobj->getName() != $name)
$roleobj->setName($name);
if ($roleobj->getRole() != $role)
$roleobj->setRole($role);
$roleobj->setNoAccess($noaccess);
return true;
}
}

View File

@ -0,0 +1,68 @@
<?php
/**
* Implementation of Transmittal Download controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for downloading a transmittal
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_TransmittalDownload extends SeedDMS_Controller_Common {
public function run() {
$dms = $this->params['dms'];
$user = $this->params['user'];
$transmittal = $this->params['transmittal'];
$items = $transmittal->getItems();
if($items) {
include("../inc/inc.ClassDownloadMgr.php");
$downmgr = new SeedDMS_Download_Mgr();
if($extraheader = $this->callHook('extraDownloadHeader'))
$downmgr->addHeader($extraheader);
foreach($items as $item) {
$content = $item->getContent();
$document = $content->getDocument();
if ($document->getAccessMode($user) >= M_READ) {
$extracols = $this->callHook('extraDownloadColumns', $document);
$filename = $this->callHook('filenameDownloadItem', $content);
if($rawcontent = $this->callHook('rawcontent', $content)) {
$downmgr->addItem($content, $extracols, $rawcontent, $filename);
} else
$downmgr->addItem($content, $extracols, null, $filename);
}
}
$filename = tempnam(sys_get_temp_dir(), 'transmittal-download-');
if($filename) {
if($downmgr->createArchive($filename)) {
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($filename));
header("Content-Disposition: attachment; filename=\"export-" .date('Y-m-d') . ".zip\"");
header("Content-Type: application/zip");
header("Cache-Control: must-revalidate");
readfile($filename);
} else {
}
unlink($filename);
}
exit;
}
}
}

View File

@ -48,16 +48,18 @@ class SeedDMS_Controller_UpdateDocument extends SeedDMS_Controller_Common {
$userfiletype = $this->getParam('userfiletype');
$reviewers = $this->getParam('reviewers');
$approvers = $this->getParam('approvers');
$recipients = $this->getParam('recipients');
$reqversion = $this->getParam('reqversion');
$comment = $this->getParam('comment');
$attributes = $this->getParam('attributes');
$workflow = $this->getParam('workflow');
$maxsizeforfulltext = $this->getParam('maxsizeforfulltext');
$initialdocumentstatus = $this->getParam('initialdocumentstatus');
$content = $this->callHook('updateDocument');
if($content === null) {
$filesize = SeedDMS_Core_File::fileSize($userfiletmp);
if($contentResult=$document->addContent($comment, $user, $userfiletmp, utf8_basename($userfilename), $filetype, $userfiletype, $reviewers, $approvers, $version=0, $attributes, $workflow)) {
if($contentResult=$document->addContent($comment, $user, $userfiletmp, utf8_basename($userfilename), $filetype, $userfiletype, $reviewers, $approvers, $version=0, $attributes, $workflow, $initialdocumentstatus)) {
if ($this->hasParam('expires')) {
if($document->setExpires($this->getParam('expires'))) {
@ -65,6 +67,21 @@ class SeedDMS_Controller_UpdateDocument extends SeedDMS_Controller_Common {
}
}
if(!empty($recipients['i'])) {
foreach($recipients['i'] as $uid) {
if($u = $dms->getUser($uid)) {
$res = $contentResult->getContent()->addIndRecipient($u, $user);
}
}
}
if(!empty($recipients['g'])) {
foreach($recipients['g'] as $gid) {
if($g = $dms->getGroup($gid)) {
$res = $contentResult->getContent()->addGrpRecipient($g, $user);
}
}
}
$content = $contentResult->getContent();
} else {
$this->errormsg = 'error_update_document';

View File

@ -1,3 +1,3 @@
#!/bin/sh
# This command retrieves the strings that need to be translated
sgrep -o "%r\n" '"getMLText(\"" __ "\""' */*.php|sort|uniq -c
sgrep -o "%r\n" '"getMLText(\"" __ "\""' */*.php views/bootstrap/*.php |sort|uniq -c

View File

@ -1,7 +1,7 @@
<?php
/* Determine all languages keys used in the php files */
$output = array();
if(exec('sgrep -o "%r\n" \'"tMLText(\"" __ "\""\' */*.php|sort|uniq -c', &$output)) {
if(exec('sgrep -o "%r\n" \'"tMLText(\"" __ "\""\' */*.php views/bootstrap/*.php|sort|uniq -c', $output)) {
$allkeys = array();
foreach($output as $line) {
$data = explode(' ', trim($line));
@ -9,8 +9,9 @@ if(exec('sgrep -o "%r\n" \'"tMLText(\"" __ "\""\' */*.php|sort|uniq -c', &$outpu
}
}
$languages = array('ar_EG', 'bg_BG', 'ca_ES', 'cs_CZ', 'de_DE', 'en_GB', 'es_ES', 'fr_FR', 'hu_HU', 'it_IT', 'nl_NL', 'pl_PL', 'pt_BR', 'ro_RO', 'ru_RU', 'sk_SK', 'sv_SE', 'tr_TR', 'zh_CN', 'zh_TW');
/* Reading languages */
foreach(array('English', 'German', 'Italian', 'Slovak', 'Czech') as $lang) {
foreach($languages as $lang) {
include('languages/'.$lang.'/lang.inc');
ksort($text);
$langarr[$lang] = $text;
@ -20,7 +21,7 @@ foreach(array('English', 'German', 'Italian', 'Slovak', 'Czech') as $lang) {
echo "List of missing keys\n";
echo "-----------------------------\n";
foreach(array_keys($allkeys) as $key) {
foreach(array('English', 'German', 'Italian', 'Slovak', 'Czech') as $lang) {
foreach($languages as $lang) {
if(!isset($langarr[$lang][$key])) {
echo "Missing key '".$key."' in language ".$lang."\n";
}
@ -31,7 +32,7 @@ echo "\n";
/* Check for phrases not used anymore */
echo "List of superflous keys\n";
echo "-----------------------------\n";
foreach(array('English', 'German', 'Italian', 'Slovak', 'Czech') as $lang) {
foreach($languages as $lang) {
$n = 0;
foreach($langarr[$lang] as $key=>$value) {
if(!isset($allkeys[$key])) {
@ -45,8 +46,8 @@ foreach(array('English', 'German', 'Italian', 'Slovak', 'Czech') as $lang) {
exit;
$fpout = fopen('php://stdout', 'w');
foreach(array_keys($langarr['English']) as $key) {
$data = array($key, $langarr['English'][$key], $langarr['German'][$key]);
foreach(array_keys($langarr['en_GB']) as $key) {
$data = array($key, $langarr['en_GB'][$key], $langarr['de_DE'][$key]);
fputcsv($fpout, $data);
}
?>

View File

@ -9,6 +9,9 @@ application/csv
application/pdf
pdftotext -nopgbrk %s - | sed -e 's/ [a-zA-Z0-9.]\{1\} / /g' -e 's/[0-9.]//g'
If pdftotext takes too long on large document you may want to pass parameter
-l to specify the last page to be converted
mutool draw -F txt -q -N -o - %s
application/vnd.openxmlformats-officedocument.wordprocessingml.document
@ -52,6 +55,9 @@ image/jpg
image/jpeg
convert -density 300 '%f' 'pdf:%o'
image/svg+xml
cairosvg -f pdf -o '%o' '%f'
application/vnd.ms-powerpoint
application/vnd.openxmlformats-officedocument.presentationml.presentation
application/vnd.oasis.opendocument.presentation
@ -90,6 +96,12 @@ image/jpeg
image/png
convert -resize %wx '%f' 'png:%o'
image/svg+xml
cairosvg -f png --output-width %w -o '%o' '%f'
text/plain
convert -density 100 -resize %wx 'text:%f[0]' 'png:%o'
application/pdf
gs -dBATCH -dNOPAUSE -sDEVICE=png16m -dPDFFitPage -r72x72 -sOutputFile=- -dFirstPage=1 -dLastPage=1 -q '%f' | convert -resize %wx png:- '%o'
@ -97,9 +109,15 @@ application/pdf
mutool draw -F png -w %w -q -N -o %o %f 1
application/postscript
convert -density 100 -resize %wx '%f[0]' 'png:%o'
text/plain
a2ps -1 -a1 -R -B -o - '%f' | gs -dBATCH -dNOPAUSE -sDEVICE=png16m -dFirstPage=1 -dLastPage=1 -dPDFFitPage -r72x72 -sOutputFile=- -q - | convert -resize %wx png:- 'png:%o'
On Linux systems you will have to set the desired value in /etc/papersize for a2ps
e.g. a4, or letter
application/msword
application/vnd.oasis.opendocument.spreadsheet
application/vnd.oasis.opendocument.text
@ -113,3 +131,9 @@ application/csv
application/vnd.wordperfect
unoconv -d document -e PageRange=1 -f pdf --stdout -v '%f' | gs -dBATCH -dNOPAUSE -sDEVICE=pngalpha -dPDFFitPage -r72x72 -sOutputFile=- -dFirstPage=1 -dLastPage=1 -q - | convert -resize %wx png:- 'png:%o'
video/webm
video/mp4
This will take 12th frame of a video and converts into a png. It requires
ffmpeg to be installed.
convert -resize %wx '%f[12]' 'png:%o'

26
doc/README.Scheduler.md Normal file
View File

@ -0,0 +1,26 @@
Scheduler
==========
The scheduler in SeedDMS manages frequently run tasks. It is very similar
to regular unix cron jobs. A task in SeedDMS is an instanciation of a task
class which itself is defined by an extension or SeedDMS itself.
SeedDMS has some predefined classes e.g. core::expireddocs.
In order for tasks to be runnalbe, a user `cli_scheduler` must exists in
SeedDMS.
All tasks are executed by a single cronjob in the directory `utils`
> */5 * * * * /home/www-data/seeddms60x/seeddms/utils/seeddms-schedulercli --mode=run
Please keep in mind, that the php interpreter used for the cronjob may be
different from the php interpreter used für the web application. Hence, two
different php.ini files might be used. php and the php extensions may differ as
well. This can cause some extensions to be disabled and consequently some task
classes are not defined.
`utils/seeddms-schedulercli` can also be run on the command line. If you
do that, run it with the same system user used for the web server. On Debian
this is www-data. Hence run it like
sudo -u www-data utils/seeddms-schedulercli --mode=list

13
doc/README.Swagger Normal file
View File

@ -0,0 +1,13 @@
Swagger
========
Swagger is used to describe a rest api. SeedDMS ships a swagger.yaml file
in the restapi directory. You can load this file into a swagger editor, e.g.
http://petstore.swagger.io/ or http://editor.swagger.io/
You may as well set up your own swagger-ui installation as described at
https://medium.com/@tatianaensslin/how-to-add-swagger-ui-to-php-server-code-f1610c01dc03
Your apache needs to have the module 'header' enabled, because some HTTP headers
are set when the file swagger.yaml is accessed by the editor.
Currently, the swagger.yaml shipped with SeedDMS uses still swagger 2.0

42
doc/README.cron Normal file
View File

@ -0,0 +1,42 @@
Running the scheduler
======================
Since version 6 of SeedDMS a scheduler is implemented which runs
scheduled tasks. Such tasks must be implemented in an extension
and can be scheduled by the administrator within the user interface.
In order to check frequently for tasks ready to run, a system cron job
must be installed. On Linux this can be done by adding the following line
to the crontab
*/5 * * * * /var/www/seeddms60x/seeddms/utils/seeddms-schedulercli --mode=run
(Of course you need to change the path to `seeddms-schedulercli`)
This will install a cronjob running every 5 minutes. `seeddms-schedulercli` will check
for tasks ready to run and execute them in that case. You can decrease the time between
two calls of the cronjob, but keep in mind that seeddms tasks may take longer and
are being started again before the previous task has been ended.
If the configuration file of SeedDMS is not found, its path can be passed
on the command, though this should not be needed in a regular installation
obeying the directory structure of the quickstart archive.
*/5 * * * * /var/www/seeddms60x/seeddms/utils/seeddms-schedulercli --config /var/www/seeddms60x/seeddms/conf/settings.xml --mode=run
For testing purposes it may be usefull to run `seeddms-schedulercli` in list mode.
seeddms-schedulercli --mode=list
This will just list all tasks and its scheduled exection time. Tasks ready to run,
because its scheduled execution time is already in the past will be marked with
a `*`. Tasks which are disabled will be marked with a `-`.
Executing `seeddms-schedulercli` in `dryrun` mode will behave just like in `run` mode
but instead of running the task it will just issue a line.
Instead of running utils/seeddms-schedulercli you may as well access
op/op.Cron.php which also runs all scheduled tasks. On Linux you do this
by setting up a cronjob like
*/5 * * * * wget -q -O - "http://<your domain>/op/op.Cron.php"

View File

@ -177,7 +177,32 @@ class SeedDMS_ExtExample_ViewFolder {
* @package SeedDMS
* @subpackage example
*/
class SeedDMS_ExtExample_Task {
public function execute() {
class SeedDMS_ExtExample_Task extends SeedDMS_SchedulerTaskBase {
/**
* Run the task
*
* @param $task task to be executed
* @return boolean true if task was executed succesfully, otherwise false
*/
public function execute($task) {
$dms = $this->dms;
$user = $this->user;
$settings = $this->settings;
$logger = $this->logger;
$taskparams = $task->getParameter();
return true;
}
public function getDescription() {
return 'Description';
}
public function getAdditionalParams() {
return array(array(
'name'=>'email',
'type'=>'string',
'description'=> '',
));
}
}

View File

@ -1,7 +1,7 @@
<?php
$EXT_CONF['example'] = array(
'title' => 'Example Extension',
'description' => 'This sample extension demonstrate the use of various hooks',
'description' => 'This sample extension demonstrates the use of various hooks',
'disable' => true,
'version' => '1.0.1',
'releasedate' => '2018-03-21',

View File

@ -1,4 +1,9 @@
<?php
$__lang['de_DE'] = array(
'folder_contents' => 'Dies war mal "Ordner enthält". Wurde von sample Extension geändert.',
'task_example_example_email' => 'Email',
);
$__lang['en_GB'] = array(
'folder_contents' => 'This used to be "Folder contents". Was changed by sample Extension.',
'task_example_example_email' => 'Email',
);

View File

@ -12,8 +12,6 @@
* @version Release: @package_version@
*/
require_once("inc.ClassNotificationService.php");
require_once("inc.ClassEmailNotify.php");
require_once("inc.ClassSession.php");
require_once("inc.ClassAccessOperation.php");
@ -23,17 +21,23 @@ if (!strncmp("/op", $refer, 3)) {
} else {
$refer = urlencode($refer);
}
/* Check if this is a ajax call. In that case do not redirect to any page */
$isajax = isset($_GET['action']) && ($_GET['action'] != 'show');
if (!isset($_COOKIE["mydms_session"])) {
if($settings->_enableGuestLogin && $settings->_enableGuestAutoLogin) {
$session = new SeedDMS_Session($db);
if(!$dms_session = $session->create(array('userid'=>$settings->_guestID, 'theme'=>$settings->_theme, 'lang'=>$settings->_language))) {
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
if(!$isajax)
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
exit;
}
$resArr = $session->load($dms_session);
} elseif($settings->_autoLoginUser) {
if(!($user = $dms->getUser($settings->_autoLoginUser))/* || !$user->isGuest()*/) {
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
if(!$isajax)
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
exit;
}
$theme = $user->getTheme();
@ -48,12 +52,14 @@ if (!isset($_COOKIE["mydms_session"])) {
}
$session = new SeedDMS_Session($db);
if(!$dms_session = $session->create(array('userid'=>$user->getID(), 'theme'=>$theme, 'lang'=>$lang))) {
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
if(!$isajax)
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
exit;
}
$resArr = $session->load($dms_session);
} else {
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
if(!$isajax)
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
exit;
}
} else {
@ -62,7 +68,8 @@ if (!isset($_COOKIE["mydms_session"])) {
$session = new SeedDMS_Session($db);
if(!$resArr = $session->load($dms_session)) {
setcookie("mydms_session", $dms_session, time()-3600, $settings->_httpRoot); //delete cookie
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
if(!$isajax)
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
exit;
}
}
@ -75,13 +82,16 @@ if((int)$resArr['lastAccess']+60 < time())
$user = $dms->getUser($resArr["userID"]);
if (!is_object($user)) {
setcookie("mydms_session", $dms_session, time()-3600, $settings->_httpRoot); //delete cookie
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
if(!$isajax)
header("Location: " . $settings->_httpRoot . "out/out.Login.php?referuri=".$refer);
exit;
}
if($user->isAdmin()) {
if($resArr["su"]) {
$user = $dms->getUser($resArr["su"]);
$origuser = null;
if($resArr["su"] && $su = $dms->getUser($resArr["su"])) {
if($user->isAdmin() || $user->maySwitchToUser($su)) {
$origuser = $user;
$user = $su;
} else {
// $session->resetSu();
}
@ -94,8 +104,8 @@ if($settings->_useHomeAsRootFolder && !$user->isAdmin() && $user->getHomeFolder(
$dms->checkWithinRootDir = true;
$dms->setRootFolderID($user->getHomeFolder());
}
require_once('inc/inc.Notification.php');
$role = $user->getRole();
$dms->noReadForStatus = $role->getNoAccess();
/* Include additional language file for view
* This file must set $LANG[xx][]
@ -104,17 +114,21 @@ if(file_exists($settings->_rootDir . "view/".$theme."/languages/" . $lang . "/la
include $settings->_rootDir . "view/".$theme."/languages/" . $lang . "/lang.inc";
}
/* if this is a ajax call, then exit early as the rest of the script is irrelevant */
if($isajax)
return;
/* Check if password needs to be changed because it expired. If it needs
* to be changed redirect to out/out.ForcePasswordChange.php. Do this
* check only if password expiration is turned on, we are not on the
* page to change the password or the page that changes the password, and
* it is not admin */
* page to change the password or the page that changes the password, the
* current user is not admin, and no user substitution has occured. */
if (!$user->isAdmin()) {
if (!$user->isAdmin() && $origuser == null) {
if($settings->_passwordExpiration > 0) {
if(basename($_SERVER['SCRIPT_NAME']) != 'out.ForcePasswordChange.php' && basename($_SERVER['SCRIPT_NAME']) != 'op.EditUserData.php' && basename($_SERVER['SCRIPT_NAME']) != 'op.Logout.php') {
$pwdexp = $user->getPwdExpiration();
if(substr($pwdexp, 0, 10) != '0000-00-00') {
if($pwdexp && substr($pwdexp, 0, 10) != '0000-00-00') {
$pwdexpts = strtotime($pwdexp); // + $pwdexp*86400;
if($pwdexpts > 0 && $pwdexpts < time()) {
header("Location: ../out/out.ForcePasswordChange.php");
@ -125,6 +139,17 @@ if (!$user->isAdmin()) {
}
}
/* Check if secret is set for 2-factor authentication. Redirect to Setup2Factor.php
* if secret is not set and 2-factor authentication is turned on. Also check if
* already on the page Setup2Factor.php and no user substiation has occured.
*/
if($settings->_enable2FactorAuthentication && $settings->_guestID != $user->getID() && $settings->_autoLoginUser != $user->getID() && $origuser == null && $user->getSecret() == '') {
if(basename($_SERVER['SCRIPT_NAME']) != 'out.Setup2Factor.php' && basename($_SERVER['SCRIPT_NAME']) != 'op.Setup2Factor.php') {
header("Location: ../out/out.Setup2Factor.php");
exit;
}
}
/* Update cookie lifetime */
if($settings->_cookieLifetime) {
$lifetime = time() + intval($settings->_cookieLifetime);

View File

@ -0,0 +1,42 @@
<?php
/**
* Create authentication service
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Markus Westphal, Malcolm Cowe, Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal,
* 2006-2008 Malcolm Cowe, 2010-2022 Uwe Steinmann
* @version Release: @package_version@
*/
require_once('inc.ClassAuthenticationService.php');
require_once('inc.ClassDbAuthentication.php');
require_once('inc.ClassLdapAuthentication.php');
global $logger;
$authenticator = new SeedDMS_AuthenticationService($logger, $settings);
if(isset($GLOBALS['SEEDDMS_HOOKS']['authentication'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['authentication'] as $authenticationObj) {
if(method_exists($authenticationObj, 'preAddService')) {
$authenticationObj->preAddService($dms, $authenticator);
}
}
}
$authenticator->addService(new SeedDMS_DbAuthentication($dms, $settings), 'db');
if(isset($settings->_ldapHost) && strlen($settings->_ldapHost)>0) {
$authenticator->addService(new SeedDMS_LdapAuthentication($dms, $settings), 'ldap');
}
if(isset($GLOBALS['SEEDDMS_HOOKS']['authentication'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['authentication'] as $authenticationObj) {
if(method_exists($authenticationObj, 'postAddService')) {
$authenticationObj->postAddService($dms, $authenticator);
}
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* Do authentication of users and session management
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Markus Westphal, Malcolm Cowe, Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal,
* 2006-2008 Malcolm Cowe, 2010 Uwe Steinmann
* @version Release: @package_version@
*/
require_once("inc.Utils.php");
require_once("inc.ClassNotificationService.php");
require_once("inc.ClassEmailNotify.php");
require_once("inc.ClassSession.php");
require_once("inc.ClassAccessOperation.php");
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="'.$settings->_siteName.'"');
header('HTTP/1.0 401 Unauthorized');
echo getMLText('cancel_basic_authentication');
exit;
} else {
if(!($user = $authenticator->authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']))) {
header('WWW-Authenticate: Basic realm="'.$settings->_siteName.'"');
header('HTTP/1.0 401 Unauthorized');
echo getMLText('cancel_basic_authentication');
exit;
}
}
/* Clear login failures if login was successful */
$user->clearLoginFailures();
$dms->setUser($user);
require_once('inc/inc.Notification.php');

View File

@ -11,6 +11,8 @@
* @version Release: @package_version@
*/
require_once "inc.ClassAcl.php";
/**
* Class to check certain access restrictions
*
@ -27,27 +29,26 @@ class SeedDMS_AccessOperation {
*/
private $dms;
/**
* @var object $obj object being accessed
* @access protected
*/
private $obj;
/**
* @var object $user user requesting the access
* @access protected
*/
private $user;
protected $user;
/**
* @var object $settings SeedDMS Settings
* @access protected
*/
private $settings;
protected $settings;
function __construct($dms, $obj, $user, $settings) { /* {{{ */
/**
* @var object $aro access request object for caching
* @access protected
*/
private $_aro;
function __construct($dms, $user, $settings) { /* {{{ */
$this->dms = $dms;
$this->obj = $obj;
$this->user = $user;
$this->settings = $settings;
} /* }}} */
@ -61,15 +62,15 @@ class SeedDMS_AccessOperation {
* document may delete versions. The admin may even delete a version
* even if is disallowed in the settings.
*/
function mayEditVersion($vno=0) { /* {{{ */
if($this->obj->isType('document')) {
function mayEditVersion($document, $vno=0) { /* {{{ */
if($document->isType('document')) {
if($vno)
$version = $this->obj->getContentByVersion($vno);
$version = $document->getContentByVersion($vno);
else
$version = $this->obj->getLatestContent();
$version = $document->getLatestContent();
if (!isset($this->settings->_editOnlineFileTypes) || !is_array($this->settings->_editOnlineFileTypes) || (!in_array(strtolower($version->getFileType()), $this->settings->_editOnlineFileTypes) && !in_array(strtolower($version->getMimeType()), $this->settings->_editOnlineFileTypes)))
return false;
if ($this->obj->getAccessMode($this->user) == M_ALL || $this->user->isAdmin()) {
if ($document->getAccessMode($this->user) == M_ALL || $this->user->isAdmin()) {
return true;
}
}
@ -85,10 +86,10 @@ class SeedDMS_AccessOperation {
* document may delete versions. The admin may even delete a version
* even if is disallowed in the settings.
*/
function mayRemoveVersion() { /* {{{ */
if($this->obj->isType('document')) {
$versions = $this->obj->getContent();
if ((($this->settings->_enableVersionDeletion && ($this->obj->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin() ) && (count($versions) > 1)) {
function mayRemoveVersion($document) { /* {{{ */
if($document->isType('document')) {
$versions = $document->getContent();
if ((($this->settings->_enableVersionDeletion && ($document->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin() ) && (count($versions) > 1)) {
return true;
}
}
@ -105,11 +106,11 @@ class SeedDMS_AccessOperation {
* The admin may even modify the status
* even if is disallowed in the settings.
*/
function mayOverwriteStatus() { /* {{{ */
if($this->obj->isType('document')) {
if($latestContent = $this->obj->getLatestContent()) {
function mayOverrideStatus($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if ((($this->settings->_enableVersionModification && ($this->obj->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin()) && ($status["status"]==S_RELEASED || $status["status"]==S_OBSOLETE )) {
if ((($this->settings->_enableVersionModification && ($document->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin()) && ($status["status"]==S_DRAFT || $status["status"]==S_RELEASED || $status["status"]==S_REJECTED || $status["status"]==S_OBSOLETE || $status["status"]==S_NEEDS_CORRECTION)) {
return true;
}
}
@ -124,12 +125,13 @@ class SeedDMS_AccessOperation {
* reviewers/approvers is only allowed if version modification is turned on
* in the settings and the document has not been reviewed/approved by any
* user/group already.
* The admin may even set reviewers/approvers if is disallowed in the
* settings.
* The admin may even set reviewers/approvers after the review/approval
* process has been started, but only if _allowChangeRevAppInProcess
* explicitly allows it.
*/
function maySetReviewersApprovers() { /* {{{ */
if($this->obj->isType('document')) {
if($latestContent = $this->obj->getLatestContent()) {
function maySetReviewersApprovers($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
$reviewstatus = $latestContent->getReviewStatus();
$hasreview = false;
@ -143,7 +145,49 @@ class SeedDMS_AccessOperation {
if($r['status'] == 1 || $r['status'] == -1)
$hasapproval = true;
}
if ((($this->settings->_enableVersionModification && ($this->obj->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin()) && (($status["status"]==S_DRAFT_REV && !$hasreview) || ($status["status"]==S_DRAFT_APP && !$hasreview && !$hasapproval))) {
if ((($this->settings->_enableVersionModification && ($document->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin()) && (($status["status"]==S_DRAFT_REV && (!$hasreview || ($this->user->isAdmin() && $this->settings->_allowChangeRevAppInProcess))) || ($status["status"]==S_DRAFT_APP && ((!$hasreview && !$hasapproval) || ($this->user->isAdmin() && $this->settings->_allowChangeRevAppInProcess))) || $status["status"]==S_DRAFT)) {
return true;
}
}
}
return false;
} /* }}} */
/**
* Check if recipients may be edited
*
* This check can only be done for documents. Setting the document
* recipients is only allowed if version modification is turned on
* in the settings. The
* admin may even set recipients if is disallowed in the
* settings.
*/
function maySetRecipients($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if (($this->settings->_enableVersionModification && ($document->getAccessMode($this->user) >= M_READWRITE)) || $this->user->isAdmin()) {
return true;
}
}
}
return false;
} /* }}} */
/**
* Check if revisors may be edited
*
* This check can only be done for documents. Setting the document
* revisors is only allowed if version modification is turned on
* in the settings. The
* admin may even set revisors if is disallowed in the
* settings.
*/
function maySetRevisors($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if ((($this->settings->_enableVersionModification && ($document->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin()) && ($status["status"]==S_RELEASED || $status["status"]==S_IN_REVISION)) {
return true;
}
}
@ -160,12 +204,12 @@ class SeedDMS_AccessOperation {
* admin may even set the workflow if is disallowed in the
* settings.
*/
function maySetWorkflow() { /* {{{ */
if($this->obj->isType('document')) {
if($latestContent = $this->obj->getLatestContent()) {
function maySetWorkflow($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$workflow = $latestContent->getWorkflow();
$workflowstate = $latestContent->getWorkflowState();
if ((($this->settings->_enableVersionModification && ($this->obj->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin()) && (!$workflow || ($workflowstate && ($workflow->getInitState()->getID() == $workflowstate->getID())))) {
if ((($this->settings->_enableVersionModification && ($document->getAccessMode($this->user) == M_ALL)) || $this->user->isAdmin()) && (!$workflow || ($workflowstate && ($workflow->getInitState()->getID() == $workflowstate->getID())))) {
return true;
}
}
@ -179,11 +223,11 @@ class SeedDMS_AccessOperation {
* This check can only be done for documents. Setting the documents
* expiration date is only allowed if the document has not been obsoleted.
*/
function maySetExpires() { /* {{{ */
if($this->obj->isType('document')) {
if($latestContent = $this->obj->getLatestContent()) {
function maySetExpires($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if ((($this->obj->getAccessMode($this->user) == M_ALL) || $this->user->isAdmin()) && ($status["status"]!=S_OBSOLETE)) {
if ((($document->getAccessMode($this->user) >= M_READWRITE) || $this->user->isAdmin()) && ($status["status"]!=S_OBSOLETE)) {
return true;
}
}
@ -200,17 +244,17 @@ class SeedDMS_AccessOperation {
* The admin may set the comment even if is
* disallowed in the settings.
*/
function mayEditComment() { /* {{{ */
if($this->obj->isType('document')) {
if($this->obj->getAccessMode($this->user) < M_READWRITE)
function mayEditComment($document) { /* {{{ */
if($document->isType('document')) {
if($document->getAccessMode($this->user) < M_READWRITE)
return false;
if($this->obj->isLocked()) {
$lockingUser = $this->obj->getLockingUser();
if (($lockingUser->getID() != $this->user->getID()) && ($this->obj->getAccessMode($this->user) != M_ALL)) {
if($document->isLocked()) {
$lockingUser = $document->getLockingUser();
if (($lockingUser->getID() != $this->user->getID()) && ($document->getAccessMode($this->user) != M_ALL)) {
return false;
}
}
if($latestContent = $this->obj->getLatestContent()) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if (($this->settings->_enableVersionModification || $this->user->isAdmin()) && !in_array($status["status"], array(S_OBSOLETE, S_EXPIRED))) {
return true;
@ -228,15 +272,15 @@ class SeedDMS_AccessOperation {
* the settings or the document is still in an approval/review
* or intial workflow step.
*/
function mayEditAttributes() { /* {{{ */
if($this->obj->isType('document')) {
if($latestContent = $this->obj->getLatestContent()) {
function mayEditAttributes($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
$workflow = $latestContent->getWorkflow();
$workflowstate = $latestContent->getWorkflowState();
if($this->obj->getAccessMode($this->user) < M_READWRITE)
if($document->getAccessMode($this->user) < M_READWRITE)
return false;
if ($this->settings->_enableVersionModification || in_array($status["status"], array(S_DRAFT_REV, S_DRAFT_APP)) || ($workflow && $workflowstate && $workflow->getInitState()->getID() == $workflowstate->getID())) {
if ($this->settings->_enableVersionModification || in_array($status["status"], array(S_DRAFT_REV, S_DRAFT_APP, S_IN_REVISION)) || ($workflow && $workflowstate && $workflow->getInitState()->getID() == $workflowstate->getID())) {
return true;
}
}
@ -251,11 +295,11 @@ class SeedDMS_AccessOperation {
* review. There are other requirements which are not taken into
* account here.
*/
function mayReview() { /* {{{ */
if($this->obj->isType('document')) {
if($latestContent = $this->obj->getLatestContent()) {
function mayReview($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if ($status["status"]==S_DRAFT_REV) {
if ($document->getAccessMode($this->user) >= M_READ && $status["status"]==S_DRAFT_REV) {
return true;
}
}
@ -269,9 +313,24 @@ class SeedDMS_AccessOperation {
* A review may only be updated by the user who originaly addedd the
* review and if it is allowed in the settings
*/
function mayUpdateReview($updateUser) { /* {{{ */
if($this->obj->isType('document')) {
if($this->settings->_enableUpdateRevApp && ($updateUser == $this->user) && !$this->obj->hasExpired()) {
function mayUpdateReview($document, $updateUser) { /* {{{ */
if($document->isType('document')) {
if($this->settings->_enableUpdateRevApp && ($updateUser == $this->user) && $document->getAccessMode($this->user) >= M_READ && !$document->hasExpired()) {
return true;
}
}
return false;
} /* }}} */
/**
* Check if a approval maybe edited
*
* An approval may only be updated by the user who originaly addedd the
* approval and if it is allowed in the settings
*/
function mayUpdateApproval($document, $updateUser) { /* {{{ */
if($document->isType('document')) {
if($this->settings->_enableUpdateRevApp && ($updateUser == $this->user) && $document->getAccessMode($this->user) >= M_READ && !$document->hasExpired()) {
return true;
}
}
@ -287,11 +346,11 @@ class SeedDMS_AccessOperation {
* There are other requirements which are not taken into
* account here.
*/
function mayApprove() { /* {{{ */
if($this->obj->isType('document')) {
if($latestContent = $this->obj->getLatestContent()) {
function mayApprove($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if ($status["status"]==S_DRAFT_APP) {
if ($document->getAccessMode($this->user) >= M_READ && $status["status"]==S_DRAFT_APP) {
return true;
}
}
@ -300,14 +359,70 @@ class SeedDMS_AccessOperation {
} /* }}} */
/**
* Check if a approval maybe edited
* Check if document content may be receipted
*
* An approval may only be updated by the user who originaly addedd the
* approval and if it is allowed in the settings
* Reviewing a document content is only allowed if the document was not
* obsoleted. There are other requirements which are not taken into
* account here.
*/
function mayUpdateApproval($updateUser) { /* {{{ */
if($this->obj->isType('document')) {
if($this->settings->_enableUpdateRevApp && ($updateUser == $this->user) && !$this->obj->hasExpired()) {
function mayReceipt($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if ($document->getAccessMode($this->user) >= M_READ && $status["status"]==S_RELEASED) {
return true;
}
}
}
return false;
} /* }}} */
/**
* Check if a review maybe edited
*
* A review may only be updated by the user who originaly addedd the
* review and if it is allowed in the settings
*/
function mayUpdateReceipt($document, $updateUser) { /* {{{ */
if($document->isType('document')) {
if($this->settings->_enableUpdateReceipt && ($updateUser == $this->user) && $document->getAccessMode($this->user) >= M_READ && !$document->hasExpired()) {
return true;
}
}
return false;
} /* }}} */
/**
* Check if document content may be revised
*
* Revising a document content is only allowed if the document was not
* obsoleted. There may be other requirements which are not taken into
* account here.
*/
function mayRevise($document) { /* {{{ */
if($document->isType('document')) {
if($latestContent = $document->getLatestContent()) {
$status = $latestContent->getStatus();
if ($document->getAccessMode($this->user) >= M_READ && $status["status"]!=S_OBSOLETE) {
return true;
}
}
}
return false;
} /* }}} */
/**
* Check if document content may be checked in
*
*
*/
function mayCheckIn($document) { /* {{{ */
if($document->isType('document')) {
$checkoutinfo = $document->getCheckOutInfo();
if(!$checkoutinfo)
return false;
$info = $checkoutinfo[0];
if($this->user->getID() == $info['userID'] || $document->getAccessMode($this->user) == M_ALL) {
return true;
}
}
@ -432,8 +547,15 @@ class SeedDMS_AccessOperation {
/**
* Check for access permission on view
*
* This function will always return true because it was added to smooth
* migration from 5.1.x to 6.0.x
* If the parameter $view is an array then each element is considered the
* name of a view and true will be returned if one of them is accessible.
* Whether access is allowed also depends on the currently logged in user
* stored in the view object. If the user is an admin the access
* on a view must be explicitly disallowed. For regular users the access
* must be explicitly allowed.
*
* If advanced access control is turn off, this function will always return
* true for admins and false for other users.
*
* @param mixed $view Instanz of view, name of view or array of view names
* @param string $get query parameters possible containing the element 'action'
@ -441,20 +563,76 @@ class SeedDMS_AccessOperation {
* no specific access right is set, otherwise false
*/
function check_view_access($view, $get=array()) { /* {{{ */
return $this->check_view_legacy_access($view, $get);
if(!$this->settings->_advancedAcl) {
return $this->check_view_legacy_access($view, $get);
}
if(is_string($view)) {
$scripts = array($view);
} elseif(is_array($view)) {
$scripts = $view;
} elseif(is_subclass_of($view, 'SeedDMS_View_Common')) {
$scripts = array($view->getParam('class'));
} else {
return false;
}
$scope = 'Views';
$action = (isset($get['action']) && $get['action']) ? $get['action'] : 'show';
$acl = new SeedDMS_Acl($this->dms);
if(!$this->_aro)
$this->_aro = SeedDMS_Aro::getInstance($this->user->getRole(), $this->dms);
foreach($scripts as $script) {
$aco = SeedDMS_Aco::getInstance($scope.'/'.$script.'/'.$action, $this->dms);
$ll = $acl->check($this->_aro, $aco);
if($ll === 1 && !$this->user->isAdmin() || $ll !== -1 && $this->user->isAdmin())
return true;
}
return false;
} /* }}} */
/**
* Check for access permission on controller
*
* This function will always return true because it was added to smooth
* migration from 5.1.x to 6.0.x
* If the parameter $controller is an array then each element is considered the
* name of a controller and true will be returned if one is accesible.
* If advanced access controll is turn off, this function will return false
* for guest users and true otherwise.
*
* @param mixed $controller Instanz of controller, name of controller or array of controller names
* @param string $get query parameters
* @return boolean true if access is allowed otherwise false
*/
function check_controller_access($controller, $get=array()) { /* {{{ */
return true;
if(!$this->settings->_advancedAcl) {
if($this->user->isGuest())
return false;
elseif($this->user->isAdmin())
return true;
else {
if($controller == 'AddDocument' && isset($get['action']) && $get['action'] == 'setOwner')
return false;
return true;
}
}
if(is_string($controller)) {
$scripts = array($controller);
} elseif(is_array($controller)) {
$scripts = $controller;
} elseif(is_subclass_of($controller, 'SeedDMS_Controller_Common')) {
$scripts = array($controller->getParam('class'));
} else {
return false;
}
$scope = 'Controllers';
$action = (isset($get['action']) && $get['action']) ? $get['action'] : 'run';
$acl = new SeedDMS_Acl($this->dms);
if(!$this->_aro)
$this->_aro = SeedDMS_Aro::getInstance($this->user->getRole(), $this->dms);
foreach($scripts as $script) {
$aco = SeedDMS_Aco::getInstance($scope.'/'.$script.'/'.$action, $this->dms);
$ll = $acl->check($this->_aro, $aco);
if($ll === 1 && !$this->user->isAdmin() || $ll !== -1 && $this->user->isAdmin())
return true;
}
return false;
} /* }}} */
}

390
inc/inc.ClassAcl.php Normal file
View File

@ -0,0 +1,390 @@
<?php
/**
* Implementation of a access control list.
*
* SeedDMS uses access control list for setting permission,
* on various operations.
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright 2016 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class to represent an access request object
*
* This class provides a model for access request objects.
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright 2016 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Acl { /* {{{ */
/**
* @var object $dms reference to dms object.
* @access public
*/
public $_dms;
/**
* Create a new instance of an acl
*
* @param object $dms object of dms
* @return object instance of SeedDMS_Acl
*/
public function __construct($dms) { /* {{{ */
$this->_dms = $dms;
} /* }}} */
/**
* Check if Aro has access on Aco
*
* @param object $aro access request object
* @param object $aco access control object
* @return integer/boolean -1 if access is explictly denied, 1 if access
* is explictly allow, 0 if no access restrictions exists, false if
* an error occured.
*/
public function check($aro, $aco) { /* {{{ */
$db = $this->_dms->getDB();
while($aco) {
$acoid = $aco->getID();
$queryStr = "SELECT * FROM `tblArosAcos` WHERE `aro`=".$aro->getID()." AND `aco`=".$acoid;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return false;
if (count($resArr) == 1)
return((int) $resArr[0]['read']);
$aco = $aco->getParent();
}
return 0;
} /* }}} */
public function toggle($aro, $aco) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT * FROM `tblArosAcos` WHERE `aro`=".$aro->getID()." AND `aco`=".$aco->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return false;
if (count($resArr) != 1)
return false;
$resArr = $resArr[0];
$newperm = $resArr['read'] == 1 ? -1 : 1;
$queryStr = "UPDATE `tblArosAcos` SET `read`=".$newperm." WHERE `aro`=".$aro->getID()." AND `aco`=".$aco->getID();
if (!$db->getResult($queryStr))
return false;
return true;
} /* }}} */
public function add($aro, $aco, $perm=-1) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT * FROM `tblArosAcos` WHERE `aro`=".$aro->getID()." AND `aco`=".$aco->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return false;
if (count($resArr) == 1) {
$resArr = $resArr[0];
$newperm = $resArr['read'] == 1 ? -1 : 1;
$queryStr = "UPDATE `tblArosAcos` SET `read`=".$newperm." WHERE `aro`=".$aro->getID()." AND `aco`=".$aco->getID();
if (!$db->getResult($queryStr))
return false;
} else {
$queryStr = "INSERT INTO `tblArosAcos` (`aro`, `aco`, `read`) VALUES (".$aro->getID().", ".$aco->getID().", ".$perm.")";
if (!$db->getResult($queryStr))
return false;
}
return true;
} /* }}} */
public function remove($aro, $aco) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "DELETE FROM `tblArosAcos` WHERE `aro`=".$aro->getID()." AND `aco`=".$aco->getID();
if (!$db->getResult($queryStr))
return false;
return true;
} /* }}} */
} /* }}} */
/**
* Class to represent an access request/controll object
*
* This class provides a model for access request/controll objects.
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright 2016 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_AroAco { /* {{{ */
/**
* @var object $dms reference to dms object.
* @access protected
*/
public $_dms;
/**
* @var integer id of access request object
*/
protected $_id;
/**
* @var integer id of parent of access request object
*/
protected $_parent;
/**
* @var string alias of access request object
*/
protected $_alias;
/**
* @var object object of access request object
*/
protected $_object;
/**
* Create a new instance of an aro
*
* @param object $dms object of dms
* @return object instance of SeedDMS_Aco
*/
function __construct($dms, $id, $parent, $object, $alias) { /* {{{ */
$this->_dmѕ = $dms;
$this->_id = $id;
$this->_parent = $parent;
$this->_object = $object;
$this->_alias = $alias;
} /* }}} */
public function setDMS($dms) { /* {{{ */
$this->_dms = $dms;
} /* }}} */
public function getDMS() { /* {{{ */
return($this->_dms);
} /* }}} */
public function getID() { /* {{{ */
return $this->_id;
} /* }}} */
public function getAlias() { /* {{{ */
return $this->_alias;
} /* }}} */
public function getObject() { /* {{{ */
return $this->_object;
} /* }}} */
} /* }}} */
/**
* Class to represent an access request object
*
* This class provides a model for access request objects.
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright 2016 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Aro extends SeedDMS_AroAco { /* {{{ */
/**
* Create a new instance of an aro
*
* @param object $dms object to access the underlying database
* @return object instance of SeedDMS_Aro
*/
public static function getInstance($id, $dms) { /* {{{ */
$db = $dms->getDB();
if(is_int($id)) {
$queryStr = "SELECT * FROM `tblAros` WHERE `id` = " . (int) $id;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return null;
if (count($resArr) != 1)
return null;
$resArr = $resArr[0];
} elseif(is_object($id)) {
if($dms->getClassname('role') == get_class($id)) {
$model = 'Role';
$queryStr = "SELECT * FROM `tblAros` WHERE `model`=".$db->qstr($model)." AND `foreignid`=".$id->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return null;
if (count($resArr) == 0) {
$queryStr = "INSERT INTO `tblAros` (`parent`, `model`, `foreignid`) VALUES (0, ".$db->qstr($model).", ".$id->getID().")";
if (!$db->getResult($queryStr))
return null;
$id = $db->getInsertID();
$queryStr = "SELECT * FROM `tblAros` WHERE `id` = " . $id;
$resArr = $db->getResultArray($queryStr);
}
$resArr = $resArr[0];
} else {
return null;
}
}
if($resArr['model'] == 'Role') {
$classname = $dms->getClassname('role');
$object = $classname::getInstance($resArr['foreignid'], $dms);
} else {
$object = null;
}
$aro = new SeedDMS_Aro($dms, $resArr["id"], $resArr['parent'], $object, $resArr['alias']);
$aro->setDMS($dms);
return $aro;
} /* }}} */
} /* }}} */
/**
* Class to represent an access control object
*
* This class provides a model for access control objects.
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright 2016 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Aco extends SeedDMS_AroAco{ /* {{{ */
/**
* Create a new instance of an aco
*
* @param object $dms object to access the underlying database
* @return object instance of SeedDMS_Aco
*/
public static function getInstance($id, $dms) { /* {{{ */
$db = $dms->getDB();
if(is_int($id)) {
$queryStr = "SELECT * FROM `tblAcos` WHERE `id` = " . (int) $id;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return null;
if (count($resArr) == 0) {
return null;
}
$resArr = $resArr[0];
} elseif(is_string($id)) {
$tmp = explode('/', $id);
$parentid = 0;
foreach($tmp as $part) {
$queryStr = "SELECT * FROM `tblAcos` WHERE `alias` = " . $db->qstr($part);
// if($parentid)
$queryStr .= " AND parent=".$parentid;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return null;
if (count($resArr) == 0) {
$queryStr = "INSERT INTO `tblAcos` (`parent`, `alias`, `model`) VALUES (".$parentid.",".$db->qstr($part).", '')";
if (!$db->getResult($queryStr))
return null;
$id = $db->getInsertID();
$queryStr = "SELECT * FROM `tblAcos` WHERE `id` = " . $id;
$resArr = $db->getResultArray($queryStr);
}
$parentid = (int) $resArr[0]['id'];
}
$resArr = $resArr[0];
}
if($resArr['model'] == 'Document') {
$classname = $dms->getClassname('document');
$object = $classname::getInstance($resArr['foreignid'], $dms);
} elseif($resArr['model'] == 'Folder') {
$classname = $dms->getClassname('focument');
$object = $classname::getInstance($resArr['foreignid'], $dms);
} else {
$object = null;
}
$aco = new SeedDMS_Aco($dms, $resArr["id"], $resArr['parent'], $object, $resArr['alias']);
$aco->setDMS($dms);
return $aco;
} /* }}} */
public function getChildren() { /* {{{ */
$dms = $this->getDMS();
$db = $dms->getDB();
$queryStr = "SELECT * FROM `tblAcos` WHERE `parent` = ".$this->_id." ORDER BY `alias`";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return null;
if (count($resArr) == 0)
return null;
$acos = array();
foreach($resArr as $row) {
$aco = new SeedDMS_Aco($dms, $row["id"], $row['parent'], null, $row['alias']);
$aco->setDMS($dms);
$acos[] = $aco;
}
return $acos;
} /* }}} */
public function getPermission($aro) { /* {{{ */
if(!$aro)
return 0;
$dms = $this->getDMS();
$db = $dms->getDB();
$queryStr = "SELECT * FROM `tblArosAcos` WHERE `aro`=".$aro->getID()." AND `aco`=".$this->_id;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return false;
if (count($resArr) != 1)
return 0;
return (int) $resArr[0]['read'];
} /* }}} */
public static function getRoot($dms) { /* {{{ */
$db = $dms->getDB();
$queryStr = "SELECT * FROM `tblAcos` WHERE `parent` = 0 ORDER BY `alias`";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return null;
$acos = array();
foreach($resArr as $row) {
$aco = new SeedDMS_Aco($dms, $row["id"], $row['parent'], null, $row['alias']);
$aco->setDMS($dms);
$acos[] = $aco;
}
return $acos;
} /* }}} */
public function getParent() { /* {{{ */
$dms = $this->getDMS();
$db = $dms->getDB();
$queryStr = "SELECT * FROM `tblAcos` WHERE `id` = ".$this->_parent;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr === false)
return null;
if (count($resArr) != 1)
return null;
$row = $resArr[0];
$aco = new SeedDMS_Aco($dms, $row["id"], $row['parent'], null, $row['alias']);
$aco->setDMS($dms);
return $aco;
} /* }}} */
} /* }}} */

View File

@ -24,34 +24,6 @@
*/
abstract class SeedDMS_Authentication
{
/**
* DMS object
*
* @var SeedDMS_Core_DMS
* @access protected
*/
protected $dms;
/**
* DMS settings
*
* @var Settings
* @access protected
*/
protected $settings;
/**
* Constructor
*
* @param SeedDMS_Core_DMS $dms DMS object
* @param Settings $settings DMS settings
*/
function __construct($dms, $settings) /* {{{ */
{
$this->dms = $dms;
$this->settings = $settings;
} /* }}} */
/**
* Do Authentication
*

View File

@ -0,0 +1,73 @@
<?php
/* Middleware for authentication based on session */
class SeedDMS_Auth_Middleware_Session { /* {{{ */
private $container;
public function __construct($container) {
$this->container = $container;
}
/**
* Example middleware invokable class
*
* @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* @param \Psr\Http\Message\ResponseInterface $response PSR7 response
* @param callable $next Next middleware
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function __invoke($request, $response, $next) {
// $this->container has the DI
$dms = $this->container->dms;
$settings = $this->container->config;
$logger = $this->container->logger;
$userobj = null;
if($this->container->has('userobj'))
$userobj = $this->container->userobj;
if($userobj) {
$response = $next($request, $response);
return $response;
}
$logger->log("Invoke middleware for method ".$request->getMethod()." on '".$request->getUri()->getPath()."'", PEAR_LOG_INFO);
require_once("inc/inc.ClassSession.php");
$session = new SeedDMS_Session($dms->getDb());
if (isset($_COOKIE["mydms_session"])) {
$dms_session = $_COOKIE["mydms_session"];
$logger->log("Session key: ".$dms_session, PEAR_LOG_DEBUG);
if(!$resArr = $session->load($dms_session)) {
/* Delete Cookie */
setcookie("mydms_session", $dms_session, time()-3600, $settings->_httpRoot);
$logger->log("Session for id '".$dms_session."' has gone", PEAR_LOG_ERR);
return $response->withStatus(403);
}
/* Load user data */
$userobj = $dms->getUser($resArr["userID"]);
if (!is_object($userobj)) {
/* Delete Cookie */
setcookie("mydms_session", $dms_session, time()-3600, $settings->_httpRoot);
if($settings->_enableGuestLogin) {
if(!($userobj = $dms->getUser($settings->_guestID)))
return $response->withStatus(403);
} else
return $response->withStatus(403);
}
if($userobj->isAdmin()) {
if($resArr["su"]) {
if(!($userobj = $dms->getUser($resArr["su"])))
return $response->withStatus(403);
}
}
$dms->setUser($userobj);
} else {
return $response->withStatus(403);
}
$this->container['userobj'] = $userobj;
$response = $next($request, $response);
return $response;
}
} /* }}} */

View File

@ -0,0 +1,89 @@
<?php
/**
* Implementation of authentication service
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2016 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Implementation of authentication service
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2016 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_AuthenticationService {
/**
* List of services for authenticating user
*/
protected $services;
/*
* List of servives with errors
*/
protected $errors;
/*
* Service for logging
*/
protected $logger;
/*
* Configuration
*/
protected $settings;
public function __construct($logger = null, $settings = null) { /* {{{ */
$this->services = array();
$this->errors = array();
$this->logger = $logger;
$this->settings = $settings;
} /* }}} */
public function addService($service, $name='') { /* {{{ */
if(!$name)
$name = md5(uniqid());
$this->services[$name] = $service;
$this->errors[$name] = true;
} /* }}} */
public function getServices() { /* {{{ */
return $this->services;
} /* }}} */
public function getErrors() { /* {{{ */
return $this->errors;
} /* }}} */
public function authenticate($username, $password) { /* {{{ */
$user = null;
foreach($this->services as $name => $service) {
if($this->logger)
$this->logger->log('Authentication service \''.$name.'\'', PEAR_LOG_INFO);
$user = $service->authenticate($username, $password);
if($user === false) {
$this->errors[$name] = false;
if($this->logger)
$this->logger->log('Authentication service \''.$name.'\': Authentication of user \''.$username.'\' failed.', PEAR_LOG_ERR);
return false;
} elseif($user === null) {
if($this->logger)
$this->logger->log('Authentication service \''.$name.'\': Authentication of user \''.$username.'\' disregarded.', PEAR_LOG_ERR);
} else {
if($this->logger)
$this->logger->log('Authentication service \''.$name.'\': Authentication of user \''.$username.'\' successful.', PEAR_LOG_INFO);
$this->errors[$name] = true;
return $user;
}
}
return $user;
} /* }}} */
}

View File

@ -52,6 +52,7 @@ class Controller {
require_once($filename);
$controller = new $classname($params);
/* Set some configuration parameters */
$controller->setParam('class', $class);
$controller->setParam('postVars', $_POST);
$controller->setParam('getVars', $_GET);
$controller->setParam('requestVars', $_REQUEST);

View File

@ -310,4 +310,26 @@ class SeedDMS_Controller_Common {
}
return false;
} /* }}} */
/**
* Check if the access on the contoller with given name or the current
* controller itself may be accessed.
*
* The function requires the parameter 'accessobject' to be available in the
* controller, because it calls SeedDMS_AccessOperation::check_controller_access()
* to check access rights. If the the optional $name is not set the
* current controller is used.
*
* @param string|array $name name of controller or list of controller names
* @return boolean true if access is allowed otherwise false
*/
protected function check_access($name='') { /* {{{ */
if(!$name)
$name = $this;
if(!isset($this->params['accessobject']))
return false;
$access = $this->params['accessobject']->check_controller_access($name);
return $access;
} /* }}} */
}

View File

@ -16,6 +16,7 @@ require_once("inc/inc.ClassConversionServiceImageToImage.php");
require_once("inc/inc.ClassConversionServiceImageToText.php");
require_once("inc/inc.ClassConversionServicePdfToImage.php");
require_once("inc/inc.ClassConversionServiceTextToText.php");
require_once("inc/inc.ClassConversionServiceTextToImage.php");
/**
* Implementation of conversion manager
@ -28,62 +29,102 @@ require_once("inc/inc.ClassConversionServiceTextToText.php");
*/
class SeedDMS_ConversionMgr {
/**
* List of services for searching fulltext
* List of services for converting documents
*/
public $services;
public function __construct() {
$this->services = array();
}
/**
* @var $success set to false if conversion failed
*/
protected $success;
public function addService($service) {
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;
}
} /* }}} */
public function hasService($from, $to) {
public function hasService($from, $to) { /* {{{ */
if(!empty($this->services[$from][$to]))
return true;
else
return false;
}
} /* }}} */
/**
* Return the list of mimetypes which can be converted
* into the given mimetype
*
* @param string $askto mimetype to be converted into
* @return array list of from mimetypes
*/
public function getFromWithTo($askto) { /* {{{ */
$fromret = [];
foreach($this->services as $from=>$toservices)
foreach($toservices as $to=>$service)
if($to == $askto)
$fromret[] = $from;
return $fromret;
} /* }}} */
/**
* 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
* @param string $to mimetype of output file
* @param string $target name of target file. If none is given the
* content of the converted document will be returned.
* @param array $params additional parameter needed for the conversion,
* e.g. the width of an image
*
* @return boolean true on success, other false
*/
public function convert($file, $from, $to, $target=null, $params=array()) {
public function convert($file, $from, $to, $target=null, $params=array()) { /* {{{ */
if(isset($this->services[$from][$to])) {
$services = $this->services[$from][$to];
for(end($services); key($services)!==null; prev($services)) {
$service = current($services);
$text = $service->convert($file, $target, $params);
if($text !== false)
if(!$service->wasSuccessful()) {
$this->success = false;
return false;
}
if($text)
return $text;
}
}
}
return true;
} /* }}} */
}

Some files were not shown because too many files have changed in this diff Show More