Merge branch 'seeddms-6.0.x'

This commit is contained in:
Uwe Steinmann 2024-11-07 07:08:11 +01:00
commit b9abff5c55
295 changed files with 13849 additions and 49822 deletions

View File

@ -3,6 +3,6 @@ root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespaces = true
trim_trailing_whitespace = true
indent_style = tab
indent_size = 2

View File

@ -6,6 +6,7 @@ Header set X-Content-Type-Options: "nosniff"
</IfModule>
RewriteEngine On
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
#RewriteRule "^favicon\.ico$" "-" [L]
#RewriteRule "^(favicon\.ico)$" %{HTTP_HOST}/views/bootstrap/images/favicon.svg [L,NC]
RewriteRule "^(favicon\.ico)" /views/bootstrap/images/favicon.svg [L,NC]
@ -32,7 +33,7 @@ RewriteRule ^ext/[^/]+/icon.(?:png|svg)$ - [L]
RewriteCond %{REQUEST_URI} "ext/[^/]+/"
RewriteRule !^ext/[^/]+/.*(?:op|out|res|node_modules) - [F]
RewriteCond %{REQUEST_URI} "ext/[^/]+/res/.*$" [NC]
RewriteRule !^ext/[^/]+/res/.*\.(?:css|js|png|gif|svg|ico|html|woff) - [F]
RewriteRule !^ext/[^/]+/res/.*\.(?:css|js|png|gif|svg|ico|html|woff|ttf) - [F]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ext/.*$ - [L]

134
CHANGELOG
View File

@ -1,3 +1,41 @@
--------------------------------------------------------------------------------
Changes in version 6.0.29
--------------------------------------------------------------------------------
- merge changes up to 5.1.36
- fix regression in FolderNotify
--------------------------------------------------------------------------------
Changes in version 6.0.28
--------------------------------------------------------------------------------
- merge changes up to 5.1.35
--------------------------------------------------------------------------------
Changes in version 6.0.27
--------------------------------------------------------------------------------
- merge changes up to 5.1.34
- Document/folder check distinguishes between documents which cannot be
receiped/revised because of access rights or the recipient/revisor being
disabled.
- fix creating user via rest api
- checkout info does not depend on whether the logged in user was substituted
- add new endpoints for managing roles by rest api
- add transmittals in menu
- add legacy access check for controllers
--------------------------------------------------------------------------------
Changes in version 6.0.26
--------------------------------------------------------------------------------
- merge changes up to 5.1.33
- add task to import files from drop folder
- add substitution of users in bootstrap4 theme
--------------------------------------------------------------------------------
Changes in version 6.0.25
--------------------------------------------------------------------------------
- merge changes up to 5.1.32
- status log can be turned on with advanced access control
- scheduler has more condensed layout
--------------------------------------------------------------------------------
Changes in version 6.0.24
--------------------------------------------------------------------------------
@ -257,6 +295,102 @@
- add document list which can be exported as an archive
- search results can be exported
--------------------------------------------------------------------------------
Changes in version 5.1.36
--------------------------------------------------------------------------------
- add new page for send test notification
- remove deprecated function formatted_size()
- fix bugs when importing files from filesystem with metadata, better logging
- fix potential xss attack when showing log file
- support for different storage of documents (not yet used)
- do not treat repository url like a directory name (will no longer add a
directory seperator)
- use rename() on windows when moving an extension to its final destination
- fixed bug in AddDocument, approver group wasn't checked properly against
mandatory approvers
- email From-address in settings can be forced for all outgoing mail
- checking of ssl certificate for smtp can be turned off
- add chart for disk space per month
- clearing cache of js files works for a large number of files
- WebDAV returns `quota-used-bytes` and `quota-available-bytes`
- fix settings SeedDMS attributes in WebDAV server
- propperly check for duplicate folder names in op/op.Ajax.php
- add searching for file size (fulltext search)
--------------------------------------------------------------------------------
Changes in version 5.1.35
--------------------------------------------------------------------------------
- check access rights on EditDocumentFile and RemoveDocumentFile
- allow to set expiration dates in the past again
- fix authentication with ldap if AD is used
- fix progress bar in list of users if quota is active
- field storing email in ldap can be configured
--------------------------------------------------------------------------------
Changes in version 5.1.34
--------------------------------------------------------------------------------
- introduce global reviewer/approver
- group managers can be used as mandatory reviewers/approvers for all
users of that group
- update sabre/dav to 4.*
- show parent folder of document in lists of documents (calendar, dashboard)
- documents can be updated by dragging a file on a document list item
- dragging a folder on a folder list item oder the drag and drop area will
recursively upload the folder hierarchy including all files
- fix checking if user is owner when sending notifications
- do not show fast upload area if access on folder is insufficient
- do not send notification mail 'submitted review/approval' to owner of
document, still send it to uploader of version
- set default language in login form if language selector is turned off
- do not show full list of notifiers to none admins
- do not list document/folders in sequence selector if its number exceeds 50
- typeahead menu works with arrow keys again, wait 1 sec. before searching
- fix sending notification mails when document/folder was moved
- tasks and clipboard can be shown in main menu, even if there are no tasks
due or the clipboard is empty
- add conversion service from text/html to text/plain
--------------------------------------------------------------------------------
Changes in version 5.1.33
--------------------------------------------------------------------------------
- use SeedDMS_Core_File::mimetype() to determine mime type when uploading a
file with drag&drop
- user images may be 300px height, do not scale them up
- do not show unreadable documents on Dashboard
- fix creating preview image of file in drop folder if it contains
special chars
- show fileѕ and folders in dropfolder dir recursively
- fix 'Add to clipboard' menu item in bootstrap theme
- open confirm box when changing category in batch operation
- hidden users can excluded when setting list of reviewers/approvers
- extension can add a legacy access right
- check if document version may be removed
- set max file size for indexing when updating a document
- nicer output on info page
- do not show filter for categories in database search if they don't exist
- show logs of finished worflows
- show preview of documents on clipboard
- minor improvements in restapi
- update layout of tab for attachments
- remove session when calling logout of restapi
- fix some potential security issues
--------------------------------------------------------------------------------
Changes in version 5.1.32
--------------------------------------------------------------------------------
- check error msg after calling AddDocument and UpdateDocument controller
in webdav module
- more verbose log messages
- show errors of none activated extensions and do not allow to activate it,
if there are errors (e.g. missing php extensions)
- move Approve/ReviewDocument in a controller
- setting password by user can be disabled
- groups from ldap can be synced with seeddms groups
- fix error when sending notification to group of reviewers
- seperate some notification messages for folders and documents
- fix various drag&drop operations
- do not sent notifidation mail to uploader if owner has received on already
--------------------------------------------------------------------------------
Changes in version 5.1.31
--------------------------------------------------------------------------------

View File

@ -74,6 +74,13 @@ module.exports = function (grunt) {
],
dest: bootstrapDir + '/select2/js',
flatten: true
},{
expand: true,
src: [
nodeDir + '/select2/dist/js/i18n/*'
],
dest: bootstrapDir + '/select2/js/i18n',
flatten: true
},{
expand: true,
src: [
@ -206,6 +213,14 @@ module.exports = function (grunt) {
],
dest: bootstrapDir + '/spectrum-colorpicker2',
flatten: true
},{
expand: true,
src: [
nodeDir + '/jquery-lazy/jquery.lazy.min.js',
nodeDir + '/jquery-lazy/jquery.lazy.plugins.js'
],
dest: bootstrapDir + '/jquery-lazy',
flatten: true
},{
expand: true,
src: [

View File

@ -24,7 +24,7 @@ dist:
rm -rf tmp
quickstart:
vendor/bin/phing -Dversion=$(VERSION) package
php7.4 vendor/bin/phing -Dversion=$(VERSION) package
unittest:
vendor/bin/phing -Dversion=$(VERSION) phpunitfast

View File

@ -1,780 +0,0 @@
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

@ -1,108 +0,0 @@
<?php
/**
* Implementation of a document in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @license GPL2
* @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli,
* Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
* 2010 Matteo Lucarelli, 2010-2012 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* @uses SeedDMS_DatabaseAccess
*/
define('USE_PDO', 1);
if(defined('USE_PDO'))
require_once('Core/inc.DBAccessPDO.php');
else
require_once('Core/inc.DBAccess.php');
/**
* @uses SeedDMS_DMS
*/
require_once('Core/inc.ClassDMS.php');
/**
* @uses SeedDMS_Decorator
*/
require_once('Core/inc.ClassDecorator.php');
/**
* @uses SeedDMS_Object
*/
require_once('Core/inc.ClassObject.php');
/**
* @uses SeedDMS_Folder
*/
require_once('Core/inc.ClassFolder.php');
/**
* @uses SeedDMS_Document
*/
require_once('Core/inc.ClassDocument.php');
/**
* @uses SeedDMS_Attribute
*/
require_once('Core/inc.ClassAttribute.php');
/**
* @uses SeedDMS_Group
*/
require_once('Core/inc.ClassGroup.php');
/**
* @uses SeedDMS_User
*/
require_once('Core/inc.ClassUser.php');
/**
* @uses SeedDMS_KeywordCategory
*/
require_once('Core/inc.ClassKeywords.php');
/**
* @uses SeedDMS_DocumentCategory
*/
require_once('Core/inc.ClassDocumentCategory.php');
/**
* @uses SeedDMS_Notification
*/
require_once('Core/inc.ClassNotification.php');
/**
* @uses SeedDMS_UserAccess
* @uses SeedDMS_GroupAccess
*/
require_once('Core/inc.ClassAccess.php');
/**
* @uses SeedDMS_Workflow
*/
require_once('Core/inc.ClassWorkflow.php');
/**
*/
require_once('Core/inc.AccessUtils.php');
/**
* @uses SeedDMS_File
*/
require_once('Core/inc.FileUtils.php');
/**
* @uses SeedDMS_Transmittal
*/
require_once('Core/inc.ClassTransmittal.php');
/**
* @uses SeedDMS_File
*/
require_once('Core/inc.ClassIterator.php');
?>

View File

@ -1,94 +0,0 @@
<?php
/**
* Some definitions for access control
*
* @category DMS
* @package SeedDMS_Core
* @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@
*/
/**
* Used to indicate that a search should return all
* results in the ACL table. See {@link SeedDMS_Core_Folder::getAccessList()}
*/
define("M_ANY", -1);
/**
* No rights at all
*/
define("M_NONE", 1);
/**
* Read access only
*/
define("M_READ", 2);
/**
* Read and write access only
*/
define("M_READWRITE", 3);
/**
* Unrestricted access
*/
define("M_ALL", 4);
/*
* Lowest and highest access right
*/
define("M_LOWEST_RIGHT", 1);
define("M_HIGHEST_RIGHT", 4);
define ("O_GTEQ", ">=");
define ("O_LTEQ", "<=");
define ("O_EQ", "=");
/**
* Folder notification
*/
define("T_FOLDER", 1); //TargetType = Folder
/**
* Document notification
*/
define("T_DOCUMENT", 2); // " = Document
/**
* Notify on all actions on the folder/document
*/
define("N_ALL", 0);
/**
* Notify when object has been deleted
*/
define("N_DELETE", 1);
/**
* Notify when object has been moved
*/
define("N_MOVE", 2);
/**
* Notify when object has been updated (no new version)
*/
define("N_UPDATE", 3);
/**
* Notify when document has new version
*/
define("N_NEW_VERSION", 4);
/**
* Notify when version of document was deleted
*/
define("N_DELETE_VERSION", 5);
/**
* Notify when version of document was deleted
*/
define("N_ADD_DOCUMENT", 6);

View File

@ -1,122 +0,0 @@
<?php
/**
* Implementation of user and group access object
*
* @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 user access right.
* This class cannot be used to modify access rights.
*
* @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_UserAccess { /* {{{ */
/**
* @var SeedDMS_Core_User
*/
var $_user;
/**
* @var
*/
var $_mode;
/**
* SeedDMS_Core_UserAccess constructor.
* @param $user
* @param $mode
*/
function __construct($user, $mode) {
$this->_user = $user;
$this->_mode = $mode;
}
/**
* @return int
*/
function getUserID() { return $this->_user->getID(); }
/**
* @return mixed
*/
function getMode() { return $this->_mode; }
/**
* @return bool
*/
function isAdmin() {
return ($this->_mode == SeedDMS_Core_User::role_admin);
}
/**
* @return SeedDMS_Core_User
*/
function getUser() {
return $this->_user;
}
} /* }}} */
/**
* Class to represent a group access right.
* This class cannot be used to modify access rights.
*
* @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_GroupAccess { /* {{{ */
/**
* @var SeedDMS_Core_Group
*/
var $_group;
/**
* @var
*/
var $_mode;
/**
* SeedDMS_Core_GroupAccess constructor.
* @param $group
* @param $mode
*/
function __construct($group, $mode) {
$this->_group = $group;
$this->_mode = $mode;
}
/**
* @return int
*/
function getGroupID() { return $this->_group->getID(); }
/**
* @return mixed
*/
function getMode() { return $this->_mode; }
/**
* @return SeedDMS_Core_Group
*/
function getGroup() {
return $this->_group;
}
} /* }}} */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +0,0 @@
<?php
/**
* Implementation of the decorator pattern
*
* @category DMS
* @package SeedDMS_Core
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which implements a simple decorator pattern
*
* @category DMS
* @package SeedDMS_Core
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_Decorator {
protected $o;
public function __construct($object) {
$this->o = $object;
}
public function __call($method, $args)
{
if (!method_exists($this->o, $method)) {
throw new Exception("Undefined method $method attempt.");
}
/* In case the called method returns the object itself, then return this object */
$result = call_user_func_array(array($this->o, $method), $args);
return $result === $this->o ? $this : $result;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,124 +0,0 @@
<?php
/**
* Implementation of document categories in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class to represent a document category in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C)2011 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_DocumentCategory {
/**
* @var integer $_id id of document category
* @access protected
*/
protected $_id;
/**
* @var string $_name name of category
* @access protected
*/
protected $_name;
/**
* @var object $_dms reference to dms this category belongs to
* @access protected
*/
protected $_dms;
function __construct($id, $name) { /* {{{ */
$this->_id = $id;
$this->_name = $name;
$this->_dms = null;
} /* }}} */
function setDMS($dms) { /* {{{ */
$this->_dms = $dms;
} /* }}} */
function getID() { return $this->_id; }
function getName() { return $this->_name; }
function setName($newName) { /* {{{ */
$newName = trim($newName);
if(!$newName)
return false;
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblCategory` SET `name` = ".$db->qstr($newName)." WHERE `id` = ". $this->_id;
if (!$db->getResult($queryStr))
return false;
$this->_name = $newName;
return true;
} /* }}} */
function isUsed() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT * FROM `tblDocumentCategory` WHERE `categoryID`=".$this->_id;
$resArr = $db->getResultArray($queryStr);
if (is_array($resArr) && count($resArr) == 0)
return false;
return true;
} /* }}} */
function remove() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "DELETE FROM `tblCategory` WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr))
return false;
return true;
} /* }}} */
function getDocumentsByCategory($limit=0, $offset=0) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT * FROM `tblDocumentCategory` where `categoryID`=".$this->_id;
if($limit && is_numeric($limit))
$queryStr .= " LIMIT ".(int) $limit;
if($offset && is_numeric($offset))
$queryStr .= " OFFSET ".(int) $offset;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr)
return false;
$documents = array();
foreach ($resArr as $row) {
if($doc = $this->_dms->getDocument($row["documentID"]))
array_push($documents, $doc);
}
return $documents;
} /* }}} */
function countDocumentsByCategory() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT COUNT(*) as `c` FROM `tblDocumentCategory` where `categoryID`=".$this->_id;
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr)
return false;
return $resArr[0]['c'];
} /* }}} */
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -1,630 +0,0 @@
<?php
/**
* Implementation of the group 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 user group 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_Group { /* {{{ */
/**
* The id of the user group
*
* @var integer
*/
protected $_id;
/**
* The name of the user group
*
* @var string
*/
protected $_name;
/**
* @var SeedDMS_Core_User[]
*/
protected $_users;
/**
* The comment of the user group
*
* @var string
*/
protected $_comment;
/**
* Back reference to DMS this user group belongs to
*
* @var SeedDMS_Core_DMS
*/
protected $_dms;
function __construct($id, $name, $comment) { /* {{{ */
$this->_id = $id;
$this->_name = $name;
$this->_comment = $comment;
$this->_dms = null;
} /* }}} */
/**
* Return an instance of a group object
*
* @param string|integer $id Id, name of group, depending
* on the 3rd parameter.
* @param SeedDMS_Core_DMS $dms instance of dms
* @param string $by search by group name if set to 'name'.
* Search by Id of group if left empty.
* @return SeedDMS_Core_Group|bool instance of class SeedDMS_Core_Group if group was
* found, null if group was not found, false in case of error
*/
public static function getInstance($id, $dms, $by='') { /* {{{ */
$db = $dms->getDB();
switch($by) {
case 'name':
$queryStr = "SELECT * FROM `tblGroups` WHERE `name` = ".$db->qstr($id);
break;
default:
$queryStr = "SELECT * FROM `tblGroups` WHERE `id` = " . (int) $id;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
else if (count($resArr) != 1) //wenn, dann wohl eher 0 als > 1 ;-)
return null;
$resArr = $resArr[0];
$group = new self($resArr["id"], $resArr["name"], $resArr["comment"]);
$group->setDMS($dms);
return $group;
} /* }}} */
/**
* @param $orderby
* @param SeedDMS_Core_DMS $dms
* @return array|bool
*/
public static function getAllInstances($orderby, $dms) { /* {{{ */
$db = $dms->getDB();
switch($orderby) {
default:
$queryStr = "SELECT * FROM `tblGroups` ORDER BY `name`";
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$groups = array();
for ($i = 0; $i < count($resArr); $i++) {
$group = new self($resArr[$i]["id"], $resArr[$i]["name"], $resArr[$i]["comment"]);
$group->setDMS($dms);
$groups[$i] = $group;
}
return $groups;
} /* }}} */
/**
* Check if this object is of type 'group'.
*
* @param string $type type of object
*/
public function isType($type) { /* {{{ */
return $type == 'group';
} /* }}} */
/**
* @param SeedDMS_Core_DMS $dms
*/
function setDMS($dms) { /* {{{ */
$this->_dms = $dms;
} /* }}} */
/**
* @return SeedDMS_Core_DMS $dms
*/
function getDMS() {
return $this->_dms;
}
/**
* @return int
*/
function getID() { return $this->_id; }
/**
* @return string
*/
function getName() { return $this->_name; }
/**
* @param $newName
* @return bool
*/
function setName($newName) { /* {{{ */
$newName = trim($newName);
if(!$newName)
return false;
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblGroups` SET `name` = ".$db->qstr($newName)." WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr))
return false;
$this->_name = $newName;
return true;
} /* }}} */
/**
* @return string
*/
function getComment() { return $this->_comment; }
/**
* @param $newComment
* @return bool
*/
function setComment($newComment) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblGroups` SET `comment` = ".$db->qstr($newComment)." WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr))
return false;
$this->_comment = $newComment;
return true;
} /* }}} */
/**
* @return SeedDMS_Core_User[]|bool
*/
function getUsers() { /* {{{ */
$db = $this->_dms->getDB();
if (!isset($this->_users)) {
$queryStr = "SELECT `tblUsers`.* FROM `tblUsers` ".
"LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`userID`=`tblUsers`.`id` ".
"WHERE `tblGroupMembers`.`groupID` = '". $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) {
/** @var SeedDMS_Core_User $user */
$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;
} /* }}} */
/**
* @return SeedDMS_Core_User[]|bool
*/
function getManagers() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT `tblUsers`.* FROM `tblUsers` ".
"LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`userID`=`tblUsers`.`id` ".
"WHERE `tblGroupMembers`.`groupID` = '". $this->_id ."' AND `tblGroupMembers`.`manager` = 1";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$managers = array();
$classnamerole = $this->_dms->getClassname('role');
$classname = $this->_dms->getClassname('user');
foreach ($resArr as $row) {
/** @var SeedDMS_Core_User $user */
$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);
}
return $managers;
} /* }}} */
/**
* @param SeedDMS_Core_User $user
* @param bool $asManager
* @return bool
*/
function addUser($user,$asManager=false) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "INSERT INTO `tblGroupMembers` (`groupID`, `userID`, `manager`) VALUES (".$this->_id.", ".$user->getID(). ", " . ($asManager?"1":"0") ." )";
$res = $db->getResult($queryStr);
if (!$res) return false;
unset($this->_users);
return true;
} /* }}} */
/**
* @param SeedDMS_Core_User $user
* @return bool
*/
function removeUser($user) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "DELETE FROM `tblGroupMembers` WHERE `groupID` = ".$this->_id." AND `userID` = ".$user->getID();
$res = $db->getResult($queryStr);
if (!$res) return false;
unset($this->_users);
return true;
} /* }}} */
/**
* Check if user is member of group
*
* @param SeedDMS_Core_User $user user to be checked
* @param boolean $asManager also check whether user is manager of group if
* set to true, otherwise does not care about manager status
* @return boolean true if user is member, otherwise false
*/
function isMember($user, $asManager=false) { /* {{{ */
if (isset($this->_users)&&!$asManager) {
foreach ($this->_users as $usr)
if ($usr->getID() == $user->getID())
return true;
return false;
}
$db = $this->_dms->getDB();
$queryStr = "SELECT * FROM `tblGroupMembers` WHERE `groupID` = " . $this->_id . " AND `userID` = " . $user->getID();
if ($asManager)
$queryStr .= " AND `manager` = 1";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false) return false;
if (count($resArr) != 1) return false;
return true;
} /* }}} */
/**
* Toggle manager status of user
*
* @param SeedDMS_Core_User $user
* @return boolean true if operation was successful, otherwise false
*/
function toggleManager($user) { /* {{{ */
$db = $this->_dms->getDB();
if (!$this->isMember($user)) return false;
if ($this->isMember($user,true)) $queryStr = "UPDATE `tblGroupMembers` SET `manager` = 0 WHERE `groupID` = ".$this->_id." AND `userID` = ".$user->getID();
else $queryStr = "UPDATE `tblGroupMembers` SET `manager` = 1 WHERE `groupID` = ".$this->_id." AND `userID` = ".$user->getID();
if (!$db->getResult($queryStr)) return false;
return true;
} /* }}} */
/**
* Delete user group
* This function deletes the user group and all it references, like access
* control lists, notifications, as a child of other groups, etc.
*
* @param SeedDMS_Core_User $user the user doing the removal (needed for entry in
* review log.
* @return boolean true on success or false in case of an error
*/
function remove($user) { /* {{{ */
$db = $this->_dms->getDB();
$db->startTransaction();
$queryStr = "DELETE FROM `tblGroupMembers` WHERE `groupID` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$queryStr = "DELETE FROM `tblACLs` WHERE `groupID` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$queryStr = "DELETE FROM `tblNotify` WHERE `groupID` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$queryStr = "DELETE FROM `tblMandatoryReviewers` WHERE `reviewerGroupID` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$queryStr = "DELETE FROM `tblMandatoryApprovers` WHERE `approverGroupID` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$queryStr = "DELETE FROM `tblWorkflowTransitionGroups` WHERE `groupid` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$queryStr = "DELETE FROM `tblGroups` WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
// TODO : update document status if reviewer/approver has been deleted
$reviewStatus = $this->getReviewStatus();
foreach ($reviewStatus as $r) {
$queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ".
"VALUES ('". $r["reviewID"] ."', '-2', 'Review group removed from process', ".$db->getCurrentDatetime().", '". $user->getID() ."')";
$res=$db->getResult($queryStr);
if(!$res) {
$db->rollbackTransaction();
return false;
}
}
$approvalStatus = $this->getApprovalStatus();
foreach ($approvalStatus as $a) {
$queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ".
"VALUES ('". $a["approveID"] ."', '-2', 'Approval group removed from process', ".$db->getCurrentDatetime().", '". $user->getID() ."')";
$res=$db->getResult($queryStr);
if(!$res) {
$db->rollbackTransaction();
return false;
}
}
$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;
} /* }}} */
function getReviewStatus($documentID=null, $version=null) { /* {{{ */
$db = $this->_dms->getDB();
if (!$db->createTemporaryTable("ttreviewid")) {
return false;
}
$status = array();
// See if the group is assigned as a reviewer.
$queryStr = "SELECT `tblDocumentReviewers`.*, `tblDocumentReviewLog`.`status`, ".
"`tblDocumentReviewLog`.`comment`, `tblDocumentReviewLog`.`date`, ".
"`tblDocumentReviewLog`.`userID` ".
"FROM `tblDocumentReviewers` ".
"LEFT JOIN `tblDocumentReviewLog` USING (`reviewID`) ".
"LEFT JOIN `ttreviewid` on `ttreviewid`.`maxLogID` = `tblDocumentReviewLog`.`reviewLogID` ".
"WHERE `ttreviewid`.`maxLogID`=`tblDocumentReviewLog`.`reviewLogID` ".
($documentID==null ? "" : "AND `tblDocumentReviewers`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentReviewers`.`version` = '". (int) $version ."' ").
"AND `tblDocumentReviewers`.`type`='1' ".
"AND `tblDocumentReviewers`.`required`='". $this->_id ."' ".
"ORDER BY `tblDocumentReviewLog`.`reviewLogID` DESC";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
if (count($resArr)>0) {
foreach ($resArr as $res)
$status[] = $res;
}
return $status;
} /* }}} */
function getApprovalStatus($documentID=null, $version=null) { /* {{{ */
$db = $this->_dms->getDB();
if (!$db->createTemporaryTable("ttapproveid")) {
return false;
}
$status = array();
// See if the group is assigned as an approver.
$queryStr = "SELECT `tblDocumentApprovers`.*, `tblDocumentApproveLog`.`status`, ".
"`tblDocumentApproveLog`.`comment`, `tblDocumentApproveLog`.`date`, ".
"`tblDocumentApproveLog`.`userID` ".
"FROM `tblDocumentApprovers` ".
"LEFT JOIN `tblDocumentApproveLog` USING (`approveID`) ".
"LEFT JOIN `ttapproveid` on `ttapproveid`.`maxLogID` = `tblDocumentApproveLog`.`approveLogID` ".
"WHERE `ttapproveid`.`maxLogID`=`tblDocumentApproveLog`.`approveLogID` ".
($documentID==null ? "" : "AND `tblDocumentApprovers`.`documentID` = '". (int) $documentID ."' ").
($version==null ? "" : "AND `tblDocumentApprovers`.`version` = '". (int) $version ."' ").
"AND `tblDocumentApprovers`.`type`='1' ".
"AND `tblDocumentApprovers`.`required`='". $this->_id ."' ";
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
if (count($resArr)>0) {
foreach ($resArr as $res)
$status[] = $res;
}
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
*
* @param int $documentID optional document id for which to retrieve the
* reviews
* @param int $version optional version of the document
* @return bool|array list of all workflows
*/
function getWorkflowStatus($documentID=null, $version=null) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = 'select distinct d.*, c.`groupid` from `tblWorkflowTransitions` a left join `tblWorkflows` b on a.`workflow`=b.`id` left join `tblWorkflowTransitionGroups` c on a.`id`=c.`transition` left join `tblWorkflowDocumentContent` d on b.`id`=d.`workflow` where d.`document` is not null and a.`state`=d.`state` and c.`groupid`='.$this->_id;
if($documentID) {
$queryStr .= ' AND d.`document`='.(int) $documentID;
if($version)
$queryStr .= ' AND d.`version`='.(int) $version;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$result = array();
if (count($resArr)>0) {
foreach ($resArr as $res) {
$result[] = $res;
}
}
return $result;
} /* }}} */
/**
* Get all notifications of group
*
* @param integer $type type of item (T_DOCUMENT or T_FOLDER)
* @return SeedDMS_Core_Notification[]|bool array of notifications
*/
function getNotifications($type=0) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT `tblNotify`.* FROM `tblNotify` ".
"WHERE `tblNotify`.`groupID` = ". $this->_id;
if($type) {
$queryStr .= " AND `tblNotify`.`targetType` = ". (int) $type;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr)
return false;
$notifications = array();
foreach ($resArr as $row) {
$not = new SeedDMS_Core_Notification($row["target"], $row["targetType"], $row["userID"], $row["groupID"]);
$not->setDMS($this->_dms);
array_push($notifications, $not);
}
return $notifications;
} /* }}} */
} /* }}} */

View File

@ -1,231 +0,0 @@
<?php
namespace SeedDMS\Core;
/**
* Implementation of the document iterartor
*
* @category DMS
* @package SeedDMS_Core
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
class DocumentIterator implements \Iterator {
/**
* @var object folder
*/
protected $_folder;
/**
* @var object dms
*/
protected $_dms;
/**
* @var array documents
*/
protected $_documents;
public function __construct($folder) {
$this->_folder = $folder;
$this->_dms = $folder->getDMS();
$this->_documents = array();
$this->_pointer = 0;
$this->_cache = array();
$this->populate();
}
public function rewind() {
$this->_pointer = 0;
}
public function valid() {
return isset($this->_documents[$this->_pointer]);
}
public function next() {
$this->_pointer++;
}
public function key() {
return $this->_folders[$this->_pointer];
}
public function current() {
if($this->_documents[$this->_pointer]) {
$documentid = $this->_documents[$this->_pointer]['id'];
if(!isset($this->_cache[$documentid])) {
// echo $documentid." not cached<br />";
$this->_cache[$documentid] = $this->_dms->getdocument($documentid);
}
return $this->_cache[$documentid];
}
return null;
}
private function populate($orderby="", $dir="asc", $limit=0, $offset=0) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT `id` FROM `tblDocuments` WHERE `folder` = " . $this->_folder->getID();
if ($orderby && $orderby[0]=="n") $queryStr .= " ORDER BY `name`";
elseif ($orderby && $orderby[0]=="s") $queryStr .= " ORDER BY `sequence`";
elseif ($orderby && $orderby[0]=="d") $queryStr .= " ORDER BY `date`";
if($dir == 'desc')
$queryStr .= " DESC";
if(is_int($limit) && $limit > 0) {
$queryStr .= " LIMIT ".$limit;
if(is_int($offset) && $offset > 0)
$queryStr .= " OFFSET ".$offset;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$this->_documents = $resArr;
} /* }}} */
}
class FolderIterator implements \Iterator { /* {{{ */
/**
* @var object folder
*/
protected $_folder;
/**
* @var object dms
*/
protected $_dms;
/**
* @var array documents
*/
protected $_folders;
public function __construct($folder) { /* {{{ */
$this->_folder = $folder;
$this->_dms = $folder->getDMS();
$this->_folders = array();
$this->_pointer = 0;
$this->_cache = array();
$this->populate();
} /* }}} */
public function rewind() { /* {{{ */
$this->_pointer = 0;
} /* }}} */
public function valid() { /* {{{ */
return isset($this->_folders[$this->_pointer]);
} /* }}} */
public function next() { /* {{{ */
$this->_pointer++;
} /* }}} */
public function key() { /* {{{ */
return $this->_folders[$this->_pointer];
} /* }}} */
public function current() { /* {{{ */
if($this->_folders[$this->_pointer]) {
$folderid = $this->_folders[$this->_pointer]['id'];
if(!isset($this->_cache[$folderid])) {
// echo $folderid." not cached<br />";
$this->_cache[$folderid] = $this->_dms->getFolder($folderid);
}
return $this->_cache[$folderid];
}
return null;
} /* }}} */
private function populate($orderby="", $dir="asc", $limit=0, $offset=0) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT `id` FROM `tblFolders` WHERE `parent` = " . $this->_folder->getID();
if ($orderby && $orderby[0]=="n") $queryStr .= " ORDER BY `name`";
elseif ($orderby && $orderby[0]=="s") $queryStr .= " ORDER BY `sequence`";
elseif ($orderby && $orderby[0]=="d") $queryStr .= " ORDER BY `date`";
if($dir == 'desc')
$queryStr .= " DESC";
if(is_int($limit) && $limit > 0) {
$queryStr .= " LIMIT ".$limit;
if(is_int($offset) && $offset > 0)
$queryStr .= " OFFSET ".$offset;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && $resArr == false)
return false;
$this->_folders = $resArr;
} /* }}} */
} /* }}} */
/**
* The FolderFilterIterator checks if the given user has access on
* the current folder.
* FilterIterator uses an inner iterator passed to the constructor
* to iterate over the sub folders of a folder.
*
$iter = new FolderIterator($folder);
$iter2 = new FolderFilterIterator($iter, $user);
foreach($iter2 as $ff) {
echo $ff->getName()."<br />";
}
*/
class FolderFilterIterator extends \FilterIterator { /* {{{ */
public function __construct(Iterator $iterator , $filter ) {
parent::__construct($iterator);
$this->userFilter = $filter;
}
public function accept() { /* {{{ */
$folder = $this->getInnerIterator()->current();
echo "accept() for ".$folder->getName()."<br />";
return true;
} /* }}} */
} /* }}} */
/**
$iter = new RecursiveFolderIterator($folder);
$iter2 = new RecursiveIteratorIterator($iter, RecursiveIteratorIterator::SELF_FIRST);
foreach($iter2 as $ff) {
echo $ff->getID().': '.$ff->getName()."<br />";
}
*/
class RecursiveFolderIterator extends FolderIterator implements \RecursiveIterator { /* {{{ */
public function hasChildren() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT id FROM `tblFolders` WHERE `parent` = ".(int) $this->current()->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr)
return false;
return true;
} /* }}} */
public function getChildren() { /* {{{ */
return new RecursiveFolderIterator($this->current());
} /* }}} */
} /* }}} */
class RecursiveFolderFilterIterator extends FolderFilterIterator { /* {{{ */
public function hasChildren() { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "SELECT id FROM `tblFolders` WHERE `parent` = ".(int) $this->current()->getID();
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr)
return false;
return true;
} /* }}} */
public function getChildren() { /* {{{ */
return new RecursiveFolderIterator($this->current());
} /* }}} */
} /* }}} */

View File

@ -1,207 +0,0 @@
<?php
/**
* Implementation of keyword categories 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-2023 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class to represent a keyword category 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-2023 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_KeywordCategory {
/**
* @var integer $_id id of keyword category
* @access protected
*/
protected $_id;
/**
* @var integer $_ownerID id of user who is the owner
* @access protected
*/
protected $_ownerID;
/**
* @var string $_name name of category
* @access protected
*/
protected $_name;
/**
* @var SeedDMS_Core_DMS $_dms reference to dms this category belongs to
* @access protected
*/
protected $_dms;
/**
* 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) { /* {{{ */
$this->_dms = $dms;
} /* }}} */
/**
* @return int
*/
function getID() { return $this->_id; }
/**
* @return string
*/
function getName() { return $this->_name; }
/**
* @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;
$db = $this->_dms->getDB();
$queryStr = "UPDATE `tblKeywordCategories` SET `name` = ".$db->qstr($newName)." WHERE `id` = ". $this->_id;
if (!$db->getResult($queryStr))
return false;
$this->_name = $newName;
return true;
} /* }}} */
/**
* @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;
if (!$db->getResult($queryStr))
return false;
$this->_ownerID = $user->getID();
$this->_owner = $user;
return true;
} /* }}} */
/**
* @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);
}
/**
* @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) { /* {{{ */
$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) { /* {{{ */
$db = $this->_dms->getDB();
$queryStr = "DELETE FROM `tblKeywords` WHERE `id` = $listID";
return $db->getResult($queryStr);
} /* }}} */
/**
* @return bool
*/
function remove() { /* {{{ */
$db = $this->_dms->getDB();
$db->startTransaction();
$queryStr = "DELETE FROM `tblKeywords` WHERE `category` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$queryStr = "DELETE FROM `tblKeywordCategories` WHERE `id` = " . $this->_id;
if (!$db->getResult($queryStr)) {
$db->rollbackTransaction();
return false;
}
$db->commitTransaction();
return true;
} /* }}} */
}

View File

@ -1,116 +0,0 @@
<?php
/**
* Implementation of a notification object
*
* @category DMS
* @package SeedDMS_Core
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class to represent a notification
*
* @category DMS
* @package SeedDMS_Core
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_Notification { /* {{{ */
/**
* @var integer id of target (document or folder)
*
* @access protected
*/
protected $_target;
/**
* @var integer document or folder
*
* @access protected
*/
protected $_targettype;
/**
* @var integer id of user to notify
*
* @access protected
*/
protected $_userid;
/**
* @var integer id of group to notify
*
* @access protected
*/
protected $_groupid;
/**
* @var object reference to the dms instance this user belongs to
*
* @access protected
*/
protected $_dms;
/**
* Constructor
*
* @param integer $target id of document/folder this notification is
* attached to.
* @param integer $targettype 1 = target is document, 2 = target is a folder
* @param integer $userid id of user. The id is -1 if the notification is
* for a group.
* @param integer $groupid id of group. The id is -1 if the notification is
* for a user.
*/
function __construct($target, $targettype, $userid, $groupid) { /* {{{ */
$this->_target = $target;
$this->_targettype = $targettype;
$this->_userid = $userid;
$this->_groupid = $groupid;
} /* }}} */
/**
* Set instance of dms this object belongs to
*
* @param object $dms instance of dms
*/
function setDMS($dms) { /* {{{ */
$this->_dms = $dms;
} /* }}} */
/**
* Get id of target (document/object) this notification is attachted to
*
* @return integer id of target
*/
function getTarget() { return $this->_target; }
/**
* Get type of target
*
* @return integer type of target (1=document/2=object)
*/
function getTargetType() { return $this->_targettype; }
/**
* Get user for this notification
*
* @return integer id of user or -1 if this notification does not belong
* to a user
*/
function getUser() { return $this->_dms->getUser($this->_userid); }
/**
* Get group for this notification
*
* @return integer id of group or -1 if this notification does not belong
* to a group
*/
function getGroup() { return $this->_dms->getGroup($this->_groupid); }
} /* }}} */
?>

View File

@ -1,331 +0,0 @@
<?php
/**
* Implementation of an generic object in the document management system
*
* @category DMS
* @package SeedDMS_Core
* @license GPL2
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2012 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class to represent a generic object in the document management system
*
* This is the base class for generic objects in SeedDMS.
*
* @category DMS
* @package SeedDMS_Core
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2012 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_Object { /* {{{ */
/**
* @var integer unique id of object
*/
protected $_id;
/**
* @var array list of attributes
*/
protected $_attributes;
/**
* @var SeedDMS_Core_DMS back reference to document management system
*/
public $_dms;
/**
* SeedDMS_Core_Object constructor.
* @param $id
*/
function __construct($id) { /* {{{ */
$this->_id = $id;
$this->_dms = null;
} /* }}} */
/**
* Check if this object is of a given type.
*
* This method must be implemened in the child class
*
* @param string $type type of object
*/
public function isType($type) {return false;}
/**
* Set dms this object belongs to.
*
* Each object needs a reference to the dms it belongs to. It will be
* set when the object is created.
* The dms has a references to the currently logged in user
* and the database connection.
*
* @param SeedDMS_Core_DMS $dms reference to dms
*/
public function setDMS($dms) { /* {{{ */
$this->_dms = $dms;
} /* }}} */
public function getDMS() { /* {{{ */
return $this->_dms;
} /* }}} */
/**
* Return the internal id of the document
*
* @return integer id of document
*/
public function getID() { return $this->_id; }
/**
* Returns all attributes set for the object
*
* @return array|bool
*/
public function getAttributes() { /* {{{ */
if (!$this->_attributes) {
$db = $this->_dms->getDB();
switch(get_class($this)) {
case $this->_dms->getClassname('document'):
$queryStr = "SELECT a.* FROM `tblDocumentAttributes` a LEFT JOIN `tblAttributeDefinitions` b ON a.`attrdef`=b.`id` WHERE a.`document` = " . $this->_id." ORDER BY b.`name`";
break;
case $this->_dms->getClassname('documentcontent'):
$queryStr = "SELECT a.* FROM `tblDocumentContentAttributes` a LEFT JOIN `tblAttributeDefinitions` b ON a.`attrdef`=b.`id` WHERE a.`content` = " . $this->_id." ORDER BY b.`name`";
break;
case $this->_dms->getClassname('folder'):
$queryStr = "SELECT a.* FROM `tblFolderAttributes` a LEFT JOIN `tblAttributeDefinitions` b ON a.`attrdef`=b.`id` WHERE a.`folder` = " . $this->_id." ORDER BY b.`name`";
break;
default:
return false;
}
$resArr = $db->getResultArray($queryStr);
if (is_bool($resArr) && !$resArr) return false;
$this->_attributes = array();
foreach ($resArr as $row) {
$attrdef = $this->_dms->getAttributeDefinition($row['attrdef']);
$attr = new SeedDMS_Core_Attribute($row["id"], $this, $attrdef, $row["value"]);
$attr->setDMS($this->_dms);
$this->_attributes[$attrdef->getId()] = $attr;
}
}
return $this->_attributes;
} /* }}} */
/**
* Returns an attribute of the object for the given attribute definition
*
* @param SeedDMS_Core_AttributeDefinition $attrdef
* @return array|string value of attritbute or false. The value is an array
* if the attribute is defined as multi value
*/
public function getAttribute($attrdef) { /* {{{ */
if (!$this->_attributes) {
$this->getAttributes();
}
if (isset($this->_attributes[$attrdef->getId()])) {
return $this->_attributes[$attrdef->getId()];
} else {
return false;
}
} /* }}} */
/**
* Returns an attribute value of the object for the given attribute definition
*
* @param SeedDMS_Core_AttributeDefinition $attrdef
* @return array|string value of attritbute or false. The value is an array
* if the attribute is defined as multi value
*/
public function getAttributeValue($attrdef) { /* {{{ */
if (!$this->_attributes) {
$this->getAttributes();
}
if (isset($this->_attributes[$attrdef->getId()])) {
$value = $this->_attributes[$attrdef->getId()]->getValue();
if($attrdef->getMultipleValues()) {
$sep = substr($value, 0, 1);
$vsep = $attrdef->getValueSetSeparator();
/* If the value doesn't start with the separator used in the value set,
* then assume that the value was not saved with a leading separator.
* This can happen, if the value was previously a single value from
* the value set and later turned into a multi value attribute.
*/
if($sep == $vsep)
return(explode($sep, substr($value, 1)));
else
return(array($value));
} else {
return $this->_attributes[$attrdef->getId()]->getParsedValue();
}
} else
return false;
} /* }}} */
/**
* Returns an attribute value of the object for the given attribute definition
*
* This is a short cut for getAttribute($attrdef)->getValueAsArray() but
* first checks if the object has an attribute for the given attribute
* definition.
*
* @param SeedDMS_Core_AttributeDefinition $attrdef
* @return array|bool
* even if the attribute is not defined as multi value
*/
public function getAttributeValueAsArray($attrdef) { /* {{{ */
if (!$this->_attributes) {
$this->getAttributes();
}
if (isset($this->_attributes[$attrdef->getId()])) {
return $this->_attributes[$attrdef->getId()]->getValueAsArray();
} else
return false;
} /* }}} */
/**
* Returns an attribute value of the object for the given attribute definition
*
* This is a short cut for getAttribute($attrdef)->getValueAsString() but
* first checks if the object has an attribute for the given attribute
* definition.
*
* @param SeedDMS_Core_AttributeDefinition $attrdef
* @return string value of attritbute or false. The value is always a string
* even if the attribute is defined as multi value
*/
public function getAttributeValueAsString($attrdef) { /* {{{ */
if (!$this->_attributes) {
$this->getAttributes();
}
if (isset($this->_attributes[$attrdef->getId()])) {
return $this->_attributes[$attrdef->getId()]->getValue();
} else
return false;
} /* }}} */
/**
* Set an attribute of the object for the given attribute definition
*
* @param SeedDMS_Core_AttributeDefinition $attrdef definition of attribute
* @param array|string $value value of attribute, for multiple values this
* must be an array
* @return boolean true if operation was successful, otherwise false
*/
public function setAttributeValue($attrdef, $value) { /* {{{ */
$db = $this->_dms->getDB();
if (!$this->_attributes) {
$this->getAttributes();
}
switch($attrdef->getType()) {
case SeedDMS_Core_AttributeDefinition::type_boolean:
$value = ($value === true || $value != '' || $value == 1) ? 1 : 0;
break;
}
if($attrdef->getMultipleValues() && is_array($value)) {
if(in_array($attrdef->getType(), array(SeedDMS_Core_AttributeDefinition::type_user, SeedDMS_Core_AttributeDefinition::type_group)))
$sep = ',';
else
$sep = substr($attrdef->getValueSet(), 0, 1);
$value = $sep.implode($sep, $value);
}
/* Handle the case if an attribute is not set already */
if(!isset($this->_attributes[$attrdef->getId()])) {
switch(get_class($this)) {
case $this->_dms->getClassname('document'):
$tablename = 'tblDocumentAttributes';
$queryStr = "INSERT INTO `tblDocumentAttributes` (`document`, `attrdef`, `value`) VALUES (".$this->_id.", ".$attrdef->getId().", ".$db->qstr($value).")";
break;
case $this->_dms->getClassname('documentcontent'):
$tablename = 'tblDocumentContentAttributes';
$queryStr = "INSERT INTO `tblDocumentContentAttributes` (`content`, `attrdef`, `value`) VALUES (".$this->_id.", ".$attrdef->getId().", ".$db->qstr($value).")";
break;
case $this->_dms->getClassname('folder'):
$tablename = 'tblFolderAttributes';
$queryStr = "INSERT INTO `tblFolderAttributes` (`folder`, `attrdef`, `value`) VALUES (".$this->_id.", ".$attrdef->getId().", ".$db->qstr($value).")";
break;
default:
return false;
}
$res = $db->getResult($queryStr);
if (!$res)
return false;
$attr = new SeedDMS_Core_Attribute($db->getInsertID($tablename), $this, $attrdef, $value);
$attr->setDMS($this->_dms);
$this->_attributes[$attrdef->getId()] = $attr;
/* Check if 'onPostAddAttribute' callback is set */
if(isset($this->_dms->callbacks['onPostAddAttribute'])) {
foreach($this->_dms->callbacks['onPostAddAttribute'] as $callback) {
if(!call_user_func($callback[0], $callback[1], $this, $attrdef, $value)) {
}
}
}
return true;
}
/* The attribute already exists. setValue() will either update or delete it. */
$this->_attributes[$attrdef->getId()]->setValue($value);
return true;
} /* }}} */
/**
* Remove an attribute of the object for the given attribute definition
* @param SeedDMS_Core_AttributeDefinition $attrdef
* @return boolean true if operation was successful, otherwise false
*/
public function removeAttribute($attrdef) { /* {{{ */
$db = $this->_dms->getDB();
if (!$this->_attributes) {
$this->getAttributes();
}
if(isset($this->_attributes[$attrdef->getId()])) {
$oldvalue = $this->_attributes[$attrdef->getId()]->getValue();
switch(get_class($this)) {
case $this->_dms->getClassname('document'):
$queryStr = "DELETE FROM `tblDocumentAttributes` WHERE `document`=".$this->_id." AND `attrdef`=".$attrdef->getId();
break;
case $this->_dms->getClassname('documentcontent'):
$queryStr = "DELETE FROM `tblDocumentContentAttributes` WHERE `content`=".$this->_id." AND `attrdef`=".$attrdef->getId();
break;
case $this->_dms->getClassname('folder'):
$queryStr = "DELETE FROM `tblFolderAttributes` WHERE `folder`=".$this->_id." AND `attrdef`=".$attrdef->getId();
break;
default:
return false;
}
$res = $db->getResult($queryStr);
if (!$res)
return false;
/* Check if 'onPostRemoveAttribute' callback is set */
if(isset($this->_dms->callbacks['onPostRemoveAttribute'])) {
foreach($this->_dms->callbacks['onPostRemoveAttribute'] as $callback) {
if(!call_user_func($callback[0], $callback[1], $this, $attrdef, $oldvalue)) {
}
}
}
unset($this->_attributes[$attrdef->getId()]);
}
return true;
} /* }}} */
} /* }}} */

View File

@ -1,398 +0,0 @@
<?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();
} /* }}} */
}
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,529 +0,0 @@
<?php
/**
* Implementation of various file system operations
*
* @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 Matteo Lucarelli,
* 2010-2022 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class to file operation in the document management system
* Use the methods of this class only for files below the content
* directory but not for temporäry files, cache files or log files.
*
* @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 Matteo Lucarelli,
* 2010-2022 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Core_File {
/**
* @param $old
* @param $new
* @return bool
*/
static function renameFile($old, $new) { /* {{{ */
return @rename($old, $new);
} /* }}} */
/**
* @param $file
* @return bool
*/
static function removeFile($file) { /* {{{ */
return @unlink($file);
} /* }}} */
/**
* @param $source
* @param $target
* @return bool
*/
static function copyFile($source, $target) { /* {{{ */
return @copy($source, $target);
} /* }}} */
/**
* @param $source
* @param $target
* @return bool
*/
static function moveFile($source, $target) { /* {{{ */
/** @noinspection PhpUndefinedFunctionInspection */
if (!self::copyFile($source, $target))
return false;
/** @noinspection PhpUndefinedFunctionInspection */
return self::removeFile($source);
} /* }}} */
/**
* @param $file
* @return bool|int
*/
static function fileSize($file) { /* {{{ */
if(!$a = @fopen($file, 'r'))
return false;
fseek($a, 0, SEEK_END);
$filesize = ftell($a);
fclose($a);
return $filesize;
} /* }}} */
/**
* Return the mimetype of a given file
*
* This method uses finfo to determine the mimetype
* but will correct some mimetypes which are
* not propperly determined or could be more specific, e.g. text/plain
* when it is actually text/markdown. In thoses cases
* the file extension will be taken into account.
*
* @param string $filename name of file on disc
* @return string mimetype
*/
static function mimetype($filename) { /* {{{ */
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimetype = finfo_file($finfo, $filename);
switch($mimetype) {
case 'application/octet-stream':
case 'text/plain':
$lastDotIndex = strrpos($filename, ".");
if($lastDotIndex === false) $fileType = ".";
else $fileType = substr($filename, $lastDotIndex);
if($fileType == '.md')
$mimetype = 'text/markdown';
break;
}
return $mimetype;
} /* }}} */
/**
* @param integer $size
* @param array $sizes list of units for 10^0, 10^3, 10^6, ..., 10^(n*3) bytes
* @return string
*/
static function format_filesize($size, $sizes = array('Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')) { /* {{{ */
if ($size == 0) return('0 Bytes');
if ($size == 1) return('1 Byte');
/** @noinspection PhpIllegalArrayKeyTypeInspection */
return (round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $sizes[$i]);
} /* }}} */
/**
* Parses a string like '[0-9]+ *[BKMGT]*' into an integer
* B,K,M,G,T stand for byte, kilo byte, mega byte, giga byte, tera byte
* If the last character is omitted, bytes are assumed.
*
* @param $str
* @return bool|int
*/
static function parse_filesize($str) { /* {{{ */
if(!preg_match('/^([0-9]+) *([BKMGT]*)$/', trim($str), $matches))
return false;
$value = $matches[1];
$unit = $matches[2] ? $matches[2] : 'B';
switch($unit) {
case 'T':
return $value * 1024 * 1024 * 1024 *1024;
break;
case 'G':
return $value * 1024 * 1024 * 1024;
break;
case 'M':
return $value * 1024 * 1024;
break;
case 'K':
return $value * 1024;
break;
default;
return (int) $value;
break;
}
/** @noinspection PhpUnreachableStatementInspection */
return false;
} /* }}} */
/**
* @param $file
* @return string
*/
static function file_exists($file) { /* {{{ */
return file_exists($file);
} /* }}} */
/**
* @param $file
* @return string
*/
static function checksum($file) { /* {{{ */
return md5_file($file);
} /* }}} */
/**
* @param $string mimetype
* @return string file extension with the dot or an empty string
*/
static function fileExtension($mimetype) { /* {{{ */
switch($mimetype) {
case "application/pdf":
case "image/png":
case "image/gif":
case "image/jpg":
$expect = substr($mimetype, -3, 3);
break;
default:
$mime_map = [
'video/3gpp2' => '3g2',
'video/3gp' => '3gp',
'video/3gpp' => '3gp',
'application/x-compressed' => '7zip',
'audio/x-acc' => 'aac',
'audio/ac3' => 'ac3',
'application/postscript' => 'ai',
'audio/x-aiff' => 'aif',
'audio/aiff' => 'aif',
'audio/x-au' => 'au',
'video/x-msvideo' => 'avi',
'video/msvideo' => 'avi',
'video/avi' => 'avi',
'application/x-troff-msvideo' => 'avi',
'application/macbinary' => 'bin',
'application/mac-binary' => 'bin',
'application/x-binary' => 'bin',
'application/x-macbinary' => 'bin',
'image/bmp' => 'bmp',
'image/x-bmp' => 'bmp',
'image/x-bitmap' => 'bmp',
'image/x-xbitmap' => 'bmp',
'image/x-win-bitmap' => 'bmp',
'image/x-windows-bmp' => 'bmp',
'image/ms-bmp' => 'bmp',
'image/x-ms-bmp' => 'bmp',
'application/bmp' => 'bmp',
'application/x-bmp' => 'bmp',
'application/x-win-bitmap' => 'bmp',
'application/cdr' => 'cdr',
'application/coreldraw' => 'cdr',
'application/x-cdr' => 'cdr',
'application/x-coreldraw' => 'cdr',
'image/cdr' => 'cdr',
'image/x-cdr' => 'cdr',
'zz-application/zz-winassoc-cdr' => 'cdr',
'application/mac-compactpro' => 'cpt',
'application/pkix-crl' => 'crl',
'application/pkcs-crl' => 'crl',
'application/x-x509-ca-cert' => 'crt',
'application/pkix-cert' => 'crt',
'text/css' => 'css',
'text/x-comma-separated-values' => 'csv',
'text/comma-separated-values' => 'csv',
'application/vnd.msexcel' => 'csv',
'application/x-director' => 'dcr',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
'application/x-dvi' => 'dvi',
'message/rfc822' => 'eml',
'application/x-msdownload' => 'exe',
'video/x-f4v' => 'f4v',
'audio/x-flac' => 'flac',
'video/x-flv' => 'flv',
'image/gif' => 'gif',
'application/gpg-keys' => 'gpg',
'application/x-gtar' => 'gtar',
'application/x-gzip' => 'gzip',
'application/mac-binhex40' => 'hqx',
'application/mac-binhex' => 'hqx',
'application/x-binhex40' => 'hqx',
'application/x-mac-binhex40' => 'hqx',
'text/html' => 'html',
'image/x-icon' => 'ico',
'image/x-ico' => 'ico',
'image/vnd.microsoft.icon' => 'ico',
'text/calendar' => 'ics',
'application/java-archive' => 'jar',
'application/x-java-application' => 'jar',
'application/x-jar' => 'jar',
'image/jp2' => 'jp2',
'video/mj2' => 'jp2',
'image/jpx' => 'jp2',
'image/jpm' => 'jp2',
'image/jpeg' => 'jpeg',
'image/pjpeg' => 'jpeg',
'application/x-javascript' => 'js',
'application/json' => 'json',
'text/json' => 'json',
'application/vnd.google-earth.kml+xml' => 'kml',
'application/vnd.google-earth.kmz' => 'kmz',
'text/x-log' => 'log',
'audio/x-m4a' => 'm4a',
'application/vnd.mpegurl' => 'm4u',
'text/markdown' => 'md',
'audio/midi' => 'mid',
'application/vnd.mif' => 'mif',
'video/quicktime' => 'mov',
'video/x-sgi-movie' => 'movie',
'audio/mpeg' => 'mp3',
'audio/mpg' => 'mp3',
'audio/mpeg3' => 'mp3',
'audio/mp3' => 'mp3',
'video/mp4' => 'mp4',
'video/mpeg' => 'mpeg',
'application/oda' => 'oda',
'audio/ogg' => 'ogg',
'video/ogg' => 'ogg',
'application/ogg' => 'ogg',
'application/x-pkcs10' => 'p10',
'application/pkcs10' => 'p10',
'application/x-pkcs12' => 'p12',
'application/x-pkcs7-signature' => 'p7a',
'application/pkcs7-mime' => 'p7c',
'application/x-pkcs7-mime' => 'p7c',
'application/x-pkcs7-certreqresp' => 'p7r',
'application/pkcs7-signature' => 'p7s',
'application/pdf' => 'pdf',
'application/octet-stream' => 'pdf',
'application/x-x509-user-cert' => 'pem',
'application/x-pem-file' => 'pem',
'application/pgp' => 'pgp',
'application/x-httpd-php' => 'php',
'application/php' => 'php',
'application/x-php' => 'php',
'text/php' => 'php',
'text/x-php' => 'php',
'application/x-httpd-php-source' => 'php',
'image/png' => 'png',
'image/x-png' => 'png',
'application/powerpoint' => 'ppt',
'application/vnd.ms-powerpoint' => 'ppt',
'application/vnd.ms-office' => 'ppt',
'application/msword' => 'doc',
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
'application/x-photoshop' => 'psd',
'image/vnd.adobe.photoshop' => 'psd',
'audio/x-realaudio' => 'ra',
'audio/x-pn-realaudio' => 'ram',
'application/x-rar' => 'rar',
'application/rar' => 'rar',
'application/x-rar-compressed' => 'rar',
'audio/x-pn-realaudio-plugin' => 'rpm',
'application/x-pkcs7' => 'rsa',
'text/rtf' => 'rtf',
'text/richtext' => 'rtx',
'video/vnd.rn-realvideo' => 'rv',
'application/x-stuffit' => 'sit',
'application/smil' => 'smil',
'text/srt' => 'srt',
'image/svg+xml' => 'svg',
'application/x-shockwave-flash' => 'swf',
'application/x-tar' => 'tar',
'application/x-gzip-compressed' => 'tgz',
'image/tiff' => 'tiff',
'text/plain' => 'txt',
'text/x-vcard' => 'vcf',
'application/videolan' => 'vlc',
'text/vtt' => 'vtt',
'audio/x-wav' => 'wav',
'audio/wave' => 'wav',
'audio/wav' => 'wav',
'application/wbxml' => 'wbxml',
'video/webm' => 'webm',
'audio/x-ms-wma' => 'wma',
'application/wmlc' => 'wmlc',
'video/x-ms-wmv' => 'wmv',
'video/x-ms-asf' => 'wmv',
'application/xhtml+xml' => 'xhtml',
'application/excel' => 'xl',
'application/msexcel' => 'xls',
'application/x-msexcel' => 'xls',
'application/x-ms-excel' => 'xls',
'application/x-excel' => 'xls',
'application/x-dos_ms_excel' => 'xls',
'application/xls' => 'xls',
'application/x-xls' => 'xls',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
'application/vnd.ms-excel' => 'xlsx',
'application/xml' => 'xml',
'text/xml' => 'xml',
'text/xsl' => 'xsl',
'application/xspf+xml' => 'xspf',
'application/x-compress' => 'z',
'application/x-zip' => 'zip',
'application/zip' => 'zip',
'application/x-zip-compressed' => 'zip',
'application/s-compressed' => 'zip',
'multipart/x-zip' => 'zip',
'text/x-scriptzsh' => 'zsh',
];
$expect = isset($mime_map[$mimetype]) === true ? $mime_map[$mimetype] : '';
}
return $expect;
} /* }}} */
/**
* @param $old
* @param $new
* @return bool
*/
static function renameDir($old, $new) { /* {{{ */
return @rename($old, $new);
} /* }}} */
/**
* @param $path
* @return bool
*/
static function makeDir($path) { /* {{{ */
if( !is_dir( $path ) ){
$res=@mkdir( $path , 0777, true);
if (!$res) return false;
}
return true;
/* some old code
if (strncmp($path, DIRECTORY_SEPARATOR, 1) == 0) {
$mkfolder = DIRECTORY_SEPARATOR;
}
else {
$mkfolder = "";
}
$path = preg_split( "/[\\\\\/]/" , $path );
for( $i=0 ; isset( $path[$i] ) ; $i++ )
{
if(!strlen(trim($path[$i])))continue;
$mkfolder .= $path[$i];
if( !is_dir( $mkfolder ) ){
$res=@mkdir( "$mkfolder" , 0777);
if (!$res) return false;
}
$mkfolder .= DIRECTORY_SEPARATOR;
}
return true;
// patch from alekseynfor safe_mod or open_basedir
global $settings;
$path = substr_replace ($path, "/", 0, strlen($settings->_contentDir));
$mkfolder = $settings->_contentDir;
$path = preg_split( "/[\\\\\/]/" , $path );
for( $i=0 ; isset( $path[$i] ) ; $i++ )
{
if(!strlen(trim($path[$i])))continue;
$mkfolder .= $path[$i];
if( !is_dir( $mkfolder ) ){
$res= @mkdir( "$mkfolder" , 0777);
if (!$res) return false;
}
$mkfolder .= DIRECTORY_SEPARATOR;
}
return true;
*/
} /* }}} */
/**
* @param $path
* @return bool
*/
static function removeDir($path) { /* {{{ */
$handle = @opendir($path);
while ($entry = @readdir($handle) )
{
if ($entry == ".." || $entry == ".")
continue;
else if (is_dir($path . DIRECTORY_SEPARATOR . $entry))
{
if (!self::removeDir($path . DIRECTORY_SEPARATOR . $entry ))
return false;
}
else
{
if (!@unlink($path . DIRECTORY_SEPARATOR . $entry))
return false;
}
}
@closedir($handle);
return @rmdir($path);
} /* }}} */
/**
* @param $sourcePath
* @param $targetPath
* @return bool
*/
static function copyDir($sourcePath, $targetPath) { /* {{{ */
if (mkdir($targetPath, 0777)) {
$handle = @opendir($sourcePath);
while ($entry = @readdir($handle) ) {
if ($entry == ".." || $entry == ".")
continue;
else if (is_dir($sourcePath . $entry)) {
if (!self::copyDir($sourcePath . DIRECTORY_SEPARATOR . $entry, $targetPath . DIRECTORY_SEPARATOR . $entry))
return false;
} else {
if (!@copy($sourcePath . DIRECTORY_SEPARATOR . $entry, $targetPath . DIRECTORY_SEPARATOR . $entry))
return false;
}
}
@closedir($handle);
}
else
return false;
return true;
} /* }}} */
/**
* @param $sourcePath
* @param $targetPath
* @return bool
*/
static function moveDir($sourcePath, $targetPath) { /* {{{ */
/** @noinspection PhpUndefinedFunctionInspection */
if (!self::copyDir($sourcePath, $targetPath))
return false;
/** @noinspection PhpUndefinedFunctionInspection */
return self::removeDir($sourcePath);
} /* }}} */
// code by Kioob (php.net manual)
/**
* @param $source
* @param bool $level
* @return bool|string
*/
static function gzcompressfile($source, $level=false) { /* {{{ */
$dest=$source.'.gz';
$mode='wb'.$level;
$error=false;
if($fp_out=@gzopen($dest,$mode)) {
if($fp_in=@fopen($source,'rb')) {
while(!feof($fp_in))
@gzwrite($fp_out,fread($fp_in,1024*512));
@fclose($fp_in);
}
else $error=true;
@gzclose($fp_out);
}
else $error=true;
if($error) return false;
else return $dest;
} /* }}} */
}

View File

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

View File

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

View File

@ -1,23 +0,0 @@
{
"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

@ -1,10 +0,0 @@
<?php
$g_config['type'] = 'mysql';
$g_config['hostname'] = 'localhost';
$g_config['user'] = 'letodms';
$g_config['passwd'] = 'letodms';
$g_config['name'] = 'letodms';
$g_config['contentDir'] = '/tmp/content';
$g_config['contentOffsetDir'] = '/tmp/content';
?>

View File

@ -1,25 +0,0 @@
<?php
include("config.php");
include("SeedDMS/SeedDMS_Core.php");
$db = new SeedDMS_Core_DatabaseAccess($g_config['type'], $g_config['hostname'], $g_config['user'], $g_config['passwd'], $g_config['name']);
$db->connect() or die ("Could not connect to db-server \"" . $g_config['hostname'] . "\"");
$dms = new SeedDMS_Core_DMS($db, $g_config['contentDir'], $g_config['contentOffsetDir']);
function tree($folder, $indent='') {
echo $indent."D ".$folder->getName()."\n";
$subfolders = $folder->getSubFolders();
foreach($subfolders as $subfolder) {
tree($subfolder, $indent.' ');
}
$documents = $folder->getDocuments();
foreach($documents as $document) {
echo $indent." ".$document->getName()."\n";
}
}
$folder = $dms->getFolder(1);
tree($folder);
?>

View File

@ -1,14 +0,0 @@
<?php
include("config.php");
include("SeedDMS/SeedDMS_Core.php");
$db = new SeedDMS_Core_DatabaseAccess($g_config['type'], $g_config['hostname'], $g_config['user'], $g_config['passwd'], $g_config['name']);
$db->connect() or die ("Could not connect to db-server \"" . $g_config['hostname'] . "\"");
$dms = new SeedDMS_Core_DMS($db, $g_config['contentDir'], $g_config['contentOffsetDir']);
$users = $dms->getAllUsers();
foreach($users as $user)
echo $user->getId()." ".$user->getLogin()." ".$user->getFullname()."\n";
?>

View File

@ -1,44 +0,0 @@
<?php
include("config.php");
include("SeedDMS/SeedDMS_Core.php");
$db = new SeedDMS_Core_DatabaseAccess($g_config['type'], $g_config['hostname'], $g_config['user'], $g_config['passwd'], $g_config['name']);
$db->connect() or die ("Could not connect to db-server \"" . $g_config['hostname'] . "\"");
$dms = new SeedDMS_Core_DMS($db, $g_config['contentDir'], $g_config['contentOffsetDir']);
$path = '/Test 1/';
echo "Searching for folder or document with path '".$path."'\n";
$root = $dms->getRootFolder();
if($path[0] == '/') {
$path = substr($path, 1);
}
$patharr = explode('/', $path);
/* The last entry is always the document, though if the path ends in '/' the
* document name will be empty.
*/
$docname = array_pop($patharr);
$parentfolder = $root;
foreach($patharr as $pathseg) {
if($folder = $dms->getFolderByName($pathseg, $parentfolder)) {
$parentfolder = $folder;
}
}
if($folder) {
if($docname) {
if($document = $dms->getDocumentByName($docname, $folder)) {
echo "Given path is document '".$document->getName()."'\n";
} else {
echo "No object found\n";
}
} else {
echo "Given path is a folder '".$folder->getName()."'\n";
}
} else {
echo "No object found\n";
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
<?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

@ -1 +0,0 @@
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

@ -1,574 +0,0 @@
<?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

@ -1,155 +0,0 @@
<?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

@ -1,324 +0,0 @@
<?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

@ -1,290 +0,0 @@
<?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

@ -1,295 +0,0 @@
<?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

@ -1,593 +0,0 @@
<?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

@ -1,290 +0,0 @@
<?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

@ -1,125 +0,0 @@
<?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

@ -1,219 +0,0 @@
<?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

@ -1,410 +0,0 @@
<?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

@ -1,147 +0,0 @@
<?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

@ -1,477 +0,0 @@
<?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

@ -1,365 +0,0 @@
<?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

@ -1,638 +0,0 @@
<?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

@ -1,94 +0,0 @@
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

@ -1,44 +0,0 @@
<?php
// SeedDMS. Document Management System
// Copyright (C) 2011-2013 Uwe Steinmann
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
/**
* @uses Zend_Search_Lucene
*/
//require_once('Zend/Search/Lucene.php');
/**
* @uses Zend_Search_Lucene_Analysis_TokenFilter_Stopwords
*/
//require_once("Zend/Search/Lucene/Analysis/TokenFilter/StopWords.php");
/**
* @uses SeedDMS_Lucene_Indexer
*/
require_once('Lucene/Indexer.php');
/**
* @uses SeedDMS_Lucene_Search
*/
require_once('Lucene/Search.php');
/**
* @uses SeedDMS_Lucene_IndexedDocument
*/
require_once('Lucene/IndexedDocument.php');
?>

View File

@ -1,266 +0,0 @@
<?php
/**
* Implementation of an indexed document
*
* @category DMS
* @package SeedDMS_Lucene
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing an indexed document.
*
* @category DMS
* @package SeedDMS_Lucene
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Lucene_IndexedDocument extends Zend_Search_Lucene_Document {
/**
* @var string
*/
protected $errormsg;
/**
* @var string
*/
protected $mimetype;
/**
* @var string
*/
protected $cmd;
/**
* Run a shell command
*
* @param $cmd
* @param int $timeout
* @return array
* @throws Exception
*/
static function execWithTimeout($cmd, $timeout=2) { /* {{{ */
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$pipes = array();
$timeout += time();
// Putting an 'exec' before the command will not fork the command
// and therefore not create any child process. proc_terminate will
// then reliably terminate the cmd and not just shell. See notes of
// https://www.php.net/manual/de/function.proc-terminate.php
$process = proc_open('exec '.$cmd, $descriptorspec, $pipes);
if (!is_resource($process)) {
throw new Exception("proc_open failed on: " . $cmd);
}
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
$output = $error = '';
$timeleft = $timeout - time();
$read = array($pipes[1], $pipes[2]);
$write = NULL;
$exeptions = NULL;
do {
$num_changed_streams = stream_select($read, $write, $exeptions, $timeleft, 200000);
if ($num_changed_streams === false) {
proc_terminate($process);
throw new Exception("stream select failed on: " . $cmd);
} elseif ($num_changed_streams > 0) {
$output .= fread($pipes[1], 8192);
$error .= fread($pipes[2], 8192);
}
$timeleft = $timeout - time();
} while (!feof($pipes[1]) && $timeleft > 0);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
if ($timeleft <= 0) {
proc_terminate($process);
throw new Exception("command timeout on: " . $cmd);
} else {
$return_value = proc_close($process);
return array('stdout'=>$output, 'stderr'=>$error, 'return'=>$return_value);
}
} /* }}} */
/**
* Constructor. Creates our indexable document and adds all
* necessary fields to it using the passed in document
* @param SeedDMS_Core_DMS $dms
* @param SeedDMS_Core_Document|Folder $document
* @param null $convcmd
* @param bool $nocontent
* @param int $timeout
*/
public function __construct($dms, $document, $convcmd=null, $nocontent=false, $timeout=5) { /* {{{ */
$this->errormsg = '';
$this->cmd = '';
$this->mimetype = '';
$this->addField(Zend_Search_Lucene_Field::Text('title', $document->getName(), 'utf-8'));
if($acllist = $document->getReadAccessList(1, 1, 1)) {
$allu = [];
foreach($acllist['users'] as $u)
$allu[] = $u->getLogin();
$this->addField(Zend_Search_Lucene_Field::Text('users', implode(' ', $allu), 'utf-8'));
/*
$allg = [];
foreach($acllist['groups'] as $g)
$allg[] = $g->getName();
$this->addField(Zend_Search_Lucene_Field::Text('groups', implode(' ', $allg), 'utf-8'));
*/
}
if($attributes = $document->getAttributes()) {
foreach($attributes as $attribute) {
$attrdef = $attribute->getAttributeDefinition();
if($attrdef->getValueSet() != '')
$this->addField(Zend_Search_Lucene_Field::Keyword('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue(), 'utf-8'));
else
$this->addField(Zend_Search_Lucene_Field::Text('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue(), 'utf-8'));
}
}
$owner = $document->getOwner();
$this->addField(Zend_Search_Lucene_Field::Text('owner', $owner->getLogin(), 'utf-8'));
if($comment = $document->getComment()) {
$this->addField(Zend_Search_Lucene_Field::Text('comment', $comment, 'utf-8'));
}
$tmp = explode(':', substr($document->getFolderList(), 1, -1));
foreach($tmp as $t)
$this->addField(Zend_Search_Lucene_Field::Keyword('path', $t));
// $this->addField(Zend_Search_Lucene_Field::Keyword('path', str_replace(':', 'x', $document->getFolderList())));
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('indexed', time()));
if($attributes = $version->getAttributes()) {
foreach($attributes as $attribute) {
$attrdef = $attribute->getAttributeDefinition();
if($attrdef->getValueSet() != '')
$this->addField(Zend_Search_Lucene_Field::Keyword('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue(), 'utf-8'));
else
$this->addField(Zend_Search_Lucene_Field::Text('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue(), 'utf-8'));
}
}
}
if($categories = $document->getCategories()) {
$names = array();
foreach($categories as $cat) {
$names[] = $cat->getName();
}
$this->addField(Zend_Search_Lucene_Field::Text('category', implode(' ', $names), 'utf-8'));
}
if($keywords = $document->getKeywords()) {
$this->addField(Zend_Search_Lucene_Field::Text('keywords', $keywords, 'utf-8'));
}
if($version) {
$status = $version->getStatus();
$this->addField(Zend_Search_Lucene_Field::Keyword('status', $status['status'], 'utf-8'));
}
if($version && !$nocontent) {
$path = $dms->contentDir . $version->getPath();
if(file_exists($path)) {
$mimetype = $version->getMimeType();
$this->mimetype = $mimetype;
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) {
self::setContent($content);
} elseif($content === false) {
$this->errormsg = 'Conversion failed';
}
$this->cmd = get_class($service);
} else {
$this->cmd = 'No service to convert '.$mimetype.' to text/plain';
}
} else {
$content = '';
$cmd = '';
$mimeparts = explode('/', $mimetype, 2);
if(isset($convcmd[$mimetype])) {
$cmd = sprintf($convcmd[$mimetype], $path);
} elseif(isset($convcmd[$mimeparts[0].'/*'])) {
$cmd = sprintf($convcmd[$mimetype], $path);
} elseif(isset($convcmd['*'])) {
$cmd = sprintf($convcmd[$mimetype], $path);
}
if($cmd) {
$this->cmd = $cmd;
try {
$content = self::execWithTimeout($cmd, $timeout);
if($content['stdout']) {
self::setContent($content['stdout']);
// $this->addField(Zend_Search_Lucene_Field::UnStored('content', $content['stdout'], 'utf-8'));
}
if($content['stderr']) {
$this->errormsg = $content['stderr'];
}
} catch (Exception $e) {
}
}
}
}
}
} 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()));
}
} /* }}} */
public function getErrorMsg() { /* {{{ */
return $this->errormsg;
} /* }}} */
public function getMimeType() { /* {{{ */
return $this->mimetype;
} /* }}} */
public function setContent($data) { /* {{{ */
$this->addField(Zend_Search_Lucene_Field::UnStored('content', $data, 'utf-8'));
} /* }}} */
public function getCmd() { /* {{{ */
return $this->cmd;
} /* }}} */
/* Use only for setting the command if e.g. an extension takes over the
* conversion to txt (like the office extension which uses the collabora
* conversion service).
*/
public function setCmd($cmd) { /* {{{ */
$this->cmd = $cmd;
} /* }}} */
}
?>

View File

@ -1,208 +0,0 @@
<?php
/**
* Implementation of lucene index
*
* @category DMS
* @package SeedDMS_Lucene
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing a lucene index.
*
* @category DMS
* @package SeedDMS_Lucene
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Lucene_Indexer {
/**
* @var string $indexname name of lucene index
* @access protected
*/
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']);
if($index)
return new self($index);
else
return null;
} catch (Exception $e) {
return null;
}
} /* }}} */
static function create($conf) { /* {{{ */
try {
$index = Zend_Search_Lucene::create($conf['indexdir']);
if($index)
return new self($index);
else
return null;
} catch (Exception $e) {
return null;
}
} /* }}} */
/**
* Do some initialization
*
*/
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();
$stopWordsFilter->loadFromFile($stopWordsFile);
$analyzer->addFilter($stopWordsFilter);
}
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

@ -1,160 +0,0 @@
<?php
/**
* Implementation of search in lucene index
*
* @category DMS
* @package SeedDMS_Lucene
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for searching in a lucene index.
*
* @category DMS
* @package SeedDMS_Lucene
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Lucene_Search {
/**
* @var object $index lucene index
* @access protected
*/
protected $index;
/**
* Create a new instance of the search
*
* @param object $index lucene index
* @return object instance of SeedDMS_Lucene_Search
*/
function __construct($index) { /* {{{ */
$this->index = $index;
$this->version = '@package_version@';
if($this->version[0] == '@')
$this->version = '3.0.0';
} /* }}} */
/**
* Get document from index
*
* @param object $index lucene index
* @return object instance of SeedDMS_Lucene_Document of false
*/
function getDocument($id) { /* {{{ */
$hits = $this->index->find('document_id:D'.$id);
return $hits ? $hits[0] : false;
} /* }}} */
/**
* Get folder from index
*
* @param object $index lucene index
* @return object instance of SeedDMS_Lucene_Document of false
*/
function getFolder($id) { /* {{{ */
$hits = $this->index->find('document_id:F'.$id);
return $hits ? $hits[0] : false;
} /* }}} */
/**
* Search in index
*
* @param object $index lucene index
* @return object instance of SeedDMS_Lucene_Search
*/
function search($term, $fields=array(), $limit=array()) { /* {{{ */
$querystr = '';
$term = trim($term);
if($term) {
$querystr = substr($term, -1) != '*' ? $term.'*' : $term;
}
if(!empty($fields['owner'])) {
if(is_string($fields['owner'])) {
if($querystr)
$querystr .= ' && ';
$querystr .= 'owner:'.$fields['owner'];
} elseif(is_array($fields['owner'])) {
if($querystr)
$querystr .= ' && ';
$querystr .= '(owner:"';
$querystr .= implode('" || owner:"', $fields['owner']);
$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 .= ' && ';
$querystr .= '(category:"';
$querystr .= implode('" && category:"', $fields['category']);
$querystr .= '")';
}
if(!empty($fields['status'])) {
if($querystr)
$querystr .= ' && ';
$querystr .= '(status:"';
$querystr .= implode('" || status:"', $fields['status']);
$querystr .= '")';
}
if(!empty($fields['user'])) {
if($querystr)
$querystr .= ' && ';
$querystr .= '(users:"';
$querystr .= implode('" || users:"', $fields['user']);
$querystr .= '")';
}
if(!empty($fields['rootFolder']) && $fields['rootFolder']->getFolderList()) {
if($querystr)
$querystr .= ' && ';
$querystr .= '(path:"';
$tmp[] = $fields['rootFolder']->getID();
$querystr .= implode('" && path:"', $tmp);
//$querystr .= $fields['rootFolder']->getFolderList().$fields['rootFolder']->getID().':';
$querystr .= '")';
}
if(!empty($fields['startFolder']) && $fields['startFolder']->getFolderList()) {
if($querystr)
$querystr .= ' && ';
$querystr .= '(path:"';
// $querystr .= str_replace(':', 'x', $fields['startFolder']->getFolderList().$fields['startFolder']->getID().':');
$tmp = array();//explode(':', substr($fields['startFolder']->getFolderList(), 1, -1));
$tmp[] = $fields['startFolder']->getID();
$querystr .= implode('" && path:"', $tmp);
// $querystr .= str_replace(':', ' ', $fields['startFolder']->getFolderList().$fields['startFolder']->getID());
$querystr .= '")';
}
try {
$query = Zend_Search_Lucene_Search_QueryParser::parse($querystr);
try {
$hits = $this->index->find($query);
$recs = array();
$c = 0;
foreach($hits as $hit) {
if($c >= $limit['offset'] && ($c-$limit['offset'] < $limit['limit']))
$recs[] = array('id'=>$hit->id, 'document_id'=>$hit->document_id);
$c++;
}
return array('count'=>count($hits), 'hits'=>$recs, 'facets'=>array());
} catch (Zend_Search_Lucene_Exception $e) {
return false;
}
} catch (Zend_Search_Lucene_Search_QueryParserException $e) {
return false;
}
} /* }}} */
}
?>

View File

@ -1,23 +0,0 @@
{
"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

@ -1,389 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.8.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>SeedDMS_Lucene</name>
<channel>pear.php.net</channel>
<summary>Fulltext search for SeedDMS</summary>
<description>SeedDMS is a web based document management system (DMS). This is
the fulltext search engine for it, based on Lucene.</description>
<lead>
<name>Uwe Steinmann</name>
<user>steinm</user>
<email>uwe@steinmann.cx</email>
<active>yes</active>
</lead>
<date>2023-01-09</date>
<time>08:55:43</time>
<version>
<release>1.1.18</release>
<api>1.1.18</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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="/">
<dir name="Lucene">
<file name="Indexer.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="Search.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="IndexedDocument.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
</dir> <!-- /Lucene -->
<dir name="tests">
</dir> <!-- /tests -->
<file name="Lucene.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
</dir> <!-- / -->
</contents>
<dependencies>
<required>
<php>
<min>4.3.0</min>
</php>
<pearinstaller>
<min>1.5.4</min>
</pearinstaller>
</required>
</dependencies>
<phprelease />
<changelog>
<release>
<date>2009-04-27</date>
<version>
<release>0.0.1</release>
<api>0.0.1</api>
</version>
<stability>
<release>alpha</release>
<api>alpha</api>
</stability>
<license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
<notes>
</notes>
</release>
<release>
<date>2011-11-06</date>
<time>08:05:38</time>
<version>
<release>1.0.1</release>
<api>1.0.0</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- New Release
</notes>
</release>
<release>
<date>2012-11-06</date>
<time>08:05:38</time>
<version>
<release>1.1.0</release>
<api>1.1.0</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</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.
</notes>
</release>
<release>
<date>2012-12-03</date>
<time>10:31:23</time>
<version>
<release>1.1.1</release>
<api>1.1.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- catch exception if index is opened but not available
</notes>
</release>
<release>
<date>2013-06-17</date>
<time>10:31:23</time>
<version>
<release>1.1.2</release>
<api>1.1.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- parse query term and catch errors before using it
</notes>
</release>
<release>
<date>2013-06-27</date>
<time>15:12:50</time>
<version>
<release>1.1.3</release>
<api>1.1.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2013-08-13</date>
<time>21:56:55</time>
<version>
<release>1.1.4</release>
<api>1.1.4</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2014-07-30</date>
<time>09:00:34</time>
<version>
<release>1.1.5</release>
<api>1.1.5</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2015-08-05</date>
<time>21:13:13</time>
<version>
<release>1.1.6</release>
<api>1.1.6</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- run external commands with a timeout
</notes>
</release>
<release>
<date>2016-02-01</date>
<time>09:14:07</time>
<version>
<release>1.1.7</release>
<api>1.1.7</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add command for indexing postѕcript files
</notes>
</release>
<release>
<date>2016-03-29</date>
<time>08:11:19</time>
<version>
<release>1.1.8</release>
<api>1.1.7</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2016-04-28</date>
<time>08:11:19</time>
<version>
<release>1.1.9</release>
<api>1.1.7</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2017-03-01</date>
<time>15:55:32</time>
<version>
<release>1.1.10</release>
<api>1.1.10</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- catch exception in execWithTimeout()
</notes>
</release>
<release>
<date>2017-12-04</date>
<time>10:58:13</time>
<version>
<release>1.1.11</release>
<api>1.1.11</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- allow conversion commands for mimetypes with wildcards
</notes>
</release>
<release>
<date>2018-01-30</date>
<time>10:58:13</time>
<version>
<release>1.1.12</release>
<api>1.1.12</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- execWithTimeout() reads data from stderr and saves it into error msg
</notes>
</release>
<release>
<date>2018-04-11</date>
<time>10:58:13</time>
<version>
<release>1.1.13</release>
<api>1.1.13</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- IndexedDocument() remembers cmd and mimetype
</notes>
</release>
<release>
<date>2020-09-02</date>
<time>08:55:43</time>
<version>
<release>1.1.14</release>
<api>1.1.14</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- Index users with at least read access on the document
</notes>
</release>
<release>
<date>2020-09-10</date>
<time>08:55:43</time>
<version>
<release>1.1.15</release>
<api>1.1.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 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()
</notes>
</release>
<release>
<date>2020-12-12</date>
<time>08:55:43</time>
<version>
<release>1.1.16</release>
<api>1.1.16</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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

@ -1,128 +0,0 @@
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

@ -1,39 +0,0 @@
<?php
// SeedDMS. Document Management System
// Copyright (C) 2011-2013 Uwe Steinmann
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
/**
* @uses Preview/Base.php
*/
require_once('Preview/Base.php');
/**
* @uses Preview/Previewer.php
*/
require_once('Preview/Previewer.php');
/**
* @uses Preview/PdfPreviewer.php
*/
require_once('Preview/PdfPreviewer.php');
/**
* @uses Preview/PdfPreviewer.php
*/
require_once('Preview/TxtPreviewer.php');
?>

View File

@ -1,247 +0,0 @@
<?php
/**
* Implementation of preview base
*
* @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 preview images 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_Base {
/**
* @var string $cacheDir location in the file system where all the
* cached data like thumbnails are located. This should be an
* absolute path.
* @access public
*/
public $previewDir;
/**
* @var array $converters list of mimetypes and commands for converting
* file into preview image
* @access protected
*/
protected $converters;
/**
* @var object $conversionmgr
* @access protected
*/
protected $conversionmgr;
/**
* @var integer $timeout maximum time for execution of external commands
* @access protected
*/
protected $timeout;
/**
* @var boolean $xsendfile set to true if mod_xѕendfile is to be used
* @access protected
*/
protected $xsendfile;
/**
* @var string $lastpreviewfile will be set to the file name of the last preview
* @access protected
*/
protected $lastpreviewfile;
function __construct($previewDir, $timeout=5, $xsendfile=true) { /* {{{ */
if(!is_dir($previewDir)) {
if (!SeedDMS_Core_File::makeDir($previewDir)) {
$this->previewDir = '';
} else {
$this->previewDir = $previewDir;
}
} else {
$this->previewDir = $previewDir;
}
$this->timeout = intval($timeout);
$this->converters = array();
$this->xsendfile = $xsendfile;
$this->conversionmgr = null;
} /* }}} */
/**
* Run a shell command
*
* @param $cmd
* @param int $timeout
* @return array
* @throws Exception
*/
static function execWithTimeout($cmd, $timeout=5) { /* {{{ */
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$pipes = array();
$timeout += time();
// Putting an 'exec' before the command will not fork the command
// and therefore not create any child process. proc_terminate will
// then reliably terminate the cmd and not just shell. See notes of
// https://www.php.net/manual/de/function.proc-terminate.php
$process = proc_open('exec '.$cmd, $descriptorspec, $pipes);
if (!is_resource($process)) {
throw new Exception("proc_open failed on: " . $cmd);
}
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
$output = $error = '';
$timeleft = $timeout - time();
$read = array($pipes[1], $pipes[2]);
$write = NULL;
$exeptions = NULL;
do {
$num_changed_streams = stream_select($read, $write, $exeptions, $timeleft, 200000);
if ($num_changed_streams === false) {
proc_terminate($process);
throw new Exception("stream select failed on: " . $cmd);
} elseif ($num_changed_streams > 0) {
$output .= fread($pipes[1], 8192);
$error .= fread($pipes[2], 8192);
}
$timeleft = $timeout - time();
} while (!feof($pipes[1]) && $timeleft > 0);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
if ($timeleft <= 0) {
proc_terminate($process);
throw new Exception("command timeout on: " . $cmd);
} else {
$return_value = proc_close($process);
return array('stdout'=>$output, 'stderr'=>$error, 'return'=>$return_value);
}
} /* }}} */
/**
* Get preview dir
*
* @return string name of preview directory on disc
*/
public function getPreviewDir() { /* {{{ */
return $this->previewDir;
} /* }}} */
/**
* Set a list of converters
*
* Merges the list of passed converters with the already existing ones.
* Existing converters will be overwritten.
*
* @param array list of converters. The key of the array contains the mimetype
* and the value is the command to be called for creating the preview
*/
function setConverters($arr) { /* {{{ */
if(is_array($arr))
$this->converters = $arr;
else
$this->converters = array();
} /* }}} */
/**
* Set conversion service manager
*
* A conversion manager is a service for converting files from one format
* to another format.
*
* @param object $conversionmgr
*/
function setConversionMgr($conversionmgr) { /* {{{ */
$this->conversionmgr = $conversionmgr;
} /* }}} */
/**
* Enable/Disable xsendfile extension
*
* Merges the list of passed converters with the already existing ones.
* Existing converters will be overwritten.
*
* @param boolean $xsendfile turn on/off use of xsendfile module in apache
*/
function setXsendfile($xsendfile) { /* {{{ */
$this->xsendfile = $xsendfile;
} /* }}} */
/**
* Add a list of converters
*
* Merges the list of passed converters with the already existing ones.
* Existing converters will be overwritten.
*
* @param array list of converters. The key of the array contains the mimetype
* and the value is the command to be called for creating the preview
*/
function addConverters($arr) { /* {{{ */
$this->converters = array_merge($this->converters, $arr);
} /* }}} */
/**
* Check if converter for a given mimetype is set
*
* @param string $from from mimetype
* @param string $to to mimetype
*
* @return boolean true if converter exists, otherwise false
*/
function hasConverter($from, $to='') { /* {{{ */
if($this->conversionmgr)
return $this->conversionmgr->hasService($from, $to);
else
return array_key_exists($from, $this->converters) && $this->converters[$from];
} /* }}} */
/**
* Send a file from disk to the browser
*
* This function uses either readfile() or the xѕendfile apache module if
* it is installed.
*
* @param string $filename
*/
protected function sendFile($filename) { /* {{{ */
if($this->xsendfile && function_exists('apache_get_modules') && in_array('mod_xsendfile',apache_get_modules())) {
header("X-Sendfile: ".$filename);
} else {
$size = filesize($filename);
header("Content-Length: " . $size);
/* Make sure output buffering is off */
if (ob_get_level()) {
ob_end_clean();
}
readfile($filename);
}
} /* }}} */
/**
* Return path of last created preview file
*
* @return string
*/
public function getPreviewFile() { /* {{{ */
return $this->lastpreviewfile;
} /* }}} */
}

View File

@ -1,309 +0,0 @@
<?php
/**
* Implementation of pdf 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 pdf 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_PdfPreviewer extends SeedDMS_Preview_Base {
function __construct($previewDir, $timeout=5, $xsendfile=true) { /* {{{ */
parent::__construct($previewDir.DIRECTORY_SEPARATOR.'pdf', $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.'p'.$object->getVersion();
break;
case $dms->getClassname('documentfile'):
$target = $dir.'f'.$object->getID();
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, 'application/pdf');
} /* }}} */
/**
* Create a pdf preview for a given file
*
* This method creates a preview in pdf 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.pdf (if set) or md5($infile).pdf.
* The $mimetype is used to select the propper conversion programm.
* An already existing pdf 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 false;
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.'.pdf';
if($target != '' && (!file_exists($target.'.pdf') || filectime($target.'.pdf') < filectime($infile))) {
if($this->conversionmgr) {
if(!$this->conversionmgr->convert($infile, $mimetype, 'application/pdf', $target.'.pdf')) {
$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.'.pdf', $mimetype), $this->converters[$mimetype]);
} elseif(isset($this->converters[$mimeparts[0].'/*'])) {
$cmd = str_replace(array('%f', '%o', '%m'), array($infile, $target.'.pdf', $mimetype), $this->converters[$mimeparts[0].'/*']);
} 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;
}
}
}
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.'.pdf') && filectime($target.'.pdf') >= filectime($infile)) {
return true;
}
return false;
} /* }}} */
/**
* Check if a preview image 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.'.pdf') && filectime($target.'.pdf') >= $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.'.pdf')) {
$this->sendFile($target.'.pdf');
}
} /* }}} */
/**
* 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.'.pdf')) {
$this->sendFile($target.'.pdf');
}
} /* }}} */
/**
* 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.'.pdf')) {
return(filesize($target.'.pdf'));
} 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.'.pdf')) {
return(unlink($target.'.pdf'));
} 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 images belonging to a document
*
* This function removes the preview images 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

@ -1,371 +0,0 @@
<?php
/**
* Implementation of 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 preview images 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_Previewer extends SeedDMS_Preview_Base {
/**
* @var integer $width maximum width/height of resized image
* @access protected
*/
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.DIRECTORY_SEPARATOR.'png', $timeout, $xsendfile);
$this->converters = array(
);
$this->width = intval($width);
} /* }}} */
/**
* 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=0) { /* {{{ */
if(!$object)
return false;
if($width == 0)
$width = $this->width;
else
$width = intval($width);
$document = $object->getDocument();
$dms = $document->_dms;
$dir = $this->previewDir.DIRECTORY_SEPARATOR.$document->getDir();
switch(get_class($object)) {
case $dms->getClassname('documentcontent'):
$target = $dir.'p'.$object->getVersion().'-'.$width;
break;
case $dms->getClassname('documentfile'):
$target = $dir.'f'.$object->getID().'-'.$width;
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, 'image/png');
} /* }}} */
/**
* Create a preview image for a given file
*
* This method creates a preview image in png 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.png (if set) or md5($infile)-$width.png.
* The $mimetype is used to select the propper conversion programm.
* An already existing preview image 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 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) { /* {{{ */
if(!self::hasConverter($mimetype))
return false;
if($width == 0)
$width = $this->width;
else
$width = intval($width);
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).'-'.$width;
$this->lastpreviewfile = $target.'.png';
if($target != '' && (!file_exists($target.'.png') || filectime($target.'.png') < filectime($infile))) {
if($this->conversionmgr) {
if(!$this->conversionmgr->convert($infile, $mimetype, 'image/png', $target.'.png', array('width'=>$width))) {
$this->lastpreviewfile = '';
return false;
}
$new = true;
} else {
$cmd = '';
$mimeparts = explode('/', $mimetype, 2);
if(isset($this->converters[$mimetype])) {
$cmd = str_replace(array('%w', '%f', '%o', '%m'), array($width, $infile, $target.'.png', $mimetype), $this->converters[$mimetype]);
} elseif(isset($this->converters[$mimeparts[0].'/*'])) {
$cmd = str_replace(array('%w', '%f', '%o', '%m'), array($width, $infile, $target.'.png', $mimetype), $this->converters[$mimeparts[0].'/*']);
} elseif(isset($this->converters['*'])) {
$cmd = str_replace(array('%w', '%f', '%o', '%m'), array($width, $infile, $target.'.png', $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
* @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) { /* {{{ */
if(!$object)
return false;
if($width == 0)
$width = $this->width;
else
$width = intval($width);
$document = $object->getDocument();
$file = $document->_dms->contentDir.$object->getPath();
$target = $this->getFileName($object, $width);
return $this->createRawPreview($file, $document->getDir(), $object->getMimeType(), $width, $target, $new);
} /* }}} */
/**
* 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
* @param integer $width desired width of preview image
* @return boolean true if preview exists, otherwise false
*/
public function hasRawPreview($infile, $dir, $width=0) { /* {{{ */
if($width == 0)
$width = $this->width;
else
$width = intval($width);
if(!$this->previewDir)
return false;
$target = $this->previewDir.$dir.md5($infile).'-'.$width;
if($target !== false && file_exists($target.'.png') && filectime($target.'.png') >= filectime($infile)) {
return true;
}
return false;
} /* }}} */
/**
* Check if a preview image 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
* @param integer $width desired width of preview image
* @return boolean true if preview exists, otherwise false
*/
public function hasPreview($object, $width=0) { /* {{{ */
if(!$object)
return false;
if($width == 0)
$width = $this->width;
else
$width = intval($width);
if(!$this->previewDir)
return false;
$target = $this->getFileName($object, $width);
if($target !== false && file_exists($target.'.png') && filectime($target.'.png') >= $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
* @param integer $width desired width of preview image
* @return boolean/string image content if preview exists, otherwise false
*/
public function getRawPreview($infile, $dir, $width=0) { /* {{{ */
if($width == 0)
$width = $this->width;
else
$width = intval($width);
if(!$this->previewDir)
return false;
$target = $this->previewDir.$dir.md5($infile).'-'.$width;
if($target && file_exists($target.'.png')) {
$this->sendFile($target.'.png');
}
} /* }}} */
/**
* 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
* @param integer $width desired width of preview image
* @return boolean/string image content if preview exists, otherwise false
*/
public function getPreview($object, $width=0) { /* {{{ */
if($width == 0)
$width = $this->width;
else
$width = intval($width);
if(!$this->previewDir)
return false;
$target = $this->getFileName($object, $width);
if($target && file_exists($target.'.png')) {
$this->sendFile($target.'.png');
}
} /* }}} */
/**
* Return file size preview image.
*
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @param integer $width desired width of preview image
* @return boolean/integer size of preview image or false if image
* does not exist
*/
public function getFilesize($object, $width=0) { /* {{{ */
if($width == 0)
$width = $this->width;
else
$width = intval($width);
$target = $this->getFileName($object, $width);
if($target && file_exists($target.'.png')) {
return(filesize($target.'.png'));
} else {
return false;
}
} /* }}} */
/**
* Delete preview image.
*
* @param object $object instance of SeedDMS_Core_DocumentContent
* or SeedDMS_Core_DocumentFile
* @param integer $width desired width of preview image
* @return boolean true if deletion succeded or false if file does not exist
*/
public function deletePreview($object, $width=0) { /* {{{ */
if($width == 0)
$width = $this->width;
else
$width = intval($width);
if(!$this->previewDir)
return false;
$target = $this->getFileName($object, $width);
if($target && file_exists($target.'.png')) {
return(unlink($target.'.png'));
} 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 images belonging to a document
*
* This function removes the preview images 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

@ -1,306 +0,0 @@
<?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

@ -1,23 +0,0 @@
{
"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

@ -1,511 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.8.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>SeedDMS_Preview</name>
<channel>pear.php.net</channel>
<summary>Create thumbnails from document content for SeedDMS</summary>
<description>SeedDMS is a web based document management system (DMS). These
are the classes to create preview images and pdf file from the document content.</description>
<lead>
<name>Uwe Steinmann</name>
<user>steinm</user>
<email>uwe@steinmann.cx</email>
<active>yes</active>
</lead>
<date>2023-01-09</date>
<time>09:49:39</time>
<version>
<release>1.5.0</release>
<api>1.5.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add previewer which creates txt
</notes>
<contents>
<dir baseinstalldir="SeedDMS" name="/">
<dir name="Preview">
<file name="Base.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="Previewer.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<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 -->
<file name="Preview.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
</dir> <!-- / -->
</contents>
<dependencies>
<required>
<php>
<min>7.4.0</min>
</php>
<pearinstaller>
<min>1.5.4</min>
</pearinstaller>
</required>
</dependencies>
<phprelease />
<changelog>
<release>
<date>2012-11-20</date>
<time>08:05:38</time>
<version>
<release>1.0.0</release>
<api>1.0.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- initial version
</notes>
</release>
<release>
<date>2013-04-29</date>
<time>19:34:07</time>
<version>
<release>1.1.0</release>
<api>1.1.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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)
</notes>
</release>
<release>
<date>2014-03-18</date>
<time>16:34:59</time>
<version>
<release>1.1.1</release>
<api>1.1.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add converters for .tar.gz, .ps, .txt
</notes>
</release>
<release>
<date>2014-04-10</date>
<time>20:29:39</time>
<version>
<release>1.1.2</release>
<api>1.1.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- create fixed width image with proportional height
</notes>
</release>
<release>
<date>2015-02-13</date>
<time>20:29:39</time>
<version>
<release>1.1.3</release>
<api>1.1.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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.
</notes>
</release>
<release>
<date>2015-08-08</date>
<time>09:36:57</time>
<version>
<release>1.1.4</release>
<api>1.1.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2016-02-11</date>
<time>09:36:57</time>
<version>
<release>1.1.5</release>
<api>1.1.5</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2016-03-08</date>
<time>09:36:57</time>
<version>
<release>1.1.6</release>
<api>1.1.6</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- check if object passed to createPreview(), hasPreview() is not null
</notes>
</release>
<release>
<date>2016-03-29</date>
<time>08:07:14</time>
<version>
<release>1.1.7</release>
<api>1.1.7</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2016-04-05</date>
<time>15:17:11</time>
<version>
<release>1.1.8</release>
<api>1.1.8</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- pass variables to stream_select (required by php7)
</notes>
</release>
<release>
<date>2016-04-26</date>
<time>15:17:11</time>
<version>
<release>1.1.9</release>
<api>1.1.9</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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()
</notes>
</release>
<release>
<date>2016-11-07</date>
<time>15:17:11</time>
<version>
<release>1.2.0</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add new previewer which converts document to pdf instead of png
</notes>
</release>
<release>
<date>2016-11-15</date>
<time>21:00:26</time>
<version>
<release>1.2.1</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- setConverters() overrides exiting converters
</notes>
</release>
<release>
<date>2017-03-02</date>
<time>07:14:59</time>
<version>
<release>1.2.2</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2017-09-18</date>
<time>07:14:32</time>
<version>
<release>1.2.3</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- createPreview() returns false if running the converter command fails
</notes>
</release>
<release>
<date>2017-10-11</date>
<time>07:14:32</time>
<version>
<release>1.2.4</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- fix typo in converter for tar.gz files
</notes>
</release>
<release>
<date>2017-10-11</date>
<time>07:14:32</time>
<version>
<release>1.2.5</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- SeedDMS_Preview_Base::hasConverter() returns only try if command is set
</notes>
</release>
<release>
<date>2017-12-04</date>
<time>10:59:39</time>
<version>
<release>1.2.6</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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.
</notes>
</release>
<release>
<date>2018-01-18</date>
<time>10:59:39</time>
<version>
<release>1.2.7</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2018-03-08</date>
<time>10:59:39</time>
<version>
<release>1.2.8</release>
<api>1.2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2018-07-13</date>
<time>10:59:39</time>
<version>
<release>1.2.9</release>
<api>1.2.9</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2019-02-11</date>
<time>10:59:39</time>
<version>
<release>1.2.10</release>
<api>1.2.10</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2020-02-17</date>
<time>09:49:39</time>
<version>
<release>1.3.0</release>
<api>1.3.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add new methode getPreviewFile()
</notes>
</release>
<release>
<date>2020-03-21</date>
<time>09:49:39</time>
<version>
<release>1.3.1</release>
<api>1.3.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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()
</notes>
</release>
<release>
<date>2020-12-23</date>
<time>09:49:39</time>
<version>
<release>1.3.2</release>
<api>1.3.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- set header Content-Length
- update package description
</notes>
</release>
<release>
<date>2020-12-23</date>
<time>09:49:39</time>
<version>
<release>1.3.3</release>
<api>1.3.3</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
- createPreview() has optional parameter by referenz to return true if a
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

@ -1,89 +0,0 @@
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

@ -1,49 +0,0 @@
<?php
// SeedDMS. Document Management System
// Copyright (C) 2011-2015 Uwe Steinmann
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
/**
* @uses SeedDMS_SQLiteFTS_Indexer
*/
require_once('SQLiteFTS/Indexer.php');
/**
* @uses SeedDMS_SQLiteFTS_Search
*/
require_once('SQLiteFTS/Search.php');
/**
* @uses SeedDMS_SQLiteFTS_Term
*/
require_once('SQLiteFTS/Term.php');
/**
* @uses SeedDMS_SQLiteFTS_QueryHit
*/
require_once('SQLiteFTS/QueryHit.php');
/**
* @uses SeedDMS_SQLiteFTS_IndexedDocument
*/
require_once('SQLiteFTS/IndexedDocument.php');
/**
* @uses SeedDMS_SQLiteFTS_Exception
*/
require_once('SQLiteFTS/Exception.php');
?>

View File

@ -1,117 +0,0 @@
<?php
/**
* Implementation of a document
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing a document.
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_SQLiteFTS_Document {
/**
* @var integer $id id of document
* @access protected
*/
public $id;
/**
* @var array $fields fields
* @access protected
*/
protected $fields;
public function ___get($key) { /* {{{ */
if(isset($this->fields[$key]))
return $this->fields[$key];
else
return false;
} /* }}} */
public function _addField($key, $value) { /* {{{ */
//if($key == 'document_id') {
if($key == 'docid') {
$this->id = $this->fields[$key] = (int) $value;
} else {
if(isset($this->fields[$key]))
$this->fields[$key] .= ' '.$value;
else
$this->fields[$key] = $value;
}
} /* }}} */
public function addField(SeedDMS_SQLiteFTS_Field $field) { /* {{{ */
$this->fields[$field->name] = $field;
if($field->name == 'docid') {
$this->id = $field->value;
}
return $this;
} /* }}} */
/**
* Return an array with the names of the fields in this document.
*
* @return array
*/
public function getFieldNames() {
return array_keys($this->fields);
}
public function _getFieldValue($key) { /* {{{ */
if(isset($this->fields[$key]))
return $this->fields[$key];
else
return false;
} /* }}} */
/**
* Proxy method for getFieldValue(), provides more convenient access to
* the string value of a field.
*
* @param string $name
* @return string
*/
public function __get($name) {
return $this->getFieldValue($name);
}
/**
* Returns Zend_Search_Lucene_Field object for a named field in this document.
*
* @param string $fieldName
* @return Zend_Search_Lucene_Field
*/
public function getField($fieldName) {
if (!array_key_exists($fieldName, $this->fields)) {
require_once 'SeedDMS/SQLiteFTS/Exception.php';
throw new SeedDMS_SQLiteFTS_Exception("Field name \"$fieldName\" not found in document.");
}
return $this->fields[$fieldName];
}
/**
* Returns the string value of a named field in this document.
*
* @see __get()
* @return string
*/
public function getFieldValue($fieldName) {
return $this->getField($fieldName)->value;
}
}
?>

View File

@ -1,41 +0,0 @@
<?php
/**
* SeedDMS_SQLiteFTS
*
* @category SeedDMS
* @package SeedDMS
* @copyright Copyright (c) 2021 uwe@steinmann.cx
* @license http://framework.zend.com/license/new-bsd New BSD License
* @version $Id$
*/
/**
* @category SeedDMS
* @package SeedDMS
* @copyright Copyright (c) 2021 uwe@steinmann.cx
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class SeedDMS_SQLiteFTS_Exception extends Exception
{
/**
* Construct the exception
*
* @param string $msg
* @param int $code
* @param Exception $previous
* @return void
*/
public function __construct($msg = '', $code = 0, Exception $previous = null) {
parent::__construct($msg, (int) $code, $previous);
}
/**
* String representation of the exception
*
* @return string
*/
public function __toString() {
return parent::__toString();
}
}

View File

@ -1,88 +0,0 @@
<?php
/**
* Implementation of a field
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing a field.
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_SQLiteFTS_Field {
/**
* Field name
*
* @var string
*/
public $name;
/**
* Field value
*
* @var boolean
*/
public $value;
/**
* Object constructor
*
* @param string $name
* @param string $value
*/
public function __construct($name, $value) {
$this->name = $name;
$this->value = $value;
}
/**
* Constructs a String-valued Field that is not tokenized, but is indexed
* and stored. Useful for non-text fields, e.g. date or url.
*
* @param string $name
* @param string $value
* @return SeedDMS_SQLiteFTS_Field
*/
public static function keyword($name, $value) {
return new self($name, $value);
}
/**
* Constructs a String-valued Field that is tokenized and indexed,
* and is stored in the index, for return with hits. Useful for short text
* fields, like "title" or "subject". Term vector will not be stored for this field.
*
* @param string $name
* @param string $value
* @return SeedDMS_SQLiteFTS_Field
*/
public static function text($name, $value) {
return new self($name, $value);
}
/**
* Constructs a String-valued Field that is tokenized and indexed,
* but that is not stored in the index.
*
* @param string $name
* @param string $value
* @return SeedDMS_SQLiteFTS_Field
*/
public static function unStored($name, $value) {
return new self($name, $value);
}
}

View File

@ -1,266 +0,0 @@
<?php
/**
* Implementation of an indexed document
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* @uses SeedDMS_SQLiteFTS_Document
*/
require_once('Document.php');
require_once('Field.php');
/**
* Class for managing an indexed document.
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_SQLiteFTS_IndexedDocument extends SeedDMS_SQLiteFTS_Document {
/**
* @var string
*/
protected $errormsg;
/**
* @var string
*/
protected $mimetype;
/**
* @var string
*/
protected $cmd;
/**
* Run a shell command
*
* @param $cmd
* @param int $timeout
* @return array
* @throws Exception
*/
static function execWithTimeout($cmd, $timeout=2) { /* {{{ */
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$pipes = array();
$timeout += time();
// Putting an 'exec' before the command will not fork the command
// and therefore not create any child process. proc_terminate will
// then reliably terminate the cmd and not just shell. See notes of
// https://www.php.net/manual/de/function.proc-terminate.php
$process = proc_open('exec '.$cmd, $descriptorspec, $pipes);
if (!is_resource($process)) {
throw new Exception("proc_open failed on: " . $cmd);
}
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
$output = $error = '';
$timeleft = $timeout - time();
$read = array($pipes[1], $pipes[2]);
$write = NULL;
$exeptions = NULL;
do {
$num_changed_streams = stream_select($read, $write, $exeptions, $timeleft, 200000);
if ($num_changed_streams === false) {
proc_terminate($process);
throw new Exception("stream select failed on: " . $cmd);
} elseif ($num_changed_streams > 0) {
$output .= fread($pipes[1], 8192);
$error .= fread($pipes[2], 8192);
}
$timeleft = $timeout - time();
} while (!feof($pipes[1]) && $timeleft > 0);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
if ($timeleft <= 0) {
proc_terminate($process);
throw new Exception("command timeout on: " . $cmd);
} else {
$return_value = proc_close($process);
return array('stdout'=>$output, 'stderr'=>$error, 'return'=>$return_value);
}
} /* }}} */
/**
* Constructor. Creates our indexable document and adds all
* necessary fields to it using the passed in document
*
* $convcmd can either be an array of conversion commands or
* an object of class SeedDMS_ConversionMgr
*/
public function __construct($dms, $document, $convcmd=null, $nocontent=false, $timeout=5) { /* {{{ */
$this->errormsg = '';
$this->cmd = '';
$this->mimetype = '';
$this->addField(SeedDMS_SQLiteFTS_Field::Text('title', $document->getName()));
if($acllist = $document->getReadAccessList(1, 1, 1)) {
$allu = [];
foreach($acllist['users'] as $u)
$allu[] = $u->getLogin();
$this->addField(SeedDMS_SQLiteFTS_Field::Text('users', implode(' ', $allu)));
/*
$allg = [];
foreach($acllist['groups'] as $g)
$allg[] = $g->getName();
$this->addField(SeedDMS_SQLiteFTS_Field::Text('groups', implode(' ', $allg)));
*/
}
if($attributes = $document->getAttributes()) {
foreach($attributes as $attribute) {
$attrdef = $attribute->getAttributeDefinition();
if($attrdef->getValueSet() != '')
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue()));
else
$this->addField(SeedDMS_SQLiteFTS_Field::Text('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue()));
}
}
$owner = $document->getOwner();
$this->addField(SeedDMS_SQLiteFTS_Field::Text('owner', $owner->getLogin()));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('path', str_replace(':', 'x', $document->getFolderList())));
if($comment = $document->getComment()) {
$this->addField(SeedDMS_SQLiteFTS_Field::Text('comment', $comment));
}
if($document->isType('document')) {
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('document_id', 'D'.$document->getID()));
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('record_type', 'document'));
$version = $document->getLatestContent();
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('indexed', time(), 'unindexed'));
if($attributes = $version->getAttributes()) {
foreach($attributes as $attribute) {
$attrdef = $attribute->getAttributeDefinition();
if($attrdef->getValueSet() != '')
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue()));
else
$this->addField(SeedDMS_SQLiteFTS_Field::Text('attr_'.str_replace(' ', '_', $attrdef->getName()), $attribute->getValue()));
}
}
}
if($categories = $document->getCategories()) {
$names = array();
foreach($categories as $cat) {
$names[] = $cat->getName();
}
$this->addField(SeedDMS_SQLiteFTS_Field::Text('category', implode('#', $names)));
}
if($keywords = $document->getKeywords()) {
$this->addField(SeedDMS_SQLiteFTS_Field::Text('keywords', $keywords));
}
if($version) {
$status = $version->getStatus();
$this->addField(SeedDMS_SQLiteFTS_Field::Keyword('status', $status['status']+10));
}
if($version && !$nocontent) {
$path = $dms->contentDir . $version->getPath();
if(file_exists($path)) {
$mimetype = $version->getMimeType();
$this->mimetype = $mimetype;
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) {
self::setContent($content);
} elseif($content === false) {
$this->errormsg = 'Conversion failed';
}
$this->cmd = get_class($service);
} else {
$this->cmd = 'No service to convert '.$mimetype.' to text/plain';
}
} else {
$content = '';
$cmd = '';
$mimeparts = explode('/', $mimetype, 2);
if(isset($convcmd[$mimetype])) {
$cmd = sprintf($convcmd[$mimetype], $path);
} elseif(isset($convcmd[$mimeparts[0].'/*'])) {
$cmd = sprintf($convcmd[$mimetype], $path);
} elseif(isset($convcmd['*'])) {
$cmd = sprintf($convcmd[$mimetype], $path);
}
if($cmd) {
$this->cmd = $cmd;
try {
$content = self::execWithTimeout($cmd, $timeout);
if($content['stdout']) {
self::setContent($content['stdout']);
// $this->addField(SeedDMS_SQLiteFTS_Field::UnStored('content', $content['stdout']));
}
if($content['stderr']) {
$this->errormsg = $content['stderr'];
}
} catch (Exception $e) {
}
}
}
}
}
} elseif($document->isType('folder')) {
$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'));
}
} /* }}} */
public function getErrorMsg() { /* {{{ */
return $this->errormsg;
} /* }}} */
public function getMimeType() { /* {{{ */
return $this->mimetype;
} /* }}} */
public function setContent($data) { /* {{{ */
$this->addField(SeedDMS_SQLiteFTS_Field::Text('content', $data));
} /* }}} */
public function getCmd() { /* {{{ */
return $this->cmd;
} /* }}} */
/* Use only for setting the command if e.g. an extension takes over the
* conversion to txt (like the office extension which uses the collabora
* conversion service).
*/
public function setCmd($cmd) { /* {{{ */
$this->cmd = $cmd;
} /* }}} */
}
?>

View File

@ -1,463 +0,0 @@
<?php
/**
* Implementation of SQLiteFTS index
*
* @category DMS
* @package SeedDMS_Lucene
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing a SQLiteFTS index.
*
* @category DMS
* @package SeedDMS_Lucene
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_SQLiteFTS_Indexer {
/**
* @var string $_ftstype
* @access protected
*/
protected $_ftstype;
/**
* @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
*
*/
function __construct($indexerDir) { /* {{{ */
$this->_conn = new PDO('sqlite:'.$indexerDir.'/index.db');
$this->_ftstype = self::ftstype;
if($this->_ftstype == 'fts5')
$this->_rawid = 'rowid';
else
$this->_rawid = 'docid';
$this->_stop_words = [];
} /* }}} */
/**
* Open an existing index
*
* @param string $indexerDir directory on disk containing the index
*/
static function open($conf) { /* {{{ */
if(file_exists($conf['indexdir'].'/index.db')) {
return new SeedDMS_SQLiteFTS_Indexer($conf['indexdir']);
} else
return static::create($conf);
} /* }}} */
/**
* Create a new index
*
* @param array $conf $conf['indexdir'] is the directory on disk containing the index
*/
static function create($conf) { /* {{{ */
if(file_exists($conf['indexdir'].'/index.db'))
unlink($conf['indexdir'].'/index.db');
$index = new SeedDMS_SQLiteFTS_Indexer($conf['indexdir']);
/* Make sure the sequence of fields is identical to the field list
* in SeedDMS_SQLiteFTS_Term
*/
$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, 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, indexed, users, status, path, matchinfo=fts3)';
$res = $index->_conn->exec($sql);
if($res === false) {
return null;
}
$sql = 'CREATE VIRTUAL TABLE docs_terms USING fts4aux(docs);';
$res = $index->_conn->exec($sql);
if($res === false) {
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, indexed unindexed, users, status, path)';
$res = $index->_conn->exec($sql);
if($res === false) {
return null;
}
$sql = 'CREATE VIRTUAL TABLE docs_terms USING fts5vocab(docs, \'col\');';
$res = $index->_conn->exec($sql);
if($res === false) {
return null;
}
} else
return null;
return($index);
} /* }}} */
/**
* Do some initialization
*
*/
public function init($stopWordsFile='') { /* {{{ */
if($stopWordsFile)
$this->_stop_words = array_flip(preg_split("/[\s,]+/", file_get_contents($stopWordsFile)));
} /* }}} */
/**
* Add document to index
*
* @param object $doc indexed document of class
* SeedDMS_SQLiteFTS_IndexedDocument
* @return boolean false in case of an error, otherwise true
*/
function addDocument($doc) { /* {{{ */
if(!$this->_conn)
return false;
foreach(array('comment', 'keywords', 'category', 'content', 'mimetype', 'origfilename', 'status', 'created', 'indexed') as $kk) {
try {
${$kk} = $doc->getFieldValue($kk);
} catch (Exception $e) {
${$kk} = '';
}
}
$sql = "DELETE FROM docs WHERE documentid=".$this->_conn->quote($doc->getFieldValue('document_id'));
$res = $this->_conn->exec($sql);
if($res === false) {
return false;
}
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;
var_dump($this->_conn->errorInfo());
}
return $res;
} /* }}} */
/**
* 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->_conn)
return false;
$sql = "DELETE FROM docs WHERE ".$this->_rawid."=".(int) $id;
$res = $this->_conn->exec($sql);
return $res;
} /* }}} */
/**
* Check if document was deleted
*
* Just for compatibility with lucene.
*
* @return boolean always false
*/
public function isDeleted($id) { /* {{{ */
return false;
} /* }}} */
/**
* Find documents in index
*
* @param string $query
* @param array $limit array with elements 'limit' and 'offset'
* @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, $filter='', $limit=array(), $order=array()) { /* {{{ */
if(!$this->_conn)
return false;
/* First count some records for facets */
foreach(array('owner', 'mimetype', 'category', 'status') as $facetname) {
$sql = "SELECT `".$facetname."`, 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 `".$facetname."`");
if(!$res)
throw new SeedDMS_SQLiteFTS_Exception("Counting records in facet \"$facetname\" failed.");
// return false;
$facets[$facetname] = array();
foreach($res as $row) {
if($row[$facetname] && $row['c']) {
if($facetname == 'category') {
$tmp = explode('#', $row[$facetname]);
if(count($tmp) > 1) {
foreach($tmp as $t) {
if(!isset($facets[$facetname][$t]))
$facets[$facetname][$t] = $row['c'];
else
$facets[$facetname][$t] += $row['c'];
}
} else {
if(!isset($facets[$facetname][$row[$facetname]]))
$facets[$facetname][$row[$facetname]] = $row['c'];
else
$facets[$facetname][$row[$facetname]] += $row['c'];
}
} elseif($facetname == 'status') {
$facets[$facetname][($row[$facetname]-10).''] = $row['c'];
} else
$facets[$facetname][$row[$facetname]] = $row['c'];
}
}
}
$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.");
// return false;
$facets['record_type'] = array('document'=>0, 'folder'=>0);
foreach($res as $row) {
$facets['record_type'][$row['record_type']] = $row['c'];
}
$total = $facets['record_type']['document'] + $facets['record_type']['folder'];
$sql = "SELECT ".$this->_rawid.", documentid FROM docs";
if($query)
$sql .= " WHERE docs MATCH ".$this->_conn->quote($query);
if($filter) {
if($query)
$sql .= " AND ".$filter;
else
$sql .= " WHERE ".$filter;
}
if($this->_ftstype == 'fts5') {
//$sql .= " ORDER BY rank";
// 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']))
$sql .= " OFFSET ".(int) $limit['offset'];
$res = $this->_conn->query($sql);
if(!$res)
throw new SeedDMS_SQLiteFTS_Exception("Searching for documents failed.");
$hits = array();
if($res) {
foreach($res as $rec) {
$hit = new SeedDMS_SQLiteFTS_QueryHit($this);
$hit->id = $rec[$this->_rawid];
$hit->documentid = $rec['documentid'];
$hits[] = $hit;
}
}
return array('count'=>$total, 'hits'=>$hits, 'facets'=>$facets);
} /* }}} */
/**
* 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->_conn)
return false;
$sql = "SELECT ".$this->_rawid.", documentid FROM docs WHERE documentid=".$this->_conn->quote($id);
$res = $this->_conn->query($sql);
$hits = array();
if($res) {
while($rec = $res->fetch(PDO::FETCH_ASSOC)) {
$hit = new SeedDMS_SQLiteFTS_QueryHit($this);
$hit->id = $rec[$this->_rawid];
$hit->documentid = $rec['documentid'];
$hits[] = $hit;
}
}
return $hits;
} /* }}} */
/**
* 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->_conn)
return false;
$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) {
if(!($rec = $res->fetch(PDO::FETCH_ASSOC)))
return false;
$doc = new SeedDMS_SQLiteFTS_Document();
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('docid', $rec[$this->_rawid]));
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('document_id', $rec['documentid']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Text('title', $rec['title']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Text('comment', $rec['comment']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Text('keywords', $rec['keywords']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Text('category', $rec['category']));
$doc->addField(SeedDMS_SQLiteFTS_Field::Keyword('mimetype', $rec['mimetype']));
$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', explode('x', substr($rec['path'], 1, -1))));
if($content)
$doc->addField(SeedDMS_SQLiteFTS_Field::UnStored('content', $rec['content']));
}
return $doc;
} /* }}} */
/**
* Return list of terms in index
*
* @return array list of SeedDMS_SQLiteFTS_Term
*/
public function terms($prefix='', $col='') { /* {{{ */
if(!$this->_conn)
return false;
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) {
while($rec = $res->fetch(PDO::FETCH_ASSOC)) {
$term = new SeedDMS_SQLiteFTS_Term($rec['term'], $rec['col'], $rec['occurrences']);
$terms[] = $term;
}
}
return $terms;
} /* }}} */
/**
* Return number of documents in index
*
* @return interger number of documents
*/
public function count() { /* {{{ */
$sql = "SELECT count(*) c FROM docs";
$res = $this->_conn->query($sql);
if($res) {
$rec = $res->fetch(PDO::FETCH_ASSOC);
return $rec['c'];
}
return 0;
} /* }}} */
/**
* Commit changes
*
* This function does nothing!
*/
function commit() { /* {{{ */
} /* }}} */
/**
* Optimize index
*
* This function does nothing!
*/
function optimize() { /* {{{ */
} /* }}} */
}
?>

View File

@ -1,72 +0,0 @@
<?php
/**
* Implementation of a query hit
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing a query hit.
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_SQLiteFTS_QueryHit {
/**
* @var SeedDMS_SQliteFTS_Indexer $index
* @access protected
*/
protected $_index;
/**
* @var SeedDMS_SQliteFTS_Document $document
* @access protected
*/
protected $_document;
/**
* @var integer $id id of index document
* @access public
*/
public $id;
/**
* @var integer $id id of real document
* @access public
*/
public $documentid;
/**
*
*/
public function __construct(SeedDMS_SQLiteFTS_Indexer $index) { /* {{{ */
$this->_index = $index;
$this->_document = null;
} /* }}} */
/**
* Return the document associated with this hit
*
* @return SeedDMS_SQLiteFTS_Document
*/
public function getDocument() { /* {{{ */
if (!$this->_document instanceof SeedDMS_SQLiteFTS_Document) {
$this->_document = $this->_index->getDocument($this->id);
}
return $this->_document;
} /* }}} */
}
?>

View File

@ -1,166 +0,0 @@
<?php
/**
* Implementation of search in SQlite FTS index
*
* @category DMS
* @package SeedDMS_Lucene
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for searching in a SQlite FTS index.
*
* @category DMS
* @package SeedDMS_Lucene
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_SQliteFTS_Search {
/**
* @var object $index SQlite FTS index
* @access protected
*/
protected $index;
/**
* Create a new instance of the search
*
* @param object $index SQlite FTS index
* @return object instance of SeedDMS_SQliteFTS_Search
*/
function __construct($index) { /* {{{ */
$this->index = $index;
$this->version = '@package_version@';
if($this->version[0] == '@')
$this->version = '3.0.0';
} /* }}} */
/**
* Get document from index
*
* @param int $id id of seeddms document
* @return object instance of SeedDMS_SQliteFTS_QueryHit or false
*/
function getDocument($id) { /* {{{ */
$hits = $this->index->findById('D'.$id);
return $hits ? $hits[0] : false;
} /* }}} */
/**
* Get folder from index
*
* @param int $id id of seeddms folder
* @return object instance of SeedDMS_SQliteFTS_QueryHit or false
*/
function getFolder($id) { /* {{{ */
$hits = $this->index->findById('F'.$id);
return $hits ? $hits[0] : false;
} /* }}} */
/**
* Search in index
*
* @param object $index SQlite FTS index
* @return object instance of SeedDMS_Lucene_Search
*/
function search($term, $fields=array(), $limit=array(), $order=array()) { /* {{{ */
$querystr = '';
$term = trim($term);
if($term) {
$querystr = substr($term, -1) != '*' ? $term.'*' : $term;
}
if(!empty($fields['owner'])) {
if(is_string($fields['owner'])) {
if($querystr)
$querystr .= ' AND ';
$querystr .= 'owner:'.$fields['owner'];
} elseif(is_array($fields['owner'])) {
if($querystr)
$querystr .= ' AND ';
$querystr .= '(owner:';
$querystr .= implode(' OR owner:', $fields['owner']);
$querystr .= ')';
}
}
if(!empty($fields['record_type'])) {
if($querystr)
$querystr .= ' AND ';
$querystr .= '(record_type:';
$querystr .= implode(' OR record_type:', $fields['record_type']);
$querystr .= ')';
}
if(!empty($fields['category'])) {
if($querystr)
$querystr .= ' AND ';
$querystr .= '(category:"';
$querystr .= implode('" AND category:"', $fields['category']);
$querystr .= '")';
}
if(!empty($fields['mimetype'])) {
if($querystr)
$querystr .= ' AND ';
$querystr .= '(mimetype:"';
$querystr .= implode('" OR mimetype:"', $fields['mimetype']);
$querystr .= '")';
}
if(!empty($fields['status'])) {
if($querystr)
$querystr .= ' AND ';
$status = array_map(function($v){return (int)$v+10;}, $fields['status']);
$querystr .= '(status:';
$querystr .= implode(' OR status:', $status);
$querystr .= ')';
}
if(!empty($fields['user'])) {
if($querystr)
$querystr .= ' AND ';
$querystr .= '(users:';
$querystr .= implode(' OR users:', $fields['user']);
$querystr .= ')';
}
if(!empty($fields['rootFolder']) && $fields['rootFolder']->getFolderList()) {
if($querystr)
$querystr .= ' AND ';
$querystr .= '(path:';
$querystr .= str_replace(':', 'x', $fields['rootFolder']->getFolderList().$fields['rootFolder']->getID().':');
$querystr .= '*)';
}
if(!empty($fields['startFolder']) && $fields['startFolder']->getFolderList()) {
if($querystr)
$querystr .= ' AND ';
$querystr .= '(path:';
$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, $filterstr, $limit, $order);
$recs = array();
foreach($result["hits"] as $hit) {
$recs[] = array('id'=>$hit->id, 'document_id'=>$hit->documentid);
}
return array('count'=>$result['count'], 'hits'=>$recs, 'facets'=>$result['facets']);
} catch (Exception $e) {
return false;
}
} /* }}} */
}
?>

View File

@ -1,75 +0,0 @@
<?php
/**
* Implementation of a term
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010, Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class for managing a term.
*
* @category DMS
* @package SeedDMS_SQLiteFTS
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2011, Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_SQLiteFTS_Term {
/**
* @var string $text
* @access public
*/
public $text;
/**
* @var string $field
* @access public
*/
public $field;
/**
* @var integer $occurrence
* @access public
*/
public $_occurrence;
/**
*
*/
public function __construct($term, $col, $occurrence) { /* {{{ */
$this->text = $term;
$fields = array(
0 => 'documentid',
1 => 'title',
2 => 'comment',
3 => 'keywords',
4 => 'category',
5 => 'mimetype',
6 => 'origfilename',
7 => 'owner',
8 => 'content',
9 => 'created',
10 => 'user',
11 => 'status',
12 => 'path',
13 => 'indexed',
);
/* fts5 pass the column name in $col, fts4 uses an integer */
if(is_int($col))
$this->field = $fields[$col];
else
$this->field = $col; //$fields[$col];
$this->_occurrence = $occurrence;
} /* }}} */
}
?>

View File

@ -1,23 +0,0 @@
{
"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

@ -1,376 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.8.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>SeedDMS_SQLiteFTS</name>
<channel>pear.php.net</channel>
<summary>Fulltext search based on sqlite for SeedDMS</summary>
<description>SeedDMS is a web based document management system (DMS). This is
the fulltext search engine for it, based on SQLite FTS.</description>
<lead>
<name>Uwe Steinmann</name>
<user>steinm</user>
<email>uwe@steinmann.cx</email>
<active>yes</active>
</lead>
<date>2023-01-09</date>
<time>08:57:44</time>
<version>
<release>1.0.18</release>
<api>1.0.18</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- 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="/">
<dir name="SQLiteFTS">
<file name="Indexer.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="IndexedDocument.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="Document.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="QueryHit.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="Search.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="Term.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="Field.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
<file name="Exception.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
</dir> <!-- /SQLiteFTS -->
<dir name="tests">
</dir> <!-- /tests -->
<file name="SQLiteFTS.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
</dir> <!-- / -->
</contents>
<dependencies>
<required>
<php>
<min>4.3.0</min>
</php>
<pearinstaller>
<min>1.5.4</min>
</pearinstaller>
</required>
</dependencies>
<phprelease />
<changelog>
<release>
<date>2015-08-10</date>
<time>21:13:13</time>
<version>
<release>1.0.0</release>
<api>1.0.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- initial release
</notes>
</release>
<release>
<date>2015-11-16</date>
<time>09:07:07</time>
<version>
<release>1.0.1</release>
<api>1.0.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2016-01-10</date>
<time>09:07:07</time>
<version>
<release>1.0.2</release>
<api>1.0.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2016-02-01</date>
<time>09:15:01</time>
<version>
<release>1.0.3</release>
<api>1.0.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add command for indexing postѕcript files
</notes>
</release>
<release>
<date>2016-03-15</date>
<time>15:59:07</time>
<version>
<release>1.0.4</release>
<api>1.0.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- make it work with sqlite3 &lt; 3.8.0
</notes>
</release>
<release>
<date>2016-03-29</date>
<time>08:09:48</time>
<version>
<release>1.0.5</release>
<api>1.0.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
</notes>
</release>
<release>
<date>2016-03-29</date>
<time>08:09:48</time>
<version>
<release>1.0.6</release>
<api>1.0.1</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- fix calculation of timeout (see bug #269)
</notes>
</release>
<release>
<date>2017-03-01</date>
<time>15:53:24</time>
<version>
<release>1.0.7</release>
<api>1.0.7</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- catch exception in execWithTimeout()
</notes>
</release>
<release>
<date>2017-12-04</date>
<time>11:00:40</time>
<version>
<release>1.0.8</release>
<api>1.0.8</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- allow conversion commands for mimetypes with wildcards
</notes>
</release>
<release>
<date>2018-01-30</date>
<time>11:00:40</time>
<version>
<release>1.0.9</release>
<api>1.0.9</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- execWithTimeout() reads data from stderr and saves it into error msg
</notes>
</release>
<release>
<date>2018-04-11</date>
<time>11:00:40</time>
<version>
<release>1.0.10</release>
<api>1.0.10</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- IndexedDocument() remembers cmd and mimetype
</notes>
</release>
<release>
<date>2019-11-28</date>
<time>11:00:40</time>
<version>
<release>1.0.11</release>
<api>1.0.11</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</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
timestamp)
</notes>
</release>
<release>
<date>2020-09-02</date>
<time>08:57:44</time>
<version>
<release>1.0.12</release>
<api>1.0.12</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- Index users with at least read access on a document
</notes>
</release>
<release>
<date>2020-09-02</date>
<time>08:57:44</time>
<version>
<release>1.0.13</release>
<api>1.0.13</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://opensource.org/licenses/gpl-license">GPL License</license>
<notes>
- add user to list of terms
</notes>
</release>
<release>
<date>2020-09-11</date>
<time>08:57:44</time>
<version>
<release>1.0.14</release>
<api>1.0.14</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 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()
</notes>
</release>
<release>
<date>2020-12-12</date>
<time>08:57:44</time>
<version>
<release>1.0.15</release>
<api>1.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 indexing folders
</notes>
</release>
<release>
<date>2021-05-10</date>
<time>08:57:44</time>
<version>
<release>1.0.16</release>
<api>1.0.16</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
- add support for fts5 (make it the default)
- 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.14" />
<property name="composer_version" value="2.2.22" />
<property name="composer_extra_params" value="" />
<property name="composer_env" value="" />
<property name="mink_driver" value="chrome" />

View File

@ -4,11 +4,10 @@
"php": "7.4"
}
},
"require": {
"robthree/twofactorauth": "^1.5",
"sabre/dav": "^3.",
"sabre/xml": "^1.4.",
"sabre/dav": "^4.",
"sabre/xml": "*",
"slim/slim": "^3.0",
"erusev/parsedown": "*",
"erusev/parsedown-extra": "*",
@ -30,6 +29,9 @@
"seeddms/sqlitefts": "dev-master",
"seeddms/http_webdav_server": "dev-master"
},
"require-dev": {
"composer/composer": "dev-main"
},
"repositories": [
{
"type": "path",

View File

@ -57,7 +57,7 @@
enableLanguageSelector = "true"
stopWordsFile = ""
sortUsersInList = ""
enableDropUpload = "false"
enableDropUpload = "true"
enableRecursiveCount = "false"
maxRecursiveCount = "0"
enableThemeSelector = "true"
@ -112,6 +112,7 @@
dropFolderDir = ""
cacheDir = ""
backupDir = ""
logFileMaxLevel="6"
/>
<!--
- enableGuestLogin: If you want anybody to login as guest, set the following line to true
@ -120,6 +121,7 @@
- restricted: Restricted access: only allow users to log in if they have an entry in the local database (irrespective of successful authentication with LDAP).
- enableUserImage: enable users images
- disableSelfEdit: if true user cannot edit his own profile
- disableChangePassword: if true user cannot change the password
- passwordStrength: minimum strength of password, set to 0 to disable
- passwordStrengthAlgorithm: algorithm used to calculate password strenght (simple or advanced)
- passwordExpiration: number of days after password expires
@ -137,6 +139,7 @@
restricted = "true"
enableUserImage = "false"
disableSelfEdit = "false"
disableChangePassword = "false"
passwordStrength = "0"
passwordStrengthAlgorithm = "simple"
passwordExpiration = "0"
@ -168,6 +171,7 @@
bindDN = ""
bindPw = ""
filter = ""
groupField = ""
/>
<!-- ***** CONNECTOR Microsoft Active Directory *****
- enable: enable/disable connector
@ -189,6 +193,8 @@
accountDomainName = "example.com"
bindDN = ""
bindPw = ""
filter = ""
groupField = ""
/>
</connectors>
</authentication>
@ -303,21 +309,15 @@
enableNotificationWorkflow = "false"
/>
<converters target="fulltext">
<converter mimeType="application/pdf">pdftotext -nopgbrk %s - | sed -e 's/ [a-zA-Z0-9.]\{1\} / /g' -e 's/[0-9.]//g'</converter>
<converter mimeType="application/pdf">pdftotext -nopgbrk %s -</converter>
<converter mimeType="application/msword">catdoc %s</converter>
<converter mimeType="application/vnd.ms-excel">ssconvert -T Gnumeric_stf:stf_csv -S %s fd://1</converter>
<converter mimeType="audio/mp3">id3 -l -R %s | egrep '(Title|Artist|Album)' | sed 's/^[^:]*: //g'</converter>
<converter mimeType="audio/mpeg">id3 -l -R %s | egrep '(Title|Artist|Album)' | sed 's/^[^:]*: //g'</converter>
<converter mimeType="text/plain">cat %s</converter>
<converter mimeType="text/html">html2text %s</converter>
<converter mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document">docx2txt %s -</converter>
</converters>
<converters target="preview">
<converter mimeType="application/msword">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:- '%o'</converter>
<converter mimeType="image/png">convert -resize %wx '%f' '%o'</converter>
<converter mimeType="image/jpg">convert -resize %wx '%f' '%o'</converter>
<converter mimeType="image/gif">convert -resize %wx '%f' '%o'</converter>
<converter mimeType="text/plain">a2ps -1 -a1 -R -B -o - '%f' | gs -dBATCH -dNOPAUSE -sDEVICE=pngalpha -dFirstPage=1 -dLastPage=1 -dPDFFitPage -r72x72 -sOutputFile=- -q - | convert -resize %wx png:- '%o'</converter>
</converters>
</advanced>
<extensions>

View File

@ -56,13 +56,40 @@ class SeedDMS_Controller_AddDocument extends SeedDMS_Controller_Common {
$reqversion = $this->getParam('reqversion');
$version_comment = $this->getParam('versioncomment');
$attributes = $this->getParam('attributes');
foreach($attributes as $attrdefid=>$attribute) {
foreach($attributes as $attrdefid=>&$attribute) {
if($attrdef = $dms->getAttributeDefinition($attrdefid)) {
if(null === ($ret = $this->callHook('validateAttribute', $attrdef, $attribute))) {
if($attribute) {
switch($attrdef->getType()) {
case SeedDMS_Core_AttributeDefinition::type_date:
$attribute = date('Y-m-d', makeTsFromDate($attribute));
if(is_array($attribute))
$attribute = array_map(fn($value): string => date('Y-m-d', makeTsFromDate($value)), $attribute);
else
$attribute = date('Y-m-d', makeTsFromDate($attribute));
break;
case SeedDMS_Core_AttributeDefinition::type_folder:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getFolder((int) $value), $attribute);
else
$attribute = $dms->getFolder((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_document:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getDocument((int) $value), $attribute);
else
$attribute = $dms->getDocument((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_user:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getUser((int) $value), $attribute);
else
$attribute = $dms->getUser((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_group:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getGroup((int) $value), $attribute);
else
$attribute = $dms->getGroup((int) $attribute);
break;
}
if(!$attrdef->validate($attribute, null, true)) {
@ -80,13 +107,40 @@ class SeedDMS_Controller_AddDocument extends SeedDMS_Controller_Common {
}
}
if($attributes_version = $this->getParam('attributesversion')) {
foreach($attributes_version as $attrdefid=>$attribute) {
foreach($attributes_version as $attrdefid=>&$attribute) {
if($attrdef = $dms->getAttributeDefinition($attrdefid)) {
if(null === ($ret = $this->callHook('validateAttribute', $attrdef, $attribute))) {
if($attribute) {
switch($attrdef->getType()) {
case SeedDMS_Core_AttributeDefinition::type_date:
$attribute = date('Y-m-d', makeTsFromDate($attribute));
if(is_array($attribute))
$attribute = array_map(fn($value): string => date('Y-m-d', makeTsFromDate($value)), $attribute);
else
$attribute = date('Y-m-d', makeTsFromDate($attribute));
break;
case SeedDMS_Core_AttributeDefinition::type_folder:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getFolder((int) $value), $attribute);
else
$attribute = $dms->getFolder((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_document:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getDocument((int) $value), $attribute);
else
$attribute = $dms->getDocument((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_user:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getUser((int) $value), $attribute);
else
$attribute = $dms->getUser((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_group:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getGroup((int) $value), $attribute);
else
$attribute = $dms->getGroup((int) $attribute);
break;
}
if(!$attrdef->validate($attribute, null, true)) {

View File

@ -41,13 +41,40 @@ class SeedDMS_Controller_AddSubFolder extends SeedDMS_Controller_Common {
$comment = $this->getParam('comment');
$sequence = $this->getParam('sequence');
$attributes = $this->getParam('attributes');
foreach($attributes as $attrdefid=>$attribute) {
foreach($attributes as $attrdefid=>&$attribute) {
if($attrdef = $dms->getAttributeDefinition($attrdefid)) {
if(null === ($ret = $this->callHook('validateAttribute', $attrdef, $attribute))) {
if($attribute) {
switch($attrdef->getType()) {
case SeedDMS_Core_AttributeDefinition::type_date:
$attribute = date('Y-m-d', makeTsFromDate($attribute));
if(is_array($attribute))
$attribute = array_map(fn($value): string => date('Y-m-d', makeTsFromDate($value)), $attribute);
else
$attribute = date('Y-m-d', makeTsFromDate($attribute));
break;
case SeedDMS_Core_AttributeDefinition::type_folder:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getFolder((int) $value), $attribute);
else
$attribute = $dms->getFolder((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_document:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getDocument((int) $value), $attribute);
else
$attribute = $dms->getDocument((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_user:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getUser((int) $value), $attribute);
else
$attribute = $dms->getUser((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_group:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getGroup((int) $value), $attribute);
else
$attribute = $dms->getGroup((int) $attribute);
break;
}
if(!$attrdef->validate($attribute, null, true)) {

View File

@ -7,36 +7,38 @@
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @copyright Copyright (C) 2010-2023 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for downloading a document
* Class which does the busines logic for approving a document
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @copyright Copyright (C) 2010-2023 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_ApproveDocument extends SeedDMS_Controller_Common {
public function run() {
public $oldstatus;
public $newstatus;
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();
$approvaltype = $this->params['type'];
$approvalstatus = $this->params['status'];
$approvalcomment = $this->params['comment'];
$approvalfile = $this->params['file'];
$approvalgroup = $this->params['group'];
$overallStatus = $content->getStatus();
$this->oldstatus = $overallStatus['status'];
$this->newstatus = $this->oldstatus;
if(!$this->callHook('preApproveDocument', $content)) {
}
@ -44,36 +46,35 @@ class SeedDMS_Controller_ApproveDocument extends SeedDMS_Controller_Common {
$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;
}
$approvalLogID = $content->setApprovalByInd($user, $user, $approvalstatus, $approvalcomment, $approvalfile);
} 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;
}
$approvalLogID = $content->setApprovalByGrp($approvalgroup, $user, $approvalstatus, $approvalcomment, $approvalfile);
} else {
$this->errormsg = "approval_wrong_type";
return false;
}
if($approvalLogID === false || 0 > $approvalLogID) {
$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);
if($approvalstatus == -1) {
$this->newstatus = S_REJECTED;
if($content->setStatus(S_REJECTED, $approvalcomment, $user)) {
if(isset($GLOBALS['SEEDDMS_HOOKS']['approveDocument'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['approveDocument'] as $hookObj) {
if (method_exists($hookObj, 'postApproveDocument')) {
$hookObj->postApproveDocument(null, $content, S_REJECTED);
}
}
}
}
} else {
$docApprovalStatus = $content->getApprovalStatus();
if (is_bool($docApprovalStatus) && !$docApprovalStatus) {
$this->error = 1;
$this->errormsg = "cannot_retrieve_approval_snapshot";
return false;
}
@ -91,8 +92,15 @@ class SeedDMS_Controller_ApproveDocument extends SeedDMS_Controller_Common {
// 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);
$this->newstatus=S_RELEASED;
if($content->setStatus($this->newstatus, getMLText("automatic_status_update"), $user)) {
if(isset($GLOBALS['SEEDDMS_HOOKS']['approveDocument'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['approveDocument'] as $hookObj) {
if (method_exists($hookObj, 'postApproveDocument')) {
$hookObj->postApproveDocument(null, $content, S_RELEASED);
}
}
}
}
}
}
@ -102,6 +110,6 @@ class SeedDMS_Controller_ApproveDocument extends SeedDMS_Controller_Common {
}
return true;
}
} /* }}} */
}

View File

@ -0,0 +1,114 @@
<?php
/**
* Implementation of CheckInDocument controller
*
* @category DMS
* @package SeedDMS
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2024 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-2024 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_CheckInDocument extends SeedDMS_Controller_Common {
public function run() { /* {{{ */
$name = $this->getParam('name');
$comment = $this->getParam('comment');
/* Call preCheckInDocument early, because it might need to modify some
* of the parameters.
*/
if(false === $this->callHook('preCheckInDocument', $this->params['document'])) {
if(empty($this->errormsg))
$this->errormsg = 'hook_preCheckInDocument_failed';
return null;
}
$comment = $this->getParam('comment');
$dms = $this->params['dms'];
$user = $this->params['user'];
$document = $this->params['document'];
$settings = $this->params['settings'];
$fulltextservice = $this->params['fulltextservice'];
$folder = $this->params['folder'];
$userfiletmp = $this->getParam('userfiletmp');
$userfilename = $this->getParam('userfilename');
$filetype = $this->getParam('filetype');
$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('checkinDocument');
if($content === null) {
if($contentResult=$document->checkIn($comment, $user, $reviewers, $approvers, $version=0, $attributes, $workflow, $initialdocumentstatus)) {
if ($this->hasParam('expires')) {
if($document->setExpires($this->getParam('expires'))) {
} else {
}
}
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_checkin_document';
$result = false;
}
} elseif($result === false) {
if(empty($this->errormsg))
$this->errormsg = 'hook_checkinDocument_failed';
return false;
}
if($fulltextservice && ($index = $fulltextservice->Indexer()) && $content) {
$idoc = $fulltextservice->IndexedDocument($document);
if(false !== $this->callHook('preIndexDocument', $document, $idoc)) {
$lucenesearch = $fulltextservice->Search();
if($hit = $lucenesearch->getDocument((int) $document->getId())) {
$index->delete($hit->id);
}
$index->addDocument($idoc);
$index->commit();
}
}
if(false === $this->callHook('postCheckInDocument', $document, $content)) {
}
return $content;
} /* }}} */
}

View File

@ -30,23 +30,23 @@ class SeedDMS_Controller_ClearCache extends SeedDMS_Controller_Common {
$ret = '';
if(!empty($post['previewpng'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/png/[1-9]*';
$cmd = 'rm -rf '.addDirSep($settings->_cacheDir).'png'.DIRECTORY_SEPARATOR.'[1-9]*';
system($cmd, $ret);
}
if(!empty($post['previewpdf'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/pdf/[1-9]*';
$cmd = 'rm -rf '.addDirSep($settings->_cacheDir).'pdf'.DIRECTORY_SEPARATOR.'[1-9]*';
system($cmd, $ret);
}
if(!empty($post['previewtxt'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/txt/[1-9]*';
$cmd = 'rm -rf '.addDirSep($settings->_cacheDir).'txt'.DIRECTORY_SEPARATOR.'[1-9]*';
system($cmd, $ret);
}
if(!empty($post['js'])) {
$cmd = 'rm -rf '.$settings->_cacheDir.'/js/*';
system($cmd, $ret);
/* system('rm ...') does not work anymore if the number of files is too large */
array_map('unlink', array_filter((array) glob(addDirSep($settings->_cacheDir).'js'.DIRECTORY_SEPARATOR.'*')));
}
if(false === $this->callHook('clear', $post)) {

View File

@ -28,6 +28,7 @@ class SeedDMS_Controller_Cron extends SeedDMS_Controller_Common {
$settings = $this->params['settings'];
$logger = $this->params['logger'];
$mode = $this->params['mode'];
$seltask = $this->params['task'];
$db = $dms->getDb();
$scheduler = new SeedDMS_Scheduler($db);
@ -35,6 +36,8 @@ class SeedDMS_Controller_Cron extends SeedDMS_Controller_Common {
$jsonarr = [];
foreach($tasks as $task) {
if($seltask && $seltask != $task->getExtension()."::".$task->getTask())
continue;
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(),

View File

@ -122,7 +122,34 @@ class SeedDMS_Controller_EditDocument extends SeedDMS_Controller_Common {
if($attribute) {
switch($attrdef->getType()) {
case SeedDMS_Core_AttributeDefinition::type_date:
$attribute = date('Y-m-d', makeTsFromDate($attribute));
if(is_array($attribute))
$attribute = array_map(fn($value): string => date('Y-m-d', makeTsFromDate($value)), $attribute);
else
$attribute = date('Y-m-d', makeTsFromDate($attribute));
break;
case SeedDMS_Core_AttributeDefinition::type_folder:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getFolder((int) $value), $attribute);
else
$attribute = $dms->getFolder((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_document:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getDocument((int) $value), $attribute);
else
$attribute = $dms->getDocument((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_user:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getUser((int) $value), $attribute);
else
$attribute = $dms->getUser((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_group:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getGroup((int) $value), $attribute);
else
$attribute = $dms->getGroup((int) $attribute);
break;
}
if(!$attrdef->validate($attribute, $document, false)) {

View File

@ -51,33 +51,61 @@ class SeedDMS_Controller_EditFolder extends SeedDMS_Controller_Common {
$oldattributes = $folder->getAttributes();
if($attributes) {
foreach($attributes as $attrdefid=>$attribute) {
$attrdef = $dms->getAttributeDefinition($attrdefid);
if(null === ($ret = $this->callHook('validateAttribute', $attrdef, $attribute))) {
if($attribute) {
switch($attrdef->getType()) {
case SeedDMS_Core_AttributeDefinition::type_date:
$attribute = date('Y-m-d', makeTsFromDate($attribute));
break;
}
if(!$attrdef->validate($attribute, $folder, false)) {
$this->errormsg = getAttributeValidationText($attrdef->getValidationError(), $attrdef->getName(), $attribute);
return false;
}
if($attrdef = $dms->getAttributeDefinition($attrdefid)) {
if(null === ($ret = $this->callHook('validateAttribute', $attrdef, $attribute))) {
if($attribute) {
switch($attrdef->getType()) {
case SeedDMS_Core_AttributeDefinition::type_date:
if(is_array($attribute))
$attribute = array_map(fn($value): string => date('Y-m-d', makeTsFromDate($value)), $attribute);
else
$attribute = date('Y-m-d', makeTsFromDate($attribute));
break;
case SeedDMS_Core_AttributeDefinition::type_folder:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getFolder((int) $value), $attribute);
else
$attribute = $dms->getFolder((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_document:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getDocument((int) $value), $attribute);
else
$attribute = $dms->getDocument((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_user:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getUser((int) $value), $attribute);
else
$attribute = $dms->getUser((int) $attribute);
break;
case SeedDMS_Core_AttributeDefinition::type_group:
if(is_array($attribute))
$attribute = array_map(fn($value): object => $dms->getGroup((int) $value), $attribute);
else
$attribute = $dms->getGroup((int) $attribute);
break;
}
if(!$attrdef->validate($attribute, $folder, false)) {
$this->errormsg = getAttributeValidationText($attrdef->getValidationError(), $attrdef->getName(), $attribute);
return false;
}
if(!isset($oldattributes[$attrdefid]) || $attribute != $oldattributes[$attrdefid]->getValue()) {
if(!$folder->setAttributeValue($dms->getAttributeDefinition($attrdefid), $attribute))
if(!isset($oldattributes[$attrdefid]) || $attribute != $oldattributes[$attrdefid]->getValue()) {
if(!$folder->setAttributeValue($dms->getAttributeDefinition($attrdefid), $attribute))
return false;
}
} elseif($attrdef->getMinValues() > 0) {
$this->errormsg = getMLText("attr_min_values", array("attrname"=>$attrdef->getName()));
return false;
} elseif(isset($oldattributes[$attrdefid])) {
if(!$folder->removeAttribute($dms->getAttributeDefinition($attrdefid)))
return false;
}
} else {
if($ret === false)
return false;
}
} elseif($attrdef->getMinValues() > 0) {
$this->errormsg = getMLText("attr_min_values", array("attrname"=>$attrdef->getName()));
return false;
} elseif(isset($oldattributes[$attrdefid])) {
if(!$folder->removeAttribute($dms->getAttributeDefinition($attrdefid)))
return false;
}
} else {
if($ret === false)
return false;
}
}
}

View File

@ -31,110 +31,12 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
return self::$user;
} /* }}} */
public function run() { /* {{{ */
$dms = $this->params['dms'];
public function _finalize($user) { /* {{{ */
$settings = $this->params['settings'];
$session = $this->params['session'];
$authenticator = $this->params['authenticator'];
$sesstheme = $this->params['sesstheme'];
$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'];
self::$user = null;
/* The preLogin hook may set self::$user which will prevent any further
* authentication process.
*/
if($this->callHook('preLogin')) {
}
$user = self::$user;
/* The password may only be empty if the guest user tries to log in.
* There is just one guest account with id $settings->_guestID which
* is allowed to log in without a password. All other guest accounts
* are treated like regular logins
*/
if(!$user && $settings->_enableGuestLogin && (int) $settings->_guestID) {
$guestUser = $dms->getUser((int) $settings->_guestID);
if($guestUser) {
if(($login != $guestUser->getLogin())) {
if ((!isset($pwd) || strlen($pwd)==0)) {
$this->setErrorMsg("login_error_text");
return false;
}
} else {
$user = $guestUser;
}
}
}
/* Run any additional authentication method. The hook must return a
* valid user, if the authentication succeeded. If it fails, it must
* return false and if the hook doesn't care at all, if must return null.
*/
if(!$user) {
$user = $this->callHook('authenticate', $source);
if(false === $user) {
if(empty($this->errormsg))
$this->setErrorMsg("authentication_failed");
return false;
}
}
/* Deprecated: Run any additional authentication implemented in a hook */
if(!is_object($user) && isset($GLOBALS['SEEDDMS_HOOKS']['authentication'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['authentication'] as $authObj) {
if(!$user && method_exists($authObj, 'authenticate')) {
$user = $authObj->authenticate($dms, $settings, $login, $pwd);
if(false === $user) {
if(empty($this->errormsg))
$this->setErrorMsg("authentication_failed");
return false;
}
}
}
}
$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");
$authobj = new SeedDMS_LdapAuthentication($dms, $settings);
$user = $authobj->authenticate($login, $pwd);
if(!$user) {
add_log_line('Authentication against LDAP failed for user '.$login);
}
} /* }}} */
/* Authenticate against SeedDMS database {{{ */
if(!is_object($user)) {
require_once("../inc/inc.ClassDbAuthentication.php");
$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)) {
/* if counting of login failures is turned on, then increment its value */
if($settings->_loginFailure) {
$user = $dms->getUserByLogin($login);
if($user) {
$failures = $user->addLoginFailure();
if($failures >= $settings->_loginFailure)
$user->setDisabled(true);
}
}
$this->callHook('loginFailed');
$this->setErrorMsg("login_error_text");
return false;
}
$lang = $this->params['lang'];
self::$user = $user;
@ -261,4 +163,113 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common {
return true;
} /* }}} */
public function run() { /* {{{ */
$dms = $this->params['dms'];
$settings = $this->params['settings'];
$session = $this->params['session'];
$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'];
self::$user = null;
/* The preLogin hook may set self::$user which will prevent any further
* authentication process.
*/
if($this->callHook('preLogin')) {
}
$user = self::$user;
/* The password may only be empty if the guest user tries to log in.
* There is just one guest account with id $settings->_guestID which
* is allowed to log in without a password. All other guest accounts
* are treated like regular logins
*/
if(!$user && $settings->_enableGuestLogin && (int) $settings->_guestID) {
$guestUser = $dms->getUser((int) $settings->_guestID);
if($guestUser) {
if(($login != $guestUser->getLogin())) {
if ((!isset($pwd) || strlen($pwd)==0)) {
$this->setErrorMsg("login_error_text");
return false;
}
} else {
$user = $guestUser;
}
}
}
/* Run any additional authentication method. The hook must return a
* valid user, if the authentication succeeded. If it fails, it must
* return false and if the hook doesn't care at all, if must return null.
*/
if(!$user) {
$user = $this->callHook('authenticate', $source);
if(false === $user) {
if(empty($this->errormsg))
$this->setErrorMsg("authentication_failed");
return false;
}
}
/* Deprecated: Run any additional authentication implemented in a hook */
if(!is_object($user) && isset($GLOBALS['SEEDDMS_HOOKS']['authentication'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['authentication'] as $authObj) {
if(!$user && method_exists($authObj, 'authenticate')) {
$user = $authObj->authenticate($dms, $settings, $login, $pwd);
if(false === $user) {
if(empty($this->errormsg))
$this->setErrorMsg("authentication_failed");
return false;
}
}
}
}
$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");
$authobj = new SeedDMS_LdapAuthentication($dms, $settings);
$user = $authobj->authenticate($login, $pwd);
if(!$user) {
add_log_line('Authentication against LDAP failed for user '.$login);
}
} /* }}} */
/* Authenticate against SeedDMS database {{{ */
if(!is_object($user)) {
require_once("../inc/inc.ClassDbAuthentication.php");
$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)) {
/* if counting of login failures is turned on, then increment its value */
if($settings->_loginFailure) {
$user = $dms->getUserByLogin($login);
if($user) {
$failures = $user->addLoginFailure();
if($failures >= $settings->_loginFailure)
$user->setDisabled(true);
}
}
$this->callHook('loginFailed');
$this->setErrorMsg("login_error_text");
return false;
}
return self::_finalize($user);
} /* }}} */
}

View File

@ -7,72 +7,70 @@
* @license GPL 2
* @version @version@
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @copyright Copyright (C) 2010-2023 Uwe Steinmann
* @version Release: @package_version@
*/
/**
* Class which does the busines logic for downloading a document
* Class which does the busines logic for reviewing a document
*
* @category DMS
* @package SeedDMS
* @author Uwe Steinmann <uwe@steinmann.cx>
* @copyright Copyright (C) 2010-2013 Uwe Steinmann
* @copyright Copyright (C) 2010-2023 Uwe Steinmann
* @version Release: @package_version@
*/
class SeedDMS_Controller_ReviewDocument extends SeedDMS_Controller_Common {
public function run() {
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();
$reviewtype = $this->params['type'];
$reviewstatus = $this->params['status'];
$reviewcomment = $this->params['comment'];
$reviewfile = $this->params['file'];
$reviewgroup = $this->params['group'];
$overallStatus = $content->getStatus();
$this->oldstatus = $overallStatus['status'];
$this->newstatus = $this->oldstatus;
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;
}
$reviewLogID = $content->setReviewByInd($user, $user, $reviewstatus, $reviewcomment, $reviewfile);
} elseif($reviewtype == "grp") {
$reviewLogID = $content->setReviewByGrp($reviewgroup, $user, $reviewstatus, $reviewcomment, $reviewfile);
} else {
$this->errormsg = "review_wrong_type";
return false;
}
if($reviewLogID === false || 0 > $reviewLogID) {
$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)) {
if($reviewstatus == -1) {
$this->newstatus = S_REJECTED;
if($content->setStatus(S_REJECTED, $reviewcomment, $user)) {
if(isset($GLOBALS['SEEDDMS_HOOKS']['reviewDocument'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['reviewDocument'] as $hookObj) {
if (method_exists($hookObj, 'postReviewDocument')) {
$hookObj->postReviewDocument(null, $content, S_REJECTED);
}
}
}
}
} else {
$docReviewStatus = $content->getReviewStatus();
if (is_bool($docReviewStatus) && !$docReviewStatus) {
$this->error = 1;
$this->errormsg = "cannot_retrieve_review_snapshot";
return false;
}
@ -91,32 +89,35 @@ class SeedDMS_Controller_ReviewDocument extends SeedDMS_Controller_Common {
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) {
foreach($docApprovalStatus as $dastat) {
if($dastat["status"] == 1) {
$approvalCT++;
}
if ($dastat["status"] != -2) {
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 {
if($approvalCT < $approvalTotal) {
$this->newstatus = S_DRAFT_APP;
} else {
// Otherwise, change the status to released.
$newStatus=S_RELEASED;
$this->newstatus = S_RELEASED;
}
if ($content->setStatus($newStatus, getMLText("automatic_status_update"), $user)) {
// Send notification to subscribers.
if($content->setStatus($this->newstatus, getMLText("automatic_status_update"), $user)) {
if(isset($GLOBALS['SEEDDMS_HOOKS']['reviewDocument'])) {
foreach($GLOBALS['SEEDDMS_HOOKS']['reviewDocument'] as $hookObj) {
if (method_exists($hookObj, 'postReviewDocument')) {
$hookObj->postReviewDocument(null, $content, $this->newstatus);
}
}
}
}
}
}
@ -126,6 +127,5 @@ class SeedDMS_Controller_ReviewDocument extends SeedDMS_Controller_Common {
}
return true;
}
} /* }}} */
}

View File

@ -22,6 +22,10 @@
*/
class SeedDMS_Controller_ReviseDocument extends SeedDMS_Controller_Common {
public $oldstatus;
public $newstatus;
public function run() {
$dms = $this->params['dms'];
$user = $this->params['user'];
@ -32,6 +36,9 @@ class SeedDMS_Controller_ReviseDocument extends SeedDMS_Controller_Common {
$revisiontype = $this->params['revisiontype'];
$group = $this->params['group'];
$comment = $this->params['comment'];
$overallStatus = $content->getStatus();
$this->oldstatus = $overallStatus['status'];
$this->newstatus = $this->oldstatus;
/* 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
@ -71,6 +78,7 @@ class SeedDMS_Controller_ReviseDocument extends SeedDMS_Controller_Common {
$result = $this->callHook('reviseUpdateDocumentStatus', $content);
if($result === null) {
if ($onevotereject && $revisionstatus == -1){
$this->newstatus = S_NEEDS_CORRECTION;
if(!$content->setStatus(S_NEEDS_CORRECTION,$comment,$user)) {
$this->error = 1;
$this->errormsg = "revision_update_failed";
@ -102,22 +110,22 @@ class SeedDMS_Controller_ReviseDocument extends SeedDMS_Controller_Common {
// 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"))) {
$this->newstatus=S_RELEASED;
if ($content->finishRevision($user, $this->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->newstatus=S_NEEDS_CORRECTION;
// if ($content->finishRevision($user, $this->newstatus, 'Finished revision workflow', getMLText("automatic_status_update"))) {
if(!$content->setStatus($this->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->newstatus=S_IN_REVISION;
if(!$content->setStatus($this->newstatus,$comment,$user)) {
$this->error = 1;
$this->errormsg = "revision_update_failed";
return false;

View File

@ -1,3 +1,23 @@
Commands for converting documents
----------------------------------
This file contains commands for converting different document types
into
* text (for fulltext search)
* png (for preview images)
* pdf (for pdf documents)
Such conversions may not necessarily output an excact equivalent of
the input file, but outputs a suitable representation, e.g.
converting an mp3 file into text may output the metadata or even the
lyrics of the song. Converting it into a preview image may result
in a picture of the album cover.
Please note, that when ever a command outputs anything to stderr,
this will considered as a failure of the command. Most command line
programs have a parameter (.e.g. `-q`) to suppress such an output.
Conversion to text for fulltext search
=======================================
@ -7,10 +27,11 @@ application/csv
cat '%s'
application/pdf
pdftotext -nopgbrk %s - | sed -e 's/ [a-zA-Z0-9.]\{1\} / /g' -e 's/[0-9.]//g'
pdftotext -q -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
-l to specify the last page to be converted. -q is for suppressing error/warnings
send to stderr
mutool draw -F txt -q -N -o - %s
@ -74,6 +95,20 @@ message/rfc822
The emailconverter can be obtained from https://github.com/nickrussler/email-to-pdf-converter
It requires wkhtmltopdf which is part of debian.
text/plain
iconv -c -f utf-8 -t latin1 '%f' | a2ps -1 -q -a1 -R -B -o - - | ps2pdf - -
The parameter `-q` is important because a2ps sends some statistical
data to stderr, which makes SeedDMS believe the command has failed.
application/x-xopp
xournalpp -p "%o" "%f"
Converting from application/x-xopp to pdf only works if the xopp file
does not use a pdf document as a background, because this pdf is not
stored in the xopp fіle.
Conversion to png for preview images
=====================================
@ -107,16 +142,22 @@ application/pdf
convert -density 100 -resize %wx '%f[0]' 'png:%o'
mutool draw -F png -w %w -q -N -o %o %f 1
mutool draw -F png -w %w -q -N -o '%o' '%f' 1
pdftocairo '%f' -png -singlefile -scale-to-x %w -scale-to-y -1 - > '%o'
pdftocairo needs to output to stdout because the output file name passed
to pdftocairo will be suffixed with png
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'
iconv -c -f utf-8 -t latin1 '%f' | a2ps -1 -q -a1 -R -B -o - - | 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
e.g. a4, or letter. Unfortunately, a2ps cannot process utf-8 encoded files. That's
why the input needs to be recoded with iconv or recode.
application/msword
application/vnd.oasis.opendocument.spreadsheet
@ -136,4 +177,19 @@ 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'
convert -resize %wx "%f[12]" "png:%o"
You may as well use ffmpeg right away
ffmpeg -i "%f" -ss 00:00:02 -frames:v 1 -loglevel quiet -vf scale=%w:-1 -f apng "%o"
audio/mpeg
sox "%f" -n spectrogram -x 600 -Y 550 -r -l -o - | convert -resize %wx png:- "png:%o"
application/x-xopp
xournalpp -i "%o" --export-png-width=%w "%f"
Converting from application/x-xopp to png only works if the xopp file
does not use a pdf document as a background, because this pdf is not
stored in the xopp fіle.

View File

@ -8,20 +8,20 @@ SeedDMS is a web-based application written in PHP. It uses MySQL,
SQLite3 or PostgreSQL to manage the documents that were uploaded into
the application. Be aware that PostgreSQL is not very well tested.
Make sure you have PHP >= 7.3 and MySQL 5 or higher installed. SeedDMS
Make sure you have PHP >= 7.4 and MySQL 5 or higher installed. SeedDMS
will work with PHP running in CGI-mode as well as running as a module under
apache.
Here is a detailed list of requirements:
1. A web server with at least php 7.3
1. A web server with at least php 7.4
2. A mysql database, unless you use SQLite
3. The php installation must have support for `pdo_mysql`, `pdo_pgsql` or `pdo_sqlite`,
`php_gd2`, `php_mbstring`, `php_xml`
4. Depending on the configuration the extensions `php_ldap`, `php_mycrypt`,
`php_gmp`, `php_libsodium` must be installed
`php_gmp`, `php_libsodium`, `php_imagick` must be installed
5. Various command line programms to convert files into text for indexing
pdftotext, catdoc, xls2csv or scconvert, cat, id3 (optional, only needed
pdftotext, catdoc, xls2csv or scconvert, cat, id3 (all optional, only needed
for fulltext search)
6. ImageMagic (the convert program) is needed for creating preview images
7. A bunch of packages from Packagist which all ship with the seeddms-quickstart
@ -29,7 +29,7 @@ Here is a detailed list of requirements:
It is highly recommended to use the quickstart archive
(seeddms-quickstart-x.y.z.tar.gz) because it includes all software packages
(excluding those listing above in item 1. to 6.) for running SeedDMS. Hence,
(excluding those listed above in item 1. to 6.) for running SeedDMS. Hence,
you still need a working web server with PHP and in addition a mysql or
PostgreSQL database unless you intend to use SQLite.
@ -99,7 +99,7 @@ SECURITY CONSIDERATIONS
You should always access your SeedDMS installation through
a secured https connection, unless you precisly know what you are doing.
SeedDMS ships an .htaccess file which already has some common security
SeedDMS ships an `.htaccess` file which already has some common security
http headers set. In order for them to apply you need to activate the
headers module. On Debian/GNU Linux this can be done with
@ -240,7 +240,7 @@ in your current installation with new versions from the quickstart archive.
current installation and make the link `seeddms` point to this new directory.
3. copy the directory `pear` from the unpacked archive into your current
installation, replacing the existing directory. Make a backup of `pear` before
the replacement if you want to ensure to be able to go back to your old version.
the replacement if you want to ensure to be able to go back to your old version.
4. you may compare your `conf/settings.xml` file with the shipped version
`conf/settings.xml.template` for new parameters. If you don't do it, the next
time you save the configuration the default values will be used.
@ -350,7 +350,7 @@ http://localhost/seeddms/
* Create a data directory with the thre sub directories staging, cache
and lucene.
Make sure the data directory is either *not* below your document root
or is protected with a .htaccess file against web access. The data directory
or is protected with a .htaccess file against web access. The data directory
needs to be writable by the web server.
* Clicking on 'Start installation' will show a form with all necessary
@ -371,9 +371,9 @@ full text search engine support, you will also need to unpack
* Unpack all the files in a public web server folder. If you're working on
a host machine your provider will tell you where to upload the files.
If possible, do not unpack the pear packages `SeedDMS_Core-<version>.tgz`,
`SeedDMS_Preview-<version>.tgz` and
`SeedDMS_Preview-<version>.tgz` and
`SeedDMS_Lucene-<version>.tgz` below the document root of your web server.
Choose a temporary folder, as the files will be moved in a second.
Choose a temporary folder, as the files will be moved in a second.
Create a directory e.g. `pear` in the same directory where you unpacked
seeddms and create a sub directory SeedDMS. Move the content except for the
@ -395,36 +395,36 @@ full text search engine support, you will also need to unpack
Since they are pear packages they can also be installed with
> pear install SeedDMS_Core-<version>.tgz
> pear install SeedDMS_Lucene-<version>.tgz
> pear install SeedDMS_Preview-<version>.tgz
> pear install SeedDMS_Core-<version>.tgz
> pear install SeedDMS_Lucene-<version>.tgz
> pear install SeedDMS_Preview-<version>.tgz
* The PEAR packages Log and Mail are also needed. They can be downloaded from
http://pear.php.net/package/Log and http://pear.php.net/package/Mail.
Either install it as a pear package
or place it under your new directory 'pear'
Either install it as a pear package
or place it under your new directory 'pear'
> pear
> Log
> Log.php
> Mail
> Mail.php
> Log
> Log.php
> Mail
> Mail.php
* The package HTTP_WebDAV_Server is also needed. It can be downloaded from
http://pear.php.net/package/HTTP_WebDAV_Server. Either install it as a
pear package or place it under your new directory 'pear'
pear package or place it under your new directory 'pear'
> pear
> HTTP
> WebDAV
> Server
> Server.php
> WebDAV
> Server
> Server.php
If you run PHP in CGI mode, you also need to place a .htaccess file
in the webdav directory with the following content.
in the webdav directory with the following content.
RewriteEngine on
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
RewriteEngine on
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
* Create a data folder somewhere on your web server including the subdirectories
staging, cache and lucene and make sure they are writable by your web server,
@ -435,24 +435,24 @@ or should be protected by a .htaccess file. The folder containing the
configuration (settings.xml) must be protected by an .htaccess file like the
following.
> <Files ~ "^settings\.xml">
> Order allow,deny
> Deny from all
> </Files>
> <Files ~ "^settings\.xml">
> Order allow,deny
> Deny from all
> </Files>
If you install SeedDMS for the first time continue with the database setup.
* Create a new database on your web server
e.g. for mysql:
create database seeddms;
create database seeddms;
* Create a new user for the database with all permissions on the new database
e.g. for mysql:
grant all privileges on seeddms.* to seeddms@localhost identified by 'secret';
(replace 'secret' with you own password)
grant all privileges on seeddms.* to seeddms@localhost identified by 'secret';
(replace 'secret' with you own password)
* Optionally import `create_tables-innodb.sql` in the new database
e.g. for mysql:
> cat create_tables-innodb.sql | mysql -useeddms -p seeddms
> cat create_tables-innodb.sql | mysql -useeddms -p seeddms
This step can also be done by the install tool.
* create a file `ENABLE_INSTALL_TOOL` in the `conf` directory and point
your browser at http://hostname/seeddms/install

View File

@ -2,14 +2,14 @@ Ldap configuration
===================
The configuration for authentication against an ldap server needs to be done
the settings.xml file using a text editor. It cannot be edited from within the
in the `settings.xml` file using a text editor. It cannot be edited from within the
web gui.
SeedDMS supports ldap authentication using an Active Directory (AD) or a
regular ldap server, e.g. openldap
The location of the ldap server is specified in two parameters: 'host' and
'port'. 'host' can be either a plain hostname or an ldap URI, including the
The location of the ldap server is specified in two parameters: `host` and
`port`. `host` can be either a plain hostname or an ldap URI, including the
protocol, the host and optionally the port, e.g. ldap://localhost:389. In case
of an URI the port in the configuration must remain empty.
@ -31,10 +31,18 @@ If the second bind succeeds the user could be successfully authenticated.
The data from the ldap server can be used to create an account in SeedDMS
if the user trying to login does not exist yet, but was able to authenticate.
This will only be done if 'authentication->restricted' in the configuration
is set to true. In that case the common name (cn) and email address is taken
from ldap. Existing accounts in SeedDMS will not be updated with data from
is set to false. In that case the common name (cn) and email address is taken
from ldap. An already existing account in SeedDMS will be updated with data from
ldap.
Since version 5.1.35 and 6.0.28 the field name of the email address in ldap
can be set with the attribute `mailField`. If it is not set it defaults to `mail`.
Since version 5.1.34 and 6.0.27 the groups of a user stored in the ldap directory
can be synchronised with the groups in SeedDMS. The ldap field storing
the groups can be configured with the attribute `mailField`. This will add
new groups in SeedDMS and aѕsign them to the user.
Examples
---------

View File

@ -23,8 +23,7 @@ smtp port: 465
Gmail
---------
smtp server: smtp.gmail.com
smtp port: 587
note: you have to turn on 'Less secure app access' in your google account,
otherwise you will not be able to send mail. It will quit with an error
complaining about wrong credentials
smtp server: ssl://smtp.gmail.com
smtp port: 465
note: since June, 2022 you will no longer be able to turn on 'Less secure app access' in your google account. Instead you need to turn on 2-Step Verification first and than
create an App Password (https://support.google.com/accounts/answer/185833)

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