| Code Coverage
| Lines | Functions and Methods | Classes and Traits
Total | 67.18% covered (warning) | 67.18% | 1785 / 2657 | 40.82% covered (danger) | 40.82% | 80 / 196 | CRAP | 0.00% covered (danger) | 0.00% | 0 / 5
SeedDMS_Core_Document | 71.67% covered (warning) | 71.67% | 931 / 1299 | 43.21% covered (danger) | 43.21% | 35 / 81 | 8037.92 | 0.00% covered (danger) | 0.00% | 0 / 1
__construct | 100.00% covered (success) | 100.00% | 18 / 18 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
clearCache | 100.00% covered (success) | 100.00% | 8 / 8 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
__toString | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
isType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getSearchFields | 100.00% covered (success) | 100.00% | 15 / 15 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getInstanceByData | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getInstance | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
applyDecorators | 33.33% covered (danger) | 33.33% | 2 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.67 |
getDir | 50.00% covered (danger) | 50.00% | 2 / 4 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2.50 |
getName | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setName | 52.94% covered (warning) | 52.94% | 9 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 14.67 |
getComment | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setComment | 52.94% covered (warning) | 52.94% | 9 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 14.67 |
getKeywords | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setKeywords | 52.94% covered (warning) | 52.94% | 9 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 14.67 |
hasCategory | 100.00% covered (success) | 100.00% | 8 / 8 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
getCategories | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
setCategories | 68.00% covered (warning) | 68.00% | 17 / 25 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13.28 |
addCategories | 72.41% covered (warning) | 72.41% | 21 / 29 | 0.00% covered (danger) | 0.00% | 0 / 1 | 15.02 |
removeCategories | 60.00% covered (warning) | 60.00% | 12 / 20 | 0.00% covered (danger) | 0.00% | 0 / 1 | 14.18 |
getDate | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setDate | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
isDescendant | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
getParent | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getFolder | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
setParent | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setFolder | 71.43% covered (warning) | 71.43% | 25 / 35 | 0.00% covered (danger) | 0.00% | 0 / 1 | 16.94 |
getOwner | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
setOwner | 65.22% covered (warning) | 65.22% | 15 / 23 | 0.00% covered (danger) | 0.00% | 0 / 1 | 14.21 |
getDefaultAccess | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
setDefaultAccess | 100.00% covered (success) | 100.00% | 11 / 11 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
inheritsAccess | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getInheritAccess | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setInheritAccess | 100.00% covered (success) | 100.00% | 9 / 9 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
expires | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
getExpires | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
setExpires | 100.00% covered (success) | 100.00% | 9 / 9 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
hasExpired | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
verifyLastestContentExpriry | 77.78% covered (warning) | 77.78% | 7 / 9 | 0.00% covered (danger) | 0.00% | 0 / 1 | 9.89 |
isLocked | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setLocked | 100.00% covered (success) | 100.00% | 13 / 13 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
getLockingUser | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
getSequence | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setSequence | 100.00% covered (success) | 100.00% | 6 / 6 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
clearAccessList | 88.89% covered (warning) | 88.89% | 8 / 9 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3.01 |
getAccessList | 95.45% covered (success) | 95.45% | 21 / 22 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12 |
addAccess | 88.89% covered (warning) | 88.89% | 16 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.09 |
changeAccess | 87.50% covered (warning) | 87.50% | 14 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.05 |
removeAccess | 81.25% covered (warning) | 81.25% | 13 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.24 |
getAccessMode | 72.50% covered (warning) | 72.50% | 29 / 40 | 0.00% covered (danger) | 0.00% | 0 / 1 | 40.06 |
getGroupAccessMode | 86.67% covered (warning) | 86.67% | 13 / 15 | 0.00% covered (danger) | 0.00% | 0 / 1 | 7.12 |
getNotifyList | 93.75% covered (success) | 93.75% | 15 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 10.02 |
cleanNotifyList | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
addNotify | 55.00% covered (warning) | 55.00% | 22 / 40 | 0.00% covered (danger) | 0.00% | 0 / 1 | 43.34 |
removeNotify | 80.00% covered (warning) | 80.00% | 16 / 20 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.51 |
addContent | 51.04% covered (warning) | 51.04% | 49 / 96 | 0.00% covered (danger) | 0.00% | 0 / 1 | 197.65 |
replaceContent | 72.55% covered (warning) | 72.55% | 37 / 51 | 0.00% covered (danger) | 0.00% | 0 / 1 | 24.70 |
getContent | 87.50% covered (warning) | 87.50% | 14 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 7.10 |
getContentByVersion | 71.43% covered (warning) | 71.43% | 15 / 21 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13.82 |
isLatestContent | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
__getLatestContent | 0.00% covered (danger) | 0.00% | 0 / 12 | 0.00% covered (danger) | 0.00% | 0 / 1 | 30 |
getLatestContent | 87.50% covered (warning) | 87.50% | 14 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.12 |
_removeContent | 38.30% covered (danger) | 38.30% | 36 / 94 | 0.00% covered (danger) | 0.00% | 0 / 1 | 322.76 |
removeContent | 62.96% covered (warning) | 62.96% | 17 / 27 | 0.00% covered (danger) | 0.00% | 0 / 1 | 23.96 |
getDocumentLink | 82.35% covered (warning) | 82.35% | 14 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.35 |
getDocumentLinks | 95.45% covered (success) | 95.45% | 21 / 22 | 0.00% covered (danger) | 0.00% | 0 / 1 | 10 |
getReverseDocumentLinks | 94.74% covered (success) | 94.74% | 18 / 19 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.01 |
addDocumentLink | 94.74% covered (success) | 94.74% | 18 / 19 | 0.00% covered (danger) | 0.00% | 0 / 1 | 10.01 |
removeDocumentLink | 85.71% covered (warning) | 85.71% | 6 / 7 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.05 |
getDocumentFile | 91.67% covered (success) | 91.67% | 11 / 12 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.02 |
getDocumentFiles | 100.00% covered (success) | 100.00% | 22 / 22 | 100.00% covered (success) | 100.00% | 1 / 1 | 9 |
addDocumentFile | 68.00% covered (warning) | 68.00% | 17 / 25 | 0.00% covered (danger) | 0.00% | 0 / 1 | 11.65 |
removeDocumentFile | 66.67% covered (warning) | 66.67% | 14 / 21 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13.70 |
remove | 58.21% covered (warning) | 58.21% | 39 / 67 | 0.00% covered (danger) | 0.00% | 0 / 1 | 80.21 |
__getApproversList | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
getReadAccessList | 69.09% covered (warning) | 69.09% | 38 / 55 | 0.00% covered (danger) | 0.00% | 0 / 1 | 45.96 |
getFolderList | 83.33% covered (warning) | 83.33% | 5 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3.04 |
repair | 0.00% covered (danger) | 0.00% | 0 / 15 | 0.00% covered (danger) | 0.00% | 0 / 1 | 30 |
getUsedDiskSpace | 83.33% covered (warning) | 83.33% | 5 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3.04 |
getTimeline | 92.00% covered (success) | 92.00% | 23 / 25 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.03 |
transferToUser | 62.50% covered (warning) | 62.50% | 15 / 24 | 0.00% covered (danger) | 0.00% | 0 / 1 | 7.90 |
SeedDMS_Core_DocumentContent | 63.36% covered (warning) | 63.36% | 742 / 1171 | 20.83% covered (danger) | 20.83% | 15 / 72 | 14288.81 | 0.00% covered (danger) | 0.00% | 0 / 1
verifyStatus | 100.00% covered (success) | 100.00% | 32 / 32 | 100.00% covered (success) | 100.00% | 1 / 1 | 20 |
__construct | 95.24% covered (success) | 95.24% | 20 / 21 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3 |
isType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getVersion | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getComment | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDate | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getOriginalFileName | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getFileType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getFileName | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
__getDir | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
getMimeType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDocument | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getUser | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
getPath | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
exists | 0.00% covered (danger) | 0.00% | 0 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
size | 0.00% covered (danger) | 0.00% | 0 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
content | 0.00% covered (danger) | 0.00% | 0 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
setDate | 84.62% covered (warning) | 84.62% | 11 / 13 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.13 |
getFileSize | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setFileSize | 72.73% covered (warning) | 72.73% | 8 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.32 |
getChecksum | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getRealChecksum | 0.00% covered (danger) | 0.00% | 0 / 4 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
setChecksum | 72.73% covered (warning) | 72.73% | 8 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.32 |
getRealMimeType | 0.00% covered (danger) | 0.00% | 0 / 4 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
setFileType | 77.78% covered (warning) | 77.78% | 14 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.40 |
setMimeType | 72.73% covered (warning) | 72.73% | 8 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.32 |
setComment | 47.06% covered (danger) | 47.06% | 8 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 17.50 |
getStatus | 89.47% covered (warning) | 89.47% | 17 / 19 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.04 |
getStatusLog | 94.12% covered (success) | 94.12% | 16 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.01 |
setStatus | 91.18% covered (success) | 91.18% | 31 / 34 | 0.00% covered (danger) | 0.00% | 0 / 1 | 16.18 |
rewriteStatusLog | 0.00% covered (danger) | 0.00% | 0 / 23 | 0.00% covered (danger) | 0.00% | 0 / 1 | 56 |
getAccessMode | 50.00% covered (danger) | 50.00% | 3 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 198.00 |
getReviewers | 94.12% covered (success) | 94.12% | 16 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 7.01 |
getReviewStatus | 87.88% covered (warning) | 87.88% | 29 / 33 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12.26 |
getReviewLog | 90.00% covered (success) | 90.00% | 9 / 10 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.02 |
rewriteReviewLog | 0.00% covered (danger) | 0.00% | 0 / 39 | 0.00% covered (danger) | 0.00% | 0 / 1 | 240 |
getApprovers | 94.12% covered (success) | 94.12% | 16 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 7.01 |
getApprovalStatus | 87.88% covered (warning) | 87.88% | 29 / 33 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12.26 |
getApproveLog | 90.00% covered (success) | 90.00% | 9 / 10 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.02 |
rewriteApprovalLog | 0.00% covered (danger) | 0.00% | 0 / 39 | 0.00% covered (danger) | 0.00% | 0 / 1 | 240 |
addIndReviewer | 84.38% covered (warning) | 84.38% | 27 / 32 | 0.00% covered (danger) | 0.00% | 0 / 1 | 18.10 |
addGrpReviewer | 86.11% covered (warning) | 86.11% | 31 / 36 | 0.00% covered (danger) | 0.00% | 0 / 1 | 21.07 |
setReviewByInd | 77.78% covered (warning) | 77.78% | 21 / 27 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13.58 |
removeReview | 80.95% covered (warning) | 80.95% | 17 / 21 | 0.00% covered (danger) | 0.00% | 0 / 1 | 10.69 |
setReviewByGrp | 76.92% covered (warning) | 76.92% | 20 / 26 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13.77 |
addIndApprover | 84.38% covered (warning) | 84.38% | 27 / 32 | 0.00% covered (danger) | 0.00% | 0 / 1 | 18.10 |
addGrpApprover | 86.11% covered (warning) | 86.11% | 31 / 36 | 0.00% covered (danger) | 0.00% | 0 / 1 | 21.07 |
setApprovalByInd | 77.78% covered (warning) | 77.78% | 21 / 27 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13.58 |
removeApproval | 80.95% covered (warning) | 80.95% | 17 / 21 | 0.00% covered (danger) | 0.00% | 0 / 1 | 10.69 |
setApprovalByGrp | 76.92% covered (warning) | 76.92% | 20 / 26 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13.77 |
delIndReviewer | 70.59% covered (warning) | 70.59% | 12 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 9.63 |
delGrpReviewer | 0.00% covered (danger) | 0.00% | 0 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 72 |
delIndApprover | 72.22% covered (warning) | 72.22% | 13 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 9.37 |
delGrpApprover | 0.00% covered (danger) | 0.00% | 0 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 72 |
setWorkflowState | 75.00% covered (warning) | 75.00% | 6 / 8 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3.14 |
getWorkflowState | 93.75% covered (success) | 93.75% | 15 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.01 |
setWorkflow | 72.22% covered (warning) | 72.22% | 13 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.77 |
getWorkflow | 92.86% covered (success) | 92.86% | 13 / 14 | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.01 |
rewriteWorkflowLog | 0.00% covered (danger) | 0.00% | 0 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 30 |
rewindWorkflow | 75.00% covered (warning) | 75.00% | 9 / 12 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3.14 |
removeWorkflow | 86.21% covered (warning) | 86.21% | 25 / 29 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.17 |
getParentWorkflow | 0.00% covered (danger) | 0.00% | 0 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 42 |
runSubWorkflow | 0.00% covered (danger) | 0.00% | 0 / 14 | 0.00% covered (danger) | 0.00% | 0 / 1 | 30 |
returnFromSubWorkflow | 0.00% covered (danger) | 0.00% | 0 / 29 | 0.00% covered (danger) | 0.00% | 0 / 1 | 90 |
triggerWorkflowTransitionIsAllowed | 66.67% covered (warning) | 66.67% | 18 / 27 | 0.00% covered (danger) | 0.00% | 0 / 1 | 19.26 |
executeWorkflowTransitionIsAllowed | 52.78% covered (warning) | 52.78% | 19 / 36 | 0.00% covered (danger) | 0.00% | 0 / 1 | 42.96 |
triggerWorkflowTransition | 65.00% covered (warning) | 65.00% | 13 / 20 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12.47 |
enterNextState | 76.00% covered (warning) | 76.00% | 19 / 25 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12.67 |
getWorkflowLog | 93.75% covered (success) | 93.75% | 15 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.01 |
getLastWorkflowLog | 91.67% covered (success) | 91.67% | 11 / 12 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3.01 |
needsWorkflowAction | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
repair | 0.00% covered (danger) | 0.00% | 0 / 24 | 0.00% covered (danger) | 0.00% | 0 / 1 | 132 |
SeedDMS_Core_DocumentLink | 85.00% covered (warning) | 85.00% | 17 / 20 | 87.50% covered (warning) | 87.50% | 7 / 8 | 13.57 | 0.00% covered (danger) | 0.00% | 0 / 1
__construct | 100.00% covered (success) | 100.00% | 6 / 6 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
isType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getID | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDocument | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getTarget | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getUser | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
isPublic | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getAccessMode | 50.00% covered (danger) | 50.00% | 3 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.00 |
SeedDMS_Core_DocumentFile | 71.58% covered (warning) | 71.58% | 68 / 95 | 76.92% covered (warning) | 76.92% | 20 / 26 | 94.58 | 0.00% covered (danger) | 0.00% | 0 / 1
__construct | 100.00% covered (success) | 100.00% | 13 / 13 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
isType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getID | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDocument | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getUserID | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getComment | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setComment | 100.00% covered (success) | 100.00% | 6 / 6 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
getDate | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setDate | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
getDir | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
getFileType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getMimeType | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getRealMimeType | 0.00% covered (danger) | 0.00% | 0 / 5 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
getOriginalFileName | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getName | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setName | 100.00% covered (success) | 100.00% | 6 / 6 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
getUser | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
getPath | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
exists | 0.00% covered (danger) | 0.00% | 0 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
size | 0.00% covered (danger) | 0.00% | 0 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
content | 0.00% covered (danger) | 0.00% | 0 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6 |
getVersion | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setVersion | 100.00% covered (success) | 100.00% | 8 / 8 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
isPublic | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setPublic | 100.00% covered (success) | 100.00% | 6 / 6 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
getAccessMode | 50.00% covered (danger) | 50.00% | 3 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.00 |
SeedDMS_Core_AddContentResultSet | 22.41% covered (danger) | 22.41% | 13 / 58 | 33.33% covered (danger) | 33.33% | 3 / 9 | 865.86 | 0.00% covered (danger) | 0.00% | 0 / 1
__construct | 100.00% covered (success) | 100.00% | 7 / 7 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
setDMS | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
addReviewer | 0.00% covered (danger) | 0.00% | 0 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 132 |
addApprover | 0.00% covered (danger) | 0.00% | 0 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 132 |
setStatus | 66.67% covered (warning) | 66.67% | 4 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.59 |
getStatus | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
getContent | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getReviewers | 0.00% covered (danger) | 0.00% | 0 / 5 | 0.00% covered (danger) | 0.00% | 0 / 1 | 42 |
getApprovers | 0.00% covered (danger) | 0.00% | 0 / 5 | 0.00% covered (danger) | 0.00% | 0 / 1 | 42 |

1 | <?php
2 | declare(strict_types=1);
3 |
4 | /**
5 | * Implementation of a document in the document management system
6 | *
7 | * @category DMS
8 | * @package SeedDMS_Core
9 | * @license GPL2
10 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli,
11 | * Uwe Steinmann <uwe@steinmann.cx>
12 | * @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
13 | * 2010 Matteo Lucarelli, 2010-2024 Uwe Steinmann
14 | * @version Release: @package_version@
15 | */
16 |
17 | /**
18 | * The different states a document can be in
19 | */
20 | /*
21 | * Document is in review state. A document is in review state when
22 | * it needs to be reviewed by a user or group.
23 | */
24 | define("S_DRAFT_REV", 0);
25 |
26 | /*
27 | * Document is in approval state. A document is in approval state when
28 | * it needs to be approved by a user or group.
29 | */
30 | define("S_DRAFT_APP", 1);
31 |
32 | /*
33 | * Document is released. A document is in release state either when
34 | * it needs no review or approval after uploaded or has been reviewed
35 | * and/or approved.
36 | */
37 | define("S_RELEASED", 2);
38 |
39 | /*
40 | * Document is in workflow. A document is in workflow if a workflow
41 | * has been started and has not reached a final state.
42 | */
43 | define("S_IN_WORKFLOW", 3);
44 |
45 | /*
46 | * Document was rejected. A document is in rejected state when
47 | * the review failed or approval was not given.
48 | */
49 | define("S_REJECTED", -1);
50 |
51 | /*
52 | * Document is obsolete. A document can be obsoleted once it was
53 | * released.
54 | */
55 | define("S_OBSOLETE", -2);
56 |
57 | /*
58 | * Document is expired. A document expires when the expiration date
59 | * is reached
60 | */
61 | define("S_EXPIRED", -3);
62 |
63 | /*
64 | * Lowest and highest status that may be set
65 | */
66 | define("S_LOWEST_STATUS", -3);
67 | define("S_HIGHEST_STATUS", 3);
68 |
69 | /**
70 | * The different states a workflow log can be in. This is used in
71 | * all tables tblDocumentXXXLog
72 | */
73 | /*
74 | * workflow is in a neutral status waiting for action of user
75 | */
76 | define("S_LOG_WAITING", 0);
77 |
78 | /*
79 | * workflow has been successful ended. The document content has been
80 | * approved, reviewed, aknowledged or revised
81 | */
82 | define("S_LOG_ACCEPTED", 1);
83 |
84 | /*
85 | * workflow has been unsuccessful ended. The document content has been
86 | * rejected
87 | */
88 | define("S_LOG_REJECTED", -1);
89 |
90 | /*
91 | * user has been removed from workflow. This can be for different reasons
92 | * 1. the user has been actively removed from the workflow, 2. the user has
93 | * been deleted.
94 | */
95 | define("S_LOG_USER_REMOVED", -2);
96 |
97 | /*
98 | * workflow is sleeping until reactivation. The workflow has been set up
99 | * but not started. This is only valid for the revision workflow, which
100 | * may run over and over again.
101 | */
102 | define("S_LOG_SLEEPING", -3);
103 |
104 | /**
105 | * Class to represent a document in the document management system
106 | *
107 | * A document in SeedDMS is a collection of content elements which are
108 | * similar to a file in a regular file system.
109 | * Documents may have any number of content elements
110 | * ({@see SeedDMS_Core_DocumentContent}). These content elements are often
111 | * called versions ordered in a timely manner. The most recent content element
112 | * is the current version of the document.
113 | *
114 | * Documents can be linked to other documents, can have attached files,
115 | * can be assigned to a category and have additional attributes.
116 | * The document content can be anything that can be stored in a regular
117 | * file.
118 | *
119 | * @category DMS
120 | * @package SeedDMS_Core
121 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli,
122 | * Uwe Steinmann <uwe@steinmann.cx>
123 | * @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe,
124 | * 2010 Matteo Lucarelli, 2010-2024 Uwe Steinmann
125 | * @version Release: @package_version@
126 | */
127 | class SeedDMS_Core_Document extends SeedDMS_Core_Object { /* {{{ */
128 | /**
129 | * @var string name of document
130 | */
131 | protected $_name;
132 |
133 | /**
134 | * @var string comment of document
135 | */
136 | protected $_comment;
137 |
138 | /**
139 | * @var integer unix timestamp of creation date
140 | */
141 | protected $_date;
142 |
143 | /**
144 | * @var integer id of user who is the owner
145 | */
146 | protected $_ownerID;
147 |
148 | /**
149 | * @var object user who is the owner
150 | */
151 | protected $_owner;
152 |
153 | /**
154 | * @var integer id of folder this document belongs to
155 | */
156 | protected $_folderID;
157 |
158 | /**
159 | * @var object parent folder this document belongs to
160 | */
161 | protected $_parent;
162 |
163 | /**
164 | * @var integer timestamp of expiration date
165 | */
166 | protected $_expires;
167 |
168 | /**
169 | * @var boolean true if access is inherited, otherwise false
170 | */
171 | protected $_inheritAccess;
172 |
173 | /**
174 | * @var integer default access if access rights are not inherited
175 | */
176 | protected $_defaultAccess;
177 |
178 | /**
179 | * @var array list of notifications for users and groups
180 | */
181 | protected $_readAccessList;
182 |
183 | /**
184 | * @var array list of notifications for users and groups
185 | */
186 | public $_notifyList;
187 |
188 | /**
189 | * @var boolean true if document is locked, otherwise false
190 | */
191 | protected $_locked;
192 |
193 | /**
194 | * @var object user who has locked the document
195 | */
196 | protected $_lockingUser;
197 |
198 | /**
199 | * @var string list of keywords
200 | */
201 | protected $_keywords;
202 |
203 | /**
204 | * @var SeedDMS_Core_DocumentCategory[] list of categories
205 | */
206 | protected $_categories;
207 |
208 | /**
209 | * @var integer position of document within the parent folder
210 | */
211 | protected $_sequence;
212 |
213 | /**
214 | * @var SeedDMS_Core_DocumentContent temp. storage for latestcontent
215 | */
216 | protected $_latestContent;
217 |
218 | /**
219 | * @var array temp. storage for content
220 | */
221 | protected $_content;
222 |
223 | /**
224 | * @var SeedDMS_Core_Folder
225 | */
226 | protected $_folder;
227 |
228 | /** @var array of SeedDMS_Core_UserAccess and SeedDMS_Core_GroupAccess */
229 | protected $_accessList;
230 |
231 | /**
232 | * @var array
233 | */
234 | protected $_documentLinks;
235 |
236 | /**
237 | * @var array
238 | */
239 | protected $_documentFiles;
240 |
241 | public function __construct($id, $name, $comment, $date, $expires, $ownerID, $folderID, $inheritAccess, $defaultAccess, $locked, $keywords, $sequence) { /* {{{ */
242 | parent::__construct($id);
243 | $this->_name = trim($name);
244 | $this->_comment = trim($comment);
245 | $this->_date = $date;
246 | $this->_expires = $expires;
247 | $this->_ownerID = $ownerID;
248 | $this->_folderID = $folderID;
249 | $this->_inheritAccess = $inheritAccess ? true : false;
250 | $this->_defaultAccess = $defaultAccess;
251 | $this->_locked = ($locked == null || $locked == '' ? -1 : $locked);
252 | $this->_lockingUser = null;
253 | $this->_keywords = trim($keywords);
254 | $this->_sequence = $sequence;
255 | $this->_categories = array();
256 | $this->_notifyList = array();
257 | $this->_latestContent = null;
258 | $this->_content = null;
259 | /* Cache */
260 | $this->clearCache();
261 | } /* }}} */
262 |
263 | /**
264 | * Clear cache of this instance.
265 | *
266 | * The result of some expensive database actions (e.g. get all subfolders
267 | * or documents) will be saved in a class variable to speed up consecutive
268 | * calls of the same method. If a second call of the same method shall not
269 | * use the cache, then it must be cleared.
270 | *
271 | */
272 | public function clearCache() { /* {{{ */
273 | $this->_parent = null;
274 | $this->_owner = null;
275 | $this->_documentLinks = null;
276 | $this->_documentFiles = null;
277 | $this->_content = null;
278 | $this->_accessList = null;
279 | $this->_notifyList = array();
280 | $this->_readAccessList = array();
281 | } /* }}} */
282 |
283 | /**
284 | * Cast to string
285 | *
286 | * @return string
287 | */
288 | public function __toString() { /* {{{ */
289 | return $this->_name;
290 | } /* }}} */
291 |
292 | /**
293 | * Check if this object is of type 'document'.
294 | *
295 | * @param string $type type of object
296 | */
297 | public function isType($type) { /* {{{ */
298 | return $type == 'document';
299 | } /* }}} */
300 |
301 | /**
302 | * Return an array of database fields which are used for searching
303 | * a term entered in the database search form
304 | *
305 | * @param SeedDMS_Core_DMS $dms
306 | * @param array $searchin integer list of search scopes (2=name, 3=comment,
307 | * 4=attributes)
308 | * @return array list of database fields
309 | */
310 | public static function getSearchFields($dms, $searchin) { /* {{{ */
311 | $db = $dms->getDB();
312 |
313 | $searchFields = array();
314 | if (in_array(1, $searchin)) {
315 | $searchFields[] = "`tblDocuments`.`keywords`";
316 | }
317 | if (in_array(2, $searchin)) {
318 | $searchFields[] = "`tblDocuments`.`name`";
319 | }
320 | if (in_array(3, $searchin)) {
321 | $searchFields[] = "`tblDocuments`.`comment`";
322 | $searchFields[] = "`tblDocumentContent`.`comment`";
323 | }
324 | if (in_array(4, $searchin)) {
325 | $searchFields[] = "`tblDocumentAttributes`.`value`";
326 | $searchFields[] = "`tblDocumentContentAttributes`.`value`";
327 | }
328 | if (in_array(5, $searchin)) {
329 | $searchFields[] = $db->castToText("`tblDocuments`.`id`");
330 | }
331 |
332 | return $searchFields;
333 | } /* }}} */
334 |
335 | /**
336 | * Return a folder by its database record
337 | *
338 | * @param array $resArr array of folder data as returned by database
339 | * @param SeedDMS_Core_DMS $dms
340 | * @return SeedDMS_Core_Folder|bool instance of SeedDMS_Core_Folder if document exists
341 | */
342 | public static function getInstanceByData($resArr, $dms) { /* {{{ */
343 | $classname = $dms->getClassname('document');
344 | /** @var SeedDMS_Core_Document $document */
345 | $document = new $classname($resArr["id"], $resArr["name"], $resArr["comment"], $resArr["date"], $resArr["expires"], $resArr["owner"], $resArr["folder"], $resArr["inheritAccess"], $resArr["defaultAccess"], $resArr['lock'], $resArr["keywords"], $resArr["sequence"]);
346 | $document->setDMS($dms);
347 | $document = $document->applyDecorators();
348 | return $document;
349 | } /* }}} */
350 |
351 | /**
352 | * Return an document by its id
353 | *
354 | * @param integer $id id of document
355 | * @param SeedDMS_Core_DMS $dms
356 | * @return bool|SeedDMS_Core_Document instance of SeedDMS_Core_Document if document exists, null
357 | * if document does not exist, false in case of error
358 | */
359 | public static function getInstance($id, $dms) { /* {{{ */
360 | $db = $dms->getDB();
361 |
362 | // $queryStr = "SELECT * FROM `tblDocuments` WHERE `id` = " . (int) $id;
363 | $queryStr = "SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lock` FROM `tblDocuments` LEFT JOIN `tblDocumentLocks` ON `tblDocuments`.`id` = `tblDocumentLocks`.`document` WHERE `id` = " . (int) $id;
364 | if ($dms->checkWithinRootDir)
365 | $queryStr .= " AND `folderList` LIKE '%:".$dms->rootFolderID.":%'";
366 | $resArr = $db->getResultArray($queryStr);
367 | if (is_bool($resArr) && $resArr == false)
368 | return false;
369 | if (count($resArr) != 1)
370 | return null;
371 | $resArr = $resArr[0];
372 |
373 | $resArr['lock'] = !$resArr['lock'] ? -1 : $resArr['lock'];
374 |
375 | return self::getInstanceByData($resArr, $dms);
376 | } /* }}} */
377 |
378 | /**
379 | * Apply decorators
380 | *
381 | * @return object final object after all decorators has been applied
382 | */
383 | public function applyDecorators() { /* {{{ */
384 | if ($decorators = $this->_dms->getDecorators('document')) {
385 | $s = $this;
386 | foreach ($decorators as $decorator) {
387 | $s = new $decorator($s);
388 | }
389 | return $s;
390 | } else {
391 | return $this;
392 | }
393 | } /* }}} */
394 |
395 | /**
396 | * Return the directory of the document in the file system relativ
397 | * to the contentDir
398 | *
399 | * @return string directory of document
400 | */
401 | public function getDir() { /* {{{ */
402 | if ($this->_dms->maxDirID) {
403 | $dirid = (int) (($this->_id-1) / $this->_dms->maxDirID) + 1;
404 | return $dirid.DIRECTORY_SEPARATOR.$this->_id.DIRECTORY_SEPARATOR;
405 | } else {
406 | return $this->_id.DIRECTORY_SEPARATOR;
407 | }
408 | } /* }}} */
409 |
410 | /**
411 | * Return the name of the document
412 | *
413 | * @return string name of document
414 | */
415 | public function getName() { return $this->_name; }
416 |
417 | /**
418 | * Set the name of the document
419 | *
420 | * @param $newName string new name of document
421 | * @return bool
422 | */
423 | public function setName($newName) { /* {{{ */
424 | $db = $this->_dms->getDB();
425 |
426 | /* Check if 'onPreSetName' callback is set */
427 | if (isset($this->_dms->callbacks['onPreSetName'])) {
428 | foreach ($this->_dms->callbacks['onPreSetName'] as $callback) {
429 | $ret = call_user_func($callback[0], $callback[1], $this, $newName);
430 | if (is_bool($ret))
431 | return $ret;
432 | }
433 | }
434 |
435 | $queryStr = "UPDATE `tblDocuments` SET `name` = ".$db->qstr($newName)." WHERE `id` = ". $this->_id;
436 | if (!$db->getResult($queryStr))
437 | return false;
438 |
439 | $oldName = $this->_name;
440 | $this->_name = $newName;
441 |
442 | /* Check if 'onPostSetName' callback is set */
443 | if (isset($this->_dms->callbacks['onPostSetName'])) {
444 | foreach ($this->_dms->callbacks['onPostSetName'] as $callback) {
445 | $ret = call_user_func($callback[0], $callback[1], $this, $oldName);
446 | if (is_bool($ret))
447 | return $ret;
448 | }
449 | }
450 |
451 | return true;
452 | } /* }}} */
453 |
454 | /**
455 | * Return the comment of the document
456 | *
457 | * @return string comment of document
458 | */
459 | public function getComment() { return $this->_comment; }
460 |
461 | /**
462 | * Set the comment of the document
463 | *
464 | * @param $newComment string new comment of document
465 | * @return bool
466 | */
467 | public function setComment($newComment) { /* {{{ */
468 | $db = $this->_dms->getDB();
469 |
470 | /* Check if 'onPreSetComment' callback is set */
471 | if (isset($this->_dms->callbacks['onPreSetComment'])) {
472 | foreach ($this->_dms->callbacks['onPreSetComment'] as $callback) {
473 | $ret = call_user_func($callback[0], $callback[1], $this, $newComment);
474 | if (is_bool($ret))
475 | return $ret;
476 | }
477 | }
478 |
479 | $queryStr = "UPDATE `tblDocuments` SET `comment` = ".$db->qstr($newComment)." WHERE `id` = ". $this->_id;
480 | if (!$db->getResult($queryStr))
481 | return false;
482 |
483 | $oldComment = $this->_comment;
484 | $this->_comment = $newComment;
485 |
486 | /* Check if 'onPostSetComment' callback is set */
487 | if (isset($this->_dms->callbacks['onPostSetComment'])) {
488 | foreach ($this->_dms->callbacks['onPostSetComment'] as $callback) {
489 | $ret = call_user_func($callback[0], $callback[1], $this, $oldComment);
490 | if (is_bool($ret))
491 | return $ret;
492 | }
493 | }
494 |
495 | return true;
496 | } /* }}} */
497 |
498 | /**
499 | * @return string
500 | */
501 | public function getKeywords() { return $this->_keywords; }
502 |
503 | /**
504 | * @param string $newKeywords
505 | * @return bool
506 | */
507 | public function setKeywords($newKeywords) { /* {{{ */
508 | $db = $this->_dms->getDB();
509 |
510 | /* Check if 'onPreSetKeywords' callback is set */
511 | if (isset($this->_dms->callbacks['onPreSetKeywords'])) {
512 | foreach ($this->_dms->callbacks['onPreSetKeywords'] as $callback) {
513 | $ret = call_user_func($callback[0], $callback[1], $this, $newKeywords);
514 | if (is_bool($ret))
515 | return $ret;
516 | }
517 | }
518 |
519 | $queryStr = "UPDATE `tblDocuments` SET `keywords` = ".$db->qstr($newKeywords)." WHERE `id` = ". $this->_id;
520 | if (!$db->getResult($queryStr))
521 | return false;
522 |
523 | $oldKeywords = $this->_keywords;
524 | $this->_keywords = $newKeywords;
525 |
526 | /* Check if 'onPostSetKeywords' callback is set */
527 | if (isset($this->_dms->callbacks['onPostSetKeywords'])) {
528 | foreach ($this->_dms->callbacks['onPostSetKeywords'] as $callback) {
529 | $ret = call_user_func($callback[0], $callback[1], $this, $oldKeywords);
530 | if (is_bool($ret))
531 | return $ret;
532 | }
533 | }
534 |
535 | return true;
536 | } /* }}} */
537 |
538 | /**
539 | * Check if document has a given category
540 | *
541 | * @param SeedDMS_Core_DocumentCategory $cat
542 | * @return bool true if document has category, otherwise false
543 | */
544 | public function hasCategory($cat) { /* {{{ */
545 | $db = $this->_dms->getDB();
546 |
547 | if (!$cat)
548 | return false;
549 |
550 | $queryStr = "SELECT * FROM `tblDocumentCategory` WHERE `documentID` = ".$this->_id." AND `categoryID`=".$cat->getId();
551 | $resArr = $db->getResultArray($queryStr);
552 | if (!$resArr)
553 | return false;
554 |
555 | return true;
556 | } /* }}} */
557 |
558 | /**
559 | * Retrieve a list of all categories this document belongs to
560 | *
561 | * @return bool|SeedDMS_Core_DocumentCategory[]
562 | */
563 | public function getCategories() { /* {{{ */
564 | $db = $this->_dms->getDB();
565 |
566 | if (!$this->_categories) {
567 | $queryStr = "SELECT * FROM `tblCategory` WHERE `id` IN (SELECT `categoryID` FROM `tblDocumentCategory` WHERE `documentID` = ".$this->_id.")";
568 | $resArr = $db->getResultArray($queryStr);
569 | if (is_bool($resArr) && !$resArr)
570 | return false;
571 |
572 | $this->_categories = [];
573 | foreach ($resArr as $row) {
574 | $cat = new SeedDMS_Core_DocumentCategory($row['id'], $row['name']);
575 | $cat->setDMS($this->_dms);
576 | $this->_categories[] = $cat;
577 | }
578 | }
579 | return $this->_categories;
580 | } /* }}} */
581 |
582 | /**
583 | * Set a list of categories for the document
584 | *
585 | * This method will delete currently assigned categories and sets new
586 | * categories.
587 | *
588 | * @param SeedDMS_Core_DocumentCategory[] $newCategories list of category objects
589 | * @return bool
590 | */
591 | public function setCategories($newCategories) { /* {{{ */
592 | $db = $this->_dms->getDB();
593 |
594 | /* Check if 'onPreSetCategories' callback is set */
595 | if (isset($this->_dms->callbacks['onPreSetCategories'])) {
596 | foreach ($this->_dms->callbacks['onPreSetCategories'] as $callback) {
597 | $ret = call_user_func($callback[0], $callback[1], $this, $newCategories);
598 | if (is_bool($ret))
599 | return $ret;
600 | }
601 | }
602 |
603 | $db->startTransaction();
604 | $queryStr = "DELETE FROM `tblDocumentCategory` WHERE `documentID` = ". $this->_id;
605 | if (!$db->getResult($queryStr)) {
606 | $db->rollbackTransaction();
607 | return false;
608 | }
609 |
610 | foreach ($newCategories as $cat) {
611 | $queryStr = "INSERT INTO `tblDocumentCategory` (`categoryID`, `documentID`) VALUES (". $cat->getId() .", ". $this->_id .")";
612 | if (!$db->getResult($queryStr)) {
613 | $db->rollbackTransaction();
614 | return false;
615 | }
616 | }
617 |
618 | $db->commitTransaction();
619 |
620 | $oldCategories = $this->_categories;
621 | $this->_categories = $newCategories;
622 |
623 | /* Check if 'onPostSetCategories' callback is set */
624 | if (isset($this->_dms->callbacks['onPostSetCategories'])) {
625 | foreach ($this->_dms->callbacks['onPostSetCategories'] as $callback) {
626 | $ret = call_user_func($callback[0], $callback[1], $this, $oldCategories);
627 | if (is_bool($ret))
628 | return $ret;
629 | }
630 | }
631 |
632 | return true;
633 | } /* }}} */
634 |
635 | /**
636 | * Add a list of categories to the document
637 | *
638 | * This method will add a list of new categories to the document.
639 | *
640 | * @param array $newCategories list of category objects
641 | */
642 | public function addCategories($newCategories) { /* {{{ */
643 | $db = $this->_dms->getDB();
644 |
645 | /* Check if 'onPreAddCategories' callback is set */
646 | if (isset($this->_dms->callbacks['onPreAddCategories'])) {
647 | foreach ($this->_dms->callbacks['onPreAddCategories'] as $callback) {
648 | $ret = call_user_func($callback[0], $callback[1], $this, $newCategories);
649 | if (is_bool($ret))
650 | return $ret;
651 | }
652 | }
653 |
654 | if (!$this->_categories)
655 | $this->getCategories();
656 |
657 | $catids = array();
658 | foreach ($this->_categories as $cat)
659 | $catids[] = $cat->getID();
660 |
661 | $db->startTransaction();
662 | $ncat = array(); // Array containing actually added new categories
663 | foreach ($newCategories as $cat) {
664 | if (!in_array($cat->getID(), $catids)) {
665 | $queryStr = "INSERT INTO `tblDocumentCategory` (`categoryID`, `documentID`) VALUES (". $cat->getId() .", ". $this->_id .")";
666 | if (!$db->getResult($queryStr)) {
667 | $db->rollbackTransaction();
668 | return false;
669 | }
670 | $ncat[] = $cat;
671 | }
672 | }
673 | $db->commitTransaction();
674 |
675 | $oldCategories = $this->_categories;
676 | $this->_categories = array_merge($this->_categories, $ncat);
677 |
678 | /* Check if 'onPostAddCategories' callback is set */
679 | if (isset($this->_dms->callbacks['onPostAddCategories'])) {
680 | foreach ($this->_dms->callbacks['onPostAddCategories'] as $callback) {
681 | $ret = call_user_func($callback[0], $callback[1], $this, $oldCategories);
682 | if (is_bool($ret))
683 | return $ret;
684 | }
685 | }
686 |
687 | return true;
688 | } /* }}} */
689 |
690 | /**
691 | * Remove a list of categories from the document
692 | *
693 | * This method will remove a list of assigned categories to the document.
694 | *
695 | * @param array $newCategories list of category objects
696 | */
697 | public function removeCategories($categories) { /* {{{ */
698 | $db = $this->_dms->getDB();
699 |
700 | /* Check if 'onPreRemoveCategories' callback is set */
701 | if (isset($this->_dms->callbacks['onPreRemoveCategories'])) {
702 | foreach ($this->_dms->callbacks['onPreRemoveCategories'] as $callback) {
703 | $ret = call_user_func($callback[0], $callback[1], $this, $categories);
704 | if (is_bool($ret))
705 | return $ret;
706 | }
707 | }
708 |
709 | $catids = array();
710 | foreach ($categories as $cat)
711 | $catids[] = $cat->getID();
712 |
713 | $queryStr = "DELETE FROM `tblDocumentCategory` WHERE `documentID` = ". $this->_id ." AND `categoryID` IN (".implode(',', $catids).")";
714 | if (!$db->getResult($queryStr)) {
715 | return false;
716 | }
717 |
718 | $oldCategories = $this->_categories;
719 | $this->_categories = null;
720 |
721 | /* Check if 'onPostRemoveCategories' callback is set */
722 | if (isset($this->_dms->callbacks['onPostRemoveCategories'])) {
723 | foreach ($this->_dms->callbacks['onPostRemoveCategories'] as $callback) {
724 | $ret = call_user_func($callback[0], $callback[1], $this, $oldCategories);
725 | if (is_bool($ret))
726 | return $ret;
727 | }
728 | }
729 |
730 | return true;
731 | } /* }}} */
732 |
733 | /**
734 | * Return creation date of the document
735 | *
736 | * @return integer unix timestamp of creation date
737 | */
738 | public function getDate() { /* {{{ */
739 | return $this->_date;
740 | } /* }}} */
741 |
742 | /**
743 | * Set creation date of the document
744 | *
745 | * @param integer $date timestamp of creation date. If false then set it
746 | * to the current timestamp
747 | * @return boolean true on success
748 | */
749 | public function setDate($date) { /* {{{ */
750 | $db = $this->_dms->getDB();
751 |
752 | if (!$date)
753 | $date = time();
754 | else {
755 | if (!is_numeric($date))
756 | return false;
757 | }
758 |
759 | $queryStr = "UPDATE `tblDocuments` SET `date` = " . (int) $date . " WHERE `id` = ". $this->_id;
760 | if (!$db->getResult($queryStr))
761 | return false;
762 | $this->_date = $date;
763 | return true;
764 | } /* }}} */
765 |
766 | /**
767 | * Check, if this document is a child of a given folder
768 | *
769 | * @param object $folder parent folder
770 | * @return boolean true if document is a direct child of the given folder
771 | */
772 | public function isDescendant($folder) { /* {{{ */
773 | /* First check if the parent folder is folder looking for */
774 | if ($this->getFolder()->getID() == $folder->getID())
775 | return true;
776 | /* Second, check for the parent folder of this document to be
777 | * below the given folder
778 | */
779 | if ($this->getFolder()->isDescendant($folder))
780 | return true;
781 | return false;
782 | } /* }}} */
783 |
784 | /**
785 | * Return the parent folder of the document
786 | *
787 | * @see SeedDMS_Core_Document::getFolder()
788 | *
789 | * @return SeedDMS_Core_Folder parent folder
790 | */
791 | public function getParent() { /* {{{ */
792 | return $this->getFolder();
793 | } /* }}} */
794 |
795 | /**
796 | * Return the parent folder of the document
797 | *
798 | * @return SeedDMS_Core_Folder parent folder
799 | */
800 | public function getFolder() { /* {{{ */
801 | if (!isset($this->_folder))
802 | $this->_folder = $this->_dms->getFolder($this->_folderID);
803 | return $this->_folder;
804 | } /* }}} */
805 |
806 | /**
807 | * Set folder of a document
808 | *
809 | * This method basically moves a document from a folder to another
810 | * folder.
811 | *
812 | * @param SeedDMS_Core_Folder $newFolder
813 | * @return boolean false in case of an error, otherwise true
814 | */
815 | public function setParent($newFolder) { /* {{{ */
816 | return $this->setFolder($newFolder);
817 | } /* }}} */
818 |
819 | /**
820 | * Set folder of a document
821 | *
822 | * This method basically moves a document from a folder to another
823 | * folder.
824 | *
825 | * @param SeedDMS_Core_Folder $newFolder
826 | * @return boolean false in case of an error, otherwise true
827 | */
828 | public function setFolder($newFolder) { /* {{{ */
829 | $db = $this->_dms->getDB();
830 |
831 | if (!$newFolder)
832 | return false;
833 |
834 | if (!$newFolder->isType('folder'))
835 | return false;
836 |
837 | /* Check if 'onPreSetFolder' callback is set */
838 | if (isset($this->_dms->callbacks['onPreSetFolder'])) {
839 | foreach ($this->_dms->callbacks['onPreSetFolder'] as $callback) {
840 | $ret = call_user_func($callback[0], $callback[1], $this, $newFolder);
841 | if (is_bool($ret))
842 | return $ret;
843 | }
844 | }
845 |
846 | $db->startTransaction();
847 |
848 | $queryStr = "UPDATE `tblDocuments` SET `folder` = " . $newFolder->getID() . " WHERE `id` = ". $this->_id;
849 | if (!$db->getResult($queryStr)) {
850 | $db->rollbackTransaction();
851 | return false;
852 | }
853 |
854 | // Make sure that the folder search path is also updated.
855 | $path = $newFolder->getPath();
856 | $flist = "";
857 | /** @var SeedDMS_Core_Folder[] $path */
858 | foreach ($path as $f) {
859 | $flist .= ":".$f->getID();
860 | }
861 | if (strlen($flist)>1) {
862 | $flist .= ":";
863 | }
864 | $queryStr = "UPDATE `tblDocuments` SET `folderList` = '" . $flist . "' WHERE `id` = ". $this->_id;
865 | if (!$db->getResult($queryStr)) {
866 | $db->rollbackTransaction();
867 | return false;
868 | }
869 |
870 | $db->commitTransaction();
871 |
872 | $oldFolder = $this->_folder;
873 | $this->_folderID = $newFolder->getID();
874 | $this->_folder = $newFolder;
875 |
876 | /* Check if 'onPostSetFolder' callback is set */
877 | if (isset($this->_dms->callbacks['onPostSetFolder'])) {
878 | foreach ($this->_dms->callbacks['onPostSetFolder'] as $callback) {
879 | $ret = call_user_func($callback[0], $callback[1], $this, $oldFolder);
880 | if (is_bool($ret))
881 | return $ret;
882 | }
883 | }
884 |
885 | return true;
886 | } /* }}} */
887 |
888 | /**
889 | * Return owner of document
890 | *
891 | * @return SeedDMS_Core_User owner of document as an instance of {@see SeedDMS_Core_User}
892 | */
893 | public function getOwner() { /* {{{ */
894 | if (!isset($this->_owner))
895 | $this->_owner = $this->_dms->getUser($this->_ownerID);
896 | return $this->_owner;
897 | } /* }}} */
898 |
899 | /**
900 | * Set owner of a document
901 | *
902 | * @param SeedDMS_Core_User $newOwner new owner
903 | * @return boolean true if successful otherwise false
904 | */
905 | public function setOwner($newOwner) { /* {{{ */
906 | $db = $this->_dms->getDB();
907 |
908 | if (!$newOwner)
909 | return false;
910 |
911 | if (!$newOwner->isType('user'))
912 | return false;
913 |
914 | /* Check if 'onPreSetOwner' callback is set */
915 | if (isset($this->_dms->callbacks['onPreSetOwner'])) {
916 | foreach ($this->_dms->callbacks['onPreSetOwner'] as $callback) {
917 | $ret = call_user_func($callback[0], $callback[1], $this, $newOwner);
918 | if (is_bool($ret))
919 | return $ret;
920 | }
921 | }
922 |
923 | $queryStr = "UPDATE `tblDocuments` set `owner` = " . $newOwner->getID() . " WHERE `id` = " . $this->_id;
924 | if (!$db->getResult($queryStr))
925 | return false;
926 |
927 | $oldOwner = $this->_owner;
928 | $this->_ownerID = $newOwner->getID();
929 | $this->_owner = $newOwner;
930 |
931 | $this->_readAccessList = array();
932 |
933 | /* Check if 'onPostSetOwner' callback is set */
934 | if (isset($this->_dms->callbacks['onPostSetOwner'])) {
935 | foreach ($this->_dms->callbacks['onPostSetOwner'] as $callback) {
936 | $ret = call_user_func($callback[0], $callback[1], $this, $oldOwner);
937 | if (is_bool($ret))
938 | return $ret;
939 | }
940 | }
941 |
942 | return true;
943 | } /* }}} */
944 |
945 | /**
946 | * @return bool|int
947 | */
948 | public function getDefaultAccess() { /* {{{ */
949 | if ($this->inheritsAccess()) {
950 | $res = $this->getFolder();
951 | if (!$res) return false;
952 | return $this->_folder->getDefaultAccess();
953 | }
954 | return $this->_defaultAccess;
955 | } /* }}} */
956 |
957 | /**
958 | * Set default access mode
959 | *
960 | * This method sets the default access mode and also removes all notifiers which
961 | * will not have read access anymore. Setting a default access mode will only
962 | * have an immediate effect if the access rights are not inherited, otherwise
963 | * it just updates the database record of the document and once the
964 | * inheritance is turn off the default access mode will take effect.
965 | *
966 | * @param integer $mode access mode
967 | * @param bool|string $noclean set to true if notifier list shall not be clean up
968 | *
969 | * @return bool
970 | */
971 | public function setDefaultAccess($mode, $noclean = false) { /* {{{ */
972 | $db = $this->_dms->getDB();
973 |
974 | if ($mode < M_LOWEST_RIGHT || $mode > M_HIGHEST_RIGHT)
975 | return false;
976 |
977 | $queryStr = "UPDATE `tblDocuments` set `defaultAccess` = " . (int) $mode . " WHERE `id` = " . $this->_id;
978 | if (!$db->getResult($queryStr))
979 | return false;
980 |
981 | $this->_defaultAccess = $mode;
982 | $this->_readAccessList = array();
983 |
984 | /* Setting the default access mode does not have any effect if access
985 | * is still inherited. In that case there is no need to clean the
986 | * notification list.
987 | */
988 | if (!$noclean && !$this->_inheritAccess)
989 | $this->cleanNotifyList();
990 |
991 | return true;
992 | } /* }}} */
993 |
994 | /**
995 | * @return bool
996 | */
997 | public function inheritsAccess() { return $this->_inheritAccess; }
998 |
999 | /**
1000 | * This is supposed to be a replacement for inheritsAccess()
1001 | *
1002 | * @return bool
1003 | */
1004 | public function getInheritAccess() { return $this->_inheritAccess; }
1005 |
1006 | /**
1007 | * Set inherited access mode
1008 | *
1009 | * Setting inherited access mode will set or unset the internal flag which
1010 | * controls if the access mode is inherited from the parent folder or not.
1011 | * It will not modify the
1012 | * access control list for the current object. It will remove all
1013 | * notifications of users which do not even have read access anymore
1014 | * after setting or unsetting inherited access.
1015 | *
1016 | * @param boolean $inheritAccess set to true for setting and false for
1017 | * unsetting inherited access mode
1018 | * @param boolean $noclean set to true if notifier list shall not be clean up
1019 | * @return boolean true if operation was successful otherwise false
1020 | */
1021 | public function setInheritAccess($inheritAccess, $noclean = false) { /* {{{ */
1022 | $db = $this->_dms->getDB();
1023 |
1024 | $queryStr = "UPDATE `tblDocuments` SET `inheritAccess` = " . ($inheritAccess ? "1" : "0") . " WHERE `id` = " . $this->_id;
1025 | if (!$db->getResult($queryStr))
1026 | return false;
1027 |
1028 | $this->_inheritAccess = ($inheritAccess ? true : false);
1029 | $this->_readAccessList = array();
1030 |
1031 | if (!$noclean)
1032 | $this->cleanNotifyList();
1033 |
1034 | return true;
1035 | } /* }}} */
1036 |
1037 | /**
1038 | * Check if document expires
1039 | *
1040 | * @return boolean true if document has expiration date set, otherwise false
1041 | */
1042 | public function expires() { /* {{{ */
1043 | if (intval($this->_expires) == 0)
1044 | return false;
1045 | else
1046 | return true;
1047 | } /* }}} */
1048 |
1049 | /**
1050 | * Get expiration time of document
1051 | *
1052 | * @return integer/boolean expiration date as unix timestamp or false
1053 | */
1054 | public function getExpires() { /* {{{ */
1055 | if (intval($this->_expires) == 0)
1056 | return false;
1057 | else
1058 | return $this->_expires;
1059 | } /* }}} */
1060 |
1061 | /**
1062 | * Set expiration date as unix timestamp
1063 | *
1064 | * @param integer $expires unix timestamp of expiration date
1065 | * @return bool
1066 | */
1067 | public function setExpires($expires) { /* {{{ */
1068 | $db = $this->_dms->getDB();
1069 |
1070 | $expires = (!$expires) ? 0 : $expires;
1071 |
1072 | if ($expires == $this->_expires) {
1073 | // No change is necessary.
1074 | return true;
1075 | }
1076 |
1077 | $queryStr = "UPDATE `tblDocuments` SET `expires` = " . (int) $expires . " WHERE `id` = " . $this->_id;
1078 | if (!$db->getResult($queryStr))
1079 | return false;
1080 |
1081 | $this->_expires = $expires;
1082 | return true;
1083 | } /* }}} */
1084 |
1085 | /**
1086 | * Check if the document has expired
1087 | *
1088 | * The method expects to database field 'expired' to hold the timestamp
1089 | * of the start of day at which end the document expires. The document will
1090 | * expire if that day is over. Hence, a document will *not*
1091 | * be expired during the day of expiration but at the end of that day
1092 | *
1093 | * @return boolean true if document has expired otherwise false
1094 | */
1095 | public function hasExpired() { /* {{{ */
1096 | if (intval($this->_expires) == 0) return false;
1097 | if (time()>=$this->_expires+24*60*60) return true;
1098 | return false;
1099 | } /* }}} */
1100 |
1101 | /**
1102 | * Check if the document has expired and set the status accordingly
1103 | *
1104 | * It will also recalculate the status if the current status is
1105 | * set to S_EXPIRED but the document isn't actually expired.
1106 | * The method will update the document status log database table
1107 | * if needed.
1108 | * FIXME: some left over reviewers/approvers are in the way if
1109 | * no workflow is set and traditional workflow mode is on. In that
1110 | * case the status is set to S_DRAFT_REV or S_DRAFT_APP
1111 | *
1112 | * @return boolean true if status has changed
1113 | */
1114 | public function verifyLastestContentExpriry() { /* {{{ */
1115 | $lc = $this->getLatestContent();
1116 | if ($lc) {
1117 | $st = $lc->getStatus();
1118 |
1119 | if (($st["status"]==S_DRAFT_REV || $st["status"]==S_DRAFT_APP || $st["status"]==S_IN_WORKFLOW || $st["status"]==S_RELEASED) && $this->hasExpired()) {
1120 | return $lc->setStatus(S_EXPIRED, "", $this->getOwner());
1121 | }
1122 | elseif ($st["status"]==S_EXPIRED && !$this->hasExpired()) {
1123 | $lc->verifyStatus(true, $this->getOwner());
1124 | return true;
1125 | }
1126 | }
1127 | return false;
1128 | } /* }}} */
1129 |
1130 | /**
1131 | * Check if document is locked
1132 | *
1133 | * @return boolean true if locked otherwise false
1134 | */
1135 | public function isLocked() { return $this->_locked != -1; }
1136 |
1137 | /**
1138 | * Lock or unlock document
1139 | *
1140 | * @param SeedDMS_Core_User|bool $falseOrUser user object for locking or false for unlocking
1141 | * @return boolean true if operation was successful otherwise false
1142 | */
1143 | public function setLocked($falseOrUser) { /* {{{ */
1144 | $db = $this->_dms->getDB();
1145 |
1146 | $lockUserID = -1;
1147 | if (is_bool($falseOrUser) && !$falseOrUser) {
1148 | $queryStr = "DELETE FROM `tblDocumentLocks` WHERE `document` = ".$this->_id;
1149 | }
1150 | elseif (is_object($falseOrUser)) {
1151 | $queryStr = "INSERT INTO `tblDocumentLocks` (`document`, `userID`) VALUES (".$this->_id.", ".$falseOrUser->getID().")";
1152 | $lockUserID = $falseOrUser->getID();
1153 | }
1154 | else {
1155 | return false;
1156 | }
1157 | if (!$db->getResult($queryStr)) {
1158 | return false;
1159 | }
1160 | unset($this->_lockingUser);
1161 | $this->_locked = $lockUserID;
1162 | return true;
1163 | } /* }}} */
1164 |
1165 | /**
1166 | * Get the user currently locking the document
1167 | *
1168 | * @return SeedDMS_Core_User|bool user have a lock
1169 | */
1170 | public function getLockingUser() { /* {{{ */
1171 | if (!$this->isLocked())
1172 | return false;
1173 |
1174 | if (!isset($this->_lockingUser))
1175 | $this->_lockingUser = $this->_dms->getUser($this->_locked);
1176 | return $this->_lockingUser;
1177 | } /* }}} */
1178 |
1179 | /**
1180 | * @return float
1181 | */
1182 | public function getSequence() { return $this->_sequence; }
1183 |
1184 | /**
1185 | * @param float $seq
1186 | * @return bool
1187 | */
1188 | public function setSequence($seq) { /* {{{ */
1189 | $db = $this->_dms->getDB();
1190 |
1191 | $queryStr = "UPDATE `tblDocuments` SET `sequence` = " . $seq . " WHERE `id` = " . $this->_id;
1192 | if (!$db->getResult($queryStr))
1193 | return false;
1194 |
1195 | $this->_sequence = $seq;
1196 | return true;
1197 | } /* }}} */
1198 |
1199 | /**
1200 | * Delete all entries for this document from the access control list
1201 | *
1202 | * @param boolean $noclean set to true if notifier list shall not be clean up
1203 | * @return boolean true if operation was successful otherwise false
1204 | */
1205 | public function clearAccessList($noclean = false) { /* {{{ */
1206 | $db = $this->_dms->getDB();
1207 |
1208 | $queryStr = "DELETE FROM `tblACLs` WHERE `targetType` = " . T_DOCUMENT . " AND `target` = " . $this->_id;
1209 | if (!$db->getResult($queryStr))
1210 | return false;
1211 |
1212 | unset($this->_accessList);
1213 | $this->_readAccessList = array();
1214 |
1215 | if (!$noclean)
1216 | $this->cleanNotifyList();
1217 |
1218 | return true;
1219 | } /* }}} */
1220 |
1221 | /**
1222 | * Returns a list of access privileges
1223 | *
1224 | * If the document inherits the access privileges from the parent folder
1225 | * those will be returned.
1226 | * $mode and $op can be set to restrict the list of returned access
1227 | * privileges. If $mode is set to M_ANY no restriction will apply
1228 | * regardless of the value of $op. The returned array contains a list
1229 | * of {@see SeedDMS_Core_UserAccess} and
1230 | * {@see SeedDMS_Core_GroupAccess} objects. Even if the document
1231 | * has no access list the returned array contains the two elements
1232 | * 'users' and 'groups' which are than empty. The methode returns false
1233 | * if the function fails.
1234 | *
1235 | * @param int $mode access mode (defaults to M_ANY)
1236 | * @param int|string $op operation (defaults to O_EQ)
1237 | * @return bool|array
1238 | */
1239 | public function getAccessList($mode = M_ANY, $op = O_EQ) { /* {{{ */
1240 | $db = $this->_dms->getDB();
1241 |
1242 | if ($this->inheritsAccess()) {
1243 | $res = $this->getFolder();
1244 | if (!$res) return false;
1245 | return $this->_folder->getAccessList($mode, $op);
1246 | }
1247 |
1248 | if (!isset($this->_accessList[$mode])) {
1249 | if ($op!=O_GTEQ && $op!=O_LTEQ && $op!=O_EQ) {
1250 | return false;
1251 | }
1252 | $modeStr = "";
1253 | if ($mode!=M_ANY) {
1254 | $modeStr = " AND `mode`".$op.(int)$mode;
1255 | }
1256 | $queryStr = "SELECT * FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT.
1257 | " AND `target` = " . $this->_id . $modeStr . " ORDER BY `targetType`";
1258 | $resArr = $db->getResultArray($queryStr);
1259 | if (is_bool($resArr) && !$resArr)
1260 | return false;
1261 |
1262 | $this->_accessList[$mode] = array("groups" => array(), "users" => array());
1263 | foreach ($resArr as $row) {
1264 | if ($row["userID"] != -1)
1265 | array_push($this->_accessList[$mode]["users"], new SeedDMS_Core_UserAccess($this->_dms->getUser($row["userID"]), (int) $row["mode"]));
1266 | else //if ($row["groupID"] != -1)
1267 | array_push($this->_accessList[$mode]["groups"], new SeedDMS_Core_GroupAccess($this->_dms->getGroup($row["groupID"]), (int) $row["mode"]));
1268 | }
1269 | }
1270 |
1271 | return $this->_accessList[$mode];
1272 | } /* }}} */
1273 |
1274 | /**
1275 | * Add access right to document
1276 | *
1277 | * This method may change in the future. Instead of passing a flag
1278 | * and a user/group id a user or group object will be expected.
1279 | * Starting with version 5.1.25 this method will first check if there
1280 | * is already an access right for the user/group.
1281 | *
1282 | * @param integer $mode access mode
1283 | * @param integer $userOrGroupID id of user or group
1284 | * @param integer $isUser set to 1 if $userOrGroupID is the id of a
1285 | * user otherwise it will be considered a group id
1286 | * @return bool true on success, otherwise false
1287 | */
1288 | public function addAccess($mode, $userOrGroupID, $isUser) { /* {{{ */
1289 | $db = $this->_dms->getDB();
1290 |
1291 | if ($mode < M_NONE || $mode > M_ALL)
1292 | return false;
1293 |
1294 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`";
1295 |
1296 | /* Adding a second access right will return false */
1297 | $queryStr = "SELECT * FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT.
1298 | " AND `target` = " . $this->_id . " AND ". $userOrGroup . " = ".$userOrGroupID;
1299 | $resArr = $db->getResultArray($queryStr);
1300 | if (is_bool($resArr) || $resArr)
1301 | return false;
1302 |
1303 | $queryStr = "INSERT INTO `tblACLs` (`target`, `targetType`, ".$userOrGroup.", `mode`) VALUES
1304 | (".$this->_id.", ".T_DOCUMENT.", " . (int) $userOrGroupID . ", " .(int) $mode. ")";
1305 | if (!$db->getResult($queryStr))
1306 | return false;
1307 |
1308 | unset($this->_accessList);
1309 | $this->_readAccessList = array();
1310 |
1311 | // Update the notify list, if necessary.
1312 | if ($mode == M_NONE) {
1313 | $this->removeNotify($userOrGroupID, $isUser);
1314 | }
1315 |
1316 | return true;
1317 | } /* }}} */
1318 |
1319 | /**
1320 | * Change access right of document
1321 | *
1322 | * This method may change in the future. Instead of passing a flag
1323 | * and a user/group id a user or group object will be expected.
1324 | *
1325 | * @param integer $newMode access mode
1326 | * @param integer $userOrGroupID id of user or group
1327 | * @param integer $isUser set to 1 if $userOrGroupID is the id of a
1328 | * user otherwise it will be considered a group id
1329 | * @return bool true on success, otherwise false
1330 | */
1331 | public function changeAccess($newMode, $userOrGroupID, $isUser) { /* {{{ */
1332 | $db = $this->_dms->getDB();
1333 |
1334 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`";
1335 |
1336 | /* Get the old access right */
1337 | $queryStr = "SELECT * FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT.
1338 | " AND `target` = " . $this->_id . " AND ". $userOrGroup . " = ". (int) $userOrGroupID;
1339 | $resArr = $db->getResultArray($queryStr);
1340 | if (!$resArr)
1341 | return false;
1342 |
1343 | $oldmode = $resArr[0]['mode'];
1344 |
1345 | $queryStr = "UPDATE `tblACLs` SET `mode` = " . (int) $newMode . " WHERE `targetType` = ".T_DOCUMENT." AND `target` = " . $this->_id . " AND " . $userOrGroup . " = " . (int) $userOrGroupID;
1346 | if (!$db->getResult($queryStr))
1347 | return false;
1348 |
1349 | unset($this->_accessList);
1350 | $this->_readAccessList = array();
1351 |
1352 | // Update the notify list, if necessary.
1353 | if ($newMode == M_NONE) {
1354 | $this->removeNotify($userOrGroupID, $isUser);
1355 | }
1356 |
1357 | return $oldmode;
1358 | } /* }}} */
1359 |
1360 | /**
1361 | * Remove access rights for a user or group
1362 | *
1363 | * @param integer $userOrGroupID ID of user or group
1364 | * @param boolean $isUser true if $userOrGroupID is a user id, false if it
1365 | * is a group id.
1366 | * @return boolean true on success, otherwise false
1367 | */
1368 | public function removeAccess($userOrGroupID, $isUser) { /* {{{ */
1369 | $db = $this->_dms->getDB();
1370 |
1371 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`";
1372 |
1373 | /* Get the old access right */
1374 | $queryStr = "SELECT * FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT.
1375 | " AND `target` = " . $this->_id . " AND ". $userOrGroup . " = ". (int) $userOrGroupID;
1376 | $resArr = $db->getResultArray($queryStr);
1377 | if (!$resArr)
1378 | return false;
1379 |
1380 | $queryStr = "DELETE FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT." AND `target` = ".$this->_id." AND ".$userOrGroup." = " . (int) $userOrGroupID;
1381 | if (!$db->getResult($queryStr))
1382 | return false;
1383 |
1384 | unset($this->_accessList);
1385 | $this->_readAccessList = array();
1386 |
1387 | // Update the notify list, if the user looses access rights.
1388 | $mode = ($isUser ? $this->getAccessMode($this->_dms->getUser($userOrGroupID)) : $this->getGroupAccessMode($this->_dms->getGroup($userOrGroupID)));
1389 | if ($mode == M_NONE) {
1390 | $this->removeNotify($userOrGroupID, $isUser);
1391 | }
1392 |
1393 | return true;
1394 | } /* }}} */
1395 |
1396 | /**
1397 | * Returns the greatest access privilege for a given user
1398 | *
1399 | * This method returns the access mode for a given user. An administrator
1400 | * and the owner of the folder has unrestricted access. A guest user has
1401 | * read only access or no access if access rights are further limited
1402 | * by access control lists. All other users have access rights according
1403 | * to the access control lists or the default access. This method will
1404 | * recursive check for access rights of parent folders if access rights
1405 | * are inherited.
1406 | *
1407 | * The function searches the access control list for entries of
1408 | * user $user. If it finds more than one entry it will return the
1409 | * one allowing the greatest privileges, but user rights will always
1410 | * precede group rights. If there is no entry in the
1411 | * access control list, it will return the default access mode.
1412 | * The function takes inherited access rights into account.
1413 | * For a list of possible access rights see @file inc.AccessUtils.php
1414 | *
1415 | * Having access on a document does not necessarily mean the document
1416 | * content is accessible too. Accessing the content is checked by
1417 | * {@see SeedDMS_Core_DocumentContent::getAccessMode()} which calls
1418 | * a callback function defined by the application. If the callback
1419 | * function is not set, access on the content is always granted.
1420 | *
1421 | * Before checking the access in the method itself a callback 'onCheckAccessDocument'
1422 | * is called. If it returns a value > 0, then this will be returned by this
1423 | * method without any further checks. The optional paramater $context
1424 | * will be passed as a third parameter to the callback. It contains
1425 | * the operation for which the access mode is retrieved. It is for example
1426 | * set to 'removeDocument' if the access mode is used to check for sufficient
1427 | * permission on deleting a document.
1428 | *
1429 | * @param $user object instance of class SeedDMS_Core_User
1430 | * @param string $context context in which the access mode is requested
1431 | * @return integer access mode
1432 | */
1433 | public function getAccessMode($user, $context = '') { /* {{{ */
1434 | if (!$user)
1435 | return M_NONE;
1436 |
1437 | /* Check if 'onCheckAccessDocument' callback is set */
1438 | if (isset($this->_dms->callbacks['onCheckAccessDocument'])) {
1439 | foreach ($this->_dms->callbacks['onCheckAccessDocument'] as $callback) {
1440 | if (($ret = call_user_func($callback[0], $callback[1], $this, $user, $context)) > 0) {
1441 | return $ret;
1442 | }
1443 | }
1444 | }
1445 |
1446 | /* Administrators have unrestricted access */
1447 | if ($user->isAdmin()) return M_ALL;
1448 |
1449 | /* The owner of the document has unrestricted access */
1450 | if ($user->getID() == $this->_ownerID) return M_ALL;
1451 |
1452 | if ($this->_dms->memcache) {
1453 | $ck = "am:d".$this->_id.":".$user->getId().":".$context;
1454 | if ($cobj = $this->_dms->memcache->get($ck))
1455 | return $cobj;
1456 | }
1457 |
1458 | /* Check ACLs */
1459 | $accessList = $this->getAccessList();
1460 | if (!$accessList) return false;
1461 |
1462 | /** @var SeedDMS_Core_UserAccess $userAccess */
1463 | foreach ($accessList["users"] as $userAccess) {
1464 | if ($userAccess->getUserID() == $user->getID()) {
1465 | $mode = $userAccess->getMode();
1466 | if ($user->isGuest()) {
1467 | if ($mode >= M_READ) $mode = M_READ;
1468 | }
1469 | if ($this->_dms->memcache)
1470 | $this->_dms->memcache->set($ck, $mode, 600);
1471 | return $mode;
1472 | }
1473 | }
1474 |
1475 | /* Get the highest right defined by a group */
1476 | if ($accessList['groups']) {
1477 | $mode = 0;
1478 | /** @var SeedDMS_Core_GroupAccess $groupAccess */
1479 | foreach ($accessList["groups"] as $groupAccess) {
1480 | if ($user->isMemberOfGroup($groupAccess->getGroup())) {
1481 | if ($groupAccess->getMode() > $mode)
1482 | $mode = $groupAccess->getMode();
1483 | }
1484 | }
1485 | if ($mode) {
1486 | if ($user->isGuest()) {
1487 | if ($mode >= M_READ) $mode = M_READ;
1488 | }
1489 | if ($this->_dms->memcache)
1490 | $this->_dms->memcache->set($ck, $mode, 600);
1491 | return $mode;
1492 | }
1493 | }
1494 |
1495 | $mode = $this->getDefaultAccess();
1496 | if ($user->isGuest()) {
1497 | if ($mode >= M_READ) $mode = M_READ;
1498 | }
1499 | if ($this->_dms->memcache)
1500 | $this->_dms->memcache->set($ck, $mode, 600);
1501 | return $mode;
1502 | } /* }}} */
1503 |
1504 | /**
1505 | * Returns the greatest access privilege for a given group
1506 | *
1507 | * This method searches the access control list for entries of
1508 | * group $group. If it finds more than one entry it will return the
1509 | * one allowing the greatest privileges. If there is no entry in the
1510 | * access control list, it will return the default access mode.
1511 | * The function takes inherited access rights into account.
1512 | * For a list of possible access rights see @file inc.AccessUtils.php
1513 | *
1514 | * @param SeedDMS_Core_Group $group object instance of class SeedDMS_Core_Group
1515 | * @return integer access mode
1516 | */
1517 | public function getGroupAccessMode($group) { /* {{{ */
1518 | $highestPrivileged = M_NONE;
1519 |
1520 | //ACLs durchforsten
1521 | $foundInACL = false;
1522 | $accessList = $this->getAccessList();
1523 | if (!$accessList)
1524 | return false;
1525 |
1526 | /** @var SeedDMS_Core_GroupAccess $groupAccess */
1527 | foreach ($accessList["groups"] as $groupAccess) {
1528 | if ($groupAccess->getGroupID() == $group->getID()) {
1529 | $foundInACL = true;
1530 | if ($groupAccess->getMode() > $highestPrivileged)
1531 | $highestPrivileged = $groupAccess->getMode();
1532 | if ($highestPrivileged == M_ALL) // max access right -> skip the rest
1533 | return $highestPrivileged;
1534 | }
1535 | }
1536 |
1537 | if ($foundInACL)
1538 | return $highestPrivileged;
1539 |
1540 | //Standard-Berechtigung verwenden
1541 | return $this->getDefaultAccess();
1542 | } /* }}} */
1543 |
1544 | /**
1545 | * Returns a list of all notifications
1546 | *
1547 | * The returned list has two elements called 'users' and 'groups'. Each one
1548 | * is an array itself countaining objects of class SeedDMS_Core_User and
1549 | * SeedDMS_Core_Group.
1550 | *
1551 | * @param integer $type type of notification (not yet used)
1552 | * @param bool $incdisabled set to true if disabled user shall be included
1553 | * @return array|bool
1554 | */
1555 | public function getNotifyList($type = 0, $incdisabled = false) { /* {{{ */
1556 | if (empty($this->_notifyList)) {
1557 | $db = $this->_dms->getDB();
1558 |
1559 | $queryStr ="SELECT * FROM `tblNotify` WHERE `targetType` = " . T_DOCUMENT . " AND `target` = " . $this->_id;
1560 | $resArr = $db->getResultArray($queryStr);
1561 | if (is_bool($resArr) && $resArr == false)
1562 | return false;
1563 |
1564 | $this->_notifyList = array("groups" => array(), "users" => array());
1565 | foreach ($resArr as $row)
1566 | {
1567 | if ($row["userID"] != -1) {
1568 | $u = $this->_dms->getUser($row["userID"]);
1569 | if ($u && (!$u->isDisabled() || $incdisabled))
1570 | array_push($this->_notifyList["users"], $u);
1571 | } else { //if ($row["groupID"] != -1)
1572 | $g = $this->_dms->getGroup($row["groupID"]);
1573 | if ($g)
1574 | array_push($this->_notifyList["groups"], $g);
1575 | }
1576 | }
1577 | }
1578 | return $this->_notifyList;
1579 | } /* }}} */
1580 |
1581 | /**
1582 | * Make sure only users/groups with read access are in the notify list
1583 | *
1584 | */
1585 | public function cleanNotifyList() { /* {{{ */
1586 | // If any of the notification subscribers no longer have read access,
1587 | // remove their subscription.
1588 | if (empty($this->_notifyList))
1589 | $this->getNotifyList();
1590 |
1591 | /* Make a copy of both notifier lists because removeNotify will empty
1592 | * $this->_notifyList and the second foreach will not work anymore.
1593 | */
1594 | /** @var SeedDMS_Core_User[] $nusers */
1595 | $nusers = $this->_notifyList["users"];
1596 | /** @var SeedDMS_Core_Group[] $ngroups */
1597 | $ngroups = $this->_notifyList["groups"];
1598 | foreach ($nusers as $u) {
1599 | if ($this->getAccessMode($u) < M_READ) {
1600 | $this->removeNotify($u->getID(), true);
1601 | }
1602 | }
1603 | foreach ($ngroups as $g) {
1604 | if ($this->getGroupAccessMode($g) < M_READ) {
1605 | $this->removeNotify($g->getID(), false);
1606 | }
1607 | }
1608 | } /* }}} */
1609 |
1610 | /**
1611 | * Add a user/group to the notification list
1612 | *
1613 | * This method does not check if the currently logged in user
1614 | * is allowed to add a notification. This must be checked by the calling
1615 | * application.
1616 | *
1617 | * @param $userOrGroupID integer id of user or group to add
1618 | * @param $isUser integer 1 if $userOrGroupID is a user,
1619 | * 0 if $userOrGroupID is a group
1620 | * @return integer 0: Update successful.
1621 | * -1: Invalid User/Group ID.
1622 | * -2: Target User / Group does not have read access.
1623 | * -3: User is already subscribed.
1624 | * -4: Database / internal error.
1625 | */
1626 | public function addNotify($userOrGroupID, $isUser) { /* {{{ */
1627 | $db = $this->_dms->getDB();
1628 |
1629 | $userOrGroup = ($isUser ? "`userID`" : "`groupID`");
1630 |
1631 | /* Verify that user / group exists. */
1632 | $obj = ($isUser ? $this->_dms->getUser($userOrGroupID) : $this->_dms->getGroup($userOrGroupID));
1633 | if (!is_object($obj)) {
1634 | return -1;
1635 | }
1636 |
1637 | /* Verify that the requesting user has permission to add the target to
1638 | * the notification system.
1639 | */
1640 | /*
1641 | * The calling application should enforce the policy on who is allowed
1642 | * to add someone to the notification system. If is shall remain here
1643 | * the currently logged in user should be passed to this function
1644 | *
1645 | GLOBAL $user;
1646 | if ($user->isGuest()) {
1647 | return -2;
1648 | }
1649 | if (!$user->isAdmin()) {
1650 | if ($isUser) {
1651 | if ($user->getID() != $obj->getID()) {
1652 | return -2;
1653 | }
1654 | }
1655 | else {
1656 | if (!$obj->isMember($user)) {
1657 | return -2;
1658 | }
1659 | }
1660 | }
1661 | */
1662 |
1663 | /* Verify that target user / group has read access to the document. */
1664 | if ($isUser) {
1665 | // Users are straightforward to check.
1666 | if ($this->getAccessMode($obj) < M_READ) {
1667 | return -2;
1668 | }
1669 | }
1670 | else {
1671 | // Groups are a little more complex.
1672 | if ($this->getDefaultAccess() >= M_READ) {
1673 | // If the default access is at least READ-ONLY, then just make sure
1674 | // that the current group has not been explicitly excluded.
1675 | $acl = $this->getAccessList(M_NONE, O_EQ);
1676 | $found = false;
1677 | /** @var SeedDMS_Core_GroupAccess $group */
1678 | foreach ($acl["groups"] as $group) {
1679 | if ($group->getGroupID() == $userOrGroupID) {
1680 | $found = true;
1681 | break;
1682 | }
1683 | }
1684 | if ($found) {
1685 | return -2;
1686 | }
1687 | }
1688 | else {
1689 | // The default access is restricted. Make sure that the group has
1690 | // been explicitly allocated access to the document.
1691 | $acl = $this->getAccessList(M_READ, O_GTEQ);
1692 | if (is_bool($acl)) {
1693 | return -4;
1694 | }
1695 | $found = false;
1696 | /** @var SeedDMS_Core_GroupAccess $group */
1697 | foreach ($acl["groups"] as $group) {
1698 | if ($group->getGroupID() == $userOrGroupID) {
1699 | $found = true;
1700 | break;
1701 | }
1702 | }
1703 | if (!$found) {
1704 | return -2;
1705 | }
1706 | }
1707 | }
1708 | /* Check to see if user/group is already on the list. */
1709 | $queryStr = "SELECT * FROM `tblNotify` WHERE `tblNotify`.`target` = '".$this->_id."' ".
1710 | "AND `tblNotify`.`targetType` = '".T_DOCUMENT."' ".
1711 | "AND `tblNotify`.".$userOrGroup." = '".(int) $userOrGroupID."'";
1712 | $resArr = $db->getResultArray($queryStr);
1713 | if (is_bool($resArr)) {
1714 | return -4;
1715 | }
1716 | if (count($resArr)>0) {
1717 | return -3;
1718 | }
1719 |
1720 | $queryStr = "INSERT INTO `tblNotify` (`target`, `targetType`, " . $userOrGroup . ") VALUES (" . $this->_id . ", " . T_DOCUMENT . ", " . (int) $userOrGroupID . ")";
1721 | if (!$db->getResult($queryStr))
1722 | return -4;
1723 |
1724 | unset($this->_notifyList);
1725 | return 0;
1726 | } /* }}} */
1727 |
1728 | /**
1729 | * Remove a user or group from the notification list
1730 | *
1731 | * This method does not check if the currently logged in user
1732 | * is allowed to remove a notification. This must be checked by the calling
1733 | * application.
1734 | *
1735 | * @param integer $userOrGroupID id of user or group
1736 | * @param boolean $isUser boolean true if a user is passed in $userOrGroupID, false
1737 | * if a group is passed in $userOrGroupID
1738 | * @param integer $type type of notification (0 will delete all) Not used yet!
1739 | * @return integer 0 if operation was succesful
1740 | * -1 if the userid/groupid is invalid
1741 | * -3 if the user/group is already subscribed
1742 | * -4 in case of an internal database error
1743 | */
1744 | public function removeNotify($userOrGroupID, $isUser, $type = 0) { /* {{{ */
1745 | $db = $this->_dms->getDB();
1746 |
1747 | /* Verify that user / group exists. */
1748 | /** @var SeedDMS_Core_Group|SeedDMS_Core_User $obj */
1749 | $obj = ($isUser ? $this->_dms->getUser($userOrGroupID) : $this->_dms->getGroup($userOrGroupID));
1750 | if (!is_object($obj)) {
1751 | return -1;
1752 | }
1753 |
1754 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`";
1755 |
1756 | /* Verify that the requesting user has permission to add the target to
1757 | * the notification system.
1758 | */
1759 | /*
1760 | * The calling application should enforce the policy on who is allowed
1761 | * to add someone to the notification system. If is shall remain here
1762 | * the currently logged in user should be passed to this function
1763 | *
1764 | GLOBAL $user;
1765 | if ($user->isGuest()) {
1766 | return -2;
1767 | }
1768 | if (!$user->isAdmin()) {
1769 | if ($isUser) {
1770 | if ($user->getID() != $obj->getID()) {
1771 | return -2;
1772 | }
1773 | }
1774 | else {
1775 | if (!$obj->isMember($user)) {
1776 | return -2;
1777 | }
1778 | }
1779 | }
1780 | */
1781 |
1782 | /* Check to see if the target is in the database. */
1783 | $queryStr = "SELECT * FROM `tblNotify` WHERE `tblNotify`.`target` = '".$this->_id."' ".
1784 | "AND `tblNotify`.`targetType` = '".T_DOCUMENT."' ".
1785 | "AND `tblNotify`.".$userOrGroup." = '".(int) $userOrGroupID."'";
1786 | $resArr = $db->getResultArray($queryStr);
1787 | if (is_bool($resArr)) {
1788 | return -4;
1789 | }
1790 | if (count($resArr)==0) {
1791 | return -3;
1792 | }
1793 |
1794 | $queryStr = "DELETE FROM `tblNotify` WHERE `target` = " . $this->_id . " AND `targetType` = " . T_DOCUMENT . " AND " . $userOrGroup . " = " . (int) $userOrGroupID;
1795 | /* If type is given then delete only those notifications */
1796 | if ($type)
1797 | $queryStr .= " AND `type` = ".(int) $type;
1798 | if (!$db->getResult($queryStr))
1799 | return -4;
1800 |
1801 | unset($this->_notifyList);
1802 | return 0;
1803 | } /* }}} */
1804 |
1805 | /**
1806 | * Add content to a document
1807 | *
1808 | * Each document may have any number of content elements attached to it.
1809 | * Each content element has a version number. Newer versions (greater
1810 | * version number) replace older versions.
1811 | *
1812 | * @param string $comment comment
1813 | * @param object $user user who shall be the owner of this content
1814 | * @param string $tmpFile file containing the actuall content
1815 | * @param string $orgFileName original file name
1816 | * @param string $fileType
1817 | * @param string $mimeType MimeType of the content
1818 | * @param array $reviewers list of reviewers
1819 | * @param array $approvers list of approvers
1820 | * @param integer $version version number of content or 0 if next higher version shall be used.
1821 | * @param array $attributes list of version attributes. The element key
1822 | * must be the id of the attribute definition.
1823 | * @param object $workflow
1824 | * @return bool|SeedDMS_Core_AddContentResultSet
1825 | */
1826 | public function addContent($comment, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $reviewers = array(), $approvers = array(), $version = 0, $attributes = array(), $workflow = null) { /* {{{ */
1827 | $db = $this->_dms->getDB();
1828 |
1829 | // the doc path is id/version.filetype
1830 | $dir = $this->getDir();
1831 |
1832 | /* The version field in table tblDocumentContent used to be auto
1833 | * increment but that requires the field to be primary as well if
1834 | * innodb is used. That's why the version is now determined here.
1835 | */
1836 | if ((int)$version<1) {
1837 | $queryStr = "SELECT MAX(`version`) AS m FROM `tblDocumentContent` WHERE `document` = ".$this->_id;
1838 | $resArr = $db->getResultArray($queryStr);
1839 | if (is_bool($resArr) && !$resArr)
1840 | return false;
1841 |
1842 | $version = $resArr[0]['m']+1;
1843 | }
1844 |
1845 | if ($fileType == '.')
1846 | $fileType = '';
1847 | $filesize = SeedDMS_Core_File::fileSize($tmpFile);
1848 | $checksum = SeedDMS_Core_File::checksum($tmpFile);
1849 |
1850 | $db->startTransaction();
1851 | $queryStr = "INSERT INTO `tblDocumentContent` (`document`, `version`, `comment`, `date`, `createdBy`, `dir`, `orgFileName`, `fileType`, `mimeType`, `fileSize`, `checksum`) VALUES ".
1852 | "(".$this->_id.", ".(int)$version.",".$db->qstr($comment).", ".$db->getCurrentTimestamp().", ".$user->getID().", ".$db->qstr($dir).", ".$db->qstr($orgFileName).", ".$db->qstr($fileType).", ".$db->qstr($mimeType).", ".$filesize.", ".$db->qstr($checksum).")";
1853 | if (!$db->getResult($queryStr)) {
1854 | $db->rollbackTransaction();
1855 | return false;
1856 | }
1857 |
1858 | $contentID = $db->getInsertID('tblDocumentContent');
1859 | $content = $this->_dms->getDocumentContent($contentID);
1860 |
1861 | if ($storage = $this->_dms->getStorage()) {
1862 | if (!$storage->saveContent($this, $content, $tmpFile)) {
1863 | $db->rollbackTransaction();
1864 | return false;
1865 | }
1866 | } else {
1867 | // copy file
1868 | if (!SeedDMS_Core_File::makeDir($this->_dms->contentDir . $dir)) {
1869 | $db->rollbackTransaction();
1870 | return false;
1871 | }
1872 | if ($this->_dms->forceRename)
1873 | $err = SeedDMS_Core_File::renameFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType);
1874 | elseif ($this->_dms->forceLink)
1875 | $err = SeedDMS_Core_File::linkFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType);
1876 | else
1877 | $err = SeedDMS_Core_File::copyFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType);
1878 | if (!$err) {
1879 | $db->rollbackTransaction();
1880 | return false;
1881 | }
1882 | }
1883 |
1884 | $this->_content = null;
1885 | $this->_latestContent = null;
1886 | $docResultSet = new SeedDMS_Core_AddContentResultSet($content);
1887 | $docResultSet->setDMS($this->_dms);
1888 |
1889 | if ($attributes) {
1890 | foreach ($attributes as $attrdefid => $attribute) {
1891 | /* $attribute can be a string or an array */
1892 | if ($attribute) {
1893 | if ($attrdef = $this->_dms->getAttributeDefinition($attrdefid)) {
1894 | if (!$content->setAttributeValue($attrdef, $attribute)) {
1895 | $this->_removeContent($content);
1896 | $db->rollbackTransaction();
1897 | return false;
1898 | }
1899 | } else {
1900 | $this->_removeContent($content);
1901 | $db->rollbackTransaction();
1902 | return false;
1903 | }
1904 | }
1905 | }
1906 | }
1907 |
1908 | $queryStr = "INSERT INTO `tblDocumentStatus` (`documentID`, `version`) ".
1909 | "VALUES (". $this->_id .", ". (int) $version .")";
1910 | if (!$db->getResult($queryStr)) {
1911 | $this->_removeContent($content);
1912 | $db->rollbackTransaction();
1913 | return false;
1914 | }
1915 |
1916 | $statusID = $db->getInsertID('tblDocumentStatus', 'statusID');
1917 |
1918 | if ($workflow)
1919 | $content->setWorkflow($workflow, $user);
1920 |
1921 | // Add reviewers into the database. Reviewers must review the document
1922 | // and submit comments, if appropriate. Reviewers can also recommend that
1923 | // a document be rejected.
1924 | $pendingReview = false;
1925 | /** @noinspection PhpUnusedLocalVariableInspection */
1926 | foreach (array("i", "g") as $i) {
1927 | if (isset($reviewers[$i])) {
1928 | foreach ($reviewers[$i] as $reviewerID) {
1929 | $reviewer = ($i == "i" ?$this->_dms->getUser($reviewerID) : $this->_dms->getGroup($reviewerID));
1930 | $res = ($i == "i" ? $docResultSet->getContent()->addIndReviewer($reviewer, $user, true) : $docResultSet->getContent()->addGrpReviewer($reviewer, $user, true));
1931 | $docResultSet->addReviewer($reviewer, $i, $res);
1932 | // res is the id of the record in the database
1933 | if ($res > 0 || $res == -3) {
1934 | $pendingReview = true;
1935 | }
1936 | }
1937 | }
1938 | }
1939 | // Add approvers to the database. Approvers must also review the document
1940 | // and make a recommendation on its release as an approved version.
1941 | $pendingApproval = false;
1942 | /** @noinspection PhpUnusedLocalVariableInspection */
1943 | foreach (array("i", "g") as $i) {
1944 | if (isset($approvers[$i])) {
1945 | foreach ($approvers[$i] as $approverID) {
1946 | $approver = ($i == "i" ? $this->_dms->getUser($approverID) : $this->_dms->getGroup($approverID));
1947 | $res = ($i == "i" ? $docResultSet->getContent()->addIndApprover($approver, $user, true) : $docResultSet->getContent()->addGrpApprover($approver, $user, !$pendingReview));
1948 | $docResultSet->addApprover($approver, $i, $res);
1949 | // res is the id of the record in the database
1950 | if ($res > 0 || $res == -3) {
1951 | $pendingApproval = true;
1952 | }
1953 | }
1954 | }
1955 | }
1956 |
1957 | // If there are no reviewers or approvers, the document is automatically
1958 | // promoted to the released state.
1959 | if ($pendingReview) {
1960 | $status = S_DRAFT_REV;
1961 | $comment = "";
1962 | }
1963 | elseif ($pendingApproval) {
1964 | $status = S_DRAFT_APP;
1965 | $comment = "";
1966 | }
1967 | elseif ($workflow) {
1968 | $status = S_IN_WORKFLOW;
1969 | $comment = ", workflow: ".$workflow->getName();
1970 | } else {
1971 | $status = S_RELEASED;
1972 | $comment = "";
1973 | }
1974 | $queryStr = "INSERT INTO `tblDocumentStatusLog` (`statusID`, `status`, `comment`, `date`, `userID`) ".
1975 | "VALUES ('". $statusID ."', '". $status."', 'New document content submitted". $comment ."', ".$db->getCurrentDatetime().", '". $user->getID() ."')";
1976 | if (!$db->getResult($queryStr)) {
1977 | $db->rollbackTransaction();
1978 | return false;
1979 | }
1980 |
1981 | /** @noinspection PhpMethodParametersCountMismatchInspection */
1982 | $docResultSet->setStatus($status);
1983 |
1984 | $db->commitTransaction();
1985 | return $docResultSet;
1986 | } /* }}} */
1987 |
1988 | /**
1989 | * Replace a version of a document
1990 | *
1991 | * Each document may have any number of content elements attached to it.
1992 | * This method replaces the file content of a given version.
1993 | * Using this function is highly discourage, because it undermines the
1994 | * idea of keeping all versions of a document as originally saved.
1995 | * Content will only be replaced if the mimetype, filetype, user and
1996 | * original filename are identical to the version being updated.
1997 | *
1998 | * This method was introduced for the webdav server because any saving
1999 | * of a document created a new version.
2000 | *
2001 | * @param object $user user who shall be the owner of this content
2002 | * @param string $tmpFile file containing the actuall content
2003 | * @param string $orgFileName original file name
2004 | * @param string $fileType
2005 | * @param string $mimeType MimeType of the content
2006 | * @param integer $version version number of content or 0 if latest version shall be replaced.
2007 | * @return bool/array false in case of an error or a result set
2008 | */
2009 | public function replaceContent($version, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $allowoverride = []) { /* {{{ */
2010 | $db = $this->_dms->getDB();
2011 |
2012 | // the doc path is id/version.filetype
2013 | $dir = $this->getDir();
2014 |
2015 | /* If $version < 1 than replace the content of the latest version.
2016 | */
2017 | if ((int) $version<1) {
2018 | $queryStr = "SELECT MAX(`version`) AS m FROM `tblDocumentContent` WHERE `document` = ".$this->_id;
2019 | $resArr = $db->getResultArray($queryStr);
2020 | if (is_bool($resArr) && !$resArr)
2021 | return false;
2022 |
2023 | $version = $resArr[0]['m'];
2024 | }
2025 |
2026 | $content = $this->getContentByVersion($version);
2027 | if (!$content)
2028 | return false;
2029 |
2030 | if ($fileType == '.')
2031 | $fileType = '';
2032 |
2033 | $sql = [];
2034 | /* Check if $user, $orgFileName, $fileType and $mimeType are the same */
2035 | if ($user->getID() != $content->getUser()->getID()) {
2036 | if (!empty($allowoverride['user']))
2037 | $sql[] = "`createdBy`=".$user->getID();
2038 | else
2039 | return false;
2040 | }
2041 | if ($orgFileName != $content->getOriginalFileName()) {
2042 | if (!empty($allowoverride['orgfilename']))
2043 | $sql[] = "`orgFileName`=".$db->qstr($orgFileName);
2044 | else
2045 | return false;
2046 | }
2047 | if ($fileType != $content->getFileType()) {
2048 | if (!empty($allowoverride['filetype']))
2049 | $sql[] = "`fileType`=".$db->qstr($fileType);
2050 | else
2051 | return false;
2052 | }
2053 | if ($mimeType != $content->getMimeType()) {
2054 | if (!empty($allowoverride['mimetype']))
2055 | $sql[] = "`mimeType`=".$db->qstr($mimeType);
2056 | else
2057 | return false;
2058 | }
2059 |
2060 | $filesize = SeedDMS_Core_File::fileSize($tmpFile);
2061 | $checksum = SeedDMS_Core_File::checksum($tmpFile);
2062 |
2063 | $db->startTransaction();
2064 | $sql[] = "`date`=".$db->getCurrentTimestamp();
2065 | $sql[] = "`fileSize`=".$filesize;
2066 | $sql[] = "`checksum`=".$db->qstr($checksum);
2067 | $queryStr = "UPDATE `tblDocumentContent` set ".implode(", ", $sql)." WHERE `id`=".$content->getID();
2068 | if (!$db->getResult($queryStr)) {
2069 | $db->rollbackTransaction();
2070 | return false;
2071 | }
2072 |
2073 | if ($storage = $this->_dms->getStorage()) {
2074 | if (!$storage->replaceContent($this, $content, $tmpFile)) {
2075 | $db->rollbackTransaction();
2076 | return false;
2077 | }
2078 | } else {
2079 | // copy file
2080 | if (!SeedDMS_Core_File::copyFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType)) {
2081 | $db->rollbackTransaction();
2082 | return false;
2083 | }
2084 | }
2085 |
2086 | $this->_content = null;
2087 | $this->_latestContent = null;
2088 | $db->commitTransaction();
2089 |
2090 | return true;
2091 | } /* }}} */
2092 |
2093 | /**
2094 | * Return all content elements of a document
2095 | *
2096 | * This method returns an array of content elements ordered by version.
2097 | * Version which are not accessible because of its status, will be filtered
2098 | * out. Access rights based on the document status are calculated for the
2099 | * currently logged in user.
2100 | *
2101 | * @return bool|SeedDMS_Core_DocumentContent[]
2102 | */
2103 | public function getContent() { /* {{{ */
2104 | $db = $this->_dms->getDB();
2105 |
2106 | if (!isset($this->_content)) {
2107 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." ORDER BY `version`";
2108 | $resArr = $db->getResultArray($queryStr);
2109 | if (is_bool($resArr) && !$resArr)
2110 | return false;
2111 |
2112 | $this->_content = array();
2113 | $classname = $this->_dms->getClassname('documentcontent');
2114 | $user = $this->_dms->getLoggedInUser();
2115 | foreach ($resArr as $row) {
2116 | /** @var SeedDMS_Core_DocumentContent $content */
2117 | $content = new $classname($row["id"], $this, $row["version"], $row["comment"], $row["date"], $row["createdBy"], $row["dir"], $row["orgFileName"], $row["fileType"], $row["mimeType"], $row['fileSize'], $row['checksum']);
2118 | /* TODO: Better use content id as key in $this->_content. This
2119 | * would allow to remove a single content object in removeContent().
2120 | * Currently removeContent() must clear $this->_content completely
2121 | */
2122 | if ($user) {
2123 | if ($content->getAccessMode($user) >= M_READ)
2124 | array_push($this->_content, $content);
2125 | } else {
2126 | array_push($this->_content, $content);
2127 | }
2128 | }
2129 | }
2130 |
2131 | return $this->_content;
2132 | } /* }}} */
2133 |
2134 | /**
2135 | * Return the content element of a document with a given version number
2136 | *
2137 | * This method will check if the version is accessible and return false
2138 | * if not. Access rights based on the document status are calculated for the
2139 | * currently logged in user.
2140 | *
2141 | * @param integer $version version number of content element
2142 | * @return SeedDMS_Core_DocumentContent|null|boolean object of class
2143 | * {@see SeedDMS_Core_DocumentContent}, null if not content was found,
2144 | * false in case of an error
2145 | */
2146 | public function getContentByVersion($version) { /* {{{ */
2147 | if (!is_numeric($version)) return false;
2148 |
2149 | if (isset($this->_content)) {
2150 | foreach ($this->_content as $revision) {
2151 | if ($revision->getVersion() == $version)
2152 | return $revision;
2153 | }
2154 | return null;
2155 | }
2156 |
2157 | $db = $this->_dms->getDB();
2158 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." AND `version` = " . (int) $version;
2159 | $resArr = $db->getResultArray($queryStr);
2160 | if (is_bool($resArr) && !$resArr)
2161 | return false;
2162 | if (count($resArr) != 1)
2163 | return null;
2164 |
2165 | $resArr = $resArr[0];
2166 | $classname = $this->_dms->getClassname('documentcontent');
2167 | /** @var SeedDMS_Core_DocumentContent $content */
2168 | if ($content = new $classname($resArr["id"], $this, $resArr["version"], $resArr["comment"], $resArr["date"], $resArr["createdBy"], $resArr["dir"], $resArr["orgFileName"], $resArr["fileType"], $resArr["mimeType"], $resArr['fileSize'], $resArr['checksum'])) {
2169 | $user = $this->_dms->getLoggedInUser();
2170 | /* A user with write access on the document may always see the version */
2171 | if ($user && $content->getAccessMode($user) == M_NONE)
2172 | return null;
2173 | else
2174 | return $content;
2175 | } else {
2176 | return false;
2177 | }
2178 | } /* }}} */
2179 |
2180 | /**
2181 | * Check if a given version is the latest version of the document
2182 | *
2183 | * @param integer $version version number of content element
2184 | * @return SeedDMS_Core_DocumentContent|boolean object of class {@see SeedDMS_Core_DocumentContent}
2185 | * or false
2186 | */
2187 | public function isLatestContent($version) { /* {{{ */
2188 | return $this->getLatestContent()->getVersion() == $version;
2189 | } /* }}} */
2190 |
2191 | /**
2192 | * @return bool|null|SeedDMS_Core_DocumentContent
2193 | */
2194 | private function __getLatestContent() { /* {{{ */
2195 | if (!$this->_latestContent) {
2196 | $db = $this->_dms->getDB();
2197 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." ORDER BY `version` DESC LIMIT 1";
2198 | $resArr = $db->getResultArray($queryStr);
2199 | if (is_bool($resArr) && !$resArr)
2200 | return false;
2201 | if (count($resArr) != 1)
2202 | return false;
2203 |
2204 | $resArr = $resArr[0];
2205 | $classname = $this->_dms->getClassname('documentcontent');
2206 | $this->_latestContent = new $classname($resArr["id"], $this, $resArr["version"], $resArr["comment"], $resArr["date"], $resArr["createdBy"], $resArr["dir"], $resArr["orgFileName"], $resArr["fileType"], $resArr["mimeType"], $resArr['fileSize'], $resArr['checksum']);
2207 | }
2208 | return $this->_latestContent;
2209 | } /* }}} */
2210 |
2211 | /**
2212 | * Get the latest version of document
2213 | *
2214 | * This method returns the latest accessible version of a document.
2215 | * If content access has been restricted by setting
2216 | * {@see SeedDMS_Core_DMS::noReadForStatus} the function will go
2217 | * backwards in history until an accessible version is found. If none
2218 | * is found null will be returned.
2219 | * Access rights based on the document status are calculated for the
2220 | * currently logged in user.
2221 | *
2222 | * @return bool|SeedDMS_Core_DocumentContent object of class {@see SeedDMS_Core_DocumentContent}
2223 | */
2224 | public function getLatestContent() { /* {{{ */
2225 | if (!$this->_latestContent) {
2226 | $db = $this->_dms->getDB();
2227 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." ORDER BY `version` DESC";
2228 | $resArr = $db->getResultArray($queryStr);
2229 | if (is_bool($resArr) && !$resArr)
2230 | return false;
2231 |
2232 | $classname = $this->_dms->getClassname('documentcontent');
2233 | $user = $this->_dms->getLoggedInUser();
2234 | foreach ($resArr as $row) {
2235 | if (!$this->_latestContent) {
2236 | /** @var SeedDMS_Core_DocumentContent $content */
2237 | $content = new $classname($row["id"], $this, $row["version"], $row["comment"], $row["date"], $row["createdBy"], $row["dir"], $row["orgFileName"], $row["fileType"], $row["mimeType"], $row['fileSize'], $row['checksum']);
2238 | if ($user) {
2239 | /* If the user may even write the document, then also allow to see all content.
2240 | * This is needed because the user could upload a new version
2241 | */
2242 | if ($content->getAccessMode($user) >= M_READ) {
2243 | $this->_latestContent = $content;
2244 | }
2245 | } else {
2246 | $this->_latestContent = $content;
2247 | }
2248 | }
2249 | }
2250 | }
2251 |
2252 | return $this->_latestContent;
2253 | } /* }}} */
2254 |
2255 | /**
2256 | * Remove version of document
2257 | *
2258 | * @param SeedDMS_Core_DocumentContent $version version number of content
2259 | * @return boolean true if successful, otherwise false
2260 | */
2261 | private function _removeContent($version) { /* {{{ */
2262 | $db = $this->_dms->getDB();
2263 |
2264 | $db->startTransaction();
2265 |
2266 | $status = $version->getStatus();
2267 | $stID = $status["statusID"];
2268 |
2269 | $queryStr = "DELETE FROM `tblDocumentContent` WHERE `document` = " . $this->getID() . " AND `version` = " . $version->getVersion();
2270 | if (!$db->getResult($queryStr)) {
2271 | $db->rollbackTransaction();
2272 | return false;
2273 | }
2274 |
2275 | $queryStr = "DELETE FROM `tblDocumentContentAttributes` WHERE `content` = " . $version->getId();
2276 | if (!$db->getResult($queryStr)) {
2277 | $db->rollbackTransaction();
2278 | return false;
2279 | }
2280 |
2281 | $queryStr = "DELETE FROM `tblDocumentStatusLog` WHERE `statusID` = '".$stID."'";
2282 | if (!$db->getResult($queryStr)) {
2283 | $db->rollbackTransaction();
2284 | return false;
2285 | }
2286 |
2287 | $queryStr = "DELETE FROM `tblDocumentStatus` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'";
2288 | if (!$db->getResult($queryStr)) {
2289 | $db->rollbackTransaction();
2290 | return false;
2291 | }
2292 |
2293 | $status = $version->getReviewStatus();
2294 | $stList = "";
2295 | foreach ($status as $st) {
2296 | $stList .= (strlen($stList)==0 ? "" : ", "). "'".$st["reviewID"]."'";
2297 | $queryStr = "SELECT * FROM `tblDocumentReviewLog` WHERE `reviewID` = " . $st['reviewID'];
2298 | $resArr = $db->getResultArray($queryStr);
2299 | if ((is_bool($resArr) && !$resArr)) {
2300 | $db->rollbackTransaction();
2301 | return false;
2302 | }
2303 | foreach ($resArr as $res) {
2304 | if ($storage = $this->_dms->getStorage()) {
2305 | $storage->deleteReview($this, $res['reviewLogID']);
2306 | } else {
2307 | $file = $this->_dms->contentDir . $this->getDir().'r'.$res['reviewLogID'];
2308 | if (SeedDMS_Core_File::file_exists($file))
2309 | SeedDMS_Core_File::removeFile($file);
2310 | }
2311 | }
2312 | }
2313 |
2314 | if (strlen($stList)>0) {
2315 | $queryStr = "DELETE FROM `tblDocumentReviewLog` WHERE `tblDocumentReviewLog`.`reviewID` IN (".$stList.")";
2316 | if (!$db->getResult($queryStr)) {
2317 | $db->rollbackTransaction();
2318 | return false;
2319 | }
2320 | }
2321 | $queryStr = "DELETE FROM `tblDocumentReviewers` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'";
2322 | if (!$db->getResult($queryStr)) {
2323 | $db->rollbackTransaction();
2324 | return false;
2325 | }
2326 | $status = $version->getApprovalStatus();
2327 | $stList = "";
2328 | foreach ($status as $st) {
2329 | $stList .= (strlen($stList)==0 ? "" : ", "). "'".$st["approveID"]."'";
2330 | $queryStr = "SELECT * FROM `tblDocumentApproveLog` WHERE `approveID` = " . $st['approveID'];
2331 | $resArr = $db->getResultArray($queryStr);
2332 | if ((is_bool($resArr) && !$resArr)) {
2333 | $db->rollbackTransaction();
2334 | return false;
2335 | }
2336 | foreach ($resArr as $res) {
2337 | if ($storage = $this->_dms->getStorage()) {
2338 | $storage->deleteApproval($this, $res['approveLogID']);
2339 | } else {
2340 | $file = $this->_dms->contentDir . $this->getDir().'a'.$res['approveLogID'];
2341 | if (SeedDMS_Core_File::file_exists($file))
2342 | SeedDMS_Core_File::removeFile($file);
2343 | }
2344 | }
2345 | }
2346 |
2347 | if (strlen($stList)>0) {
2348 | $queryStr = "DELETE FROM `tblDocumentApproveLog` WHERE `tblDocumentApproveLog`.`approveID` IN (".$stList.")";
2349 | if (!$db->getResult($queryStr)) {
2350 | $db->rollbackTransaction();
2351 | return false;
2352 | }
2353 | }
2354 | $queryStr = "DELETE FROM `tblDocumentApprovers` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'";
2355 | if (!$db->getResult($queryStr)) {
2356 | $db->rollbackTransaction();
2357 | return false;
2358 | }
2359 |
2360 | $queryStr = "DELETE FROM `tblWorkflowDocumentContent` WHERE `document` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'";
2361 | if (!$db->getResult($queryStr)) {
2362 | $db->rollbackTransaction();
2363 | return false;
2364 | }
2365 |
2366 | $queryStr = "DELETE FROM `tblWorkflowLog` WHERE `document` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'";
2367 | if (!$db->getResult($queryStr)) {
2368 | $db->rollbackTransaction();
2369 | return false;
2370 | }
2371 |
2372 | // remove only those document files attached to version
2373 | $res = $this->getDocumentFiles($version->getVersion(), false);
2374 | if (is_bool($res) && !$res) {
2375 | $db->rollbackTransaction();
2376 | return false;
2377 | }
2378 |
2379 | foreach ($res as $documentfile)
2380 | if (!$this->removeDocumentFile($documentfile->getId())) {
2381 | $db->rollbackTransaction();
2382 | return false;
2383 | }
2384 |
2385 | if ($storage = $this->_dms->getStorage()) {
2386 | if (!$storage->deleteContent($this, $version)) {
2387 | $db->rollbackTransaction();
2388 | return false;
2389 | }
2390 | } else {
2391 | if (SeedDMS_Core_File::file_exists($this->_dms->contentDir.$version->getPath()))
2392 | if (!SeedDMS_Core_File::removeFile($this->_dms->contentDir.$version->getPath())) {
2393 | $db->rollbackTransaction();
2394 | return false;
2395 | }
2396 | }
2397 |
2398 | $db->commitTransaction();
2399 | return true;
2400 | } /* }}} */
2401 |
2402 | /**
2403 | * Call callback onPreRemoveDocument before deleting content
2404 | *
2405 | * @param SeedDMS_Core_DocumentContent $version version number of content
2406 | * @return bool|mixed
2407 | */
2408 | public function removeContent($version) { /* {{{ */
2409 | $this->_dms->lasterror = '';
2410 | $db = $this->_dms->getDB();
2411 |
2412 | /* Make sure the version exists */
2413 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = " . $this->getID() . " AND `version` = " . $version->getVersion();
2414 | $resArr = $db->getResultArray($queryStr);
2415 | if (is_bool($resArr) && !$resArr)
2416 | return false;
2417 | if (count($resArr)==0)
2418 | return false;
2419 |
2420 | /* Make sure this is not the last version */
2421 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = " . $this->getID();
2422 | $resArr = $db->getResultArray($queryStr);
2423 | if (is_bool($resArr) && !$resArr)
2424 | return false;
2425 | if (count($resArr)==1)
2426 | return false;
2427 |
2428 | /* Check if 'onPreRemoveDocument' callback is set */
2429 | if (isset($this->_dms->callbacks['onPreRemoveContent'])) {
2430 | foreach ($this->_dms->callbacks['onPreRemoveContent'] as $callback) {
2431 | $ret = call_user_func($callback[0], $callback[1], $this, $version);
2432 | if (is_bool($ret))
2433 | return $ret;
2434 | }
2435 | }
2436 |
2437 | if (false === ($ret = self::_removeContent($version))) {
2438 | return false;
2439 | }
2440 |
2441 | /* Invalidate the content list and the latest content of this document,
2442 | * otherwise getContent() and getLatestContent()
2443 | * will still return the content just deleted.
2444 | */
2445 | $this->_latestContent = null;
2446 | $this->_content = null;
2447 |
2448 | /* Check if 'onPostRemoveDocument' callback is set */
2449 | if (isset($this->_dms->callbacks['onPostRemoveContent'])) {
2450 | foreach ($this->_dms->callbacks['onPostRemoveContent'] as $callback) {
2451 | if (!call_user_func($callback[0], $callback[1], $version)) {
2452 | }
2453 | }
2454 | }
2455 |
2456 | return $ret;
2457 | } /* }}} */
2458 |
2459 | /**
2460 | * Return a certain document link
2461 | *
2462 | * @param integer $linkID id of link
2463 | * @return SeedDMS_Core_DocumentLink|bool of SeedDMS_Core_DocumentLink or false in case of
2464 | * an error.
2465 | */
2466 | public function getDocumentLink($linkID) { /* {{{ */
2467 | $db = $this->_dms->getDB();
2468 |
2469 | if (!is_numeric($linkID)) return false;
2470 |
2471 | $queryStr = "SELECT * FROM `tblDocumentLinks` WHERE `document` = " . $this->_id ." AND `id` = " . (int) $linkID;
2472 | $resArr = $db->getResultArray($queryStr);
2473 | if (is_bool($resArr) && !$resArr)
2474 | return false;
2475 | if (count($resArr)==0)
2476 | return null;
2477 |
2478 | $resArr = $resArr[0];
2479 | $document = $this->_dms->getDocument($resArr["document"]);
2480 | $target = $this->_dms->getDocument($resArr["target"]);
2481 | if ($document && $target) {
2482 | $link = new SeedDMS_Core_DocumentLink($resArr["id"], $document, $target, $resArr["userID"], $resArr["public"]);
2483 | $user = $this->_dms->getLoggedInUser();
2484 | if ($link->getAccessMode($user, $document, $target) >= M_READ)
2485 | return $link;
2486 | }
2487 | return null;
2488 | } /* }}} */
2489 |
2490 | /**
2491 | * Return all document links
2492 | *
2493 | * The list may contain all links to other documents, even those which
2494 | * may not be visible by certain users, unless you pass appropriate
2495 | * parameters to filter out public links and those created by
2496 | * the given user. The two parameters are or'ed. If $publiconly
2497 | * is set the method will return all public links disregarding the
2498 | * user. If $publiconly is not set but a user is set, the method
2499 | * will return all links of that user (public and none public).
2500 | * Setting a user and $publiconly to true will *not* return the
2501 | * public links of that user but all links which are public or
2502 | * owned by that user.
2503 | *
2504 | * The application must call
2505 | * SeedDMS_Core_DMS::filterDocumentLinks() afterwards to filter out
2506 | * those links pointing to a document not accessible by a given user.
2507 | *
2508 | * @param boolean $publiconly return all publically visible links
2509 | * @param SeedDMS_Core_User $user return also private links of this user
2510 | *
2511 | * @return array list of objects of class {@see SeedDMS_Core_DocumentLink}
2512 | */
2513 | public function getDocumentLinks($publiconly = false, $user = null) { /* {{{ */
2514 | if (!isset($this->_documentLinks)) {
2515 | $db = $this->_dms->getDB();
2516 |
2517 | $queryStr = "SELECT * FROM `tblDocumentLinks` WHERE `document` = " . $this->_id;
2518 | $tmp = array();
2519 | if ($publiconly)
2520 | $tmp[] = "`public`=1";
2521 | if ($user)
2522 | $tmp[] = "`userID`=".$user->getID();
2523 | if ($tmp) {
2524 | $queryStr .= " AND (".implode(" OR ", $tmp).")";
2525 | }
2526 |
2527 | $resArr = $db->getResultArray($queryStr);
2528 | if (is_bool($resArr) && !$resArr)
2529 | return false;
2530 | $this->_documentLinks = array();
2531 |
2532 | $user = $this->_dms->getLoggedInUser();
2533 | foreach ($resArr as $row) {
2534 | $target = $this->_dms->getDocument($row["target"]);
2535 | if ($target) {
2536 | $link = new SeedDMS_Core_DocumentLink($row["id"], $this, $target, $row["userID"], $row["public"]);
2537 | if ($link->getAccessMode($user, $this, $target) >= M_READ)
2538 | array_push($this->_documentLinks, $link);
2539 | }
2540 | }
2541 | }
2542 | return $this->_documentLinks;
2543 | } /* }}} */
2544 |
2545 | /**
2546 | * Return all document having a link on this document
2547 | *
2548 | * The list contains all documents which have a link to the current
2549 | * document. The list contains even those documents which
2550 | * may not be accessible by the user, unless you pass appropriate
2551 | * parameters to filter out public links and those created by
2552 | * the given user.
2553 | * This method is basically the reverse of
2554 | * {@see SeedDMS_Core_Document::getDocumentLinks()}
2555 | *
2556 | * The application must call
2557 | * SeedDMS_Core_DMS::filterDocumentLinks() afterwards to filter out
2558 | * those links pointing to a document not accessible by a given user.
2559 | *
2560 | * @param boolean $publiconly return all publically visible links
2561 | * @param SeedDMS_Core_User $user return also private links of this user
2562 | *
2563 | * @return array list of objects of class SeedDMS_Core_DocumentLink
2564 | */
2565 | public function getReverseDocumentLinks($publiconly = false, $user = null) { /* {{{ */
2566 | $db = $this->_dms->getDB();
2567 |
2568 | $queryStr = "SELECT * FROM `tblDocumentLinks` WHERE `target` = " . $this->_id;
2569 | $tmp = array();
2570 | if ($publiconly)
2571 | $tmp[] = "`public`=1";
2572 | if ($user)
2573 | $tmp[] = "`userID`=".$user->getID();
2574 | if ($tmp) {
2575 | $queryStr .= " AND (".implode(" OR ", $tmp).")";
2576 | }
2577 |
2578 | $resArr = $db->getResultArray($queryStr);
2579 | if (is_bool($resArr) && !$resArr)
2580 | return false;
2581 |
2582 | $links = array();
2583 | foreach ($resArr as $row) {
2584 | $document = $this->_dms->getDocument($row["document"]);
2585 | $link = new SeedDMS_Core_DocumentLink($row["id"], $document, $this, $row["userID"], $row["public"]);
2586 | if ($link->getAccessMode($user, $document, $this) >= M_READ)
2587 | array_push($links, $link);
2588 | }
2589 |
2590 | return $links;
2591 | } /* }}} */
2592 |
2593 | /**
2594 | * Add a link to a target document
2595 | *
2596 | * @param int $targetID Id of target document
2597 | * @param int $userID Id of user adding the link
2598 | * @param boolean true if link is public
2599 | * @return SeedDMS_Core_DocumentLink|boolean
2600 | */
2601 | public function addDocumentLink($targetID, $userID, $public) { /* {{{ */
2602 | $db = $this->_dms->getDB();
2603 |
2604 | $public = ($public) ? 1 : 0;
2605 |
2606 | if (!is_numeric($targetID) || $targetID < 1)
2607 | return false;
2608 |
2609 | if ($targetID == $this->_id)
2610 | return false;
2611 |
2612 | if (!is_numeric($userID) || $userID < 1)
2613 | return false;
2614 |
2615 | if (!($target = $this->_dms->getDocument($targetID)))
2616 | return false;
2617 |
2618 | if (!($user = $this->_dms->getUser($userID)))
2619 | return false;
2620 |
2621 | $queryStr = "INSERT INTO `tblDocumentLinks` (`document`, `target`, `userID`, `public`) VALUES (".$this->_id.", ".(int)$targetID.", ".(int)$userID.", ".$public.")";
2622 | if (!$db->getResult($queryStr))
2623 | return false;
2624 |
2625 | unset($this->_documentLinks);
2626 |
2627 | $id = $db->getInsertID('tblDocumentLinks');
2628 | $link = new SeedDMS_Core_DocumentLink($id, $this, $target, $user->getId(), $public);
2629 | return $link;
2630 | } /* }}} */
2631 |
2632 | public function removeDocumentLink($linkID) { /* {{{ */
2633 | $db = $this->_dms->getDB();
2634 |
2635 | if (!is_numeric($linkID) || $linkID < 1)
2636 | return false;
2637 |
2638 | $queryStr = "DELETE FROM `tblDocumentLinks` WHERE `document` = " . $this->_id ." AND `id` = " . (int) $linkID;
2639 | if (!$db->getResult($queryStr)) return false;
2640 | unset($this->_documentLinks);
2641 | return true;
2642 | } /* }}} */
2643 |
2644 | /**
2645 | * Get attached file by its id
2646 | *
2647 | * @return object instance of SeedDMS_Core_DocumentFile, null if file is not
2648 | * accessible, false in case of an sql error
2649 | */
2650 | public function getDocumentFile($ID) { /* {{{ */
2651 | $db = $this->_dms->getDB();
2652 |
2653 | if (!is_numeric($ID)) return false;
2654 |
2655 | $queryStr = "SELECT * FROM `tblDocumentFiles` WHERE `document` = " . $this->_id ." AND `id` = " . (int) $ID;
2656 | $resArr = $db->getResultArray($queryStr);
2657 | if ((is_bool($resArr) && !$resArr) || count($resArr)==0) return false;
2658 |
2659 | $resArr = $resArr[0];
2660 | $classname = $this->_dms->getClassname('documentfile');
2661 | $file = new $classname($resArr["id"], $this, $resArr["userID"], $resArr["comment"], $resArr["date"], $resArr["dir"], $resArr["fileType"], $resArr["mimeType"], $resArr["orgFileName"], $resArr["name"], $resArr["version"], $resArr["public"]);
2662 | $user = $this->_dms->getLoggedInUser();
2663 | if ($file->getAccessMode($user) >= M_READ)
2664 | return $file;
2665 | return null;
2666 | } /* }}} */
2667 |
2668 | /**
2669 | * Get list of files attached to document
2670 | *
2671 | * @param integer $version get only attachments for this version
2672 | * @param boolean $incnoversion include attachments without a version
2673 | *
2674 | * @return array list of files, false in case of an sql error
2675 | */
2676 | public function getDocumentFiles($version = 0, $incnoversion = true) { /* {{{ */
2677 | /* use a smarter caching because removing a document will call this function
2678 | * for each version and the document itself.
2679 | */
2680 | $hash = substr(md5($version.$incnoversion), 0, 4);
2681 | if (!isset($this->_documentFiles[$hash])) {
2682 | $db = $this->_dms->getDB();
2683 |
2684 | $queryStr = "SELECT * FROM `tblDocumentFiles` WHERE `document` = " . $this->_id;
2685 | if ($version) {
2686 | if ($incnoversion)
2687 | $queryStr .= " AND (`version`=0 OR `version`=".(int) $version.")";
2688 | else
2689 | $queryStr .= " AND (`version`=".(int) $version.")";
2690 | }
2691 | $queryStr .= " ORDER BY ";
2692 | if ($version) {
2693 | $queryStr .= "`version` DESC,";
2694 | }
2695 | $queryStr .= "`date` DESC";
2696 | $resArr = $db->getResultArray($queryStr);
2697 | if (is_bool($resArr) && !$resArr) return false;
2698 |
2699 | $this->_documentFiles = array($hash => array());
2700 |
2701 | $user = $this->_dms->getLoggedInUser();
2702 | $classname = $this->_dms->getClassname('documentfile');
2703 | foreach ($resArr as $row) {
2704 | $file = new $classname($row["id"], $this, $row["userID"], $row["comment"], $row["date"], $row["dir"], $row["fileType"], $row["mimeType"], $row["orgFileName"], $row["name"], $row["version"], $row["public"]);
2705 | if ($file->getAccessMode($user) >= M_READ)
2706 | array_push($this->_documentFiles[$hash], $file);
2707 | }
2708 | }
2709 | return $this->_documentFiles[$hash];
2710 | } /* }}} */
2711 |
2712 | /**
2713 | * Add an attachment to the document
2714 | *
2715 | */
2716 | public function addDocumentFile($name, $comment, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $version = 0, $public = 1) { /* {{{ */
2717 | $db = $this->_dms->getDB();
2718 |
2719 | $dir = $this->getDir();
2720 |
2721 | $db->startTransaction();
2722 | $queryStr = "INSERT INTO `tblDocumentFiles` (`comment`, `date`, `dir`, `document`, `fileType`, `mimeType`, `orgFileName`, `userID`, `name`, `version`, `public`) VALUES ".
2723 | "(".$db->qstr($comment).", ".$db->getCurrentTimestamp().", ".$db->qstr($dir).", ".$this->_id.", ".$db->qstr($fileType).", ".$db->qstr($mimeType).", ".$db->qstr($orgFileName).",".$user->getID().",".$db->qstr($name).", ".((int) $version).", ".($public ? 1 : 0).")";
2724 | if (!$db->getResult($queryStr)) {
2725 | $db->rollbackTransaction();
2726 | return false;
2727 | }
2728 |
2729 | $id = $db->getInsertID('tblDocumentFiles');
2730 |
2731 | $file = $this->getDocumentFile($id);
2732 | if (is_bool($file) && !$file) {
2733 | $db->rollbackTransaction();
2734 | return false;
2735 | }
2736 |
2737 | if ($storage = $this->_dms->getStorage()) {
2738 | $err = $storage->saveAttachment($this, $file, $tmpFile);
2739 | } else {
2740 | // copy file
2741 | if (!SeedDMS_Core_File::makeDir($this->_dms->contentDir . $dir)) return false;
2742 | if ($this->_dms->forceRename)
2743 | $err = SeedDMS_Core_File::renameFile($tmpFile, $this->_dms->contentDir . $file->getPath());
2744 | else
2745 | $err = SeedDMS_Core_File::copyFile($tmpFile, $this->_dms->contentDir . $file->getPath());
2746 | }
2747 | if (!$err) {
2748 | $db->rollbackTransaction();
2749 | return false;
2750 | }
2751 |
2752 | $db->commitTransaction();
2753 | unset($this->_documentFiles);
2754 | return $file;
2755 | } /* }}} */
2756 |
2757 | public function removeDocumentFile($ID) { /* {{{ */
2758 | $db = $this->_dms->getDB();
2759 |
2760 | if (!is_numeric($ID) || $ID < 1)
2761 | return false;
2762 |
2763 | $file = $this->getDocumentFile($ID);
2764 | if (is_bool($file) && !$file) return false;
2765 |
2766 | $db->startTransaction();
2767 | /* First delete the database record, because that can be undone
2768 | * if deletion of the file fails.
2769 | */
2770 | $queryStr = "DELETE FROM `tblDocumentFiles` WHERE `document` = " . $this->getID() . " AND `id` = " . (int) $ID;
2771 | if (!$db->getResult($queryStr)) {
2772 | $db->rollbackTransaction();
2773 | return false;
2774 | }
2775 |
2776 | if ($storage = $this->_dms->getStorage()) {
2777 | if (!$storage->deleteAttachment($this, $file)) {
2778 | $db->rollbackTransaction();
2779 | return false;
2780 | }
2781 | } else {
2782 | if (SeedDMS_Core_File::file_exists($this->_dms->contentDir . $file->getPath())) {
2783 | if (!SeedDMS_Core_File::removeFile($this->_dms->contentDir . $file->getPath())) {
2784 | $db->rollbackTransaction();
2785 | return false;
2786 | }
2787 | }
2788 | }
2789 |
2790 | $db->commitTransaction();
2791 | unset($this->_documentFiles);
2792 |
2793 | return true;
2794 | } /* }}} */
2795 |
2796 | /**
2797 | * Remove a document completly
2798 | *
2799 | * This methods calls the callback 'onPreRemoveDocument' before removing
2800 | * the document. The current document will be passed as the second
2801 | * parameter to the callback function. After successful deletion the
2802 | * 'onPostRemoveDocument' callback will be used. The current document id
2803 | * will be passed as the second parameter. If onPreRemoveDocument fails
2804 | * the whole function will fail and the document will not be deleted.
2805 | * The return value of 'onPostRemoveDocument' will be disregarded.
2806 | *
2807 | * @return boolean true on success, otherwise false
2808 | */
2809 | public function remove() { /* {{{ */
2810 | $db = $this->_dms->getDB();
2811 | $this->_dms->lasterror = '';
2812 |
2813 | /* Check if 'onPreRemoveDocument' callback is set */
2814 | if (isset($this->_dms->callbacks['onPreRemoveDocument'])) {
2815 | foreach ($this->_dms->callbacks['onPreRemoveDocument'] as $callback) {
2816 | $ret = call_user_func($callback[0], $callback[1], $this);
2817 | if (is_bool($ret))
2818 | return $ret;
2819 | }
2820 | }
2821 |
2822 | $res = $this->getContent();
2823 | if (is_bool($res) && !$res) return false;
2824 |
2825 | $db->startTransaction();
2826 |
2827 | // remove content of document
2828 | foreach ($this->_content as $version) {
2829 | if (!$this->_removeContent($version)) {
2830 | $db->rollbackTransaction();
2831 | return false;
2832 | }
2833 | }
2834 |
2835 | // remove all document files
2836 | $res = $this->getDocumentFiles();
2837 | if (is_bool($res) && !$res) {
2838 | $db->rollbackTransaction();
2839 | return false;
2840 | }
2841 |
2842 | foreach ($res as $documentfile)
2843 | if (!$this->removeDocumentFile($documentfile->getId())) {
2844 | $db->rollbackTransaction();
2845 | return false;
2846 | }
2847 |
2848 | // TODO: versioning file?
2849 |
2850 | if ($storage = $this->_dms->getStorage()) {
2851 | if (!$storage->deleteDocDir($this)) {
2852 | $db->rollbackTransaction();
2853 | return false;
2854 | }
2855 | } else {
2856 | if (SeedDMS_Core_File::file_exists($this->_dms->contentDir . $this->getDir()))
2857 | if (!SeedDMS_Core_File::removeDir($this->_dms->contentDir . $this->getDir())) {
2858 | $db->rollbackTransaction();
2859 | return false;
2860 | }
2861 | }
2862 |
2863 | $queryStr = "DELETE FROM `tblDocuments` WHERE `id` = " . $this->_id;
2864 | if (!$db->getResult($queryStr)) {
2865 | $db->rollbackTransaction();
2866 | return false;
2867 | }
2868 | $queryStr = "DELETE FROM `tblDocumentAttributes` WHERE `document` = " . $this->_id;
2869 | if (!$db->getResult($queryStr)) {
2870 | $db->rollbackTransaction();
2871 | return false;
2872 | }
2873 | $queryStr = "DELETE FROM `tblACLs` WHERE `target` = " . $this->_id . " AND `targetType` = " . T_DOCUMENT;
2874 | if (!$db->getResult($queryStr)) {
2875 | $db->rollbackTransaction();
2876 | return false;
2877 | }
2878 | $queryStr = "DELETE FROM `tblDocumentLinks` WHERE `document` = " . $this->_id . " OR `target` = " . $this->_id;
2879 | if (!$db->getResult($queryStr)) {
2880 | $db->rollbackTransaction();
2881 | return false;
2882 | }
2883 | $queryStr = "DELETE FROM `tblDocumentLocks` WHERE `document` = " . $this->_id;
2884 | if (!$db->getResult($queryStr)) {
2885 | $db->rollbackTransaction();
2886 | return false;
2887 | }
2888 | $queryStr = "DELETE FROM `tblDocumentFiles` WHERE `document` = " . $this->_id;
2889 | if (!$db->getResult($queryStr)) {
2890 | $db->rollbackTransaction();
2891 | return false;
2892 | }
2893 | $queryStr = "DELETE FROM `tblDocumentCategory` WHERE `documentID` = " . $this->_id;
2894 | if (!$db->getResult($queryStr)) {
2895 | $db->rollbackTransaction();
2896 | return false;
2897 | }
2898 |
2899 | // Delete the notification list.
2900 | $queryStr = "DELETE FROM `tblNotify` WHERE `target` = " . $this->_id . " AND `targetType` = " . T_DOCUMENT;
2901 | if (!$db->getResult($queryStr)) {
2902 | $db->rollbackTransaction();
2903 | return false;
2904 | }
2905 |
2906 | $db->commitTransaction();
2907 |
2908 | /* Check if 'onPostRemoveDocument' callback is set */
2909 | if (isset($this->_dms->callbacks['onPostRemoveDocument'])) {
2910 | foreach ($this->_dms->callbacks['onPostRemoveDocument'] as $callback) {
2911 | if (!call_user_func($callback[0], $callback[1], $this)) {
2912 | }
2913 | }
2914 | }
2915 |
2916 | return true;
2917 | } /* }}} */
2918 |
2919 | /**
2920 | * Get List of users and groups which have read access on the document.
2921 | * The list will not include any guest users,
2922 | * administrators and the owner of the document.
2923 | *
2924 | * This method is deprecated. Use
2925 | * {@see SeedDMS_Core_Document::getReadAccessList()} instead.
2926 | */
2927 | protected function __getApproversList() { /* {{{ */
2928 | return $this->getReadAccessList(0, 0, 0);
2929 | } /* }}} */
2930 |
2931 | /**
2932 | * Returns a list of groups and users with read access on the document
2933 | *
2934 | * @param boolean $listadmin if set to true any admin will be listed too
2935 | * @param boolean $listowner if set to true the owner will be listed too
2936 | * @param boolean $listguest if set to true any guest will be listed too
2937 | *
2938 | * @return array list of users and groups
2939 | */
2940 | public function getReadAccessList($listadmin = 0, $listowner = 0, $listguest = 0) { /* {{{ */
2941 | $db = $this->_dms->getDB();
2942 |
2943 | $cachehash = substr(md5($listadmin.$listowner.$listguest), 0, 3);
2944 | if (!isset($this->_readAccessList[$cachehash])) {
2945 | $this->_readAccessList[$cachehash] = array("groups" => array(), "users" => array());
2946 | $userIDs = "";
2947 | $groupIDs = "";
2948 | $defAccess = $this->getDefaultAccess();
2949 |
2950 | /* Check if the default access is < read access or >= read access.
2951 | * If default access is less than read access, then create a list
2952 | * of users and groups with read access.
2953 | * If default access is equal or greater then read access, then
2954 | * create a list of users and groups without read access.
2955 | */
2956 | if ($defAccess<M_READ) {
2957 | // Get the list of all users and groups that are listed in the ACL as
2958 | // having read access to the document.
2959 | $tmpList = $this->getAccessList(M_READ, O_GTEQ);
2960 | }
2961 | else {
2962 | // Get the list of all users and groups that DO NOT have read access
2963 | // to the document.
2964 | $tmpList = $this->getAccessList(M_NONE, O_LTEQ);
2965 | }
2966 | /** @var SeedDMS_Core_GroupAccess $groupAccess */
2967 | foreach ($tmpList["groups"] as $groupAccess) {
2968 | $groupIDs .= (strlen($groupIDs)==0 ? "" : ", ") . $groupAccess->getGroupID();
2969 | }
2970 |
2971 | /** @var SeedDMS_Core_UserAccess $userAccess */
2972 | foreach ($tmpList["users"] as $userAccess) {
2973 | $user = $userAccess->getUser();
2974 | // if (!$listadmin && $user->isAdmin()) continue;
2975 | // if (!$listowner && $user->getID() == $this->_ownerID) continue;
2976 | // if (!$listguest && $user->isGuest()) continue;
2977 | $userIDs .= (strlen($userIDs)==0 ? "" : ", ") . $user->getID();
2978 | }
2979 |
2980 | // Construct a query against the users table to identify those users
2981 | // that have read access on this document, either directly through an
2982 | // ACL entry, by virtue of ownership or by having administrative rights
2983 | // on the database.
2984 | $queryStr = "";
2985 | /* If default access is less then read, $userIDs and $groupIDs contains
2986 | * a list of user with read access
2987 | */
2988 | if ($defAccess < M_READ) {
2989 | $queryStr = "SELECT DISTINCT `tblUsers`.* FROM `tblUsers` ".
2990 | "LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`userID`=`tblUsers`.`id` ".
2991 | "WHERE 1=0".
2992 | ((strlen($groupIDs) > 0) ? " OR (`tblGroupMembers`.`groupID` IN (". $groupIDs ."))" : "").
2993 | ((strlen($userIDs) > 0) ? " OR (`tblUsers`.`id` IN (". $userIDs ."))" : "").
2994 | " OR (`tblUsers`.`role` = ".SeedDMS_Core_User::role_admin.")".
2995 | " OR (`tblUsers`.`id` = ". $this->_ownerID . ")".
2996 | " ORDER BY `login`";
2997 | }
2998 | /* If default access is equal or greater than M_READ, $userIDs and
2999 | * $groupIDs contains a list of user without read access
3000 | * The sql statement will exclude those users and groups but include
3001 | * admins and the owner
3002 | */
3003 | else {
3004 | $queryStr = "SELECT DISTINCT `tblUsers`.* FROM `tblUsers` ".
3005 | "LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`userID`=`tblUsers`.`id` ".
3006 | "WHERE 1=1".
3007 | (strlen($groupIDs) == 0 ? "" : " AND (`tblGroupMembers`.`groupID` NOT IN (". $groupIDs .") OR `tblGroupMembers`.`groupID` IS NULL)").
3008 | (strlen($userIDs) == 0 ? "" : " AND (`tblUsers`.`id` NOT IN (". $userIDs ."))").
3009 | " OR `tblUsers`.`id` = ". $this->_ownerID . " OR `tblUsers`.`role` = ".SeedDMS_Core_User::role_admin." ORDER BY `login` ";
3010 | }
3011 | $resArr = $db->getResultArray($queryStr);
3012 | if (!is_bool($resArr)) {
3013 | foreach ($resArr as $row) {
3014 | $user = $this->_dms->getUser($row['id']);
3015 | if (!$listadmin && $user->isAdmin()) continue;
3016 | if (!$listowner && $user->getID() == $this->_ownerID) continue;
3017 | if (!$listguest && $user->isGuest()) continue;
3018 | $this->_readAccessList[$cachehash]["users"][] = $user;
3019 | }
3020 | }
3021 |
3022 | // Assemble the list of groups that have read access to the document.
3023 | $queryStr = "";
3024 | if ($defAccess < M_READ) {
3025 | if (strlen($groupIDs)>0) {
3026 | $queryStr = "SELECT `tblGroups`.* FROM `tblGroups` ".
3027 | "WHERE `tblGroups`.`id` IN (". $groupIDs .") ORDER BY `name`";
3028 | }
3029 | }
3030 | else {
3031 | if (strlen($groupIDs)>0) {
3032 | $queryStr = "SELECT `tblGroups`.* FROM `tblGroups` ".
3033 | "WHERE `tblGroups`.`id` NOT IN (". $groupIDs .") ORDER BY `name`";
3034 | }
3035 | else {
3036 | $queryStr = "SELECT `tblGroups`.* FROM `tblGroups` ORDER BY `name`";
3037 | }
3038 | }
3039 | if (strlen($queryStr)>0) {
3040 | $resArr = $db->getResultArray($queryStr);
3041 | if (!is_bool($resArr)) {
3042 | foreach ($resArr as $row) {
3043 | $group = $this->_dms->getGroup($row["id"]);
3044 | $this->_readAccessList[$cachehash]["groups"][] = $group;
3045 | }
3046 | }
3047 | }
3048 | }
3049 | return $this->_readAccessList[$cachehash];
3050 | } /* }}} */
3051 |
3052 | /**
3053 | * Get the internally used folderList which stores the ids of folders from
3054 | * the root folder to the parent folder.
3055 | *
3056 | * @return string column separated list of folder ids
3057 | */
3058 | public function getFolderList() { /* {{{ */
3059 | $db = $this->_dms->getDB();
3060 |
3061 | $queryStr = "SELECT `folderList` FROM `tblDocuments` WHERE id = ".$this->_id;
3062 | $resArr = $db->getResultArray($queryStr);
3063 | if (is_bool($resArr) && !$resArr)
3064 | return false;
3065 |
3066 | return $resArr[0]['folderList'];
3067 | } /* }}} */
3068 |
3069 | /**
3070 | * Checks the internal data of the document and repairs it.
3071 | * Currently, this function only repairs an incorrect folderList
3072 | *
3073 | * @return boolean true on success, otherwise false
3074 | */
3075 | public function repair() { /* {{{ */
3076 | $db = $this->_dms->getDB();
3077 |
3078 | $curfolderlist = $this->getFolderList();
3079 |
3080 | // calculate the folderList of the folder
3081 | $parent = $this->getFolder();
3082 | $pathPrefix = "";
3083 | $path = $parent->getPath();
3084 | foreach ($path as $f) {
3085 | $pathPrefix .= ":".$f->getID();
3086 | }
3087 | if (strlen($pathPrefix)>1) {
3088 | $pathPrefix .= ":";
3089 | }
3090 | if ($curfolderlist != $pathPrefix) {
3091 | $queryStr = "UPDATE `tblDocuments` SET `folderList`='".$pathPrefix."' WHERE `id` = ". $this->_id;
3092 | $res = $db->getResult($queryStr);
3093 | if (!$res)
3094 | return false;
3095 | }
3096 | return true;
3097 | } /* }}} */
3098 |
3099 | /**
3100 | * Calculate the disk space including all versions of the document
3101 | *
3102 | * This is done by using the internal database field storing the
3103 | * filesize of a document version.
3104 | *
3105 | * @return integer total disk space in Bytes
3106 | */
3107 | public function getUsedDiskSpace(): int { /* {{{ */
3108 | $db = $this->_dms->getDB();
3109 |
3110 | $queryStr = "SELECT SUM(`fileSize`) sum FROM `tblDocumentContent` WHERE `document` = " . $this->_id;
3111 | $resArr = $db->getResultArray($queryStr);
3112 | if (is_bool($resArr) && $resArr == false)
3113 | return false;
3114 |
3115 | return (int) $resArr[0]['sum'];
3116 | } /* }}} */
3117 |
3118 | /**
3119 | * Returns a list of events happend during the life of the document
3120 | *
3121 | * This includes the creation of new versions, approval and reviews, etc.
3122 | *
3123 | * @return array list of events
3124 | */
3125 | public function getTimeline() { /* {{{ */
3126 | $db = $this->_dms->getDB();
3127 |
3128 | $timeline = array();
3129 |
3130 | /* No need to add entries for new version because the status log
3131 | * will generate an entry as well.
3132 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = " . $this->_id;
3133 | $resArr = $db->getResultArray($queryStr);
3134 | if (is_bool($resArr) && $resArr == false)
3135 | return false;
3136 |
3137 | foreach ($resArr as $row) {
3138 | $date = date('Y-m-d H:i:s', $row['date']);
3139 | $timeline[] = array('date'=>$date, 'msg'=>'Added version '.$row['version'], 'type'=>'add_version', 'version'=>$row['version'], 'document'=>$this, 'params'=>array($row['version']));
3140 | }
3141 | */
3142 |
3143 | $queryStr = "SELECT * FROM `tblDocumentFiles` WHERE `document` = " . $this->_id;
3144 | $resArr = $db->getResultArray($queryStr);
3145 | if (is_bool($resArr) && $resArr == false)
3146 | return false;
3147 |
3148 | foreach ($resArr as $row) {
3149 | $date = date('Y-m-d H:i:s', (int) $row['date']);
3150 | $timeline[] = array('date'=>$date, 'msg'=>'Added attachment "'.$row['name'].'"', 'document'=>$this, 'type'=>'add_file', 'fileid'=>$row['id']);
3151 | }
3152 |
3153 | $queryStr =
3154 | "SELECT `tblDocumentStatus`.*, `tblDocumentStatusLog`.`statusLogID`,`tblDocumentStatusLog`.`status`, ".
3155 | "`tblDocumentStatusLog`.`comment`, `tblDocumentStatusLog`.`date`, ".
3156 | "`tblDocumentStatusLog`.`userID` ".
3157 | "FROM `tblDocumentStatus` ".
3158 | "LEFT JOIN `tblDocumentStatusLog` USING (`statusID`) ".
3159 | "WHERE `tblDocumentStatus`.`documentID` = '". $this->_id ."' ".
3160 | "ORDER BY `tblDocumentStatusLog`.`statusLogID` DESC";
3161 | $resArr = $db->getResultArray($queryStr);
3162 | if (is_bool($resArr) && !$resArr)
3163 | return false;
3164 |
3165 | /* The above query will also contain entries where a document status exists
3166 | * but no status log entry. Those records will have no date and must be
3167 | * skipped.
3168 | */
3169 | foreach ($resArr as $row) {
3170 | if ($row['date']) {
3171 | $date = $row['date'];
3172 | $timeline[] = array('date'=>$date, 'msg'=>'Version '.$row['version'].': Status change to '.$row['status'], 'type'=>'status_change', 'version'=>$row['version'], 'document'=>$this, 'status'=>$row['status'], 'statusid'=>$row['statusID'], 'statuslogid'=>$row['statusLogID']);
3173 | }
3174 | }
3175 | return $timeline;
3176 | } /* }}} */
3177 |
3178 | /**
3179 | * Transfers the document to a new user
3180 | *
3181 | * This method not just sets a new owner of the document but also
3182 | * transfers the document links, attachments and locks to the new user.
3183 | *
3184 | * @return boolean true if successful, otherwise false
3185 | */
3186 | public function transferToUser($newuser) { /* {{{ */
3187 | $db = $this->_dms->getDB();
3188 |
3189 | if ($newuser->getId() == $this->_ownerID)
3190 | return true;
3191 |
3192 | $db->startTransaction();
3193 | $queryStr = "UPDATE `tblDocuments` SET `owner` = ".$newuser->getId()." WHERE `id` = " . $this->_id;
3194 | if (!$db->getResult($queryStr)) {
3195 | $db->rollbackTransaction();
3196 | return false;
3197 | }
3198 |
3199 | $queryStr = "UPDATE `tblDocumentLocks` SET `userID` = ".$newuser->getId()." WHERE `document` = " . $this->_id . " AND `userID` = ".$this->_ownerID;
3200 | if (!$db->getResult($queryStr)) {
3201 | $db->rollbackTransaction();
3202 | return false;
3203 | }
3204 |
3205 | $queryStr = "UPDATE `tblDocumentLinks` SET `userID` = ".$newuser->getId()." WHERE `document` = " . $this->_id . " AND `userID` = ".$this->_ownerID;
3206 | if (!$db->getResult($queryStr)) {
3207 | $db->rollbackTransaction();
3208 | return false;
3209 | }
3210 |
3211 | $queryStr = "UPDATE `tblDocumentFiles` SET `userID` = ".$newuser->getId()." WHERE `document` = " . $this->_id . " AND `userID` = ".$this->_ownerID;
3212 | if (!$db->getResult($queryStr)) {
3213 | $db->rollbackTransaction();
3214 | return false;
3215 | }
3216 |
3217 | $this->_ownerID = $newuser->getID();
3218 | $this->_owner = $newuser;
3219 |
3220 | $db->commitTransaction();
3221 | return true;
3222 | } /* }}} */
3223 |
3224 | } /* }}} */
3225 |
3226 |
3227 | /**
3228 | * Class to represent content of a document
3229 | *
3230 | * Each document has content attached to it, often called a 'version' of the
3231 | * document. The document content represents a file on the disk with some
3232 | * meta data stored in the database. A document content has a version number
3233 | * which is incremented with each replacement of the old content. Old versions
3234 | * are kept unless they are explicitly deleted by
3235 | * {@see SeedDMS_Core_Document::removeContent()}.
3236 | *
3237 | * @category DMS
3238 | * @package SeedDMS_Core
3239 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli,
3240 | * Uwe Steinmann <uwe@steinmann.cx>
3241 | * @copyright Copyright (C) 2002-2005 Markus Westphal,
3242 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli,
3243 | * 2010-2024 Uwe Steinmann
3244 | * @version Release: @package_version@
3245 | */
3246 | class SeedDMS_Core_DocumentContent extends SeedDMS_Core_Object { /* {{{ */
3247 | /**
3248 | * @var object document
3249 | */
3250 | protected $_document;
3251 |
3252 | /**
3253 | * @var integer version
3254 | */
3255 | protected $_version;
3256 |
3257 | /**
3258 | * @var string comment
3259 | */
3260 | protected $_comment;
3261 |
3262 | /**
3263 | * @var string date
3264 | */
3265 | protected $_date;
3266 |
3267 | /**
3268 | * @var integer $_userID
3269 | */
3270 | protected $_userID;
3271 |
3272 | /**
3273 | * @var object $_user
3274 | */
3275 | protected $_user;
3276 |
3277 | /**
3278 | * @var string dir on disk (deprecated)
3279 | */
3280 | protected $_dir;
3281 |
3282 | /**
3283 | * @var string original file name
3284 | */
3285 | protected $_orgFileName;
3286 |
3287 | /**
3288 | * @var string file type (actually the extension without the leading dot)
3289 | */
3290 | protected $_fileType;
3291 |
3292 | /**
3293 | * @var string mime type
3294 | */
3295 | protected $_mimeType;
3296 |
3297 | /**
3298 | * @var string checksum of content
3299 | */
3300 | protected $_checksum;
3301 |
3302 | /**
3303 | * @var int size of content file
3304 | */
3305 | protected $_fileSize;
3306 |
3307 | /**
3308 | * @var object workflow
3309 | */
3310 | protected $_workflow;
3311 |
3312 | /**
3313 | * @var object workflow state
3314 | */
3315 | protected $_workflowState;
3316 |
3317 | /**
3318 | * @var int $_status state
3319 | */
3320 | protected $_status;
3321 |
3322 | /**
3323 | * @var int $_reviewStatus state
3324 | */
3325 | protected $_reviewStatus;
3326 |
3327 | /**
3328 | * @var int $_approvalStatus state
3329 | */
3330 | protected $_approvalStatus;
3331 |
3332 | /**
3333 | * @var array $_readAccessList
3334 | */
3335 | protected $_readAccessList;
3336 |
3337 | /**
3338 | * @var object dms
3339 | */
3340 | public $_dms;
3341 |
3342 | /**
3343 | * Recalculate the status of a document
3344 | *
3345 | * The methods checks the review and approval status and sets the
3346 | * status of the document accordingly.
3347 | *
3348 | * If status is S_RELEASED and the version has a workflow, then set
3349 | * the status to S_IN_WORKFLOW
3350 | * If status is S_RELEASED and there are reviewers => set status S_DRAFT_REV
3351 | * If status is S_RELEASED or S_DRAFT_REV and there are approvers => set
3352 | * status S_DRAFT_APP
3353 | * If status is draft and there are no approver and no reviewers => set
3354 | * status to S_RELEASED
3355 | * The status of a document with the current status S_OBSOLETE, S_REJECTED,
3356 | * or S_EXPIRED will not be changed unless the parameter
3357 | * $ignorecurrentstatus is set to true.
3358 | *
3359 | * This method may not be called after a negative approval or review to
3360 | * recalculated the status, because
3361 | * it doesn't take a defeating approval or review into account. This method
3362 | * does not set the status to S_REJECTED! It will
3363 | * just check for a pending workflow, approval or review and set the status
3364 | * accordingly, e.g. after the list of reviewers or appovers has been
3365 | * modified. If there is no pending workflow, approval or review the
3366 | * status will be set to S_RELEASED.
3367 | *
3368 | * This method will call {@see SeedDMS_Core_DocumentContent::setStatus()}
3369 | * which checks if the status has actually changed. This is, why this
3370 | * function can be called at any time without harm to the status log.
3371 | *
3372 | * @param boolean $ignorecurrentstatus ignore the current status and
3373 | * recalculate a new status in any case
3374 | * @param object $user the user initiating this method
3375 | * @param string $msg message stored in status log when status is set
3376 | */
3377 | public function verifyStatus($ignorecurrentstatus = false, $user = null, $msg = '') { /* {{{ */
3378 |
3379 | unset($this->_status);
3380 | $st = $this->getStatus();
3381 |
3382 | if (!$ignorecurrentstatus && ($st["status"]==S_OBSOLETE || $st["status"]==S_REJECTED || $st["status"]==S_EXPIRED )) return $st['status'];
3383 |
3384 | $this->_workflow = null; // force to be reloaded from DB
3385 | $hasworkflow = $this->getWorkflow() ? true : false;
3386 |
3387 | /* $pendingReview will be set when there are still open reviews */
3388 | $pendingReview = false;
3389 | /* $hasReview will be set if there is at least one positiv review */
3390 | $hasReview = false;
3391 | unset($this->_reviewStatus); // force to be reloaded from DB
3392 | $reviewStatus = $this->getReviewStatus();
3393 | if (is_array($reviewStatus) && count($reviewStatus)>0) {
3394 | foreach ($reviewStatus as $r) {
3395 | if ($r["status"]==0) {
3396 | $pendingReview = true;
3397 | break;
3398 | } elseif ($r["status"]==1) {
3399 | $hasReview = true;
3400 | }
3401 | }
3402 | }
3403 |
3404 | /* $pendingApproval will be set when there are still open approvals */
3405 | $pendingApproval = false;
3406 | /* $hasApproval will be set if there is at least one positiv review */
3407 | $hasApproval = false;
3408 | unset($this->_approvalStatus); // force to be reloaded from DB
3409 | $approvalStatus = $this->getApprovalStatus();
3410 | if (is_array($approvalStatus) && count($approvalStatus)>0) {
3411 | foreach ($approvalStatus as $a) {
3412 | if ($a["status"]==0) {
3413 | $pendingApproval = true;
3414 | break;
3415 | } elseif ($a["status"]==1) {
3416 | $hasApproval = true;
3417 | }
3418 | }
3419 | }
3420 |
3421 | /* First check for a running workflow or open reviews or approvals. */
3422 | if ($hasworkflow) { $newstatus = S_IN_WORKFLOW; $ret = $this->setStatus(S_IN_WORKFLOW, $msg, $user); }
3423 | elseif ($pendingReview) { $newstatus = S_DRAFT_REV; $ret = $this->setStatus(S_DRAFT_REV, $msg, $user); }
3424 | elseif ($pendingApproval) { $newstatus = S_DRAFT_APP; $ret = $this->setStatus(S_DRAFT_APP, $msg, $user); }
3425 | else { $newstatus = S_RELEASED; $ret = $this->setStatus(S_RELEASED, $msg, $user); }
3426 | return $ret ? $newstatus : $ret;
3427 | } /* }}} */
3428 |
3429 | public function __construct($id, $document, $version, $comment, $date, $userID, $dir, $orgFileName, $fileType, $mimeType, $fileSize = 0, $checksum = '') { /* {{{ */
3430 | parent::__construct($id);
3431 | $this->_document = $document;
3432 | $this->_version = (int) $version;
3433 | $this->_comment = trim($comment);
3434 | $this->_date = (int) $date;
3435 | $this->_userID = (int) $userID;
3436 | $this->_user = null;
3437 | $this->_dir = $dir;
3438 | $this->_orgFileName = trim($orgFileName);
3439 | $this->_fileType = trim($fileType);
3440 | $this->_mimeType = trim($mimeType);
3441 | $this->_dms = $document->getDMS();
3442 | if (!$fileSize) {
3443 | if ($storage = $this->_dms->getStorage()) {
3444 | $filesize = $storage->getContentFilesize($document, $this);
3445 | } else {
3446 | $this->_fileSize = SeedDMS_Core_File::fileSize($this->_dms->contentDir . $this->getPath());
3447 | }
3448 | } else {
3449 | $this->_fileSize = (int) $fileSize;
3450 | }
3451 | $this->_checksum = $checksum;
3452 | $this->_workflow = null;
3453 | $this->_workflowState = null;
3454 | $this->_readAccessList = null;
3455 | } /* }}} */
3456 |
3457 | /**
3458 | * Check if this object is of type 'documentcontent'.
3459 | *
3460 | * @param string $type type of object
3461 | */
3462 | public function isType($type) { /* {{{ */
3463 | return $type == 'documentcontent';
3464 | } /* }}} */
3465 |
3466 | public function getVersion() { return $this->_version; }
3467 | public function getComment() { return $this->_comment; }
3468 | public function getDate() { return $this->_date; }
3469 | public function getOriginalFileName() { return $this->_orgFileName; }
3470 | public function getFileType() { return $this->_fileType; }
3471 | public function getFileName() { return $this->_version . $this->_fileType; }
3472 | /**
3473 | * getDir and the corresponding database table field are deprecated
3474 | */
3475 | private function __getDir() { return $this->_dir; }
3476 | public function getMimeType() { return $this->_mimeType; }
3477 | public function getDocument() { return $this->_document; }
3478 |
3479 | public function getUser() { /* {{{ */
3480 | if (!isset($this->_user))
3481 | $this->_user = $this->_document->getDMS()->getUser($this->_userID);
3482 | return $this->_user;
3483 | } /* }}} */
3484 |
3485 | /**
3486 | * Return path of file on disk relative to the content directory
3487 | *
3488 | * Since version 5.1.13 a single '.' in the fileType will be skipped.
3489 | * On Windows a file named 'name.' will be saved as 'name' but the fileType
3490 | * will contain the a single '.'.
3491 | *
3492 | * @return string path of file on disc
3493 | */
3494 | public function getPath() { return $this->_document->getDir() . $this->_version . $this->_fileType; }
3495 |
3496 | /*
3497 | * Check if content exists in storage
3498 | *
3499 | * @return boolean true if file exists
3500 | */
3501 | public function exists() { /* {{{ */
3502 | $document = $this->_document;
3503 | $dms = $document->getDMS();
3504 | $storage = $dms->getStorage();
3505 | if($storage) {
3506 | return $storage->hasContent($document, $this);
3507 | } else {
3508 | return file_exists($dms->contentDir . $this->getPath());
3509 | }
3510 | return true;
3511 | } /* }}} */
3512 |
3513 | /*
3514 | * Return file size
3515 | *
3516 | * @return int
3517 | */
3518 | public function size() { /* {{{ */
3519 | $document = $this->_document;
3520 | $dms = $document->getDMS();
3521 | $storage = $dms->getStorage();
3522 | if($storage) {
3523 | return $storage->getContentFilesize($document, $this);
3524 | } else {
3525 | return filesize($dms->contentDir . $this->getPath());
3526 | }
3527 | return true;
3528 | } /* }}} */
3529 |
3530 | /*
3531 | * Return content of file
3532 | *
3533 | * @return string file content
3534 | */
3535 | public function content() { /* {{{ */
3536 | $document = $this->_document;
3537 | $dms = $document->getDMS();
3538 | $storage = $dms->getStorage();
3539 | if($storage) {
3540 | return $storage->getContent($document, $this);
3541 | } else {
3542 | return file_get_contents($dms->contentDir . $this->getPath());
3543 | }
3544 | return true;
3545 | } /* }}} */
3546 |
3547 | /**
3548 | * Set upload date of document content
3549 | *
3550 | * @param string $date date must be a timestamp or in the format 'Y-m-d H:i:s'
3551 | *
3552 | * @return boolean true on success, otherwise false
3553 | */
3554 | public function setDate($date = false) { /* {{{ */
3555 | $db = $this->_document->getDMS()->getDB();
3556 |
3557 | if (!$date)
3558 | $date = time();
3559 | else {
3560 | if (is_string($date) && SeedDMS_Core_DMS::checkDate($date, 'Y-m-d H:i:s')) {
3561 | $date = strtotime($date);
3562 | } elseif (is_numeric($date))
3563 | $date = (int) $date;
3564 | else
3565 | return false;
3566 | }
3567 |
3568 | $queryStr = "UPDATE `tblDocumentContent` SET `date` = ". $date." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version;
3569 | if (!$db->getResult($queryStr))
3570 | return false;
3571 |
3572 | $this->_date = $date;
3573 |
3574 | return true;
3575 | } /* }}} */
3576 |
3577 | public function getFileSize() { /* {{{ */
3578 | return $this->_fileSize;
3579 | } /* }}} */
3580 |
3581 | /**
3582 | * Set file size by reading the file
3583 | */
3584 | public function setFileSize() { /* {{{ */
3585 | if ($storage = $this->_dms->getStorage()) {
3586 | $filesize = $storage->getContentFilesize($this->_document, $this);
3587 | } else {
3588 | $filesize = SeedDMS_Core_File::fileSize($this->_dms->contentDir . $this->_document->getDir() . $this->getFileName());
3589 | }
3590 | if ($filesize === false)
3591 | return false;
3592 |
3593 | $db = $this->_document->getDMS()->getDB();
3594 | $queryStr = "UPDATE `tblDocumentContent` SET `fileSize` = ".$filesize." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version;
3595 | if (!$db->getResult($queryStr))
3596 | return false;
3597 | $this->_fileSize = $filesize;
3598 |
3599 | return true;
3600 | } /* }}} */
3601 |
3602 | public function getChecksum() { /* {{{ */
3603 | return $this->_checksum;
3604 | } /* }}} */
3605 |
3606 | public function getRealChecksum() { /* {{{ */
3607 | if ($storage = $this->_dms->getStorage()) {
3608 | $checksum = $storage->getContentChecksum($this->_document, $this);
3609 | } else {
3610 | $checksum = SeedDMS_Core_File::checksum($this->_dms->contentDir . $this->getPath());
3611 | }
3612 | return $checksum;
3613 | } /* }}} */
3614 |
3615 | /**
3616 | * Set checksum by reading the file
3617 | */
3618 | public function setChecksum() { /* {{{ */
3619 | if ($storage = $this->_dms->getStorage()) {
3620 | $checksum = $storage->getContentChecksum($this->_document, $this);
3621 | } else {
3622 | $checksum = SeedDMS_Core_File::checksum($this->_dms->contentDir . $this->_document->getDir() . $this->getFileName());
3623 | }
3624 | if ($checksum === false)
3625 | return false;
3626 |
3627 | $db = $this->_document->getDMS()->getDB();
3628 | $queryStr = "UPDATE `tblDocumentContent` SET `checksum` = ".$db->qstr($checksum)." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version;
3629 | if (!$db->getResult($queryStr))
3630 | return false;
3631 | $this->_checksum = $checksum;
3632 |
3633 | return true;
3634 | } /* }}} */
3635 |
3636 | public function getRealMimeType() { /* {{{ */
3637 | if ($storage = $this->_dms->getStorage()) {
3638 | $mimetype = $storage->getContentMimetype($this->_document, $this);
3639 | } else {
3640 | $mimetype = SeedDMS_Core_File::mimetype($this->_dms->contentDir . $this->getPath());
3641 | }
3642 | return $mimetype;
3643 | } /* }}} */
3644 |
3645 | /**
3646 | * Set file type by evaluating the mime type
3647 | */
3648 | public function setFileType() { /* {{{ */
3649 | $mimetype = $this->getMimeType();
3650 |
3651 | $expect = SeedDMS_Core_File::fileExtension($mimetype);
3652 | if ($expect && '.'.$expect != $this->_fileType) {
3653 | $db = $this->_document->getDMS()->getDB();
3654 | $db->startTransaction();
3655 | $queryStr = "UPDATE `tblDocumentContent` SET `fileType`='.".$expect."' WHERE `id` = ". $this->_id;
3656 | $res = $db->getResult($queryStr);
3657 | if ($res) {
3658 | if ($storage = $this->_dms->getStorage()) {
3659 | $err = $storage->setFileType($this->_document, $this, '.'.$expect);
3660 | } else {
3661 | $err = SeedDMS_Core_File::renameFile($this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType, $this->_dms->contentDir.$this->_document->getDir() . $this->_version . '.' . $expect);
3662 | }
3663 | if (!$err) {
3664 | $db->rollbackTransaction();
3665 | } else {
3666 | $this->_fileType = '.'.$expect;
3667 | $db->commitTransaction();
3668 | return true;
3669 | }
3670 | } else {
3671 | $db->rollbackTransaction();
3672 | }
3673 | }
3674 |
3675 | return false;
3676 | } /* }}} */
3677 |
3678 | public function setMimeType($newMimetype) { /* {{{ */
3679 | $db = $this->_document->getDMS()->getDB();
3680 |
3681 | if (!$newMimetype)
3682 | return false;
3683 |
3684 | $newMimetype = trim($newMimetype);
3685 |
3686 | if (!$newMimetype)
3687 | return false;
3688 |
3689 | $queryStr = "UPDATE `tblDocumentContent` SET `mimeType` = ".$db->qstr($newMimetype)." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version;
3690 | if (!$db->getResult($queryStr))
3691 | return false;
3692 |
3693 | $this->_mimeType = $newMimetype;
3694 |
3695 | return true;
3696 | } /* }}} */
3697 |
3698 | public function setComment($newComment) { /* {{{ */
3699 | $db = $this->_document->getDMS()->getDB();
3700 |
3701 | /* Check if 'onPreSetVersionComment' callback is set */
3702 | if (isset($this->_dms->callbacks['onPreSetVersionComment'])) {
3703 | foreach ($this->_dms->callbacks['onPreSetVersionComment'] as $callback) {
3704 | $ret = call_user_func($callback[0], $callback[1], $this, $newComment);
3705 | if (is_bool($ret))
3706 | return $ret;
3707 | }
3708 | }
3709 |
3710 | $queryStr = "UPDATE `tblDocumentContent` SET `comment` = ".$db->qstr($newComment)." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version;
3711 | if (!$db->getResult($queryStr))
3712 | return false;
3713 |
3714 | $oldComment = $this->_comment;
3715 | $this->_comment = $newComment;
3716 |
3717 | /* Check if 'onPostSetVersionComment' callback is set */
3718 | if (isset($this->_dms->callbacks['onPostSetVersionComment'])) {
3719 | foreach ($this->_dms->callbacks['onPostSetVersionComment'] as $callback) {
3720 | $ret = call_user_func($callback[0], $callback[1], $this, $oldComment);
3721 | if (is_bool($ret))
3722 | return $ret;
3723 | }
3724 | }
3725 |
3726 | return true;
3727 | } /* }}} */
3728 |
3729 | /**
3730 | * Get the latest status of the content
3731 | *
3732 | * The status of the content reflects its current review, approval or workflow
3733 | * state. A status can be a negative or positive number or 0. A negative
3734 | * numbers indicate a missing approval, review or an obsolete content.
3735 | * Positive numbers indicate some kind of approval or workflow being
3736 | * active, but not necessarily a release.
3737 | * S_DRAFT_REV, 0
3738 | * S_DRAFT_APP, 1
3739 | * S_RELEASED, 2
3740 | * S_IN_WORKFLOW, 3
3741 | * S_REJECTED, -1
3742 | * S_OBSOLETE, -2
3743 | * S_EXPIRED, -3
3744 | * When a content is inserted and does not need approval nor review,
3745 | * then its status is set to S_RELEASED immediately. Any change of
3746 | * the status is monitored in the table tblDocumentStatusLog. This
3747 | * function will always return the latest entry for the content.
3748 | *
3749 | * @return array latest record from tblDocumentStatusLog
3750 | */
3751 | public function getStatus($limit = 1) { /* {{{ */
3752 | $db = $this->_document->getDMS()->getDB();
3753 |
3754 | if (!is_numeric($limit)) return false;
3755 |
3756 | // Retrieve the current overall status of the content represented by
3757 | // this object.
3758 | if (!isset($this->_status)) {
3759 | $queryStr =
3760 | "SELECT `tblDocumentStatus`.*, `tblDocumentStatusLog`.`status`, ".
3761 | "`tblDocumentStatusLog`.`comment`, `tblDocumentStatusLog`.`date`, ".
3762 | "`tblDocumentStatusLog`.`userID` ".
3763 | "FROM `tblDocumentStatus` ".
3764 | "LEFT JOIN `tblDocumentStatusLog` USING (`statusID`) ".
3765 | "WHERE `tblDocumentStatus`.`documentID` = '". $this->_document->getID() ."' ".
3766 | "AND `tblDocumentStatus`.`version` = '". $this->_version ."' ".
3767 | "ORDER BY `tblDocumentStatusLog`.`statusLogID` DESC LIMIT ".(int) $limit;
3768 |
3769 | $res = $db->getResultArray($queryStr);
3770 | if (is_bool($res) && !$res)
3771 | return false;
3772 | if (count($res)!=1)
3773 | return false;
3774 | $this->_status = $res[0];
3775 | }
3776 | return $this->_status;
3777 | } /* }}} */
3778 |
3779 | /**
3780 | * Get current and former states of the document content
3781 | *
3782 | * @param integer $limit if not set all log entries will be returned
3783 | * @return array list of status changes
3784 | */
3785 | public function getStatusLog($limit = 0) { /* {{{ */
3786 | $db = $this->_document->getDMS()->getDB();
3787 |
3788 | if (!is_numeric($limit)) return false;
3789 |
3790 | $queryStr =
3791 | "SELECT `tblDocumentStatus`.*, `tblDocumentStatusLog`.`status`, ".
3792 | "`tblDocumentStatusLog`.`comment`, `tblDocumentStatusLog`.`date`, ".
3793 | "`tblDocumentStatusLog`.`userID` ".
3794 | "FROM `tblDocumentStatus` ".
3795 | "LEFT JOIN `tblDocumentStatusLog` USING (`statusID`) ".
3796 | "WHERE `tblDocumentStatus`.`documentID` = '". $this->_document->getID() ."' ".
3797 | "AND `tblDocumentStatus`.`version` = '". $this->_version ."' ".
3798 | "ORDER BY `tblDocumentStatusLog`.`statusLogID` DESC ";
3799 | if ($limit)
3800 | $queryStr .= "LIMIT ".(int) $limit;
3801 |
3802 | $res = $db->getResultArray($queryStr);
3803 | if (is_bool($res) && !$res)
3804 | return false;
3805 |
3806 | return $res;
3807 | } /* }}} */
3808 |
3809 | /**
3810 | * Set the status of the content
3811 | *
3812 | * Setting the status means to add another entry into the table
3813 | * tblDocumentStatusLog. The method returns also false if the status
3814 | * is already set on the value passed to the method.
3815 | *
3816 | * @param integer $status new status of content
3817 | * @param string $comment comment for this status change
3818 | * @param object $updateUser user initiating the status change
3819 | * @param string $date date in the format 'Y-m-d H:i:s'
3820 | *
3821 | * @return boolean true on success, otherwise false
3822 | */
3823 | public function setStatus(int $status, string $comment, $updateUser, $date = '') { /* {{{ */
3824 | $db = $this->_document->getDMS()->getDB();
3825 |
3826 | if (!is_numeric($status)) return false;
3827 |
3828 | /* return an error if $updateuser is not set */
3829 | if (!$updateUser || !$updateUser->isType('user'))
3830 | return false;
3831 |
3832 | // If the supplied value lies outside of the accepted range, return an
3833 | // error.
3834 | if ($status < S_LOWEST_STATUS || $status > S_HIGHEST_STATUS) {
3835 | return false;
3836 | }
3837 |
3838 | // Retrieve the current overall status of the content represented by
3839 | // this object, if it hasn't been done already.
3840 | if (!isset($this->_status)) {
3841 | $this->getStatus();
3842 | }
3843 | if ($this->_status["status"]==$status) {
3844 | return true;
3845 | }
3846 | if ($date) {
3847 | if (!SeedDMS_Core_DMS::checkDate($date, 'Y-m-d H:i:s'))
3848 | return false;
3849 | $ddate = $db->qstr($date);
3850 | } else
3851 | $ddate = $db->getCurrentDatetime();
3852 | $db->startTransaction();
3853 | $queryStr = "INSERT INTO `tblDocumentStatusLog` (`statusID`, `status`, `comment`, `date`, `userID`) ".
3854 | "VALUES ('". $this->_status["statusID"] ."', '". (int) $status ."', ".$db->qstr($comment).", ".$ddate.", '". $updateUser->getID() ."')";
3855 | $res = $db->getResult($queryStr);
3856 | if (is_bool($res) && !$res) {
3857 | $db->rollbackTransaction();
3858 | return false;
3859 | }
3860 |
3861 | /* Check if 'onSetStatus' callback is set */
3862 | if (isset($this->_dms->callbacks['onSetStatus'])) {
3863 | foreach ($this->_dms->callbacks['onSetStatus'] as $callback) {
3864 | $ret = call_user_func($callback[0], $callback[1], $this, $updateUser, $this->_status["status"], $status);
3865 | if (is_bool($ret)) {
3866 | unset($this->_status);
3867 | if ($ret)
3868 | $db->commitTransaction();
3869 | else
3870 | $db->rollbackTransaction();
3871 | return $ret;
3872 | }
3873 | }
3874 | }
3875 |
3876 | $db->commitTransaction();
3877 | unset($this->_status);
3878 | return true;
3879 | } /* }}} */
3880 |
3881 | /**
3882 | * Rewrites the complete status log
3883 | *
3884 | * Attention: this function is highly dangerous.
3885 | * It removes an existing status log and rewrites it.
3886 | * This method was added for importing an xml dump.
3887 | *
3888 | * @param array $statuslog new status log with the newest log entry first.
3889 | * @return boolean true on success, otherwise false
3890 | */
3891 | public function rewriteStatusLog($statuslog) { /* {{{ */
3892 | $db = $this->_document->getDMS()->getDB();
3893 |
3894 | $queryStr = "SELECT `tblDocumentStatus`.* FROM `tblDocumentStatus` WHERE `tblDocumentStatus`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentStatus`.`version` = '". $this->_version ."' ";
3895 | $res = $db->getResultArray($queryStr);
3896 | if (is_bool($res) && !$res)
3897 | return false;
3898 |
3899 | $statusID = $res[0]['statusID'];
3900 |
3901 | $db->startTransaction();
3902 |
3903 | /* First, remove the old entries */
3904 | $queryStr = "DELETE FROM `tblDocumentStatusLog` WHERE `statusID`=".$statusID;
3905 | if (!$db->getResult($queryStr)) {
3906 | $db->rollbackTransaction();
3907 | return false;
3908 | }
3909 |
3910 | /* Second, insert the new entries */
3911 | $statuslog = array_reverse($statuslog);
3912 | foreach ($statuslog as $log) {
3913 | if (!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) {
3914 | $db->rollbackTransaction();
3915 | return false;
3916 | }
3917 | $queryStr = "INSERT INTO `tblDocumentStatusLog` (`statusID`, `status`, `comment`, `date`, `userID`) ".
3918 | "VALUES ('".$statusID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".$log['user']->getID().")";
3919 | if (!$db->getResult($queryStr)) {
3920 | $db->rollbackTransaction();
3921 | return false;
3922 | }
3923 | }
3924 |
3925 | $db->commitTransaction();
3926 | return true;
3927 | } /* }}} */
3928 |
3929 |
3930 | /**
3931 | * Returns the access mode similar to a document
3932 | *
3933 | * There is no real access mode for document content, so this is more
3934 | * like a virtual access mode, derived from the status of the document
3935 | * content. The function checks if {@see SeedDMS_Core_DMS::noReadForStatus}
3936 | * contains the status of the version and returns M_NONE if it exists and
3937 | * the user is not involved in a workflow or review/approval/revision.
3938 | * This method is called by all functions that returns the content e.g.
3939 | * {@see SeedDMS_Core_Document::getLatestContent()}
3940 | * It is also used by {@see SeedDMS_Core_Document::getAccessMode()} to
3941 | * prevent access on the whole document if there is no accessible version.
3942 | *
3943 | * FIXME: This method only works propperly if $u is the currently logged in
3944 | * user, because noReadForStatus will be set for this user.
3945 | * FIXED: instead of using $dms->noReadForStatus it is take from the user's role
3946 | *
3947 | * @param object $u user
3948 | * @return integer either M_NONE or M_READ
3949 | */
3950 | public function getAccessMode($u) { /* {{{ */
3951 | $dms = $this->_document->getDMS();
3952 |
3953 | /* Check if 'onCheckAccessDocumentContent' callback is set */
3954 | if (isset($this->_dms->callbacks['onCheckAccessDocumentContent'])) {
3955 | foreach ($this->_dms->callbacks['onCheckAccessDocumentContent'] as $callback) {
3956 | if (($ret = call_user_func($callback[0], $callback[1], $this, $u)) > 0) {
3957 | return $ret;
3958 | }
3959 | }
3960 | }
3961 |
3962 | return M_READ;
3963 |
3964 | if (!$u)
3965 | return M_NONE;
3966 |
3967 | /* If read access isn't further restricted by status, than grant read access */
3968 | if (!$dms->noReadForStatus)
3969 | return M_READ;
3970 | $noReadForStatus = $dms->noReadForStatus;
3971 |
3972 | /* If the current status is not in list of status without read access, then grant read access */
3973 | if (!in_array($this->getStatus()['status'], $noReadForStatus))
3974 | return M_READ;
3975 |
3976 | /* Administrators have unrestricted access */
3977 | if ($u->isAdmin()) return M_READ;
3978 |
3979 | /* The owner of the document has unrestricted access */
3980 | $owner = $this->_document->getOwner();
3981 | if ($u->getID() == $owner->getID()) return M_READ;
3982 |
3983 | /* Read/Write access on the document will also grant access on the version */
3984 | if ($this->_document->getAccessMode($u) >= M_READWRITE) return M_READ;
3985 |
3986 | /* At this point the current status is in the list of status without read access.
3987 | * The only way to still gain read access is, if the user is involved in the
3988 | * process, e.g. is a reviewer, approver or an active person in the workflow.
3989 | */
3990 | $s = $this->getStatus();
3991 | switch($s['status']) {
3992 | case S_DRAFT_REV:
3993 | $status = $this->getReviewStatus();
3994 | foreach ($status as $r) {
3995 | if ($r['status'] != -2) // Check if reviewer was removed
3996 | switch ($r["type"]) {
3997 | case 0: // Reviewer is an individual.
3998 | if ($u->getId() == $r["required"])
3999 | return M_READ;
4000 | break;
4001 | case 1: // Reviewer is a group.
4002 | $required = $dms->getGroup($r["required"]);
4003 | if (is_object($required) && $required->isMember($u))
4004 | return M_READ;
4005 | break;
4006 | }
4007 | }
4008 | break;
4009 | case S_DRAFT_APP:
4010 | $status = $this->getApprovalStatus();
4011 | foreach ($status as $r) {
4012 | if ($r['status'] != -2) // Check if approver was removed
4013 | switch ($r["type"]) {
4014 | case 0: // Reviewer is an individual.
4015 | if ($u->getId() == $r["required"])
4016 | return M_READ;
4017 | break;
4018 | case 1: // Reviewer is a group.
4019 | $required = $dms->getGroup($r["required"]);
4020 | if (is_object($required) && $required->isMember($u))
4021 | return M_READ;
4022 | break;
4023 | }
4024 | }
4025 | break;
4026 | case S_RELEASED:
4027 | break;
4028 | case S_IN_WORKFLOW:
4029 | if (!$this->_workflow)
4030 | $this->getWorkflow();
4031 |
4032 | if ($this->_workflow) {
4033 | if (!$this->_workflowState)
4034 | $this->getWorkflowState();
4035 | $transitions = $this->_workflow->getNextTransitions($this->_workflowState);
4036 | foreach ($transitions as $transition) {
4037 | if ($this->triggerWorkflowTransitionIsAllowed($u, $transition))
4038 | return M_READ;
4039 | }
4040 | }
4041 | break;
4042 | case S_REJECTED:
4043 | break;
4044 | case S_OBSOLETE:
4045 | break;
4046 | case S_EXPIRED:
4047 | break;
4048 | }
4049 |
4050 | return M_NONE;
4051 | } /* }}} */
4052 |
4053 | /**
4054 | * Return a list of all reviewers separated by individuals and groups
4055 | * This list will not take the review log into account. Therefore it
4056 | * can contain reviewers which has actually been deleted as a reviewer.
4057 | *
4058 | * @return array|bool|null
4059 | */
4060 | public function getReviewers() { /* {{{ */
4061 | $dms = $this->_document->getDMS();
4062 | $db = $dms->getDB();
4063 |
4064 | $queryStr =
4065 | "SELECT * FROM `tblDocumentReviewers` WHERE `version`='".$this->_version
4066 | ."' AND `documentID` = '". $this->_document->getID() ."' ";
4067 |
4068 | $recs = $db->getResultArray($queryStr);
4069 | if (is_bool($recs))
4070 | return false;
4071 | $reviewers = array('i'=>array(), 'g'=>array());
4072 | foreach ($recs as $rec) {
4073 | if ($rec['type'] == 0) {
4074 | if ($u = $dms->getUser($rec['required']))
4075 | $reviewers['i'][] = $u;
4076 | } elseif ($rec['type'] == 1) {
4077 | if ($g = $dms->getGroup($rec['required']))
4078 | $reviewers['g'][] = $g;
4079 | }
4080 | }
4081 | return $reviewers;
4082 | } /* }}} */
4083 |
4084 | /**
4085 | * Get the current review status of the document content
4086 | * The review status is a list of reviewers and its current status
4087 | *
4088 | * @param integer $limit the number of recent status changes per reviewer
4089 | * @return array list of review status
4090 | */
4091 | public function getReviewStatus($limit = 1) { /* {{{ */
4092 | $db = $this->_document->getDMS()->getDB();
4093 |
4094 | if (!is_numeric($limit)) return false;
4095 |
4096 | // Retrieve the current status of each assigned reviewer for the content
4097 | // represented by this object.
4098 | // FIXME: caching was turned off to make list of review log in ViewDocument
4099 | // possible
4100 | if (1 || !isset($this->_reviewStatus)) {
4101 | /* First get a list of all reviews for this document content */
4102 | $queryStr =
4103 | "SELECT `reviewID` FROM `tblDocumentReviewers` WHERE `version`='".$this->_version
4104 | ."' AND `documentID` = '". $this->_document->getID() ."' ";
4105 | $recs = $db->getResultArray($queryStr);
4106 | if (is_bool($recs) && !$recs)
4107 | return false;
4108 | $this->_reviewStatus = array();
4109 | if ($recs) {
4110 | foreach ($recs as $rec) {
4111 | $queryStr =
4112 | "SELECT `tblDocumentReviewers`.*, `tblDocumentReviewLog`.`reviewLogID`, `tblDocumentReviewLog`.`status`, ".
4113 | "`tblDocumentReviewLog`.`comment`, `tblDocumentReviewLog`.`date`, ".
4114 | "`tblDocumentReviewLog`.`userID`, `tblUsers`.`fullName`, `tblGroups`.`name` AS `groupName` ".
4115 | "FROM `tblDocumentReviewers` ".
4116 | "LEFT JOIN `tblDocumentReviewLog` USING (`reviewID`) ".
4117 | "LEFT JOIN `tblUsers` on `tblUsers`.`id` = `tblDocumentReviewers`.`required`".
4118 | "LEFT JOIN `tblGroups` on `tblGroups`.`id` = `tblDocumentReviewers`.`required`".
4119 | "WHERE `tblDocumentReviewers`.`reviewID` = '". $rec['reviewID'] ."' ".
4120 | "ORDER BY `tblDocumentReviewLog`.`reviewLogID` DESC LIMIT ".(int) $limit;
4121 |
4122 | $res = $db->getResultArray($queryStr);
4123 | if (is_bool($res) && !$res) {
4124 | unset($this->_reviewStatus);
4125 | return false;
4126 | }
4127 | foreach ($res as &$t) {
4128 | $filename = $this->_dms->contentDir . $this->_document->getDir().'r'.$t['reviewLogID'];
4129 | if (SeedDMS_Core_File::file_exists($filename))
4130 | $t['file'] = $filename;
4131 | else
4132 | $t['file'] = '';
4133 | }
4134 | $this->_reviewStatus = array_merge($this->_reviewStatus, $res);
4135 | }
4136 | }
4137 | }
4138 | return $this->_reviewStatus;
4139 | } /* }}} */
4140 |
4141 | /**
4142 | * Get the latest entries from the review log of the document content
4143 | *
4144 | * @param integer $limit the number of log entries returned, defaults to 1
4145 | * @return array list of review log entries
4146 | */
4147 | public function getReviewLog($limit = 1) { /* {{{ */
4148 | $db = $this->_document->getDMS()->getDB();
4149 |
4150 | if (!is_numeric($limit)) return false;
4151 |
4152 | $queryStr =
4153 | "SELECT * FROM `tblDocumentReviewLog` LEFT JOIN `tblDocumentReviewers` ON `tblDocumentReviewLog`.`reviewID` = `tblDocumentReviewers`.`reviewID` WHERE `version`='".$this->_version
4154 | ."' AND `documentID` = '". $this->_document->getID() ."' "
4155 | ."ORDER BY `tblDocumentReviewLog`.`reviewLogID` DESC LIMIT ".(int) $limit;
4156 | $recs = $db->getResultArray($queryStr);
4157 | if (is_bool($recs) && !$recs)
4158 | return false;
4159 | return($recs);
4160 | } /* }}} */
4161 |
4162 | /**
4163 | * Rewrites the complete review log
4164 | *
4165 | * Attention: this function is highly dangerous.
4166 | * It removes an existing review log and rewrites it.
4167 | * This method was added for importing an xml dump.
4168 | *
4169 | * @param array $reviewlog new status log with the newest log entry first.
4170 | * @return boolean true on success, otherwise false
4171 | */
4172 | public function rewriteReviewLog($reviewers) { /* {{{ */
4173 | $db = $this->_document->getDMS()->getDB();
4174 |
4175 | $queryStr = "SELECT `tblDocumentReviewers`.* FROM `tblDocumentReviewers` WHERE `tblDocumentReviewers`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentReviewers`.`version` = '". $this->_version ."' ";
4176 | $res = $db->getResultArray($queryStr);
4177 | if (is_bool($res) && !$res)
4178 | return false;
4179 |
4180 | $db->startTransaction();
4181 |
4182 | if ($res) {
4183 | foreach ($res as $review) {
4184 | $reviewID = $review['reviewID'];
4185 |
4186 | /* First, remove the old entries */
4187 | $queryStr = "DELETE FROM `tblDocumentReviewLog` WHERE `reviewID`=".$reviewID;
4188 | if (!$db->getResult($queryStr)) {
4189 | $db->rollbackTransaction();
4190 | return false;
4191 | }
4192 |
4193 | $queryStr = "DELETE FROM `tblDocumentReviewers` WHERE `reviewID`=".$reviewID;
4194 | if (!$db->getResult($queryStr)) {
4195 | $db->rollbackTransaction();
4196 | return false;
4197 | }
4198 | }
4199 | }
4200 |
4201 | /* Second, insert the new entries */
4202 | foreach ($reviewers as $review) {
4203 | $queryStr = "INSERT INTO `tblDocumentReviewers` (`documentID`, `version`, `type`, `required`) ".
4204 | "VALUES ('".$this->_document->getID()."', '".$this->_version."', ".$review['type'] .", ".(is_object($review['required']) ? $review['required']->getID() : (int) $review['required']).")";
4205 | if (!$db->getResult($queryStr)) {
4206 | $db->rollbackTransaction();
4207 | return false;
4208 | }
4209 | $reviewID = $db->getInsertID('tblDocumentReviewers', 'reviewID');
4210 | $reviewlog = array_reverse($review['logs']);
4211 | foreach ($reviewlog as $log) {
4212 | if (!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) {
4213 | $db->rollbackTransaction();
4214 | return false;
4215 | }
4216 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ".
4217 | "VALUES ('".$reviewID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".(is_object($log['user']) ? $log['user']->getID() : (int) $log['user']).")";
4218 | if (!$db->getResult($queryStr)) {
4219 | $db->rollbackTransaction();
4220 | return false;
4221 | }
4222 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID');
4223 | if (!empty($log['file'])) {
4224 | SeedDMS_Core_File::copyFile($log['file'], $this->_dms->contentDir . $this->_document->getDir() . 'r' . $reviewLogID);
4225 | }
4226 | }
4227 | }
4228 |
4229 | $db->commitTransaction();
4230 | return true;
4231 | } /* }}} */
4232 |
4233 | /**
4234 | * Return a list of all approvers separated by individuals and groups
4235 | * This list will not take the approval log into account. Therefore it
4236 | * can contain approvers which has actually been deleted as an approver.
4237 | *
4238 | * @return array|bool|null
4239 | */
4240 | public function getApprovers() { /* {{{ */
4241 | $dms = $this->_document->getDMS();
4242 | $db = $dms->getDB();
4243 |
4244 | $queryStr =
4245 | "SELECT * FROM `tblDocumentApprovers` WHERE `version`='".$this->_version
4246 | ."' AND `documentID` = '". $this->_document->getID() ."' ";
4247 |
4248 | $recs = $db->getResultArray($queryStr);
4249 | if (is_bool($recs))
4250 | return false;
4251 | $approvers = array('i'=>array(), 'g'=>array());
4252 | foreach ($recs as $rec) {
4253 | if ($rec['type'] == 0) {
4254 | if ($u = $dms->getUser($rec['required']))
4255 | $approvers['i'][] = $u;
4256 | } elseif ($rec['type'] == 1) {
4257 | if ($g = $dms->getGroup($rec['required']))
4258 | $approvers['g'][] = $g;
4259 | }
4260 | }
4261 | return $approvers;
4262 | } /* }}} */
4263 |
4264 | /**
4265 | * Get the current approval status of the document content
4266 | * The approval status is a list of approvers and its current status
4267 | *
4268 | * @param integer $limit the number of recent status changes per approver
4269 | * @return array list of approval status
4270 | */
4271 | public function getApprovalStatus($limit = 1) { /* {{{ */
4272 | $db = $this->_document->getDMS()->getDB();
4273 |
4274 | if (!is_numeric($limit)) return false;
4275 |
4276 | // Retrieve the current status of each assigned approver for the content
4277 | // represented by this object.
4278 | // FIXME: caching was turned off to make list of approval log in ViewDocument
4279 | // possible
4280 | if (1 || !isset($this->_approvalStatus)) {
4281 | /* First get a list of all approvals for this document content */
4282 | $queryStr =
4283 | "SELECT `approveID` FROM `tblDocumentApprovers` WHERE `version`='".$this->_version
4284 | ."' AND `documentID` = '". $this->_document->getID() ."' ";
4285 | $recs = $db->getResultArray($queryStr);
4286 | if (is_bool($recs) && !$recs)
4287 | return false;
4288 | $this->_approvalStatus = array();
4289 | if ($recs) {
4290 | foreach ($recs as $rec) {
4291 | $queryStr =
4292 | "SELECT `tblDocumentApprovers`.*, `tblDocumentApproveLog`.`approveLogID`, `tblDocumentApproveLog`.`status`, ".
4293 | "`tblDocumentApproveLog`.`comment`, `tblDocumentApproveLog`.`date`, ".
4294 | "`tblDocumentApproveLog`.`userID`, `tblUsers`.`fullName`, `tblGroups`.`name` AS `groupName` ".
4295 | "FROM `tblDocumentApprovers` ".
4296 | "LEFT JOIN `tblDocumentApproveLog` USING (`approveID`) ".
4297 | "LEFT JOIN `tblUsers` on `tblUsers`.`id` = `tblDocumentApprovers`.`required` ".
4298 | "LEFT JOIN `tblGroups` on `tblGroups`.`id` = `tblDocumentApprovers`.`required`".
4299 | "WHERE `tblDocumentApprovers`.`approveID` = '". $rec['approveID'] ."' ".
4300 | "ORDER BY `tblDocumentApproveLog`.`approveLogID` DESC LIMIT ".(int) $limit;
4301 |
4302 | $res = $db->getResultArray($queryStr);
4303 | if (is_bool($res) && !$res) {
4304 | unset($this->_approvalStatus);
4305 | return false;
4306 | }
4307 | foreach ($res as &$t) {
4308 | $filename = $this->_dms->contentDir . $this->_document->getDir().'a'.$t['approveLogID'];
4309 | if (SeedDMS_Core_File::file_exists($filename))
4310 | $t['file'] = $filename;
4311 | else
4312 | $t['file'] = '';
4313 | }
4314 | $this->_approvalStatus = array_merge($this->_approvalStatus, $res);
4315 | }
4316 | }
4317 | }
4318 | return $this->_approvalStatus;
4319 | } /* }}} */
4320 |
4321 | /**
4322 | * Get the latest entries from the approval log of the document content
4323 | *
4324 | * @param integer $limit the number of log entries returned, defaults to 1
4325 | * @return array list of approval log entries
4326 | */
4327 | public function getApproveLog($limit = 1) { /* {{{ */
4328 | $db = $this->_document->getDMS()->getDB();
4329 |
4330 | if (!is_numeric($limit)) return false;
4331 |
4332 | $queryStr =
4333 | "SELECT * FROM `tblDocumentApproveLog` LEFT JOIN `tblDocumentApprovers` ON `tblDocumentApproveLog`.`approveID` = `tblDocumentApprovers`.`approveID` WHERE `version`='".$this->_version
4334 | ."' AND `documentID` = '". $this->_document->getID() ."' "
4335 | ."ORDER BY `tblDocumentApproveLog`.`approveLogID` DESC LIMIT ".(int) $limit;
4336 | $recs = $db->getResultArray($queryStr);
4337 | if (is_bool($recs) && !$recs)
4338 | return false;
4339 | return($recs);
4340 | } /* }}} */
4341 |
4342 | /**
4343 | * Rewrites the complete approval log
4344 | *
4345 | * Attention: this function is highly dangerous.
4346 | * It removes an existing review log and rewrites it.
4347 | * This method was added for importing an xml dump.
4348 | *
4349 | * @param array $reviewlog new status log with the newest log entry first.
4350 | * @return boolean true on success, otherwise false
4351 | */
4352 | public function rewriteApprovalLog($reviewers) { /* {{{ */
4353 | $db = $this->_document->getDMS()->getDB();
4354 |
4355 | $queryStr = "SELECT `tblDocumentApprovers`.* FROM `tblDocumentApprovers` WHERE `tblDocumentApprovers`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentApprovers`.`version` = '". $this->_version ."' ";
4356 | $res = $db->getResultArray($queryStr);
4357 | if (is_bool($res) && !$res)
4358 | return false;
4359 |
4360 | $db->startTransaction();
4361 |
4362 | if ($res) {
4363 | foreach ($res as $review) {
4364 | $reviewID = $review['reviewID'];
4365 |
4366 | /* First, remove the old entries */
4367 | $queryStr = "DELETE FROM `tblDocumentApproveLog` WHERE `approveID`=".$reviewID;
4368 | if (!$db->getResult($queryStr)) {
4369 | $db->rollbackTransaction();
4370 | return false;
4371 | }
4372 |
4373 | $queryStr = "DELETE FROM `tblDocumentApprovers` WHERE `approveID`=".$reviewID;
4374 | if (!$db->getResult($queryStr)) {
4375 | $db->rollbackTransaction();
4376 | return false;
4377 | }
4378 | }
4379 | }
4380 |
4381 | /* Second, insert the new entries */
4382 | foreach ($reviewers as $review) {
4383 | $queryStr = "INSERT INTO `tblDocumentApprovers` (`documentID`, `version`, `type`, `required`) ".
4384 | "VALUES ('".$this->_document->getID()."', '".$this->_version."', ".$review['type'] .", ".(is_object($review['required']) ? $review['required']->getID() : (int) $review['required']).")";
4385 | if (!$db->getResult($queryStr)) {
4386 | $db->rollbackTransaction();
4387 | return false;
4388 | }
4389 | $reviewID = $db->getInsertID('tblDocumentApprovers', 'approveID');
4390 | $reviewlog = array_reverse($review['logs']);
4391 | foreach ($reviewlog as $log) {
4392 | if (!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) {
4393 | $db->rollbackTransaction();
4394 | return false;
4395 | }
4396 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ".
4397 | "VALUES ('".$reviewID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".(is_object($log['user']) ? $log['user']->getID() : (int) $log['user']).")";
4398 | if (!$db->getResult($queryStr)) {
4399 | $db->rollbackTransaction();
4400 | return false;
4401 | }
4402 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID');
4403 | if (!empty($log['file'])) {
4404 | SeedDMS_Core_File::copyFile($log['file'], $this->_dms->contentDir . $this->_document->getDir() . 'a' . $approveLogID);
4405 | }
4406 | }
4407 | }
4408 |
4409 | $db->commitTransaction();
4410 | return true;
4411 | } /* }}} */
4412 |
4413 | /**
4414 | * Add user as new reviewer
4415 | *
4416 | * @param object $user user in charge for the review
4417 | * @param object $requestUser user requesting the operation (usually the
4418 | * currently logged in user)
4419 | *
4420 | * @return integer|false if > 0 the id of the review log, if < 0 the error
4421 | * code, false in case of an sql error
4422 | */
4423 | public function addIndReviewer($user, $requestUser) { /* {{{ */
4424 | if (!$user || !$requestUser)
4425 | return -1;
4426 |
4427 | $db = $this->_document->getDMS()->getDB();
4428 |
4429 | if (!$user->isType('user'))
4430 | return -1;
4431 |
4432 | $userID = $user->getID();
4433 |
4434 | // Get the list of users and groups with read access to this document.
4435 | if ($this->_document->getAccessMode($user) < M_READ) {
4436 | return -2;
4437 | }
4438 |
4439 | // Check to see if the user has already been added to the review list.
4440 | $reviewStatus = $user->getReviewStatus($this->_document->getID(), $this->_version);
4441 | if (is_bool($reviewStatus) && !$reviewStatus) {
4442 | return false;
4443 | }
4444 | $indstatus = false;
4445 | if (count($reviewStatus["indstatus"]) > 0) {
4446 | $indstatus = array_pop($reviewStatus["indstatus"]);
4447 | if ($indstatus["status"]!=-2) {
4448 | // User is already on the list of reviewers; return an error.
4449 | return -3;
4450 | }
4451 | }
4452 |
4453 | // Add the user into the review database.
4454 | if (!$indstatus || ($indstatus && $indstatus["status"]!=-2)) {
4455 | $queryStr = "INSERT INTO `tblDocumentReviewers` (`documentID`, `version`, `type`, `required`) ".
4456 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '0', '". $userID ."')";
4457 | $res = $db->getResult($queryStr);
4458 | if (is_bool($res) && !$res) {
4459 | return false;
4460 | }
4461 | $reviewID = $db->getInsertID('tblDocumentReviewers', 'reviewID');
4462 | }
4463 | else {
4464 | $reviewID = isset($indstatus["reviewID"]) ? $indstatus["reviewID"] : null;
4465 | }
4466 |
4467 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ".
4468 | "VALUES ('". $reviewID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
4469 | $res = $db->getResult($queryStr);
4470 | if (is_bool($res) && !$res) {
4471 | return false;
4472 | }
4473 |
4474 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID');
4475 | $db->dropTemporaryTable('ttreviewid');
4476 | return $reviewLogID;
4477 | } /* }}} */
4478 |
4479 | /**
4480 | * Add group as new reviewer
4481 | *
4482 | * @param object $group group in charge for the review
4483 | * @param object $requestUser user requesting the operation (usually the
4484 | * currently logged in user)
4485 | *
4486 | * @return integer|false if > 0 the id of the review log, if < 0 the error
4487 | * code, false in case of an sql error
4488 | */
4489 | public function addGrpReviewer($group, $requestUser) { /* {{{ */
4490 | if (!$group || !$requestUser)
4491 | return -1;
4492 |
4493 | $db = $this->_document->getDMS()->getDB();
4494 |
4495 | if (!$group->isType('group'))
4496 | return -1;
4497 |
4498 | $groupID = $group->getID();
4499 |
4500 | // Get the list of users and groups with read access to this document.
4501 | if (!isset($this->_readAccessList)) {
4502 | // TODO: error checking.
4503 | $this->_readAccessList = $this->_document->getReadAccessList();
4504 | }
4505 | $approved = false;
4506 | foreach ($this->_readAccessList["groups"] as $appGroup) {
4507 | if ($groupID == $appGroup->getID()) {
4508 | $approved = true;
4509 | break;
4510 | }
4511 | }
4512 | if (!$approved) {
4513 | return -2;
4514 | }
4515 |
4516 | // Check to see if the group has already been added to the review list.
4517 | $reviewStatus = $group->getReviewStatus($this->_document->getID(), $this->_version);
4518 | if (is_bool($reviewStatus) && !$reviewStatus) {
4519 | return false;
4520 | }
4521 | if (count($reviewStatus) > 0 && $reviewStatus[0]["status"]!=-2) {
4522 | // Group is already on the list of reviewers; return an error.
4523 | return -3;
4524 | }
4525 |
4526 | // Add the group into the review database.
4527 | if (!isset($reviewStatus[0]["status"]) || (isset($reviewStatus[0]["status"]) && $reviewStatus[0]["status"]!=-2)) {
4528 | $queryStr = "INSERT INTO `tblDocumentReviewers` (`documentID`, `version`, `type`, `required`) ".
4529 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '1', '". $groupID ."')";
4530 | $res = $db->getResult($queryStr);
4531 | if (is_bool($res) && !$res) {
4532 | return false;
4533 | }
4534 | $reviewID = $db->getInsertID('tblDocumentReviewers', 'reviewID');
4535 | }
4536 | else {
4537 | $reviewID = isset($reviewStatus[0]["reviewID"]) ? $reviewStatus[0]["reviewID"] : null;
4538 | }
4539 |
4540 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ".
4541 | "VALUES ('". $reviewID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
4542 | $res = $db->getResult($queryStr);
4543 | if (is_bool($res) && !$res) {
4544 | return false;
4545 | }
4546 |
4547 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID');
4548 | $db->dropTemporaryTable('ttreviewid');
4549 | return $reviewLogID;
4550 | } /* }}} */
4551 |
4552 | /**
4553 | * Add a review to the document content
4554 | *
4555 | * This method will add an entry to the table tblDocumentReviewLog.
4556 | * It will first check if the user is ment to review the document version.
4557 | * It not the return value is -3.
4558 | * Next it will check if the users has been removed from the list of
4559 | * reviewers. In that case -4 will be returned.
4560 | * If the given review status has been set by the user before, it cannot
4561 | * be set again and 0 will be returned. Іf the review could be succesfully
4562 | * added, the review log id will be returned.
4563 | *
4564 | * @see SeedDMS_Core_DocumentContent::setApprovalByInd()
4565 | *
4566 | * @param object $user user doing the review
4567 | * @param object $requestUser user asking for the review, this is mostly
4568 | * the user currently logged in.
4569 | * @param integer $status status of review
4570 | * @param string $comment comment for review
4571 | *
4572 | * @return integer|bool new review log id, error code 0 till -4,
4573 | * false in case of an sql error
4574 | */
4575 | public function setReviewByInd($user, $requestUser, $status, $comment, $file = '') { /* {{{ */
4576 | if (!$user || !$requestUser)
4577 | return -1;
4578 |
4579 | $db = $this->_document->getDMS()->getDB();
4580 |
4581 | if (!$user->isType('user'))
4582 | return -1;
4583 |
4584 | // Check if the user is on the review list at all.
4585 | $reviewStatus = $user->getReviewStatus($this->_document->getID(), $this->_version);
4586 | if (is_bool($reviewStatus) && !$reviewStatus) {
4587 | return false;
4588 | }
4589 | if (count($reviewStatus["indstatus"])==0) {
4590 | // User is not assigned to review this document. No action required.
4591 | // Return an error.
4592 | return -3;
4593 | }
4594 | $indstatus = array_pop($reviewStatus["indstatus"]);
4595 | if ($indstatus["status"]==-2) {
4596 | // User has been deleted from reviewers
4597 | return -4;
4598 | }
4599 | // Check if the status is really different from the current status
4600 | if ($indstatus["status"] == $status)
4601 | return 0;
4602 |
4603 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`,
4604 | `comment`, `date`, `userID`) ".
4605 | "VALUES ('". $indstatus["reviewID"] ."', '".
4606 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '".
4607 | $requestUser->getID() ."')";
4608 | $res = $db->getResult($queryStr);
4609 | if (is_bool($res) && !$res)
4610 | return false;
4611 |
4612 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID');
4613 | if ($file) {
4614 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'r' . $reviewLogID);
4615 | }
4616 | return $reviewLogID;
4617 | } /* }}} */
4618 |
4619 | /**
4620 | * Add another entry to review log which resets the status
4621 | *
4622 | * This method will not delete anything from the database, but will add
4623 | * a new review log entry which sets the status to 0. This is only allowed
4624 | * if the current status is either 1 (reviewed) or -1 (rejected).
4625 | *
4626 | * After calling this method SeedDMS_Core_DocumentContent::verifyStatus()
4627 | * should be called to recalculate the document status.
4628 | *
4629 | * @param integer $reviewid id of review
4630 | * @param SeedDMS_Core_User $requestUser user requesting the removal
4631 | * @param string $comment comment
4632 | *
4633 | * @return integer|bool true if successful, error code < 0,
4634 | * false in case of an sql error
4635 | */
4636 | public function removeReview($reviewid, $requestUser, $comment = '') { /* {{{ */
4637 | $db = $this->_document->getDMS()->getDB();
4638 |
4639 | // Check to see if the user can be removed from the review list.
4640 | $reviews = $this->getReviewStatus();
4641 | if (is_bool($reviews) && !$reviews) {
4642 | return false;
4643 | }
4644 | $reviewStatus = null;
4645 | foreach ($reviews as $review) {
4646 | if ($review['reviewID'] == $reviewid) {
4647 | $reviewStatus = $review;
4648 | break;
4649 | }
4650 | }
4651 | if (!$reviewStatus)
4652 | return -2;
4653 |
4654 | // The review log entry may only be removed if the status is 1 or -1
4655 | if ($reviewStatus["status"] != 1 && $reviewStatus["status"] != -1)
4656 | return -3;
4657 |
4658 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`,
4659 | `comment`, `date`, `userID`) ".
4660 | "VALUES ('". $reviewStatus["reviewID"] ."', '0', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '".
4661 | $requestUser->getID() ."')";
4662 | $res = $db->getResult($queryStr);
4663 | if (is_bool($res) && !$res)
4664 | return false;
4665 |
4666 | return true;
4667 | } /* }}} */
4668 |
4669 | /**
4670 | * Add a review to the document content
4671 | *
4672 | * This method is similar to
4673 | * {@see SeedDMS_Core_DocumentContent::setReviewByInd()} but adds a review
4674 | * for a group instead of a user.
4675 | *
4676 | * @param object $group group doing the review
4677 | * @param object $requestUser user asking for the review, this is mostly
4678 | * the user currently logged in.
4679 | * @param integer $status status of review
4680 | * @param string $comment comment for review
4681 | *
4682 | * @return integer|bool new review log id, error code 0 till -4,
4683 | * false in case of an sql error
4684 | */
4685 | public function setReviewByGrp($group, $requestUser, $status, $comment, $file = '') { /* {{{ */
4686 | if (!$group || !$requestUser)
4687 | return -1;
4688 |
4689 | $db = $this->_document->getDMS()->getDB();
4690 |
4691 | if (!$group->isType('group'))
4692 | return -1;
4693 |
4694 | // Check if the group is on the review list at all.
4695 | $reviewStatus = $group->getReviewStatus($this->_document->getID(), $this->_version);
4696 | if (is_bool($reviewStatus) && !$reviewStatus) {
4697 | return false;
4698 | }
4699 | if (count($reviewStatus)==0) {
4700 | // User is not assigned to review this document. No action required.
4701 | // Return an error.
4702 | return -3;
4703 | }
4704 | if ((int) $reviewStatus[0]["status"]==-2) {
4705 | // Group has been deleted from reviewers
4706 | return -4;
4707 | }
4708 |
4709 | // Check if the status is really different from the current status
4710 | if ($reviewStatus[0]["status"] == $status)
4711 | return 0;
4712 |
4713 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`,
4714 | `comment`, `date`, `userID`) ".
4715 | "VALUES ('". $reviewStatus[0]["reviewID"] ."', '".
4716 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '".
4717 | $requestUser->getID() ."')";
4718 | $res = $db->getResult($queryStr);
4719 | if (is_bool($res) && !$res)
4720 | return false;
4721 |
4722 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID');
4723 | if ($file) {
4724 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'r' . $reviewLogID);
4725 | }
4726 | return $reviewLogID;
4727 | } /* }}} */
4728 |
4729 | /**
4730 | * Add user as new approver
4731 | *
4732 | * @param object $user user in charge for the approval
4733 | * @param object $requestUser user requesting the operation (usually the
4734 | * currently logged in user)
4735 | *
4736 | * @return integer|false if > 0 the id of the approval log, if < 0 the error
4737 | * code, false in case of an sql error
4738 | */
4739 | public function addIndApprover($user, $requestUser) { /* {{{ */
4740 | if (!$user || !$requestUser)
4741 | return -1;
4742 |
4743 | $db = $this->_document->getDMS()->getDB();
4744 |
4745 | if (!$user->isType('user'))
4746 | return -1;
4747 |
4748 | $userID = $user->getID();
4749 |
4750 | // Get the list of users and groups with read access to this document.
4751 | if ($this->_document->getAccessMode($user) < M_READ) {
4752 | return -2;
4753 | }
4754 |
4755 | // Check if the user has already been added to the approvers list.
4756 | $approvalStatus = $user->getApprovalStatus($this->_document->getID(), $this->_version);
4757 | if (is_bool($approvalStatus) && !$approvalStatus) {
4758 | return false;
4759 | }
4760 | $indstatus = false;
4761 | if (count($approvalStatus["indstatus"]) > 0) {
4762 | $indstatus = array_pop($approvalStatus["indstatus"]);
4763 | if ($indstatus["status"]!=-2) {
4764 | // User is already on the list of approverss; return an error.
4765 | return -3;
4766 | }
4767 | }
4768 |
4769 | if (!$indstatus || (isset($indstatus["status"]) && $indstatus["status"]!=-2)) {
4770 | // Add the user into the approvers database.
4771 | $queryStr = "INSERT INTO `tblDocumentApprovers` (`documentID`, `version`, `type`, `required`) ".
4772 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '0', '". $userID ."')";
4773 | $res = $db->getResult($queryStr);
4774 | if (is_bool($res) && !$res) {
4775 | return false;
4776 | }
4777 | $approveID = $db->getInsertID('tblDocumentApprovers', 'approveID');
4778 | }
4779 | else {
4780 | $approveID = isset($indstatus["approveID"]) ? $indstatus["approveID"] : null;
4781 | }
4782 |
4783 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ".
4784 | "VALUES ('". $approveID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
4785 | $res = $db->getResult($queryStr);
4786 | if (is_bool($res) && !$res) {
4787 | return false;
4788 | }
4789 |
4790 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID');
4791 | $db->dropTemporaryTable('ttapproveid');
4792 | return $approveLogID;
4793 | } /* }}} */
4794 |
4795 | /**
4796 | * Add group as new approver
4797 | *
4798 | * @param object $group group in charge for the approval
4799 | * @param object $requestUser user requesting the operation (usually the
4800 | * currently logged in user)
4801 | *
4802 | * @return integer|false if > 0 the id of the approval log, if < 0 the error
4803 | * code, false in case of an sql error
4804 | */
4805 | public function addGrpApprover($group, $requestUser) { /* {{{ */
4806 | if (!$group || !$requestUser)
4807 | return -1;
4808 |
4809 | $db = $this->_document->getDMS()->getDB();
4810 |
4811 | if (!$group->isType('group'))
4812 | return -1;
4813 |
4814 | $groupID = $group->getID();
4815 |
4816 | // Get the list of users and groups with read access to this document.
4817 | if (!isset($this->_readAccessList)) {
4818 | // TODO: error checking.
4819 | $this->_readAccessList = $this->_document->getReadAccessList();
4820 | }
4821 | $approved = false;
4822 | foreach ($this->_readAccessList["groups"] as $appGroup) {
4823 | if ($groupID == $appGroup->getID()) {
4824 | $approved = true;
4825 | break;
4826 | }
4827 | }
4828 | if (!$approved) {
4829 | return -2;
4830 | }
4831 |
4832 | // Check if the group has already been added to the approver list.
4833 | $approvalStatus = $group->getApprovalStatus($this->_document->getID(), $this->_version);
4834 | if (is_bool($approvalStatus) && !$approvalStatus) {
4835 | return false;
4836 | }
4837 | if (count($approvalStatus) > 0 && $approvalStatus[0]["status"]!=-2) {
4838 | // Group is already on the list of approvers; return an error.
4839 | return -3;
4840 | }
4841 |
4842 | // Add the group into the approver database.
4843 | if (!isset($approvalStatus[0]["status"]) || (isset($approvalStatus[0]["status"]) && $approvalStatus[0]["status"]!=-2)) {
4844 | $queryStr = "INSERT INTO `tblDocumentApprovers` (`documentID`, `version`, `type`, `required`) ".
4845 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '1', '". $groupID ."')";
4846 | $res = $db->getResult($queryStr);
4847 | if (is_bool($res) && !$res) {
4848 | return false;
4849 | }
4850 | $approveID = $db->getInsertID('tblDocumentApprovers', 'approveID');
4851 | }
4852 | else {
4853 | $approveID = isset($approvalStatus[0]["approveID"]) ? $approvalStatus[0]["approveID"] :null;
4854 | }
4855 |
4856 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ".
4857 | "VALUES ('". $approveID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
4858 | $res = $db->getResult($queryStr);
4859 | if (is_bool($res) && !$res) {
4860 | return false;
4861 | }
4862 |
4863 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID');
4864 | $db->dropTemporaryTable('ttapproveid');
4865 | return $approveLogID;
4866 | } /* }}} */
4867 |
4868 | /**
4869 | * Sets approval status of a document content for a user
4870 | *
4871 | * This method can be used to approve or reject a document content, or
4872 | * to reset its approval state. In most cases this function will be
4873 | * called by an user, but an admin may set the approval for
4874 | * somebody else.
4875 | * It is first checked if the user is in the list of approvers at all.
4876 | * Then it is check if the approval status is already -2. In both cases
4877 | * the function returns with an error.
4878 | *
4879 | * @see SeedDMS_Core_DocumentContent::setReviewByInd()
4880 | *
4881 | * @param object $user user in charge for doing the approval
4882 | * @param object $requestUser user actually calling this function
4883 | * @param integer $status the status of the approval, possible values are
4884 | * 0=unprocessed (maybe used to reset a status)
4885 | * 1=approved,
4886 | * -1=rejected,
4887 | * -2=user is deleted (use {link
4888 | * SeedDMS_Core_DocumentContent::delIndApprover} instead)
4889 | * @param string $comment approval comment
4890 | *
4891 | * @return integer|bool new review log id, error code 0 till -4,
4892 | * false in case of an sql error
4893 | */
4894 | public function setApprovalByInd($user, $requestUser, $status, $comment, $file = '') { /* {{{ */
4895 | if (!$user || !$requestUser)
4896 | return -1;
4897 |
4898 | $db = $this->_document->getDMS()->getDB();
4899 |
4900 | if (!$user->isType('user'))
4901 | return -1;
4902 |
4903 | // Check if the user is on the approval list at all.
4904 | $approvalStatus = $user->getApprovalStatus($this->_document->getID(), $this->_version);
4905 | if (is_bool($approvalStatus) && !$approvalStatus) {
4906 | return false;
4907 | }
4908 | if (count($approvalStatus["indstatus"])==0) {
4909 | // User is not assigned to approve this document. No action required.
4910 | // Return an error.
4911 | return -3;
4912 | }
4913 | $indstatus = array_pop($approvalStatus["indstatus"]);
4914 | if ($indstatus["status"]==-2) {
4915 | // User has been deleted from approvers
4916 | return -4;
4917 | }
4918 | // Check if the status is really different from the current status
4919 | if ($indstatus["status"] == $status)
4920 | return 0;
4921 |
4922 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`,
4923 | `comment`, `date`, `userID`) ".
4924 | "VALUES ('". $indstatus["approveID"] ."', '".
4925 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '".
4926 | $requestUser->getID() ."')";
4927 | $res = $db->getResult($queryStr);
4928 | if (is_bool($res) && !$res)
4929 | return false;
4930 |
4931 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID');
4932 | if ($file) {
4933 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'a' . $approveLogID);
4934 | }
4935 | return $approveLogID;
4936 | } /* }}} */
4937 |
4938 | /**
4939 | * Add another entry to approval log which resets the status
4940 | *
4941 | * This method will not delete anything from the database, but will add
4942 | * a new approval log entry which sets the status to 0. This is only allowed
4943 | * if the current status is either 1 (approved) or -1 (rejected).
4944 | *
4945 | * After calling this method SeedDMS_Core_DocumentContent::verifyStatus()
4946 | * should be called to recalculate the document status.
4947 | *
4948 | * @param integer $approveid id of approval
4949 | * @param SeedDMS_Core_User $requestUser user requesting the removal
4950 | * @param string $comment comment
4951 | *
4952 | * @return integer|bool true if successful, error code < 0,
4953 | * false in case of an sql error
4954 | */
4955 | public function removeApproval($approveid, $requestUser, $comment = '') { /* {{{ */
4956 | $db = $this->_document->getDMS()->getDB();
4957 |
4958 | // Check to see if the user can be removed from the approval list.
4959 | $approvals = $this->getApprovalStatus();
4960 | if (is_bool($approvals) && !$approvals) {
4961 | return false;
4962 | }
4963 | $approvalStatus = null;
4964 | foreach ($approvals as $approval) {
4965 | if ($approval['approveID'] == $approveid) {
4966 | $approvalStatus = $approval;
4967 | break;
4968 | }
4969 | }
4970 | if (!$approvalStatus)
4971 | return -2;
4972 |
4973 | // The approval log entry may only be removed if the status is 1 or -1
4974 | if ($approvalStatus["status"] != 1 && $approvalStatus["status"] != -1)
4975 | return -3;
4976 |
4977 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`,
4978 | `comment`, `date`, `userID`) ".
4979 | "VALUES ('". $approvalStatus["approveID"] ."', '0', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '".
4980 | $requestUser->getID() ."')";
4981 | $res = $db->getResult($queryStr);
4982 | if (is_bool($res) && !$res)
4983 | return false;
4984 |
4985 | return true;
4986 | } /* }}} */
4987 |
4988 | /**
4989 | * Sets approval status of a document content for a group
4990 | *
4991 | * The functions behaves like
4992 | * {link SeedDMS_Core_DocumentContent::setApprovalByInd} but does it for
4993 | * a group instead of a user
4994 | */
4995 | public function setApprovalByGrp($group, $requestUser, $status, $comment, $file = '') { /* {{{ */
4996 | if (!$group || !$requestUser)
4997 | return -1;
4998 |
4999 | $db = $this->_document->getDMS()->getDB();
5000 |
5001 | if (!$group->isType('group'))
5002 | return -1;
5003 |
5004 | // Check if the group is on the approval list at all.
5005 | $approvalStatus = $group->getApprovalStatus($this->_document->getID(), $this->_version);
5006 | if (is_bool($approvalStatus) && !$approvalStatus) {
5007 | return false;
5008 | }
5009 | if (count($approvalStatus)==0) {
5010 | // User is not assigned to approve this document. No action required.
5011 | // Return an error.
5012 | return -3;
5013 | }
5014 | if ($approvalStatus[0]["status"]==-2) {
5015 | // Group has been deleted from approvers
5016 | return -4;
5017 | }
5018 |
5019 | // Check if the status is really different from the current status
5020 | if ($approvalStatus[0]["status"] == $status)
5021 | return 0;
5022 |
5023 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`,
5024 | `comment`, `date`, `userID`) ".
5025 | "VALUES ('". $approvalStatus[0]["approveID"] ."', '".
5026 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '".
5027 | $requestUser->getID() ."')";
5028 | $res = $db->getResult($queryStr);
5029 | if (is_bool($res) && !$res)
5030 | return false;
5031 |
5032 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID');
5033 | if ($file) {
5034 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'a' . $approveLogID);
5035 | }
5036 | return $approveLogID;
5037 | } /* }}} */
5038 |
5039 | public function delIndReviewer($user, $requestUser, $msg = '') { /* {{{ */
5040 | $db = $this->_document->getDMS()->getDB();
5041 |
5042 | if (!$user->isType('user'))
5043 | return -1;
5044 |
5045 | // Check to see if the user can be removed from the review list.
5046 | $reviewStatus = $user->getReviewStatus($this->_document->getID(), $this->_version);
5047 | if (is_bool($reviewStatus) && !$reviewStatus) {
5048 | return false;
5049 | }
5050 | if (count($reviewStatus["indstatus"])==0) {
5051 | // User is not assigned to review this document. No action required.
5052 | // Return an error.
5053 | return -2;
5054 | }
5055 | $indstatus = array_pop($reviewStatus["indstatus"]);
5056 | if ($indstatus["status"]!=0) {
5057 | // User has already submitted a review or has already been deleted;
5058 | // return an error.
5059 | return -3;
5060 | }
5061 |
5062 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ".
5063 | "VALUES ('". $indstatus["reviewID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
5064 | $res = $db->getResult($queryStr);
5065 | if (is_bool($res) && !$res) {
5066 | return false;
5067 | }
5068 |
5069 | return 0;
5070 | } /* }}} */
5071 |
5072 | public function delGrpReviewer($group, $requestUser, $msg = '') { /* {{{ */
5073 | $db = $this->_document->getDMS()->getDB();
5074 |
5075 | if (!$group->isType('group'))
5076 | return -1;
5077 |
5078 | $groupID = $group->getID();
5079 |
5080 | // Check to see if the user can be removed from the review list.
5081 | $reviewStatus = $group->getReviewStatus($this->_document->getID(), $this->_version);
5082 | if (is_bool($reviewStatus) && !$reviewStatus) {
5083 | return false;
5084 | }
5085 | if (count($reviewStatus)==0) {
5086 | // User is not assigned to review this document. No action required.
5087 | // Return an error.
5088 | return -2;
5089 | }
5090 | if ($reviewStatus[0]["status"]!=0) {
5091 | // User has already submitted a review or has already been deleted;
5092 | // return an error.
5093 | return -3;
5094 | }
5095 |
5096 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ".
5097 | "VALUES ('". $reviewStatus[0]["reviewID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
5098 | $res = $db->getResult($queryStr);
5099 | if (is_bool($res) && !$res) {
5100 | return false;
5101 | }
5102 |
5103 | return 0;
5104 | } /* }}} */
5105 |
5106 | public function delIndApprover($user, $requestUser, $msg = '') { /* {{{ */
5107 | $db = $this->_document->getDMS()->getDB();
5108 |
5109 | if (!$user->isType('user'))
5110 | return -1;
5111 |
5112 | $userID = $user->getID();
5113 |
5114 | // Check if the user is on the approval list at all.
5115 | $approvalStatus = $user->getApprovalStatus($this->_document->getID(), $this->_version);
5116 | if (is_bool($approvalStatus) && !$approvalStatus) {
5117 | return false;
5118 | }
5119 | if (count($approvalStatus["indstatus"])==0) {
5120 | // User is not assigned to approve this document. No action required.
5121 | // Return an error.
5122 | return -2;
5123 | }
5124 | $indstatus = array_pop($approvalStatus["indstatus"]);
5125 | if ($indstatus["status"]!=0) {
5126 | // User has already submitted an approval or has already been deleted;
5127 | // return an error.
5128 | return -3;
5129 | }
5130 |
5131 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ".
5132 | "VALUES ('". $indstatus["approveID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
5133 | $res = $db->getResult($queryStr);
5134 | if (is_bool($res) && !$res) {
5135 | return false;
5136 | }
5137 |
5138 | return 0;
5139 | } /* }}} */
5140 |
5141 | public function delGrpApprover($group, $requestUser, $msg = '') { /* {{{ */
5142 | $db = $this->_document->getDMS()->getDB();
5143 |
5144 | if (!$group->isType('group'))
5145 | return -1;
5146 |
5147 | $groupID = $group->getID();
5148 |
5149 | // Check if the group is on the approval list at all.
5150 | $approvalStatus = $group->getApprovalStatus($this->_document->getID(), $this->_version);
5151 | if (is_bool($approvalStatus) && !$approvalStatus) {
5152 | return false;
5153 | }
5154 | if (count($approvalStatus)==0) {
5155 | // User is not assigned to approve this document. No action required.
5156 | // Return an error.
5157 | return -2;
5158 | }
5159 | if ($approvalStatus[0]["status"]!=0) {
5160 | // User has already submitted an approval or has already been deleted;
5161 | // return an error.
5162 | return -3;
5163 | }
5164 |
5165 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ".
5166 | "VALUES ('". $approvalStatus[0]["approveID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')";
5167 | $res = $db->getResult($queryStr);
5168 | if (is_bool($res) && !$res) {
5169 | return false;
5170 | }
5171 |
5172 | return 0;
5173 | } /* }}} */
5174 |
5175 | /**
5176 | * Set state of workflow assigned to the document content
5177 | *
5178 | * @param object $state
5179 | */
5180 | public function setWorkflowState($state) { /* {{{ */
5181 | $db = $this->_document->getDMS()->getDB();
5182 |
5183 | if ($this->_workflow) {
5184 | $queryStr = "UPDATE `tblWorkflowDocumentContent` set `state`=". $state->getID() ." WHERE `workflow`=". intval($this->_workflow->getID()). " AND `document`=". intval($this->_document->getID()) ." AND version=". intval($this->_version) ."";
5185 | if (!$db->getResult($queryStr)) {
5186 | return false;
5187 | }
5188 | $this->_workflowState = $state;
5189 | return true;
5190 | }
5191 | return false;
5192 | } /* }}} */
5193 |
5194 | /**
5195 | * Get state of workflow assigned to the document content
5196 | *
5197 | * @return object/boolean an object of class SeedDMS_Core_Workflow_State
5198 | * or false in case of error, e.g. the version has not a workflow
5199 | */
5200 | public function getWorkflowState() { /* {{{ */
5201 | $db = $this->_document->getDMS()->getDB();
5202 |
5203 | if (!$this->_workflow)
5204 | $this->getWorkflow();
5205 |
5206 | if (!$this->_workflow)
5207 | return false;
5208 |
5209 | if (!$this->_workflowState) {
5210 | $queryStr =
5211 | "SELECT b.* FROM `tblWorkflowDocumentContent` a LEFT JOIN `tblWorkflowStates` b ON a.`state` = b.`id` WHERE a.`state` IS NOT NULL AND `workflow`=". intval($this->_workflow->getID())
5212 | ." AND a.`version`='".$this->_version
5213 | ."' AND a.`document` = '". $this->_document->getID() ."' ";
5214 | $recs = $db->getResultArray($queryStr);
5215 | if (!$recs)
5216 | return false;
5217 | $this->_workflowState = new SeedDMS_Core_Workflow_State($recs[0]['id'], $recs[0]['name'], $recs[0]['maxtime'], $recs[0]['precondfunc'], $recs[0]['documentstatus']);
5218 | $this->_workflowState->setDMS($this->_document->getDMS());
5219 | }
5220 | return $this->_workflowState;
5221 | } /* }}} */
5222 |
5223 | /**
5224 | * Assign a workflow to a document content
5225 | *
5226 | * @param object $workflow
5227 | */
5228 | public function setWorkflow($workflow, $user) { /* {{{ */
5229 | $db = $this->_document->getDMS()->getDB();
5230 |
5231 | $this->getWorkflow();
5232 | if ($this->_workflow)
5233 | return false;
5234 |
5235 | if ($workflow && is_object($workflow)) {
5236 | $db->startTransaction();
5237 | $initstate = $workflow->getInitState();
5238 | $queryStr = "INSERT INTO `tblWorkflowDocumentContent` (`workflow`, `document`, `version`, `state`, `date`) VALUES (". $workflow->getID(). ", ". $this->_document->getID() .", ". $this->_version .", ".$initstate->getID().", ".$db->getCurrentDatetime().")";
5239 | if (!$db->getResult($queryStr)) {
5240 | $db->rollbackTransaction();
5241 | return false;
5242 | }
5243 | $this->_workflow = $workflow;
5244 | if (!$this->setStatus(S_IN_WORKFLOW, "Added workflow '".$workflow->getName()."'", $user)) {
5245 | $db->rollbackTransaction();
5246 | return false;
5247 | }
5248 | $db->commitTransaction();
5249 | return true;
5250 | }
5251 | return false;
5252 | } /* }}} */
5253 |
5254 | /**
5255 | * Get workflow assigned to the document content
5256 | *
5257 | * The method returns the last workflow if one was assigned.
5258 | * If the document version is in a sub workflow, it will have
5259 | * a never date and therefore will be found first.
5260 | *
5261 | * @return object/boolean an object of class SeedDMS_Core_Workflow
5262 | * or false in case of error, e.g. the version has not a workflow
5263 | */
5264 | public function getWorkflow() { /* {{{ */
5265 | $db = $this->_document->getDMS()->getDB();
5266 |
5267 | if (!$this->_workflow) {
5268 | $queryStr =
5269 | "SELECT b.* FROM `tblWorkflowDocumentContent` a LEFT JOIN `tblWorkflows` b ON a.`workflow` = b.`id` WHERE a.`version`='".$this->_version
5270 | ."' AND a.`document` = '". $this->_document->getID() ."' "
5271 | ." ORDER BY `date` DESC LIMIT 1";
5272 | $recs = $db->getResultArray($queryStr);
5273 | if (is_bool($recs) && !$recs)
5274 | return false;
5275 | if (!$recs)
5276 | return false;
5277 | $this->_workflow = new SeedDMS_Core_Workflow($recs[0]['id'], $recs[0]['name'], $this->_document->getDMS()->getWorkflowState($recs[0]['initstate']));
5278 | $this->_workflow->setDMS($this->_document->getDMS());
5279 | }
5280 | return $this->_workflow;
5281 | } /* }}} */
5282 |
5283 | /**
5284 | * Rewrites the complete workflow log
5285 | *
5286 | * Attention: this function is highly dangerous.
5287 | * It removes an existing workflow log and rewrites it.
5288 | * This method was added for importing an xml dump.
5289 | *
5290 | * @param array $workflowlog new workflow log with the newest log entry first.
5291 | * @return boolean true on success, otherwise false
5292 | */
5293 | public function rewriteWorkflowLog($workflowlog) { /* {{{ */
5294 | $db = $this->_document->getDMS()->getDB();
5295 |
5296 | $db->startTransaction();
5297 |
5298 | /* First, remove the old entries */
5299 | $queryStr = "DELETE FROM `tblWorkflowLog` WHERE `tblWorkflowLog`.`document` = '". $this->_document->getID() ."' AND `tblWorkflowLog`.`version` = '". $this->_version ."'";
5300 | if (!$db->getResult($queryStr)) {
5301 | $db->rollbackTransaction();
5302 | return false;
5303 | }
5304 |
5305 | /* Second, insert the new entries */
5306 | $workflowlog = array_reverse($workflowlog);
5307 | foreach ($workflowlog as $log) {
5308 | if (!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) {
5309 | $db->rollbackTransaction();
5310 | return false;
5311 | }
5312 | $queryStr = "INSERT INTO `tblWorkflowLog` (`document`, `version`, `workflow`, `transition`, `comment`, `date`, `userid`) ".
5313 | "VALUES ('".$this->_document->getID() ."', '".(int) $this->_version."', '".(int) $log['workflow']->getID()."', '".(int) $log['transition']->getID()."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".$log['user']->getID().")";
5314 | if (!$db->getResult($queryStr)) {
5315 | $db->rollbackTransaction();
5316 | return false;
5317 | }
5318 | }
5319 |
5320 | $db->commitTransaction();
5321 | return true;
5322 | } /* }}} */
5323 |
5324 | /**
5325 | * Restart workflow from its initial state
5326 | *
5327 | * @return boolean true if workflow could be restarted
5328 | * or false in case of error
5329 | */
5330 | public function rewindWorkflow() { /* {{{ */
5331 | $db = $this->_document->getDMS()->getDB();
5332 |
5333 | $this->getWorkflow();
5334 |
5335 | if (!$this->_workflow) {
5336 | return true;
5337 | }
5338 |
5339 | $db->startTransaction();
5340 | $queryStr = "DELETE from `tblWorkflowLog` WHERE `document` = ". $this->_document->getID() ." AND `version` = ".$this->_version." AND `workflow` = ".$this->_workflow->getID();
5341 | if (!$db->getResult($queryStr)) {
5342 | $db->rollbackTransaction();
5343 | return false;
5344 | }
5345 |
5346 | $this->setWorkflowState($this->_workflow->getInitState());
5347 | $db->commitTransaction();
5348 |
5349 | return true;
5350 | } /* }}} */
5351 |
5352 | /**
5353 | * Remove workflow
5354 | *
5355 | * Fully removing a workflow including entries in the workflow log is
5356 | * only allowed if the workflow is still its initial state.
5357 | * At a later point of time only unlinking the document from the
5358 | * workflow is allowed. It will keep any log entries.
5359 | * A workflow is unlinked from a document when enterNextState()
5360 | * succeeds.
5361 | *
5362 | * @param object $user user doing initiating the removal
5363 | * @param boolean $unlink if true, just unlink the workflow from the
5364 | * document but do not remove the workflow log. The $unlink
5365 | * flag has been added to detach the workflow from the document
5366 | * when it has reached a valid end state
5367 | * (see SeedDMS_Core_DocumentContent::enterNextState())
5368 | * @return boolean true if workflow could be removed
5369 | * or false in case of error
5370 | */
5371 | public function removeWorkflow($user, $unlink = false) { /* {{{ */
5372 | $db = $this->_document->getDMS()->getDB();
5373 |
5374 | $this->getWorkflow();
5375 |
5376 | if (!$this->_workflow) {
5377 | return true;
5378 | }
5379 |
5380 | /* A workflow should always be in a state, but in case it isn't, the
5381 | * at least allow to remove the workflow.
5382 | */
5383 | $currentstate = $this->getWorkflowState();
5384 | if (!$currentstate || SeedDMS_Core_DMS::checkIfEqual($this->_workflow->getInitState(), $currentstate) || $unlink == true) {
5385 | $db->startTransaction();
5386 | if (!$unlink) {
5387 | $queryStr =
5388 | "DELETE FROM `tblWorkflowLog` WHERE "
5389 | ."`version`='".$this->_version."' "
5390 | ." AND `document` = '". $this->_document->getID() ."' "
5391 | ." AND `workflow` = '". $this->_workflow->getID() ."' ";
5392 | if (!$db->getResult($queryStr)) {
5393 | $db->rollbackTransaction();
5394 | return false;
5395 | }
5396 | }
5397 | $queryStr =
5398 | "DELETE FROM `tblWorkflowDocumentContent` WHERE "
5399 | ."`version`='".$this->_version."' "
5400 | ." AND `document` = '". $this->_document->getID() ."' "
5401 | ." AND `workflow` = '". $this->_workflow->getID() ."' ";
5402 | if (!$db->getResult($queryStr)) {
5403 | $db->rollbackTransaction();
5404 | return false;
5405 | }
5406 | $this->_workflow = null;
5407 | $this->_workflowState = null;
5408 | $this->verifyStatus(false, $user, 'Workflow removed');
5409 | $db->commitTransaction();
5410 | }
5411 |
5412 | return true;
5413 | } /* }}} */
5414 |
5415 | /**
5416 | * Run a sub workflow
5417 | *
5418 | * @param object $subworkflow
5419 | */
5420 | public function getParentWorkflow() { /* {{{ */
5421 | $db = $this->_document->getDMS()->getDB();
5422 |
5423 | /* document content must be in a workflow */
5424 | $this->getWorkflow();
5425 | if (!$this->_workflow)
5426 | return false;
5427 |
5428 | $queryStr =
5429 | "SELECT * FROM `tblWorkflowDocumentContent` WHERE "
5430 | ."`version`='".$this->_version."' "
5431 | ." AND `document` = '". $this->_document->getID() ."' "
5432 | ." AND `workflow` = '". $this->_workflow->getID() ."' ";
5433 | $recs = $db->getResultArray($queryStr);
5434 | if (is_bool($recs) && !$recs)
5435 | return false;
5436 | if (!$recs)
5437 | return false;
5438 |
5439 | if ($recs[0]['parentworkflow'])
5440 | return $this->_document->getDMS()->getWorkflow($recs[0]['parentworkflow']);
5441 |
5442 | return false;
5443 | } /* }}} */
5444 |
5445 | /**
5446 | * Run a sub workflow
5447 | *
5448 | * @param object $subworkflow
5449 | */
5450 | public function runSubWorkflow($subworkflow) { /* {{{ */
5451 | $db = $this->_document->getDMS()->getDB();
5452 |
5453 | /* document content must be in a workflow */
5454 | $this->getWorkflow();
5455 | if (!$this->_workflow)
5456 | return false;
5457 |
5458 | /* The current workflow state must match the sub workflows initial state */
5459 | if ($subworkflow->getInitState()->getID() != $this->_workflowState->getID())
5460 | return false;
5461 |
5462 | if ($subworkflow) {
5463 | $initstate = $subworkflow->getInitState();
5464 | $queryStr = "INSERT INTO `tblWorkflowDocumentContent` (`parentworkflow`, `workflow`, `document`, `version`, `state`, `date`) VALUES (". $this->_workflow->getID(). ", ". $subworkflow->getID(). ", ". $this->_document->getID() .", ". $this->_version .", ".$initstate->getID().", ".$db->getCurrentDatetime().")";
5465 | if (!$db->getResult($queryStr)) {
5466 | return false;
5467 | }
5468 | $this->_workflow = $subworkflow;
5469 | return true;
5470 | }
5471 | return true;
5472 | } /* }}} */
5473 |
5474 | /**
5475 | * Return from sub workflow to parent workflow.
5476 | * The method will trigger the given transition
5477 | *
5478 | * FIXME: Needs much better checking if this is allowed
5479 | *
5480 | * @param object $user intiating the return
5481 | * @param object $transtion to trigger
5482 | * @param string comment for the transition trigger
5483 | */
5484 | public function returnFromSubWorkflow($user, $transition = null, $comment = '') { /* {{{ */
5485 | $db = $this->_document->getDMS()->getDB();
5486 |
5487 | /* document content must be in a workflow */
5488 | $this->getWorkflow();
5489 | if (!$this->_workflow)
5490 | return false;
5491 |
5492 | if ($this->_workflow) {
5493 | $db->startTransaction();
5494 |
5495 | $queryStr =
5496 | "SELECT * FROM `tblWorkflowDocumentContent` WHERE `workflow`=". intval($this->_workflow->getID())
5497 | . " AND `version`='".$this->_version
5498 | ."' AND `document` = '". $this->_document->getID() ."' ";
5499 | $recs = $db->getResultArray($queryStr);
5500 | if (is_bool($recs) && !$recs) {
5501 | $db->rollbackTransaction();
5502 | return false;
5503 | }
5504 | if (!$recs) {
5505 | $db->rollbackTransaction();
5506 | return false;
5507 | }
5508 |
5509 | $queryStr = "DELETE FROM `tblWorkflowDocumentContent` WHERE `workflow` =". intval($this->_workflow->getID())." AND `document` = '". $this->_document->getID() ."' AND `version` = '" . $this->_version."'";
5510 | if (!$db->getResult($queryStr)) {
5511 | $db->rollbackTransaction();
5512 | return false;
5513 | }
5514 |
5515 | $this->_workflow = $this->_document->getDMS()->getWorkflow($recs[0]['parentworkflow']);
5516 | $this->_workflow->setDMS($this->_document->getDMS());
5517 |
5518 | if ($transition) {
5519 | if (false === $this->triggerWorkflowTransition($user, $transition, $comment)) {
5520 | $db->rollbackTransaction();
5521 | return false;
5522 | }
5523 | }
5524 |
5525 | $db->commitTransaction();
5526 | }
5527 | return $this->_workflow;
5528 | } /* }}} */
5529 |
5530 | /**
5531 | * Check if the user is allowed to trigger the transition
5532 | * A user is allowed if either the user itself or
5533 | * a group of which the user is a member of is registered for
5534 | * triggering a transition. This method does not change the workflow
5535 | * state of the document content.
5536 | *
5537 | * @param object $user
5538 | * @return boolean true if user may trigger transaction
5539 | */
5540 | public function triggerWorkflowTransitionIsAllowed($user, $transition) { /* {{{ */
5541 | $db = $this->_document->getDMS()->getDB();
5542 |
5543 | if (!$this->_workflow)
5544 | $this->getWorkflow();
5545 |
5546 | if (!$this->_workflow)
5547 | return false;
5548 |
5549 | if (!$this->_workflowState)
5550 | $this->getWorkflowState();
5551 |
5552 | /* Check if the user has already triggered the transition */
5553 | $queryStr =
5554 | "SELECT * FROM `tblWorkflowLog` WHERE `version`='".$this->_version ."' AND `document` = '". $this->_document->getID() ."' AND `workflow` = ". $this->_workflow->getID(). " AND userid = ".$user->getID();
5555 | $queryStr .= " AND `transition` = ".$transition->getID();
5556 | $resArr = $db->getResultArray($queryStr);
5557 | if (is_bool($resArr) && !$resArr)
5558 | return false;
5559 |
5560 | if (count($resArr))
5561 | return false;
5562 |
5563 | /* Get all transition users allowed to trigger the transition */
5564 | $transusers = $transition->getUsers();
5565 | if ($transusers) {
5566 | foreach ($transusers as $transuser) {
5567 | if ($user->getID() == $transuser->getUser()->getID())
5568 | return true;
5569 | }
5570 | }
5571 |
5572 | /* Get all transition groups whose members are allowed to trigger
5573 | * the transition */
5574 | $transgroups = $transition->getGroups();
5575 | if ($transgroups) {
5576 | foreach ($transgroups as $transgroup) {
5577 | $group = $transgroup->getGroup();
5578 | if ($group->isMember($user))
5579 | return true;
5580 | }
5581 | }
5582 |
5583 | return false;
5584 | } /* }}} */
5585 |
5586 | /**
5587 | * Check if all conditions are met to change the workflow state
5588 | * of a document content (run the transition).
5589 | * The conditions are met if all explicitly set users and a sufficient
5590 | * number of users of the groups have acknowledged the content.
5591 | *
5592 | * @return boolean true if transaction maybe executed
5593 | */
5594 | public function executeWorkflowTransitionIsAllowed($transition) { /* {{{ */
5595 | if (!$this->_workflow)
5596 | $this->getWorkflow();
5597 |
5598 | if (!$this->_workflow)
5599 | return false;
5600 |
5601 | if (!$this->_workflowState)
5602 | $this->getWorkflowState();
5603 |
5604 | /* Get the Log of transition triggers */
5605 | $entries = $this->getWorkflowLog($transition);
5606 | if (!$entries)
5607 | return false;
5608 |
5609 | /* Get all transition users allowed to trigger the transition
5610 | * $allowedusers is a list of all users allowed to trigger the
5611 | * transition
5612 | */
5613 | $transusers = $transition->getUsers();
5614 | $allowedusers = array();
5615 | foreach ($transusers as $transuser) {
5616 | $a = $transuser->getUser();
5617 | $allowedusers[$a->getID()] = $a;
5618 | }
5619 |
5620 | /* Get all transition groups whose members are allowed to trigger
5621 | * the transition */
5622 | $transgroups = $transition->getGroups();
5623 | foreach ($entries as $entry) {
5624 | $loguser = $entry->getUser();
5625 | /* Unset each allowed user if it was found in the log */
5626 | if (isset($allowedusers[$loguser->getID()]))
5627 | unset($allowedusers[$loguser->getID()]);
5628 | /* Also check groups if required. Count the group membership of
5629 | * each user in the log in the array $gg
5630 | */
5631 | if ($transgroups) {
5632 | $loggroups = $loguser->getGroups();
5633 | foreach ($loggroups as $loggroup) {
5634 | if (!isset($gg[$loggroup->getID()]))
5635 | $gg[$loggroup->getID()] = 1;
5636 | else
5637 | $gg[$loggroup->getID()]++;
5638 | }
5639 | }
5640 | }
5641 | /* If there are allowed users left, then there some users still
5642 | * need to trigger the transition.
5643 | */
5644 | if ($allowedusers)
5645 | return false;
5646 |
5647 | if ($transgroups) {
5648 | foreach ($transgroups as $transgroup) {
5649 | $group = $transgroup->getGroup();
5650 | $minusers = $transgroup->getNumOfUsers();
5651 | if (!isset($gg[$group->getID()]))
5652 | return false;
5653 | if ($gg[$group->getID()] < $minusers)
5654 | return false;
5655 | }
5656 | }
5657 | return true;
5658 | } /* }}} */
5659 |
5660 | /**
5661 | * Trigger transition
5662 | *
5663 | * This method will be deprecated
5664 | *
5665 | * The method will first check if the user is allowed to trigger the
5666 | * transition. If the user is allowed, an entry in the workflow log
5667 | * will be added, which is later used to check if the transition
5668 | * can actually be processed. The method will finally call
5669 | * executeWorkflowTransitionIsAllowed() which checks all log entries
5670 | * and does the transitions post function if all users and groups have
5671 | * triggered the transition. Finally enterNextState() is called which
5672 | * will try to enter the next state.
5673 | *
5674 | * @param object $user
5675 | * @param object $transition
5676 | * @param string $comment user comment
5677 | * @return boolean/object next state if transition could be triggered and
5678 | * then next state could be entered,
5679 | * true if the transition could just be triggered or
5680 | * false in case of an error
5681 | */
5682 | public function triggerWorkflowTransition($user, $transition, $comment = '') { /* {{{ */
5683 | $db = $this->_document->getDMS()->getDB();
5684 |
5685 | if (!$this->_workflow)
5686 | $this->getWorkflow();
5687 |
5688 | if (!$this->_workflow)
5689 | return false;
5690 |
5691 | if (!$this->_workflowState)
5692 | $this->getWorkflowState();
5693 |
5694 | if (!$this->_workflowState)
5695 | return false;
5696 |
5697 | /* Check if the user is allowed to trigger the transition.
5698 | */
5699 | if (!$this->triggerWorkflowTransitionIsAllowed($user, $transition))
5700 | return false;
5701 |
5702 | $state = $this->_workflowState;
5703 | $queryStr = "INSERT INTO `tblWorkflowLog` (`document`, `version`, `workflow`, `userid`, `transition`, `date`, `comment`) VALUES (".$this->_document->getID().", ".$this->_version.", " . (int) $this->_workflow->getID() . ", " .(int) $user->getID(). ", ".(int) $transition->getID().", ".$db->getCurrentDatetime().", ".$db->qstr($comment).")";
5704 | if (!$db->getResult($queryStr))
5705 | return false;
5706 |
5707 | /* Check if this transition is processed. Run the post function in
5708 | * that case. A transition is processed when all users and groups
5709 | * have triggered it.
5710 | */
5711 | if ($this->executeWorkflowTransitionIsAllowed($transition)) {
5712 | /* run post function of transition */
5713 | // echo "run post function of transition ".$transition->getID()."<br />";
5714 | }
5715 |
5716 | /* Go into the next state. This will only succeed if the pre condition
5717 | * function of that states succeeds.
5718 | */
5719 | $nextstate = $transition->getNextState();
5720 | if ($this->enterNextState($user, $nextstate)) {
5721 | return $nextstate;
5722 | }
5723 | return true;
5724 |
5725 | } /* }}} */
5726 |
5727 | /**
5728 | * Enter next state of workflow if possible
5729 | *
5730 | * The method will check if one of the following states in the workflow
5731 | * can be reached.
5732 | * It does it by running
5733 | * the precondition function of that state. The precondition function
5734 | * gets a list of all transitions leading to the state. It will
5735 | * determine, whether the transitions has been triggered and if that
5736 | * is sufficient to enter the next state. If no pre condition function
5737 | * is set, then 1 of n transtions are enough to enter the next state.
5738 | *
5739 | * If moving in the next state is possible and this state has a
5740 | * corresponding document state, then the document state will be
5741 | * updated and the workflow will be detached from the document.
5742 | *
5743 | * @param object $user
5744 | * @param object $nextstate
5745 | * @return boolean true if the state could be reached
5746 | * false if not
5747 | */
5748 | public function enterNextState($user, $nextstate) { /* {{{ */
5749 |
5750 | /* run the pre condition of the next state. If it is not set
5751 | * the next state will be reached if one of the transitions
5752 | * leading to the given state can be processed.
5753 | */
5754 | if ($nextstate->getPreCondFunc() == '') {
5755 | $transitions = $this->_workflow->getPreviousTransitions($nextstate);
5756 | foreach ($transitions as $transition) {
5757 | // echo "transition ".$transition->getID()." led to state ".$nextstate->getName()."<br />";
5758 | if ($this->executeWorkflowTransitionIsAllowed($transition)) {
5759 | // echo "stepping into next state<br />";
5760 | $this->setWorkflowState($nextstate);
5761 |
5762 | /* Check if the new workflow state has a mapping into a
5763 | * document state. If yes, set the document state will
5764 | * be updated and the workflow will be removed from the
5765 | * document.
5766 | */
5767 | $docstate = $nextstate->getDocumentStatus();
5768 | if ($docstate == S_RELEASED || $docstate == S_REJECTED) {
5769 | $this->setStatus($docstate, "Workflow has ended", $user);
5770 | /* Detach the workflow from the document, but keep the
5771 | * workflow log
5772 | */
5773 | $this->removeWorkflow($user, true);
5774 | return true ;
5775 | }
5776 |
5777 | /* make sure the users and groups allowed to trigger the next
5778 | * transitions are also allowed to read the document
5779 | */
5780 | $transitions = $this->_workflow->getNextTransitions($nextstate);
5781 | foreach ($transitions as $tran) {
5782 | // echo "checking access for users/groups allowed to trigger transition ".$tran->getID()."<br />";
5783 | $transusers = $tran->getUsers();
5784 | foreach ($transusers as $transuser) {
5785 | $u = $transuser->getUser();
5786 | // echo $u->getFullName()."<br />";
5787 | if ($this->_document->getAccessMode($u) < M_READ) {
5788 | $this->_document->addAccess(M_READ, $u->getID(), 1);
5789 | // echo "granted read access<br />";
5790 | } else {
5791 | // echo "has already access<br />";
5792 | }
5793 | }
5794 | $transgroups = $tran->getGroups();
5795 | foreach ($transgroups as $transgroup) {
5796 | $g = $transgroup->getGroup();
5797 | // echo $g->getName()."<br />";
5798 | if ($this->_document->getGroupAccessMode($g) < M_READ) {
5799 | $this->_document->addAccess(M_READ, $g->getID(), 0);
5800 | // echo "granted read access<br />";
5801 | } else {
5802 | // echo "has already access<br />";
5803 | }
5804 | }
5805 | }
5806 | return true;
5807 | } else {
5808 | // echo "transition not ready for process now<br />";
5809 | }
5810 | }
5811 | return false;
5812 | } else {
5813 | return false;
5814 | }
5815 |
5816 | } /* }}} */
5817 |
5818 | /**
5819 | * Get the so far logged operations on the document content within the
5820 | * workflow. Even after finishing the workflow (when the document content
5821 | * does not have workflow set anymore) this function returns the list of all
5822 | * log entries.
5823 | *
5824 | * @return array list of objects
5825 | */
5826 | public function getWorkflowLog($transition = null) { /* {{{ */
5827 | $db = $this->_document->getDMS()->getDB();
5828 |
5829 | /*
5830 | if (!$this->_workflow)
5831 | $this->getWorkflow();
5832 |
5833 | if (!$this->_workflow)
5834 | return false;
5835 | */
5836 | $queryStr =
5837 | "SELECT * FROM `tblWorkflowLog` WHERE `version`='".$this->_version ."' AND `document` = '". $this->_document->getID() ."'"; // AND `workflow` = ". $this->_workflow->getID();
5838 | if ($transition)
5839 | $queryStr .= " AND `transition` = ".$transition->getID();
5840 | $queryStr .= " ORDER BY `date`";
5841 | $resArr = $db->getResultArray($queryStr);
5842 | if (is_bool($resArr) && !$resArr)
5843 | return false;
5844 |
5845 | $workflowlogs = array();
5846 | for ($i = 0; $i < count($resArr); $i++) {
5847 | $workflow = $this->_document->getDMS()->getWorkflow($resArr[$i]["workflow"]);
5848 | $workflowlog = new SeedDMS_Core_Workflow_Log($resArr[$i]["id"], $this->_document->getDMS()->getDocument($resArr[$i]["document"]), $resArr[$i]["version"], $workflow, $this->_document->getDMS()->getUser($resArr[$i]["userid"]), $workflow->getTransition($resArr[$i]["transition"]), $resArr[$i]["date"], $resArr[$i]["comment"]);
5849 | $workflowlog->setDMS($this);
5850 | $workflowlogs[$i] = $workflowlog;
5851 | }
5852 |
5853 | return $workflowlogs;
5854 | } /* }}} */
5855 |
5856 | /**
5857 | * Get the latest workflow log entry for the document content within the
5858 | * workflow. Even after finishing the workflow (when the document content
5859 | * does not have workflow set anymore) this function returns the last
5860 | * log entry.
5861 | *
5862 | * @return object
5863 | */
5864 | public function getLastWorkflowLog() { /* {{{ */
5865 | $db = $this->_document->getDMS()->getDB();
5866 |
5867 | /*
5868 | if (!$this->_workflow)
5869 | $this->getWorkflow();
5870 |
5871 | if (!$this->_workflow)
5872 | return false;
5873 | */
5874 | $queryStr =
5875 | "SELECT * FROM `tblWorkflowLog` WHERE `version`='".$this->_version ."' AND `document` = '". $this->_document->getID() ."'"; // AND `workflow` = ". $this->_workflow->getID();
5876 | $queryStr .= " ORDER BY `id` DESC LIMIT 1";
5877 | $resArr = $db->getResultArray($queryStr);
5878 | if (is_bool($resArr) && !$resArr)
5879 | return false;
5880 |
5881 | $i = 0;
5882 | $workflow = $this->_document->getDMS()->getWorkflow($resArr[$i]["workflow"]);
5883 | $workflowlog = new SeedDMS_Core_Workflow_Log($resArr[$i]["id"], $this->_document->getDMS()->getDocument($resArr[$i]["document"]), $resArr[$i]["version"], $workflow, $this->_document->getDMS()->getUser($resArr[$i]["userid"]), $workflow->getTransition($resArr[$i]["transition"]), $resArr[$i]["date"], $resArr[$i]["comment"]);
5884 | $workflowlog->setDMS($this);
5885 |
5886 | return $workflowlog;
5887 | } /* }}} */
5888 |
5889 | /**
5890 | * Check if the document content needs an action by a user
5891 | *
5892 | * This method will return true if document content is in a transition
5893 | * which can be triggered by the given user.
5894 | *
5895 | * @param SeedDMS_Core_User $user
5896 | * @return boolean true is action is needed
5897 | */
5898 | public function needsWorkflowAction($user) { /* {{{ */
5899 | $needwkflaction = false;
5900 | if ($this->_workflow) {
5901 | if (!$this->_workflowState)
5902 | $this->getWorkflowState();
5903 | $workflowstate = $this->_workflowState;
5904 | if ($transitions = $this->_workflow->getNextTransitions($workflowstate)) {
5905 | foreach ($transitions as $transition) {
5906 | if ($this->triggerWorkflowTransitionIsAllowed($user, $transition)) {
5907 | $needwkflaction = true;
5908 | }
5909 | }
5910 | }
5911 | }
5912 | return $needwkflaction;
5913 | } /* }}} */
5914 |
5915 | /**
5916 | * Checks the internal data of the document version and repairs it.
5917 | * Currently, this function only repairs a missing filetype
5918 | *
5919 | * @return boolean true on success, otherwise false
5920 | */
5921 | public function repair() { /* {{{ */
5922 | $dms = $this->_document->getDMS();
5923 | $db = $this->_dms->getDB();
5924 |
5925 | if (SeedDMS_Core_File::file_exists($this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType)) {
5926 | if (strlen($this->_fileType) < 2) {
5927 | switch($this->_mimeType) {
5928 | case "application/pdf":
5929 | case "image/png":
5930 | case "image/gif":
5931 | case "image/jpg":
5932 | $expect = substr($this->_mimeType, -3, 3);
5933 | if ($this->_fileType != '.'.$expect) {
5934 | $db->startTransaction();
5935 | $queryStr = "UPDATE `tblDocumentContent` SET `fileType`='.".$expect."' WHERE `id` = ". $this->_id;
5936 | $res = $db->getResult($queryStr);
5937 | if ($res) {
5938 | if (!SeedDMS_Core_File::renameFile($this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType, $this->_dms->contentDir.$this->_document->getDir() . $this->_version . '.' . $expect)) {
5939 | $db->rollbackTransaction();
5940 | } else {
5941 | $db->commitTransaction();
5942 | }
5943 | } else {
5944 | $db->rollbackTransaction();
5945 | }
5946 | }
5947 | break;
5948 | }
5949 | }
5950 | } elseif (SeedDMS_Core_File::file_exists($this->_document->getDir() . $this->_version . '.')) {
5951 | echo "no file";
5952 | } else {
5953 | echo $this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType;
5954 | }
5955 | return true;
5956 | } /* }}} */
5957 |
5958 | } /* }}} */
5959 |
5960 |
5961 | /**
5962 | * Class to represent a link between two document
5963 | *
5964 | * Document links are to establish a reference from one document to
5965 | * another document. The owner of the document link may not be the same
5966 | * as the owner of one of the documents.
5967 | * Use {@see SeedDMS_Core_Document::addDocumentLink()} to add a reference
5968 | * to another document.
5969 | *
5970 | * @category DMS
5971 | * @package SeedDMS_Core
5972 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli,
5973 | * Uwe Steinmann <uwe@steinmann.cx>
5974 | * @copyright Copyright (C) 2002-2005 Markus Westphal,
5975 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli,
5976 | * 2010-2024 Uwe Steinmann
5977 | * @version Release: @package_version@
5978 | */
5979 | class SeedDMS_Core_DocumentLink { /* {{{ */
5980 | /**
5981 | * @var integer internal id of document link
5982 | */
5983 | protected $_id;
5984 |
5985 | /**
5986 | * @var SeedDMS_Core_Document reference to document this link belongs to
5987 | */
5988 | protected $_document;
5989 |
5990 | /**
5991 | * @var object reference to target document this link points to
5992 | */
5993 | protected $_target;
5994 |
5995 | /**
5996 | * @var integer id of user who is the owner of this link
5997 | */
5998 | protected $_userID;
5999 |
6000 | /**
6001 | * @var object $_user user who is the owner of this link
6002 | */
6003 | protected $_user;
6004 |
6005 | /**
6006 | * @var integer 1 if this link is public, or 0 if is only visible to the owner
6007 | */
6008 | protected $_public;
6009 |
6010 | /**
6011 | * SeedDMS_Core_DocumentLink constructor.
6012 | * @param $id
6013 | * @param $document
6014 | * @param $target
6015 | * @param $userID
6016 | * @param $public
6017 | */
6018 | public function __construct($id, $document, $target, $userID, $public) {
6019 | $this->_id = $id;
6020 | $this->_document = $document;
6021 | $this->_target = $target;
6022 | $this->_userID = $userID;
6023 | $this->_user = null;
6024 | $this->_public = $public ? true : false;
6025 | }
6026 |
6027 | /**
6028 | * Check if this object is of type 'documentlink'.
6029 | *
6030 | * @param string $type type of object
6031 | */
6032 | public function isType($type) { /* {{{ */
6033 | return $type == 'documentlink';
6034 | } /* }}} */
6035 |
6036 | /**
6037 | * @return int
6038 | */
6039 | public function getID() { return $this->_id; }
6040 |
6041 | /**
6042 | * @return SeedDMS_Core_Document
6043 | */
6044 | public function getDocument() {
6045 | return $this->_document;
6046 | }
6047 |
6048 | /**
6049 | * @return object
6050 | */
6051 | public function getTarget() {
6052 | return $this->_target;
6053 | }
6054 |
6055 | /**
6056 | * @return bool|SeedDMS_Core_User
6057 | */
6058 | public function getUser() {
6059 | if (!isset($this->_user)) {
6060 | $this->_user = $this->_document->getDMS()->getUser($this->_userID);
6061 | }
6062 | return $this->_user;
6063 | }
6064 |
6065 | /**
6066 | * @return int
6067 | */
6068 | public function isPublic() { return $this->_public; }
6069 |
6070 | /**
6071 | * Returns the access mode similar to a document
6072 | *
6073 | * There is no real access mode for document links, so this is just
6074 | * another way to add more access restrictions than the default restrictions.
6075 | * It is only called for public document links, not accessed by the owner
6076 | * or the administrator.
6077 | *
6078 | * @param SeedDMS_Core_User $u user
6079 | * @param $source
6080 | * @param $target
6081 | * @return int either M_NONE or M_READ
6082 | */
6083 | public function getAccessMode($u, $source, $target) { /* {{{ */
6084 | $dms = $this->_document->getDMS();
6085 |
6086 | /* Check if 'onCheckAccessDocumentLink' callback is set */
6087 | if (isset($dms->callbacks['onCheckAccessDocumentLink'])) {
6088 | foreach ($dms->callbacks['onCheckAccessDocumentLink'] as $callback) {
6089 | if (($ret = call_user_func($callback[0], $callback[1], $this, $u, $source, $target)) > 0) {
6090 | return $ret;
6091 | }
6092 | }
6093 | }
6094 |
6095 | return M_READ;
6096 | } /* }}} */
6097 |
6098 | } /* }}} */
6099 |
6100 | /**
6101 | * Class to represent a file attached to a document
6102 | *
6103 | * Beside the regular document content arbitrary files can be attached
6104 | * to a document. This is a similar concept as attaching files to emails.
6105 | * The owner of the attached file and the document may not be the same.
6106 | * Use {@see SeedDMS_Core_Document::addDocumentFile()} to attach a file.
6107 | *
6108 | * @category DMS
6109 | * @package SeedDMS_Core
6110 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli,
6111 | * Uwe Steinmann <uwe@steinmann.cx>
6112 | * @copyright Copyright (C) 2002-2005 Markus Westphal,
6113 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli,
6114 | * 2010-2024 Uwe Steinmann
6115 | * @version Release: @package_version@
6116 | */
6117 | class SeedDMS_Core_DocumentFile { /* {{{ */
6118 | /**
6119 | * @var integer internal id of document file
6120 | */
6121 | protected $_id;
6122 |
6123 | /**
6124 | * @var SeedDMS_Core_Document reference to document this file belongs to
6125 | */
6126 | protected $_document;
6127 |
6128 | /**
6129 | * @var integer id of user who is the owner of this link
6130 | */
6131 | protected $_userID;
6132 |
6133 | /**
6134 | * @var object user who is the owner of this link
6135 | */
6136 | protected $_user;
6137 |
6138 | /**
6139 | * @var string comment for the attached file
6140 | */
6141 | protected $_comment;
6142 |
6143 | /**
6144 | * @var string date when the file was attached
6145 | */
6146 | protected $_date;
6147 |
6148 | /**
6149 | * @var integer version of document this file is attached to
6150 | */
6151 | protected $_version;
6152 |
6153 | /**
6154 | * @var integer 1 if this link is public, or 0 if is only visible to the owner
6155 | */
6156 | protected $_public;
6157 |
6158 | /**
6159 | * @var string directory where the file is stored. This is the
6160 | * document id with a proceding '/'.
6161 | * FIXME: looks like this isn't used anymore. The file path is
6162 | * constructed by getPath()
6163 | */
6164 | protected $_dir;
6165 |
6166 | /**
6167 | * @var string extension of the original file name with a leading '.'
6168 | */
6169 | protected $_fileType;
6170 |
6171 | /**
6172 | * @var string mime type of the file
6173 | */
6174 | protected $_mimeType;
6175 |
6176 | /**
6177 | * @var string name of the file that was originally uploaded
6178 | */
6179 | protected $_orgFileName;
6180 |
6181 | /**
6182 | * @var string name of the file as given by the user
6183 | */
6184 | protected $_name;
6185 |
6186 | /**
6187 | * SeedDMS_Core_DocumentFile constructor.
6188 | * @param $id
6189 | * @param $document
6190 | * @param $userID
6191 | * @param $comment
6192 | * @param $date
6193 | * @param $dir
6194 | * @param $fileType
6195 | * @param $mimeType
6196 | * @param $orgFileName
6197 | * @param $name
6198 | * @param $version
6199 | * @param $public
6200 | */
6201 | public function __construct($id, $document, $userID, $comment, $date, $dir, $fileType, $mimeType, $orgFileName, $name, $version, $public) {
6202 | $this->_id = $id;
6203 | $this->_document = $document;
6204 | $this->_userID = $userID;
6205 | $this->_user = null;
6206 | $this->_comment = trim($comment);
6207 | $this->_date = $date;
6208 | $this->_dir = $dir;
6209 | $this->_fileType = trim($fileType);
6210 | $this->_mimeType = trim($mimeType);
6211 | $this->_orgFileName = trim($orgFileName);
6212 | $this->_name = trim($name);
6213 | $this->_version = $version;
6214 | $this->_public = $public ? true : false;
6215 | }
6216 |
6217 | /**
6218 | * Check if this object is of type 'documentfile'.
6219 | *
6220 | * @param string $type type of object
6221 | */
6222 | public function isType($type) { /* {{{ */
6223 | return $type == 'documentfile';
6224 | } /* }}} */
6225 |
6226 | /**
6227 | * @return int
6228 | */
6229 | public function getID() { return $this->_id; }
6230 |
6231 | /**
6232 | * @return SeedDMS_Core_Document
6233 | */
6234 | public function getDocument() { return $this->_document; }
6235 |
6236 | /**
6237 | * @return int
6238 | */
6239 | public function getUserID() { return $this->_userID; }
6240 |
6241 | /**
6242 | * @return string
6243 | */
6244 | public function getComment() { return $this->_comment; }
6245 |
6246 | /*
6247 | * Set the comment of the document file
6248 | *
6249 | * @param string $newComment string new comment of document
6250 | */
6251 | public function setComment($newComment) { /* {{{ */
6252 | $db = $this->_document->getDMS()->getDB();
6253 |
6254 | $queryStr = "UPDATE `tblDocumentFiles` SET `comment` = ".$db->qstr($newComment)." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id;
6255 | if (!$db->getResult($queryStr))
6256 | return false;
6257 |
6258 | $this->_comment = $newComment;
6259 | return true;
6260 | } /* }}} */
6261 |
6262 | /**
6263 | * @return string
6264 | */
6265 | public function getDate() { return $this->_date; }
6266 |
6267 | /**
6268 | * Set creation date of the document file
6269 | *
6270 | * @param integer $date timestamp of creation date. If false then set it
6271 | * to the current timestamp
6272 | * @return boolean true on success
6273 | */
6274 | public function setDate($date = null) { /* {{{ */
6275 | $db = $this->_document->getDMS()->getDB();
6276 |
6277 | if (!$date)
6278 | $date = time();
6279 | else {
6280 | if (!is_numeric($date))
6281 | return false;
6282 | }
6283 |
6284 | $queryStr = "UPDATE `tblDocumentFiles` SET `date` = " . (int) $date . " WHERE `id` = ". $this->_id;
6285 | if (!$db->getResult($queryStr))
6286 | return false;
6287 | $this->_date = $date;
6288 | return true;
6289 | } /* }}} */
6290 |
6291 | /**
6292 | * @return string
6293 | */
6294 | public function getDir() { return $this->_dir; }
6295 |
6296 | /**
6297 | * @return string
6298 | */
6299 | public function getFileType() { return $this->_fileType; }
6300 |
6301 | /**
6302 | * @return string
6303 | */
6304 | public function getMimeType() { return $this->_mimeType; }
6305 |
6306 | public function getRealMimeType() { /* {{{ */
6307 | $dms = $this->_document->getDMS();
6308 | if ($storage = $dms->getStorage()) {
6309 | $mimetype = $storage->getAttachmentMimetype($this->_document, $this);
6310 | } else {
6311 | $mimetype = SeedDMS_Core_File::mimetype($dms->contentDir . $this->getPath());
6312 | }
6313 | return $mimetype;
6314 | } /* }}} */
6315 |
6316 | /**
6317 | * @return string
6318 | */
6319 | public function getOriginalFileName() { return $this->_orgFileName; }
6320 |
6321 | /**
6322 | * @return string
6323 | */
6324 | public function getName() { return $this->_name; }
6325 |
6326 | /*
6327 | * Set the name of the document file
6328 | *
6329 | * @param $newComment string new name of document
6330 | */
6331 | public function setName($newName) { /* {{{ */
6332 | $db = $this->_document->getDMS()->getDB();
6333 |
6334 | $queryStr = "UPDATE `tblDocumentFiles` SET `name` = ".$db->qstr($newName)." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id;
6335 | if (!$db->getResult($queryStr))
6336 | return false;
6337 |
6338 | $this->_name = $newName;
6339 |
6340 | return true;
6341 | } /* }}} */
6342 |
6343 | /**
6344 | * @return bool|SeedDMS_Core_User
6345 | */
6346 | public function getUser() {
6347 | if (!isset($this->_user))
6348 | $this->_user = $this->_document->getDMS()->getUser($this->_userID);
6349 | return $this->_user;
6350 | }
6351 |
6352 | /**
6353 | * @return string
6354 | */
6355 | public function getPath() {
6356 | return $this->_document->getDir() . "f" .$this->_id . $this->_fileType;
6357 | }
6358 |
6359 | /*
6360 | * Check if file exists in storage
6361 | *
6362 | * @return boolean true if file exists
6363 | */
6364 | public function exists() { /* {{{ */
6365 | $document = $this->_document;
6366 | $dms = $document->getDMS();
6367 | $storage = $dms->getStorage();
6368 | if($storage) {
6369 | return $storage->hasAttachment($document, $this);
6370 | } else {
6371 | return file_exists($dms->contentDir . $this->getPath());
6372 | }
6373 | return true;
6374 | } /* }}} */
6375 |
6376 | /*
6377 | * Return size of file
6378 | *
6379 | * @return boolean true if file exists
6380 | */
6381 | public function size() { /* {{{ */
6382 | $document = $this->_document;
6383 | $dms = $document->getDMS();
6384 | $storage = $dms->getStorage();
6385 | if($storage) {
6386 | return $storage->getAttachmentFilesize($document, $this);
6387 | } else {
6388 | return filesize($dms->contentDir . $this->getPath());
6389 | }
6390 | return true;
6391 | } /* }}} */
6392 |
6393 | /*
6394 | * Return content of file
6395 | *
6396 | * @return sting file content
6397 | */
6398 | public function content() { /* {{{ */
6399 | $document = $this->_document;
6400 | $dms = $document->getDMS();
6401 | $storage = $dms->getStorage();
6402 | if($storage) {
6403 | return $storage->getAttachment($document, $this);
6404 | } else {
6405 | return file_get_contents($dms->contentDir . $this->getPath());
6406 | }
6407 | return true;
6408 | } /* }}} */
6409 |
6410 | /**
6411 | * @return int
6412 | */
6413 | public function getVersion() { return $this->_version; }
6414 |
6415 | /*
6416 | * Set the version of the document file
6417 | *
6418 | * @param $newComment string new version of document
6419 | */
6420 | public function setVersion($newVersion) { /* {{{ */
6421 | $db = $this->_document->getDMS()->getDB();
6422 |
6423 | if (!is_numeric($newVersion) && $newVersion != '')
6424 | return false;
6425 |
6426 | $queryStr = "UPDATE `tblDocumentFiles` SET `version` = ".(int) $newVersion." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id;
6427 | if (!$db->getResult($queryStr))
6428 | return false;
6429 |
6430 | $this->_version = (int) $newVersion;
6431 | return true;
6432 | } /* }}} */
6433 |
6434 | /**
6435 | * @return int
6436 | */
6437 | public function isPublic() { return $this->_public; }
6438 |
6439 | /*
6440 | * Set the public flag of the document file
6441 | *
6442 | * @param $newComment string new comment of document
6443 | */
6444 | public function setPublic($newPublic) { /* {{{ */
6445 | $db = $this->_document->getDMS()->getDB();
6446 |
6447 | $queryStr = "UPDATE `tblDocumentFiles` SET `public` = ".($newPublic ? 1 : 0)." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id;
6448 | if (!$db->getResult($queryStr))
6449 | return false;
6450 |
6451 | $this->_public = $newPublic ? true : false;
6452 | return true;
6453 | } /* }}} */
6454 |
6455 | /**
6456 | * Returns the access mode similar to a document
6457 | *
6458 | * There is no real access mode for document files, so this is just
6459 | * another way to add more access restrictions than the default restrictions.
6460 | * It is only called for public document files, not accessed by the owner
6461 | * or the administrator.
6462 | *
6463 | * @param object $u user
6464 | * @return integer either M_NONE or M_READ
6465 | */
6466 | public function getAccessMode($u) { /* {{{ */
6467 | $dms = $this->_document->getDMS();
6468 |
6469 | /* Check if 'onCheckAccessDocumentLink' callback is set */
6470 | if (isset($this->_dms->callbacks['onCheckAccessDocumentFile'])) {
6471 | foreach ($this->_dms->callbacks['onCheckAccessDocumentFile'] as $callback) {
6472 | if (($ret = call_user_func($callback[0], $callback[1], $this, $u)) > 0) {
6473 | return $ret;
6474 | }
6475 | }
6476 | }
6477 |
6478 | return M_READ;
6479 | } /* }}} */
6480 |
6481 | } /* }}} */
6482 |
6483 | //
6484 | // Perhaps not the cleanest object ever devised, it exists to encapsulate all
6485 | // of the data generated during the addition of new content to the database.
6486 | // The object stores a copy of the new DocumentContent object, the newly assigned
6487 | // reviewers and approvers and the status.
6488 | //
6489 | /**
6490 | * Class to represent a list of document contents
6491 | *
6492 | * @category DMS
6493 | * @package SeedDMS_Core
6494 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli,
6495 | * Uwe Steinmann <uwe@steinmann.cx>
6496 | * @copyright Copyright (C) 2002-2005 Markus Westphal,
6497 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli,
6498 | * 2010-2024 Uwe Steinmann
6499 | * @version Release: @package_version@
6500 | */
6501 | class SeedDMS_Core_AddContentResultSet { /* {{{ */
6502 |
6503 | /**
6504 | * @var null
6505 | */
6506 | protected $_indReviewers;
6507 |
6508 | /**
6509 | * @var null
6510 | */
6511 | protected $_grpReviewers;
6512 |
6513 | /**
6514 | * @var null
6515 | */
6516 | protected $_indApprovers;
6517 |
6518 | /**
6519 | * @var null
6520 | */
6521 | protected $_grpApprovers;
6522 |
6523 | /**
6524 | * @var
6525 | */
6526 | protected $_content;
6527 |
6528 | /**
6529 | * @var null
6530 | */
6531 | protected $_status;
6532 |
6533 | /**
6534 | * @var SeedDMS_Core_DMS back reference to document management system
6535 | */
6536 | protected $_dms;
6537 |
6538 | /**
6539 | * SeedDMS_Core_AddContentResultSet constructor.
6540 | * @param $content
6541 | */
6542 | public function __construct($content) { /* {{{ */
6543 | $this->_content = $content;
6544 | $this->_indReviewers = null;
6545 | $this->_grpReviewers = null;
6546 | $this->_indApprovers = null;
6547 | $this->_grpApprovers = null;
6548 | $this->_status = null;
6549 | $this->_dms = null;
6550 | } /* }}} */
6551 |
6552 | /**
6553 | * Set dms this object belongs to.
6554 | *
6555 | * Each object needs a reference to the dms it belongs to. It will be
6556 | * set when the object is created.
6557 | * The dms has a references to the currently logged in user
6558 | * and the database connection.
6559 | *
6560 | * @param SeedDMS_Core_DMS $dms reference to dms
6561 | */
6562 | public function setDMS($dms) { /* {{{ */
6563 | $this->_dms = $dms;
6564 | } /* }}} */
6565 |
6566 | /**
6567 | * @param $reviewer
6568 | * @param $type
6569 | * @param $status
6570 | * @return bool
6571 | */
6572 | public function addReviewer($reviewer, $type, $status) { /* {{{ */
6573 | $dms = $this->_dms;
6574 |
6575 | if (!is_object($reviewer) || (strcasecmp($type, "i") && strcasecmp($type, "g")) && !is_integer($status)) {
6576 | return false;
6577 | }
6578 | if (!strcasecmp($type, "i")) {
6579 | if (strcasecmp(get_class($reviewer), $dms->getClassname("user"))) {
6580 | return false;
6581 | }
6582 | if ($this->_indReviewers == null) {
6583 | $this->_indReviewers = array();
6584 | }
6585 | $this->_indReviewers[$status][] = $reviewer;
6586 | }
6587 | if (!strcasecmp($type, "g")) {
6588 | if (strcasecmp(get_class($reviewer), $dms->getClassname("group"))) {
6589 | return false;
6590 | }
6591 | if ($this->_grpReviewers == null) {
6592 | $this->_grpReviewers = array();
6593 | }
6594 | $this->_grpReviewers[$status][] = $reviewer;
6595 | }
6596 | return true;
6597 | } /* }}} */
6598 |
6599 | /**
6600 | * @param $approver
6601 | * @param $type
6602 | * @param $status
6603 | * @return bool
6604 | */
6605 | public function addApprover($approver, $type, $status) { /* {{{ */
6606 | $dms = $this->_dms;
6607 |
6608 | if (!is_object($approver) || (strcasecmp($type, "i") && strcasecmp($type, "g")) && !is_integer($status)) {
6609 | return false;
6610 | }
6611 | if (!strcasecmp($type, "i")) {
6612 | if (strcasecmp(get_class($approver), $dms->getClassname("user"))) {
6613 | return false;
6614 | }
6615 | if ($this->_indApprovers == null) {
6616 | $this->_indApprovers = array();
6617 | }
6618 | $this->_indApprovers[$status][] = $approver;
6619 | }
6620 | if (!strcasecmp($type, "g")) {
6621 | if (strcasecmp(get_class($approver), $dms->getClassname("group"))) {
6622 | return false;
6623 | }
6624 | if ($this->_grpApprovers == null) {
6625 | $this->_grpApprovers = array();
6626 | }
6627 | $this->_grpApprovers[$status][] = $approver;
6628 | }
6629 | return true;
6630 | } /* }}} */
6631 |
6632 | /**
6633 | * @param $status
6634 | * @return bool
6635 | */
6636 | public function setStatus($status) { /* {{{ */
6637 | if (!is_integer($status)) {
6638 | return false;
6639 | }
6640 | if ($status<-3 || $status>3) {
6641 | return false;
6642 | }
6643 | $this->_status = $status;
6644 | return true;
6645 | } /* }}} */
6646 |
6647 | /**
6648 | * @return null
6649 | */
6650 | public function getStatus() { /* {{{ */
6651 | return $this->_status;
6652 | } /* }}} */
6653 |
6654 | /**
6655 | * @return mixed
6656 | */
6657 | public function getContent() { /* {{{ */
6658 | return $this->_content;
6659 | } /* }}} */
6660 |
6661 | /**
6662 | * @param $type
6663 | * @return array|bool|null
6664 | */
6665 | public function getReviewers($type) { /* {{{ */
6666 | if (strcasecmp($type, "i") && strcasecmp($type, "g")) {
6667 | return false;
6668 | }
6669 | if (!strcasecmp($type, "i")) {
6670 | return ($this->_indReviewers == null ? array() : $this->_indReviewers);
6671 | }
6672 | else {
6673 | return ($this->_grpReviewers == null ? array() : $this->_grpReviewers);
6674 | }
6675 | } /* }}} */
6676 |
6677 | /**
6678 | * @param $type
6679 | * @return array|bool|null
6680 | */
6681 | public function getApprovers($type) { /* {{{ */
6682 | if (strcasecmp($type, "i") && strcasecmp($type, "g")) {
6683 | return false;
6684 | }
6685 | if (!strcasecmp($type, "i")) {
6686 | return ($this->_indApprovers == null ? array() : $this->_indApprovers);
6687 | }
6688 | else {
6689 | return ($this->_grpApprovers == null ? array() : $this->_grpApprovers);
6690 | }
6691 | } /* }}} */
6692 | } /* }}} */