Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
64.31% |
2177 / 3385 |
|
38.29% |
85 / 222 |
CRAP | |
0.00% |
0 / 5 |
SeedDMS_Core_Document | |
68.32% |
962 / 1408 |
|
40.23% |
35 / 87 |
12556.11 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
4 | |||
clearCache | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
isType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSearchFields | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
6 | |||
getInstanceByData | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
getInstance | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
6 | |||
applyDecorators | |
33.33% |
2 / 6 |
|
0.00% |
0 / 1 |
5.67 | |||
getDir | |
50.00% |
2 / 4 |
|
0.00% |
0 / 1 |
2.50 | |||
getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setName | |
52.94% |
9 / 17 |
|
0.00% |
0 / 1 |
14.67 | |||
getComment | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setComment | |
52.94% |
9 / 17 |
|
0.00% |
0 / 1 |
14.67 | |||
getKeywords | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setKeywords | |
52.94% |
9 / 17 |
|
0.00% |
0 / 1 |
14.67 | |||
hasCategory | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
getCategories | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
5 | |||
setCategories | |
68.00% |
17 / 25 |
|
0.00% |
0 / 1 |
13.28 | |||
addCategories | |
72.41% |
21 / 29 |
|
0.00% |
0 / 1 |
15.02 | |||
removeCategories | |
60.00% |
12 / 20 |
|
0.00% |
0 / 1 |
14.18 | |||
getDate | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDate | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
4 | |||
isDescendant | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getParent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFolder | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setParent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setFolder | |
71.43% |
25 / 35 |
|
0.00% |
0 / 1 |
16.94 | |||
getOwner | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setOwner | |
68.00% |
17 / 25 |
|
0.00% |
0 / 1 |
13.28 | |||
getDefaultAccess | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
setDefaultAccess | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
inheritsAccess | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getInheritAccess | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setInheritAccess | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
5 | |||
expires | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getExpires | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setExpires | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 | |||
hasExpired | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
verifyLastestContentExpriry | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
11.10 | |||
checkForDueRevisionWorkflow | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
isLocked | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setLocked | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
5 | |||
getLockingUser | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
isCheckedOut | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
getCheckOutInfo | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
checkOut | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
42 | |||
checkIn | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
90 | |||
cancelCheckOut | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
checkOutStatus | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
getSequence | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setSequence | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
clearAccessList | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
3.02 | |||
getAccessList | |
95.83% |
23 / 24 |
|
0.00% |
0 / 1 |
12 | |||
addAccess | |
88.24% |
15 / 17 |
|
0.00% |
0 / 1 |
8.10 | |||
changeAccess | |
86.67% |
13 / 15 |
|
0.00% |
0 / 1 |
6.09 | |||
removeAccess | |
80.00% |
8 / 10 |
|
0.00% |
0 / 1 |
5.20 | |||
getAccessMode | |
83.33% |
25 / 30 |
|
0.00% |
0 / 1 |
23.04 | |||
getGroupAccessMode | |
86.67% |
13 / 15 |
|
0.00% |
0 / 1 |
7.12 | |||
getNotifyList | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
10.02 | |||
cleanNotifyList | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
addNotify | |
55.00% |
22 / 40 |
|
0.00% |
0 / 1 |
43.34 | |||
removeNotify | |
80.00% |
16 / 20 |
|
0.00% |
0 / 1 |
8.51 | |||
addContent | |
51.58% |
49 / 95 |
|
0.00% |
0 / 1 |
201.93 | |||
replaceContent | |
76.60% |
36 / 47 |
|
0.00% |
0 / 1 |
19.28 | |||
getContent | |
87.50% |
14 / 16 |
|
0.00% |
0 / 1 |
7.10 | |||
getContentByVersion | |
71.43% |
15 / 21 |
|
0.00% |
0 / 1 |
13.82 | |||
isLatestContent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__getLatestContent | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
30 | |||
getLatestContent | |
87.50% |
14 / 16 |
|
0.00% |
0 / 1 |
8.12 | |||
_removeContent | |
62.50% |
75 / 120 |
|
0.00% |
0 / 1 |
140.51 | |||
removeContent | |
62.96% |
17 / 27 |
|
0.00% |
0 / 1 |
23.96 | |||
getDocumentLink | |
81.25% |
13 / 16 |
|
0.00% |
0 / 1 |
6.24 | |||
getDocumentLinks | |
95.24% |
20 / 21 |
|
0.00% |
0 / 1 |
9 | |||
getReverseDocumentLinks | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
8.01 | |||
addDocumentLink | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
10.01 | |||
removeDocumentLink | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
4.05 | |||
getDocumentFile | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
6.02 | |||
getDocumentFiles | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
9 | |||
addDocumentFile | |
69.57% |
16 / 23 |
|
0.00% |
0 / 1 |
9.80 | |||
removeDocumentFile | |
76.47% |
13 / 17 |
|
0.00% |
0 / 1 |
8.83 | |||
remove | |
59.70% |
40 / 67 |
|
0.00% |
0 / 1 |
70.24 | |||
__getApproversList | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getReadAccessList | |
57.97% |
40 / 69 |
|
0.00% |
0 / 1 |
102.35 | |||
getFolderList | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
repair | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
30 | |||
getUsedDiskSpace | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
getTimeline | |
87.88% |
29 / 33 |
|
0.00% |
0 / 1 |
13.30 | |||
transferToUser | |
62.50% |
15 / 24 |
|
0.00% |
0 / 1 |
7.90 | |||
SeedDMS_Core_DocumentContent | |
60.80% |
1101 / 1811 |
|
20.83% |
20 / 96 |
39463.04 | |
0.00% |
0 / 1 |
verifyStatus | |
93.22% |
55 / 59 |
|
0.00% |
0 / 1 |
38.45 | |||
__construct | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
2 | |||
getInstance | |
83.33% |
15 / 18 |
|
0.00% |
0 / 1 |
6.17 | |||
isType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getComment | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDate | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getOriginalFileName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFileType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFileName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__getDir | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMimeType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRevisionDate | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDocument | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUser | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setRevisionDate | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
setDate | |
84.62% |
11 / 13 |
|
0.00% |
0 / 1 |
6.13 | |||
getFileSize | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setFileSize | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
3.10 | |||
getChecksum | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setChecksum | |
77.78% |
7 / 9 |
|
0.00% |
0 / 1 |
3.10 | |||
setFileType | |
80.00% |
12 / 15 |
|
0.00% |
0 / 1 |
5.20 | |||
setMimeType | |
72.73% |
8 / 11 |
|
0.00% |
0 / 1 |
4.32 | |||
setComment | |
43.75% |
7 / 16 |
|
0.00% |
0 / 1 |
19.39 | |||
getStatus | |
89.47% |
17 / 19 |
|
0.00% |
0 / 1 |
6.04 | |||
getStatusLog | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
5.01 | |||
setStatus | |
91.18% |
31 / 34 |
|
0.00% |
0 / 1 |
16.18 | |||
rewriteStatusLog | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
56 | |||
getAccessMode | |
7.23% |
6 / 83 |
|
0.00% |
0 / 1 |
1589.76 | |||
getReviewers | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
7.01 | |||
getReviewStatus | |
87.88% |
29 / 33 |
|
0.00% |
0 / 1 |
12.26 | |||
getReviewLog | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
4.02 | |||
rewriteReviewLog | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
240 | |||
getApprovers | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
7.01 | |||
getApprovalStatus | |
87.88% |
29 / 33 |
|
0.00% |
0 / 1 |
12.26 | |||
getApproveLog | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
4.02 | |||
rewriteApprovalLog | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
240 | |||
getRecipients | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
7.01 | |||
getReceiptStatus | |
85.37% |
35 / 41 |
|
0.00% |
0 / 1 |
14.61 | |||
getReceiptLog | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
4.02 | |||
rewriteReceiptLog | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
240 | |||
getRevisors | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
7.01 | |||
getRevisionStatus | |
90.00% |
27 / 30 |
|
0.00% |
0 / 1 |
10.10 | |||
getRevisionLog | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
4.02 | |||
rewriteRevisionLog | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
240 | |||
checkForDueRevisionWorkflow | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
182 | |||
addIndReviewer | |
84.38% |
27 / 32 |
|
0.00% |
0 / 1 |
18.10 | |||
addGrpReviewer | |
86.11% |
31 / 36 |
|
0.00% |
0 / 1 |
21.07 | |||
setReviewByInd | |
77.78% |
21 / 27 |
|
0.00% |
0 / 1 |
13.58 | |||
removeReview | |
80.95% |
17 / 21 |
|
0.00% |
0 / 1 |
10.69 | |||
setReviewByGrp | |
76.92% |
20 / 26 |
|
0.00% |
0 / 1 |
13.77 | |||
addIndApprover | |
84.38% |
27 / 32 |
|
0.00% |
0 / 1 |
18.10 | |||
addGrpApprover | |
86.11% |
31 / 36 |
|
0.00% |
0 / 1 |
21.07 | |||
setApprovalByInd | |
77.78% |
21 / 27 |
|
0.00% |
0 / 1 |
13.58 | |||
removeApproval | |
80.95% |
17 / 21 |
|
0.00% |
0 / 1 |
10.69 | |||
setApprovalByGrp | |
76.92% |
20 / 26 |
|
0.00% |
0 / 1 |
13.77 | |||
addIndRecipient | |
84.38% |
27 / 32 |
|
0.00% |
0 / 1 |
18.10 | |||
addGrpRecipient | |
87.18% |
34 / 39 |
|
0.00% |
0 / 1 |
20.84 | |||
addRevisor | |
82.50% |
33 / 40 |
|
0.00% |
0 / 1 |
20.93 | |||
addIndRevisor | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
addGrpRevisor | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
setReceiptByInd | |
80.00% |
20 / 25 |
|
0.00% |
0 / 1 |
11.97 | |||
setReceiptByGrp | |
80.00% |
20 / 25 |
|
0.00% |
0 / 1 |
11.97 | |||
setRevision | |
80.00% |
24 / 30 |
|
0.00% |
0 / 1 |
16.80 | |||
setRevisionByInd | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
setRevisionByGrp | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
delIndReviewer | |
70.59% |
12 / 17 |
|
0.00% |
0 / 1 |
11.06 | |||
delGrpReviewer | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
72 | |||
delIndApprover | |
72.22% |
13 / 18 |
|
0.00% |
0 / 1 |
9.37 | |||
delGrpApprover | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
72 | |||
delIndRecipient | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
56 | |||
delGrpRecipient | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
56 | |||
delRevisor | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
182 | |||
delIndRevisor | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
delGrpRevisor | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
startRevision | |
80.00% |
20 / 25 |
|
0.00% |
0 / 1 |
8.51 | |||
finishRevision | |
76.00% |
19 / 25 |
|
0.00% |
0 / 1 |
11.38 | |||
setWorkflowState | |
75.00% |
6 / 8 |
|
0.00% |
0 / 1 |
3.14 | |||
getWorkflowState | |
92.86% |
13 / 14 |
|
0.00% |
0 / 1 |
5.01 | |||
setWorkflow | |
66.67% |
14 / 21 |
|
0.00% |
0 / 1 |
8.81 | |||
getWorkflow | |
88.24% |
15 / 17 |
|
0.00% |
0 / 1 |
6.06 | |||
rewriteWorkflowLog | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
72 | |||
rewindWorkflow | |
76.92% |
10 / 13 |
|
0.00% |
0 / 1 |
3.11 | |||
removeWorkflow | |
83.33% |
20 / 24 |
|
0.00% |
0 / 1 |
8.30 | |||
getParentWorkflow | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
56 | |||
runSubWorkflow | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
returnFromSubWorkflow | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
56 | |||
triggerWorkflowTransitionIsAllowed | |
66.67% |
18 / 27 |
|
0.00% |
0 / 1 |
19.26 | |||
executeWorkflowTransitionIsAllowed | |
52.78% |
19 / 36 |
|
0.00% |
0 / 1 |
42.96 | |||
triggerWorkflowTransition | |
63.16% |
12 / 19 |
|
0.00% |
0 / 1 |
13.05 | |||
enterNextState | |
80.00% |
20 / 25 |
|
0.00% |
0 / 1 |
11.97 | |||
getWorkflowLog | |
86.36% |
19 / 22 |
|
0.00% |
0 / 1 |
8.16 | |||
getLastWorkflowLog | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
3.01 | |||
needsWorkflowAction | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
6 | |||
repair | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
132 | |||
SeedDMS_Core_DocumentLink | |
85.00% |
17 / 20 |
|
87.50% |
7 / 8 |
13.57 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
isType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getID | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDocument | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTarget | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUser | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
isPublic | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getAccessMode | |
50.00% |
3 / 6 |
|
0.00% |
0 / 1 |
6.00 | |||
SeedDMS_Core_DocumentFile | |
94.37% |
67 / 71 |
|
90.91% |
20 / 22 |
38.26 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
2 | |||
isType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getID | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDocument | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUserID | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getComment | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setComment | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
getDate | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDate | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
4 | |||
getDir | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFileType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getMimeType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getOriginalFileName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setName | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
getUser | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setVersion | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
isPublic | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setPublic | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
4 | |||
getAccessMode | |
50.00% |
3 / 6 |
|
0.00% |
0 / 1 |
6.00 | |||
SeedDMS_Core_AddContentResultSet | |
22.41% |
13 / 58 |
|
33.33% |
3 / 9 |
865.86 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
setDMS | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addReviewer | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
132 | |||
addApprover | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
132 | |||
setStatus | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
4.59 | |||
getStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getContent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getReviewers | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
42 | |||
getApprovers | |
0.00% |
0 / 5 |
|
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 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 is in a revision workflow. A revision workflow is started |
47 | * some time after the document has been released. |
48 | */ |
49 | define("S_IN_REVISION", 4); |
50 | |
51 | /* |
52 | * Document is in draft status. Being in draft means that the document |
53 | * is still worked on. This status is mainly for uploading documents |
54 | * which aren't fully complete but needs to accessible for the public, |
55 | * e.g. in order to colaborate on them. |
56 | */ |
57 | define("S_DRAFT", 5); |
58 | |
59 | /* |
60 | * Document needs correction after revision. This needs to be different from |
61 | * the regular S_REJECTED because documents which has been rejected |
62 | * in revision are not necessarily invalid but just needs correction. |
63 | */ |
64 | define("S_NEEDS_CORRECTION", 6); |
65 | |
66 | /* |
67 | * Document was rejected. A document is in rejected state when |
68 | * the review failed or approval was not given. |
69 | */ |
70 | define("S_REJECTED", -1); |
71 | |
72 | /* |
73 | * Document is obsolete. A document can be obsoleted once it was |
74 | * released. |
75 | */ |
76 | define("S_OBSOLETE", -2); |
77 | |
78 | /* |
79 | * Document is expired. A document expires when the expiration date |
80 | * is reached |
81 | */ |
82 | define("S_EXPIRED", -3); |
83 | |
84 | /* |
85 | * Lowest and highest status that may be set |
86 | */ |
87 | define("S_LOWEST_STATUS", -3); |
88 | define("S_HIGHEST_STATUS", 6); |
89 | |
90 | /** |
91 | * The different states a workflow log can be in. This is used in |
92 | * all tables tblDocumentXXXLog |
93 | */ |
94 | /* |
95 | * workflow is in a neutral status waiting for action of user |
96 | */ |
97 | define("S_LOG_WAITING", 0); |
98 | |
99 | /* |
100 | * workflow has been successful ended. The document content has been |
101 | * approved, reviewed, aknowledged or revised |
102 | */ |
103 | define("S_LOG_ACCEPTED", 1); |
104 | |
105 | /* |
106 | * workflow has been unsuccessful ended. The document content has been |
107 | * rejected |
108 | */ |
109 | define("S_LOG_REJECTED", -1); |
110 | |
111 | /* |
112 | * user has been removed from workflow. This can be for different reasons |
113 | * 1. the user has been actively removed from the workflow, 2. the user has |
114 | * been deleted. |
115 | */ |
116 | define("S_LOG_USER_REMOVED", -2); |
117 | |
118 | /* |
119 | * workflow is sleeping until reactivation. The workflow has been set up |
120 | * but not started. This is only valid for the revision workflow, which |
121 | * may run over and over again. |
122 | */ |
123 | define("S_LOG_SLEEPING", -3); |
124 | |
125 | /** |
126 | * Class to represent a document in the document management system |
127 | * |
128 | * A document in SeedDMS is similar to a file in a regular file system. |
129 | * Documents may have any number of content elements |
130 | * ({@link SeedDMS_Core_DocumentContent}). These content elements are often |
131 | * called versions ordered in a timely manner. The most recent content element |
132 | * is the current version. |
133 | * |
134 | * Documents can be linked to other documents and can have attached files. |
135 | * The document content can be anything that can be stored in a regular |
136 | * file. |
137 | * |
138 | * @category DMS |
139 | * @package SeedDMS_Core |
140 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli, |
141 | * Uwe Steinmann <uwe@steinmann.cx> |
142 | * @copyright Copyright (C) 2002-2005 Markus Westphal, 2006-2008 Malcolm Cowe, |
143 | * 2010 Matteo Lucarelli, 2010-2022 Uwe Steinmann |
144 | * @version Release: @package_version@ |
145 | */ |
146 | class SeedDMS_Core_Document extends SeedDMS_Core_Object { /* {{{ */ |
147 | /** |
148 | * @var string name of document |
149 | */ |
150 | protected $_name; |
151 | |
152 | /** |
153 | * @var string comment of document |
154 | */ |
155 | protected $_comment; |
156 | |
157 | /** |
158 | * @var integer unix timestamp of creation date |
159 | */ |
160 | protected $_date; |
161 | |
162 | /** |
163 | * @var integer id of user who is the owner |
164 | */ |
165 | protected $_ownerID; |
166 | |
167 | /** |
168 | * @var object user who is the owner |
169 | */ |
170 | protected $_owner; |
171 | |
172 | /** |
173 | * @var integer id of folder this document belongs to |
174 | */ |
175 | protected $_folderID; |
176 | |
177 | /** |
178 | * @var object parent folder this document belongs to |
179 | */ |
180 | protected $_parent; |
181 | |
182 | /** |
183 | * @var integer timestamp of expiration date |
184 | */ |
185 | protected $_expires; |
186 | |
187 | /** |
188 | * @var boolean true if access is inherited, otherwise false |
189 | */ |
190 | protected $_inheritAccess; |
191 | |
192 | /** |
193 | * @var integer default access if access rights are not inherited |
194 | */ |
195 | protected $_defaultAccess; |
196 | |
197 | /** |
198 | * @var array list of notifications for users and groups |
199 | */ |
200 | protected $_readAccessList; |
201 | |
202 | /** |
203 | * @var array list of notifications for users and groups |
204 | */ |
205 | public $_notifyList; |
206 | |
207 | /** |
208 | * @var boolean true if document is locked, otherwise false |
209 | */ |
210 | protected $_locked; |
211 | |
212 | /** |
213 | * @var string list of keywords |
214 | */ |
215 | protected $_keywords; |
216 | |
217 | /** |
218 | * @var SeedDMS_Core_DocumentCategory[] list of categories |
219 | */ |
220 | protected $_categories; |
221 | |
222 | /** |
223 | * @var integer position of document within the parent folder |
224 | */ |
225 | protected $_sequence; |
226 | |
227 | /** |
228 | * @var SeedDMS_Core_DocumentContent temp. storage for latestcontent |
229 | */ |
230 | protected $_latestContent; |
231 | |
232 | /** |
233 | * @var array temp. storage for content |
234 | */ |
235 | protected $_content; |
236 | |
237 | /** |
238 | * @var SeedDMS_Core_Folder |
239 | */ |
240 | protected $_folder; |
241 | |
242 | /** @var array of SeedDMS_Core_UserAccess and SeedDMS_Core_GroupAccess */ |
243 | protected $_accessList; |
244 | |
245 | /** |
246 | * @var array |
247 | */ |
248 | protected $_documentLinks; |
249 | |
250 | /** |
251 | * @var array |
252 | */ |
253 | protected $_documentFiles; |
254 | |
255 | function __construct($id, $name, $comment, $date, $expires, $ownerID, $folderID, $inheritAccess, $defaultAccess, $locked, $keywords, $sequence) { /* {{{ */ |
256 | parent::__construct($id); |
257 | $this->_name = $name; |
258 | $this->_comment = $comment; |
259 | $this->_date = $date; |
260 | $this->_expires = $expires; |
261 | $this->_ownerID = $ownerID; |
262 | $this->_folderID = $folderID; |
263 | $this->_inheritAccess = $inheritAccess ? true : false; |
264 | $this->_defaultAccess = $defaultAccess; |
265 | $this->_locked = ($locked == null || $locked == '' ? -1 : $locked); |
266 | $this->_keywords = $keywords; |
267 | $this->_sequence = $sequence; |
268 | $this->_categories = array(); |
269 | $this->_notifyList = array(); |
270 | $this->_latestContent = null; |
271 | $this->_content = null; |
272 | /* Cache */ |
273 | $this->clearCache(); |
274 | } /* }}} */ |
275 | |
276 | /** |
277 | * Clear cache of this instance. |
278 | * |
279 | * The result of some expensive database actions (e.g. get all subfolders |
280 | * or documents) will be saved in a class variable to speed up consecutive |
281 | * calls of the same method. If a second call of the same method shall not |
282 | * use the cache, then it must be cleared. |
283 | * |
284 | */ |
285 | public function clearCache() { /* {{{ */ |
286 | $this->_parent = null; |
287 | $this->_owner = null; |
288 | $this->_documentLinks = null; |
289 | $this->_documentFiles = null; |
290 | $this->_content = null; |
291 | $this->_accessList = null; |
292 | $this->_notifyList = null; |
293 | } /* }}} */ |
294 | |
295 | /** |
296 | * Check if this object is of type 'document'. |
297 | * |
298 | * @param string $type type of object |
299 | */ |
300 | public function isType($type) { /* {{{ */ |
301 | return $type == 'document'; |
302 | } /* }}} */ |
303 | |
304 | /** |
305 | * Return an array of database fields which are used for searching |
306 | * a term entered in the database search form |
307 | * |
308 | * @param SeedDMS_Core_DMS $dms |
309 | * @param array $searchin integer list of search scopes (2=name, 3=comment, |
310 | * 4=attributes) |
311 | * @return array list of database fields |
312 | */ |
313 | public static function getSearchFields($dms, $searchin) { /* {{{ */ |
314 | $db = $dms->getDB(); |
315 | |
316 | $searchFields = array(); |
317 | if (in_array(1, $searchin)) { |
318 | $searchFields[] = "`tblDocuments`.`keywords`"; |
319 | } |
320 | if (in_array(2, $searchin)) { |
321 | $searchFields[] = "`tblDocuments`.`name`"; |
322 | } |
323 | if (in_array(3, $searchin)) { |
324 | $searchFields[] = "`tblDocuments`.`comment`"; |
325 | $searchFields[] = "`tblDocumentContent`.`comment`"; |
326 | } |
327 | if (in_array(4, $searchin)) { |
328 | $searchFields[] = "`tblDocumentAttributes`.`value`"; |
329 | $searchFields[] = "`tblDocumentContentAttributes`.`value`"; |
330 | } |
331 | if (in_array(5, $searchin)) { |
332 | $searchFields[] = $db->castToText("`tblDocuments`.`id`"); |
333 | } |
334 | |
335 | return $searchFields; |
336 | } /* }}} */ |
337 | |
338 | /** |
339 | * Return a folder by its database record |
340 | * |
341 | * @param array $resArr array of folder data as returned by database |
342 | * @param SeedDMS_Core_DMS $dms |
343 | * @return SeedDMS_Core_Folder|bool instance of SeedDMS_Core_Folder if document exists |
344 | */ |
345 | public static function getInstanceByData($resArr, $dms) { /* {{{ */ |
346 | $classname = $dms->getClassname('document'); |
347 | /** @var SeedDMS_Core_Document $document */ |
348 | $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"]); |
349 | $document->setDMS($dms); |
350 | $document = $document->applyDecorators(); |
351 | return $document; |
352 | } /* }}} */ |
353 | |
354 | /** |
355 | * Return an document by its id |
356 | * |
357 | * @param integer $id id of document |
358 | * @param SeedDMS_Core_DMS $dms |
359 | * @return bool|SeedDMS_Core_Document instance of SeedDMS_Core_Document if document exists, null |
360 | * if document does not exist, false in case of error |
361 | */ |
362 | public static function getInstance($id, $dms) { /* {{{ */ |
363 | $db = $dms->getDB(); |
364 | |
365 | // $queryStr = "SELECT * FROM `tblDocuments` WHERE `id` = " . (int) $id; |
366 | $queryStr = "SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lock` FROM `tblDocuments` LEFT JOIN `tblDocumentLocks` ON `tblDocuments`.`id` = `tblDocumentLocks`.`document` WHERE `id` = " . (int) $id; |
367 | if($dms->checkWithinRootDir) |
368 | $queryStr .= " AND `folderList` LIKE '%:".$dms->rootFolderID.":%'"; |
369 | $resArr = $db->getResultArray($queryStr); |
370 | if (is_bool($resArr) && $resArr == false) |
371 | return false; |
372 | if (count($resArr) != 1) |
373 | return null; |
374 | $resArr = $resArr[0]; |
375 | |
376 | $resArr['lock'] = !$resArr['lock'] ? -1 : $resArr['lock']; |
377 | |
378 | return self::getInstanceByData($resArr, $dms); |
379 | } /* }}} */ |
380 | |
381 | /** |
382 | * Apply decorators |
383 | * |
384 | * @return object final object after all decorators has been applied |
385 | */ |
386 | function applyDecorators() { /* {{{ */ |
387 | if($decorators = $this->_dms->getDecorators('document')) { |
388 | $s = $this; |
389 | foreach($decorators as $decorator) { |
390 | $s = new $decorator($s); |
391 | } |
392 | return $s; |
393 | } else { |
394 | return $this; |
395 | } |
396 | } /* }}} */ |
397 | |
398 | /** |
399 | * Return the directory of the document in the file system relativ |
400 | * to the contentDir |
401 | * |
402 | * @return string directory of document |
403 | */ |
404 | function getDir() { /* {{{ */ |
405 | if($this->_dms->maxDirID) { |
406 | $dirid = (int) (($this->_id-1) / $this->_dms->maxDirID) + 1; |
407 | return $dirid."/".$this->_id."/"; |
408 | } else { |
409 | return $this->_id."/"; |
410 | } |
411 | } /* }}} */ |
412 | |
413 | /** |
414 | * Return the name of the document |
415 | * |
416 | * @return string name of document |
417 | */ |
418 | function getName() { return $this->_name; } |
419 | |
420 | /** |
421 | * Set the name of the document |
422 | * |
423 | * @param $newName string new name of document |
424 | * @return bool |
425 | */ |
426 | function setName($newName) { /* {{{ */ |
427 | $db = $this->_dms->getDB(); |
428 | |
429 | /* Check if 'onPreSetName' callback is set */ |
430 | if(isset($this->_dms->callbacks['onPreSetName'])) { |
431 | foreach($this->_dms->callbacks['onPreSetName'] as $callback) { |
432 | $ret = call_user_func($callback[0], $callback[1], $this, $newName); |
433 | if(is_bool($ret)) |
434 | return $ret; |
435 | } |
436 | } |
437 | |
438 | $queryStr = "UPDATE `tblDocuments` SET `name` = ".$db->qstr($newName)." WHERE `id` = ". $this->_id; |
439 | if (!$db->getResult($queryStr)) |
440 | return false; |
441 | |
442 | $oldName = $this->_name; |
443 | $this->_name = $newName; |
444 | |
445 | /* Check if 'onPostSetName' callback is set */ |
446 | if(isset($this->_dms->callbacks['onPostSetName'])) { |
447 | foreach($this->_dms->callbacks['onPostSetName'] as $callback) { |
448 | $ret = call_user_func($callback[0], $callback[1], $this, $oldName); |
449 | if(is_bool($ret)) |
450 | return $ret; |
451 | } |
452 | } |
453 | |
454 | return true; |
455 | } /* }}} */ |
456 | |
457 | /** |
458 | * Return the comment of the document |
459 | * |
460 | * @return string comment of document |
461 | */ |
462 | function getComment() { return $this->_comment; } |
463 | |
464 | /** |
465 | * Set the comment of the document |
466 | * |
467 | * @param $newComment string new comment of document |
468 | * @return bool |
469 | */ |
470 | function setComment($newComment) { /* {{{ */ |
471 | $db = $this->_dms->getDB(); |
472 | |
473 | /* Check if 'onPreSetComment' callback is set */ |
474 | if(isset($this->_dms->callbacks['onPreSetComment'])) { |
475 | foreach($this->_dms->callbacks['onPreSetComment'] as $callback) { |
476 | $ret = call_user_func($callback[0], $callback[1], $this, $newComment); |
477 | if(is_bool($ret)) |
478 | return $ret; |
479 | } |
480 | } |
481 | |
482 | $queryStr = "UPDATE `tblDocuments` SET `comment` = ".$db->qstr($newComment)." WHERE `id` = ". $this->_id; |
483 | if (!$db->getResult($queryStr)) |
484 | return false; |
485 | |
486 | $oldComment = $this->_comment; |
487 | $this->_comment = $newComment; |
488 | |
489 | /* Check if 'onPostSetComment' callback is set */ |
490 | if(isset($this->_dms->callbacks['onPostSetComment'])) { |
491 | foreach($this->_dms->callbacks['onPostSetComment'] as $callback) { |
492 | $ret = call_user_func($callback[0], $callback[1], $this, $oldComment); |
493 | if(is_bool($ret)) |
494 | return $ret; |
495 | } |
496 | } |
497 | |
498 | return true; |
499 | } /* }}} */ |
500 | |
501 | /** |
502 | * @return string |
503 | */ |
504 | function getKeywords() { return $this->_keywords; } |
505 | |
506 | /** |
507 | * @param string $newKeywords |
508 | * @return bool |
509 | */ |
510 | function setKeywords($newKeywords) { /* {{{ */ |
511 | $db = $this->_dms->getDB(); |
512 | |
513 | /* Check if 'onPreSetKeywords' callback is set */ |
514 | if(isset($this->_dms->callbacks['onPreSetKeywords'])) { |
515 | foreach($this->_dms->callbacks['onPreSetKeywords'] as $callback) { |
516 | $ret = call_user_func($callback[0], $callback[1], $this, $newKeywords); |
517 | if(is_bool($ret)) |
518 | return $ret; |
519 | } |
520 | } |
521 | |
522 | $queryStr = "UPDATE `tblDocuments` SET `keywords` = ".$db->qstr($newKeywords)." WHERE `id` = ". $this->_id; |
523 | if (!$db->getResult($queryStr)) |
524 | return false; |
525 | |
526 | $oldKeywords = $this->_keywords; |
527 | $this->_keywords = $newKeywords; |
528 | |
529 | /* Check if 'onPostSetKeywords' callback is set */ |
530 | if(isset($this->_dms->callbacks['onPostSetKeywords'])) { |
531 | foreach($this->_dms->callbacks['onPostSetKeywords'] as $callback) { |
532 | $ret = call_user_func($callback[0], $callback[1], $this, $oldKeywords); |
533 | if(is_bool($ret)) |
534 | return $ret; |
535 | } |
536 | } |
537 | |
538 | return true; |
539 | } /* }}} */ |
540 | |
541 | /** |
542 | * Check if document has a given category |
543 | * |
544 | * @param SeedDMS_Core_DocumentCategory $cat |
545 | * @return bool true if document has category, otherwise false |
546 | */ |
547 | function hasCategory($cat) { /* {{{ */ |
548 | $db = $this->_dms->getDB(); |
549 | |
550 | if(!$cat) |
551 | return false; |
552 | |
553 | $queryStr = "SELECT * FROM `tblDocumentCategory` WHERE `documentID` = ".$this->_id." AND `categoryID`=".$cat->getId(); |
554 | $resArr = $db->getResultArray($queryStr); |
555 | if (!$resArr) |
556 | return false; |
557 | |
558 | return true; |
559 | } /* }}} */ |
560 | |
561 | /** |
562 | * Retrieve a list of all categories this document belongs to |
563 | * |
564 | * @return bool|SeedDMS_Core_DocumentCategory[] |
565 | */ |
566 | function getCategories() { /* {{{ */ |
567 | $db = $this->_dms->getDB(); |
568 | |
569 | if(!$this->_categories) { |
570 | $queryStr = "SELECT * FROM `tblCategory` WHERE `id` IN (SELECT `categoryID` FROM `tblDocumentCategory` WHERE `documentID` = ".$this->_id.")"; |
571 | $resArr = $db->getResultArray($queryStr); |
572 | if (is_bool($resArr) && !$resArr) |
573 | return false; |
574 | |
575 | $this->_categories = []; |
576 | foreach ($resArr as $row) { |
577 | $cat = new SeedDMS_Core_DocumentCategory($row['id'], $row['name']); |
578 | $cat->setDMS($this->_dms); |
579 | $this->_categories[] = $cat; |
580 | } |
581 | } |
582 | return $this->_categories; |
583 | } /* }}} */ |
584 | |
585 | /** |
586 | * Set a list of categories for the document |
587 | * This function will delete currently assigned categories and sets new |
588 | * categories. |
589 | * |
590 | * @param SeedDMS_Core_DocumentCategory[] $newCategories list of category objects |
591 | * @return bool |
592 | */ |
593 | function setCategories($newCategories) { /* {{{ */ |
594 | $db = $this->_dms->getDB(); |
595 | |
596 | /* Check if 'onPreSetCategories' callback is set */ |
597 | if(isset($this->_dms->callbacks['onPreSetCategories'])) { |
598 | foreach($this->_dms->callbacks['onPreSetCategories'] as $callback) { |
599 | $ret = call_user_func($callback[0], $callback[1], $this, $newCategories); |
600 | if(is_bool($ret)) |
601 | return $ret; |
602 | } |
603 | } |
604 | |
605 | $db->startTransaction(); |
606 | $queryStr = "DELETE FROM `tblDocumentCategory` WHERE `documentID` = ". $this->_id; |
607 | if (!$db->getResult($queryStr)) { |
608 | $db->rollbackTransaction(); |
609 | return false; |
610 | } |
611 | |
612 | foreach($newCategories as $cat) { |
613 | $queryStr = "INSERT INTO `tblDocumentCategory` (`categoryID`, `documentID`) VALUES (". $cat->getId() .", ". $this->_id .")"; |
614 | if (!$db->getResult($queryStr)) { |
615 | $db->rollbackTransaction(); |
616 | return false; |
617 | } |
618 | } |
619 | |
620 | $db->commitTransaction(); |
621 | |
622 | $oldCategories = $this->_categories; |
623 | $this->_categories = $newCategories; |
624 | |
625 | /* Check if 'onPostSetCategories' callback is set */ |
626 | if(isset($this->_dms->callbacks['onPostSetCategories'])) { |
627 | foreach($this->_dms->callbacks['onPostSetCategories'] as $callback) { |
628 | $ret = call_user_func($callback[0], $callback[1], $this, $oldCategories); |
629 | if(is_bool($ret)) |
630 | return $ret; |
631 | } |
632 | } |
633 | |
634 | return true; |
635 | } /* }}} */ |
636 | |
637 | /** |
638 | * Add a list of categories to the document |
639 | * This function will add a list of new categories to the document. |
640 | * |
641 | * @param array $newCategories list of category objects |
642 | */ |
643 | function addCategories($newCategories) { /* {{{ */ |
644 | $db = $this->_dms->getDB(); |
645 | |
646 | /* Check if 'onPreAddCategories' callback is set */ |
647 | if(isset($this->_dms->callbacks['onPreAddCategories'])) { |
648 | foreach($this->_dms->callbacks['onPreAddCategories'] as $callback) { |
649 | $ret = call_user_func($callback[0], $callback[1], $this, $newCategories); |
650 | if(is_bool($ret)) |
651 | return $ret; |
652 | } |
653 | } |
654 | |
655 | if(!$this->_categories) |
656 | $this->getCategories(); |
657 | |
658 | $catids = array(); |
659 | foreach($this->_categories as $cat) |
660 | $catids[] = $cat->getID(); |
661 | |
662 | $db->startTransaction(); |
663 | $ncat = array(); // Array containing actually added new categories |
664 | foreach($newCategories as $cat) { |
665 | if(!in_array($cat->getID(), $catids)) { |
666 | $queryStr = "INSERT INTO `tblDocumentCategory` (`categoryID`, `documentID`) VALUES (". $cat->getId() .", ". $this->_id .")"; |
667 | if (!$db->getResult($queryStr)) { |
668 | $db->rollbackTransaction(); |
669 | return false; |
670 | } |
671 | $ncat[] = $cat; |
672 | } |
673 | } |
674 | $db->commitTransaction(); |
675 | |
676 | $oldCategories = $this->_categories; |
677 | $this->_categories = array_merge($this->_categories, $ncat); |
678 | |
679 | /* Check if 'onPostAddCategories' callback is set */ |
680 | if(isset($this->_dms->callbacks['onPostAddCategories'])) { |
681 | foreach($this->_dms->callbacks['onPostAddCategories'] as $callback) { |
682 | $ret = call_user_func($callback[0], $callback[1], $this, $oldCategories); |
683 | if(is_bool($ret)) |
684 | return $ret; |
685 | } |
686 | } |
687 | |
688 | return true; |
689 | } /* }}} */ |
690 | |
691 | /** |
692 | * Remove a list of categories from the document |
693 | * This function will remove a list of assigned categories to the document. |
694 | * |
695 | * @param array $newCategories list of category objects |
696 | */ |
697 | 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 | 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 | 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 | 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 | * @return SeedDMS_Core_Folder parent folder |
788 | */ |
789 | function getParent() { /* {{{ */ |
790 | return $this->getFolder(); |
791 | } /* }}} */ |
792 | |
793 | function getFolder() { /* {{{ */ |
794 | if (!isset($this->_folder)) |
795 | $this->_folder = $this->_dms->getFolder($this->_folderID); |
796 | return $this->_folder; |
797 | } /* }}} */ |
798 | |
799 | /** |
800 | * Set folder of a document |
801 | * |
802 | * This function basically moves a document from a folder to another |
803 | * folder. |
804 | * |
805 | * @param SeedDMS_Core_Folder $newFolder |
806 | * @return boolean false in case of an error, otherwise true |
807 | */ |
808 | function setParent($newFolder) { /* {{{ */ |
809 | return $this->setFolder($newFolder); |
810 | } /* }}} */ |
811 | |
812 | /** |
813 | * Set folder of a document |
814 | * |
815 | * This function basically moves a document from a folder to another |
816 | * folder. |
817 | * |
818 | * @param SeedDMS_Core_Folder $newFolder |
819 | * @return boolean false in case of an error, otherwise true |
820 | */ |
821 | function setFolder($newFolder) { /* {{{ */ |
822 | $db = $this->_dms->getDB(); |
823 | |
824 | if(!$newFolder) |
825 | return false; |
826 | |
827 | if(!$newFolder->isType('folder')) |
828 | return false; |
829 | |
830 | /* Check if 'onPreSetFolder' callback is set */ |
831 | if(isset($this->_dms->callbacks['onPreSetFolder'])) { |
832 | foreach($this->_dms->callbacks['onPreSetFolder'] as $callback) { |
833 | $ret = call_user_func($callback[0], $callback[1], $this, $newFolder); |
834 | if(is_bool($ret)) |
835 | return $ret; |
836 | } |
837 | } |
838 | |
839 | $db->startTransaction(); |
840 | |
841 | $queryStr = "UPDATE `tblDocuments` SET `folder` = " . $newFolder->getID() . " WHERE `id` = ". $this->_id; |
842 | if (!$db->getResult($queryStr)) { |
843 | $db->rollbackTransaction(); |
844 | return false; |
845 | } |
846 | |
847 | // Make sure that the folder search path is also updated. |
848 | $path = $newFolder->getPath(); |
849 | $flist = ""; |
850 | /** @var SeedDMS_Core_Folder[] $path */ |
851 | foreach ($path as $f) { |
852 | $flist .= ":".$f->getID(); |
853 | } |
854 | if (strlen($flist)>1) { |
855 | $flist .= ":"; |
856 | } |
857 | $queryStr = "UPDATE `tblDocuments` SET `folderList` = '" . $flist . "' WHERE `id` = ". $this->_id; |
858 | if (!$db->getResult($queryStr)) { |
859 | $db->rollbackTransaction(); |
860 | return false; |
861 | } |
862 | |
863 | $db->commitTransaction(); |
864 | |
865 | $oldFolder = $this->_folder; |
866 | $this->_folderID = $newFolder->getID(); |
867 | $this->_folder = $newFolder; |
868 | |
869 | /* Check if 'onPostSetFolder' callback is set */ |
870 | if(isset($this->_dms->callbacks['onPostSetFolder'])) { |
871 | foreach($this->_dms->callbacks['onPostSetFolder'] as $callback) { |
872 | $ret = call_user_func($callback[0], $callback[1], $this, $oldFolder); |
873 | if(is_bool($ret)) |
874 | return $ret; |
875 | } |
876 | } |
877 | |
878 | return true; |
879 | } /* }}} */ |
880 | |
881 | /** |
882 | * Return owner of document |
883 | * |
884 | * @return SeedDMS_Core_User owner of document as an instance of {@link SeedDMS_Core_User} |
885 | */ |
886 | function getOwner() { /* {{{ */ |
887 | if (!isset($this->_owner)) |
888 | $this->_owner = $this->_dms->getUser($this->_ownerID); |
889 | return $this->_owner; |
890 | } /* }}} */ |
891 | |
892 | /** |
893 | * Set owner of a document |
894 | * |
895 | * @param SeedDMS_Core_User $newOwner new owner |
896 | * @return boolean true if successful otherwise false |
897 | */ |
898 | function setOwner($newOwner) { /* {{{ */ |
899 | $db = $this->_dms->getDB(); |
900 | |
901 | if(!$newOwner) |
902 | return false; |
903 | |
904 | if(!$newOwner->isType('user')) |
905 | return false; |
906 | |
907 | $oldOwner = self::getOwner(); |
908 | |
909 | $db->startTransaction(); |
910 | |
911 | /* Check if 'onPreSetOwner' callback is set */ |
912 | if(isset($this->_dms->callbacks['onPreSetOwner'])) { |
913 | foreach($this->_dms->callbacks['onPreSetOwner'] as $callback) { |
914 | $ret = call_user_func($callback[0], $callback[1], $this, $newOwner); |
915 | if(is_bool($ret)) |
916 | return $ret; |
917 | } |
918 | } |
919 | |
920 | $queryStr = "UPDATE `tblDocuments` set `owner` = " . $newOwner->getID() . " WHERE `id` = " . $this->_id; |
921 | if (!$db->getResult($queryStr)) { |
922 | $db->rollbackTransaction(); |
923 | return false; |
924 | } |
925 | |
926 | /* FIXME: Update also all locks and checkouts done by the previous owner */ |
927 | /* |
928 | $queryStr = "UPDATE `tblDocumentLocks` set `userID` = " . $newOwner->getID() . " WHERE `document` = " . $this->_id . " AND `userID` = " . $oldOwner->getID(); |
929 | if (!$db->getResult($queryStr)) { |
930 | $db->rollbackTransaction(); |
931 | return false; |
932 | } |
933 | |
934 | $queryStr = "UPDATE `tblDocumentCheckOuts` set `userID` = " . $newOwner->getID() . " WHERE `document` = " . $this->_id . " AND `userID` = " . $oldOwner->getID(); |
935 | if (!$db->getResult($queryStr)) { |
936 | $db->rollbackTransaction(); |
937 | return false; |
938 | } |
939 | */ |
940 | |
941 | $db->commitTransaction(); |
942 | |
943 | $this->_ownerID = $newOwner->getID(); |
944 | $this->_owner = $newOwner; |
945 | |
946 | /* Check if 'onPostSetOwner' callback is set */ |
947 | if(isset($this->_dms->callbacks['onPostSetOwner'])) { |
948 | foreach($this->_dms->callbacks['onPostSetOwner'] as $callback) { |
949 | $ret = call_user_func($callback[0], $callback[1], $this, $oldOwner); |
950 | if(is_bool($ret)) |
951 | return $ret; |
952 | } |
953 | } |
954 | |
955 | return true; |
956 | } /* }}} */ |
957 | |
958 | /** |
959 | * @return bool|int |
960 | */ |
961 | function getDefaultAccess() { /* {{{ */ |
962 | if ($this->inheritsAccess()) { |
963 | $res = $this->getFolder(); |
964 | if (!$res) return false; |
965 | return $this->_folder->getDefaultAccess(); |
966 | } |
967 | return $this->_defaultAccess; |
968 | } /* }}} */ |
969 | |
970 | /** |
971 | * Set default access mode |
972 | * |
973 | * This method sets the default access mode and also removes all notifiers which |
974 | * will not have read access anymore. Setting a default access mode will only |
975 | * have an immediate effect if the access rights are not inherited, otherwise |
976 | * it just updates the database record of the document and once the |
977 | * inheritance is turn off the default access mode will take effect. |
978 | * |
979 | * @param integer $mode access mode |
980 | * @param bool|string $noclean set to true if notifier list shall not be clean up |
981 | * |
982 | * @return bool |
983 | */ |
984 | function setDefaultAccess($mode, $noclean=false) { /* {{{ */ |
985 | $db = $this->_dms->getDB(); |
986 | |
987 | if($mode < M_LOWEST_RIGHT || $mode > M_HIGHEST_RIGHT) |
988 | return false; |
989 | |
990 | $queryStr = "UPDATE `tblDocuments` set `defaultAccess` = " . (int) $mode . " WHERE `id` = " . $this->_id; |
991 | if (!$db->getResult($queryStr)) |
992 | return false; |
993 | |
994 | $this->_defaultAccess = $mode; |
995 | |
996 | /* Setting the default access mode does not have any effect if access |
997 | * is still inherited. In that case there is no need to clean the |
998 | * notification list. |
999 | */ |
1000 | if(!$noclean && !$this->_inheritAccess) |
1001 | $this->cleanNotifyList(); |
1002 | |
1003 | return true; |
1004 | } /* }}} */ |
1005 | |
1006 | /** |
1007 | * @return bool |
1008 | */ |
1009 | function inheritsAccess() { return $this->_inheritAccess; } |
1010 | |
1011 | /** |
1012 | * This is supposed to be a replacement for inheritsAccess() |
1013 | * |
1014 | * @return bool |
1015 | */ |
1016 | function getInheritAccess() { return $this->_inheritAccess; } |
1017 | |
1018 | /** |
1019 | * Set inherited access mode |
1020 | * Setting inherited access mode will set or unset the internal flag which |
1021 | * controls if the access mode is inherited from the parent folder or not. |
1022 | * It will not modify the |
1023 | * access control list for the current object. It will remove all |
1024 | * notifications of users which do not even have read access anymore |
1025 | * after setting or unsetting inherited access. |
1026 | * |
1027 | * @param boolean $inheritAccess set to true for setting and false for |
1028 | * unsetting inherited access mode |
1029 | * @param boolean $noclean set to true if notifier list shall not be clean up |
1030 | * @return boolean true if operation was successful otherwise false |
1031 | */ |
1032 | function setInheritAccess($inheritAccess, $noclean=false) { /* {{{ */ |
1033 | $db = $this->_dms->getDB(); |
1034 | |
1035 | $queryStr = "UPDATE `tblDocuments` SET `inheritAccess` = " . ($inheritAccess ? "1" : "0") . " WHERE `id` = " . $this->_id; |
1036 | if (!$db->getResult($queryStr)) |
1037 | return false; |
1038 | |
1039 | $this->_inheritAccess = ($inheritAccess ? true : false); |
1040 | |
1041 | if(!$noclean) |
1042 | $this->cleanNotifyList(); |
1043 | |
1044 | return true; |
1045 | } /* }}} */ |
1046 | |
1047 | /** |
1048 | * Check if document expires |
1049 | * |
1050 | * @return boolean true if document has expiration date set, otherwise false |
1051 | */ |
1052 | function expires() { /* {{{ */ |
1053 | if (intval($this->_expires) == 0) |
1054 | return false; |
1055 | else |
1056 | return true; |
1057 | } /* }}} */ |
1058 | |
1059 | /** |
1060 | * Get expiration time of document |
1061 | * |
1062 | * @return integer/boolean expiration date as unix timestamp or false |
1063 | */ |
1064 | function getExpires() { /* {{{ */ |
1065 | if (intval($this->_expires) == 0) |
1066 | return false; |
1067 | else |
1068 | return $this->_expires; |
1069 | } /* }}} */ |
1070 | |
1071 | /** |
1072 | * Set expiration date as unix timestamp |
1073 | * |
1074 | * @param integer $expires unix timestamp of expiration date |
1075 | * @return bool |
1076 | */ |
1077 | function setExpires($expires) { /* {{{ */ |
1078 | $db = $this->_dms->getDB(); |
1079 | |
1080 | $expires = (!$expires) ? 0 : $expires; |
1081 | |
1082 | if ($expires == $this->_expires) { |
1083 | // No change is necessary. |
1084 | return true; |
1085 | } |
1086 | |
1087 | $queryStr = "UPDATE `tblDocuments` SET `expires` = " . (int) $expires . " WHERE `id` = " . $this->_id; |
1088 | if (!$db->getResult($queryStr)) |
1089 | return false; |
1090 | |
1091 | $this->_expires = $expires; |
1092 | return true; |
1093 | } /* }}} */ |
1094 | |
1095 | /** |
1096 | * Check if the document has expired |
1097 | * |
1098 | * The method expects to database field 'expired' to hold the timestamp |
1099 | * of the start of day at which end the document expires. The document will |
1100 | * expire if that day is over. Hence, a document will *not* |
1101 | * be expired during the day of expiration but at the end of that day |
1102 | * |
1103 | * @return boolean true if document has expired otherwise false |
1104 | */ |
1105 | function hasExpired() { /* {{{ */ |
1106 | if (intval($this->_expires) == 0) return false; |
1107 | if (time()>=$this->_expires+24*60*60) return true; |
1108 | return false; |
1109 | } /* }}} */ |
1110 | |
1111 | /** |
1112 | * Check if the document has expired and set the status accordingly |
1113 | * It will also recalculate the status if the current status is |
1114 | * set to S_EXPIRED but the document isn't actually expired. |
1115 | * The method will update the document status log database table |
1116 | * if needed. |
1117 | * FIXME: some left over reviewers/approvers are in the way if |
1118 | * no workflow is set and traditional workflow mode is on. In that |
1119 | * case the status is set to S_DRAFT_REV or S_DRAFT_APP |
1120 | * |
1121 | * @return boolean true if status has changed |
1122 | */ |
1123 | function verifyLastestContentExpriry(){ /* {{{ */ |
1124 | $lc=$this->getLatestContent(); |
1125 | if($lc) { |
1126 | $st=$lc->getStatus(); |
1127 | |
1128 | if (($st["status"]==S_DRAFT_REV || $st["status"]==S_DRAFT_APP || $st["status"]==S_IN_WORKFLOW || $st["status"]==S_RELEASED || $st["status"]==S_IN_REVISION) && $this->hasExpired()){ |
1129 | return $lc->setStatus(S_EXPIRED,"", $this->getOwner()); |
1130 | } |
1131 | elseif ($st["status"]==S_EXPIRED && !$this->hasExpired() ){ |
1132 | $lc->verifyStatus(true, $this->getOwner()); |
1133 | return true; |
1134 | } |
1135 | } |
1136 | return false; |
1137 | } /* }}} */ |
1138 | |
1139 | /** |
1140 | * Check if latest content of the document has a scheduled |
1141 | * revision workflow. |
1142 | * |
1143 | * This method was moved into SeedDMS_Core_DocumentContent and |
1144 | * the original method in SeedDMS_Core_Document now uses it for |
1145 | * the latest version. |
1146 | * |
1147 | * @param object $user user requesting the possible automatic change |
1148 | * @param string $next next date for review |
1149 | * @return boolean true if status has changed |
1150 | */ |
1151 | function checkForDueRevisionWorkflow($user, $next=''){ /* {{{ */ |
1152 | $lc=$this->getLatestContent(); |
1153 | if($lc) { |
1154 | return $lc->checkForDueRevisionWorkflow($user, $next); |
1155 | } |
1156 | return false; |
1157 | } /* }}} */ |
1158 | |
1159 | /** |
1160 | * Check if document is locked |
1161 | * |
1162 | * @return boolean true if locked otherwise false |
1163 | */ |
1164 | function isLocked() { return $this->_locked != -1; } |
1165 | |
1166 | /** |
1167 | * Lock or unlock document |
1168 | * |
1169 | * @param SeedDMS_Core_User|bool $falseOrUser user object for locking or false for unlocking |
1170 | * @return boolean true if operation was successful otherwise false |
1171 | */ |
1172 | function setLocked($falseOrUser) { /* {{{ */ |
1173 | $db = $this->_dms->getDB(); |
1174 | |
1175 | $lockUserID = -1; |
1176 | if (is_bool($falseOrUser) && !$falseOrUser) { |
1177 | $queryStr = "DELETE FROM `tblDocumentLocks` WHERE `document` = ".$this->_id; |
1178 | } |
1179 | else if (is_object($falseOrUser)) { |
1180 | $queryStr = "INSERT INTO `tblDocumentLocks` (`document`, `userID`) VALUES (".$this->_id.", ".$falseOrUser->getID().")"; |
1181 | $lockUserID = $falseOrUser->getID(); |
1182 | } |
1183 | else { |
1184 | return false; |
1185 | } |
1186 | if (!$db->getResult($queryStr)) { |
1187 | return false; |
1188 | } |
1189 | unset($this->_lockingUser); |
1190 | $this->_locked = $lockUserID; |
1191 | return true; |
1192 | } /* }}} */ |
1193 | |
1194 | /** |
1195 | * Get the user currently locking the document |
1196 | * |
1197 | * @return SeedDMS_Core_User|bool user have a lock |
1198 | */ |
1199 | function getLockingUser() { /* {{{ */ |
1200 | if (!$this->isLocked()) |
1201 | return false; |
1202 | |
1203 | if (!isset($this->_lockingUser)) |
1204 | $this->_lockingUser = $this->_dms->getUser($this->_locked); |
1205 | return $this->_lockingUser; |
1206 | } /* }}} */ |
1207 | |
1208 | /** |
1209 | * Check if document is checked out |
1210 | * |
1211 | * @return boolean true if checked out otherwise false |
1212 | */ |
1213 | function isCheckedOut() { /* {{{ */ |
1214 | $db = $this->_dms->getDB(); |
1215 | |
1216 | $queryStr = "SELECT * FROM `tblDocumentCheckOuts` WHERE `document` = " . (int) $this->_id; |
1217 | $resArr = $db->getResultArray($queryStr); |
1218 | if ((is_bool($resArr) && $resArr==false) || (count($resArr)==0)) { |
1219 | // Could not find a check out for the selected document. |
1220 | return false; |
1221 | } else { |
1222 | // A check out has been identified for this document. |
1223 | return true; |
1224 | } |
1225 | } /* }}} */ |
1226 | |
1227 | /** |
1228 | * Get checkout info for document |
1229 | * |
1230 | * This returns the checkouts for a document. There could be several checkouts |
1231 | * for one document, but usually there is just one. |
1232 | * |
1233 | * @return array/boolean records from table tblDocumentCheckOuts or false |
1234 | * in case of an error. |
1235 | */ |
1236 | function getCheckOutInfo() { /* {{{ */ |
1237 | $db = $this->_dms->getDB(); |
1238 | |
1239 | $queryStr = "SELECT * FROM `tblDocumentCheckOuts` WHERE `document` = " . (int) $this->_id; |
1240 | $resArr = $db->getResultArray($queryStr); |
1241 | if ((is_bool($resArr) && $resArr==false) || (count($resArr)==0)) { |
1242 | // Could not find a check out for the selected document. |
1243 | return false; |
1244 | } else { |
1245 | // A check out has been identified for this document. |
1246 | return $resArr; |
1247 | } |
1248 | } /* }}} */ |
1249 | |
1250 | |
1251 | /** |
1252 | * Check out document |
1253 | * |
1254 | * Creates a check out record for the document and copies the latest |
1255 | * version of the document into the given checkout dir. |
1256 | * |
1257 | * @param object $user object of user doing the checkout |
1258 | * @param string $checkoutdir directory where the file will be placed |
1259 | * @return object object of class SeedDMS_Core_DocumentCheckOut |
1260 | */ |
1261 | function checkOut($user, $checkoutdir) { /* {{{ */ |
1262 | $db = $this->_dms->getDB(); |
1263 | |
1264 | if(self::isCheckedOut()) |
1265 | return false; |
1266 | |
1267 | /* Check if checkout dir is writable */ |
1268 | if(!file_exists($checkoutdir)) { |
1269 | return false; |
1270 | } |
1271 | |
1272 | $db->startTransaction(); |
1273 | |
1274 | $lc = self::getLatestContent(); |
1275 | |
1276 | $ext = pathinfo($this->getName(), PATHINFO_EXTENSION); |
1277 | $oext = pathinfo($lc->getOriginalFileName(), PATHINFO_EXTENSION); |
1278 | if($ext == $oext) |
1279 | $filename = preg_replace('/[^A-Za-z0-9_.-]/', '_', $this->getName()); |
1280 | else { |
1281 | $filename = preg_replace('/[^A-Za-z0-9_-]/', '_', $this->getName()).'.'.$oext; |
1282 | } |
1283 | $filename = $checkoutdir.$this->getID().'-'.$lc->getVersion().'-'.$filename; //$lc->getOriginalFileName(); |
1284 | $queryStr = "INSERT INTO `tblDocumentCheckOuts` (`document`, `version`, `userID`, `date`, `filename`) VALUES (".$this->_id.", ".$lc->getVersion().", ".$user->getID().", ".$db->getCurrentDatetime().", ".$db->qstr($filename).")"; |
1285 | if (!$db->getResult($queryStr)) |
1286 | return false; |
1287 | |
1288 | /* Try to copy the file */ |
1289 | $err = SeedDMS_Core_File::copyFile($this->_dms->contentDir . $this->getDir() . $lc->getFileName(), $filename); |
1290 | if (!$err) { |
1291 | $db->rollbackTransaction(); |
1292 | return false; |
1293 | } |
1294 | |
1295 | $db->commitTransaction(); |
1296 | return true; |
1297 | } /* }}} */ |
1298 | |
1299 | /** |
1300 | * Check in document |
1301 | * |
1302 | * Τhis function is similar to SeedDMS_Core_Document::addContent() |
1303 | * but reads the content from the file which was previously checked out. |
1304 | * Internal this method calls |
1305 | * SeedDMS_Core_Document::addContent() but takes over the original |
1306 | * filename, filetype and mimetype from the checked out version. |
1307 | * No matter in which state the current checked out file is, the |
1308 | * document will be checked back in afterwards. |
1309 | * |
1310 | * There are various reason why a check in may fail. In those cases |
1311 | * this method will return false, but if the checked out document has |
1312 | * disappeared, the checkout will be ended and the method returns true |
1313 | * without creating a new version. |
1314 | * |
1315 | * The check in may not be done by the user who has done the check out, |
1316 | * but if it is a different user, this user must have unlimited access |
1317 | * on the document. |
1318 | * |
1319 | * @param string $comment |
1320 | * @param object $user |
1321 | * @param array $reviewers |
1322 | * @param array $approvers |
1323 | * @param integer $version |
1324 | * @param array $attributes |
1325 | * @param object $workflow |
1326 | * @param integer $initstate intial document status |
1327 | * @return boolean|object false in case of error, true if no error occurs but |
1328 | * the document remains unchanged (because the checked out file has not |
1329 | * changed or it has disappeared and couldnt't be checked in), or |
1330 | * an instance of class SeedDMS_Core_AddContentResultSet if the document |
1331 | * was updated. |
1332 | */ |
1333 | function checkIn($comment, $user, $reviewers=array(), $approvers=array(), $version=0, $attributes=array(), $workflow=null, $initstate=S_RELEASED) { /* {{{ */ |
1334 | $db = $this->_dms->getDB(); |
1335 | |
1336 | $infos = self::getCheckOutInfo(); |
1337 | if(!$infos) |
1338 | return false; |
1339 | $info = $infos[0]; |
1340 | $lc = self::getLatestContent(); |
1341 | |
1342 | /* If file doesn't exist anymore, then just remove the record from the db */ |
1343 | if(!file_exists($info['filename'])) { |
1344 | $queryStr = "DELETE FROM `tblDocumentCheckOuts` WHERE `document` = ".$this->_id; |
1345 | $db->getResult($queryStr); |
1346 | return true; |
1347 | } |
1348 | |
1349 | /* Check if version of checked out file is equal to current version */ |
1350 | if($lc->getVersion() != $info['version']) { |
1351 | return false; |
1352 | } |
1353 | |
1354 | /* Check if the user doing the check in is the same use as the one |
1355 | * have done the check out or at least have unlimited rights on the |
1356 | * document. |
1357 | */ |
1358 | if($user->getID() != $info['userID'] && $this->getAccessMode($user) < M_ALL) { |
1359 | return false; |
1360 | } |
1361 | |
1362 | $content = true; |
1363 | /* Do not create a new version if the file was unchanged */ |
1364 | $checksum = SeedDMS_Core_File::checksum($info['filename']); |
1365 | if($checksum != $lc->getChecksum()) { |
1366 | $content = $this->addContent($comment, $user, $info['filename'], $lc->getOriginalFileName(), $lc->getFileType(), $lc->getMimeType(), $reviewers, $approvers, $version, $attributes, $workflow, $initstate); |
1367 | if($content) { |
1368 | if(!$this->_dms->forceRename) { |
1369 | SeedDMS_Core_File::removeFile($info['filename']); |
1370 | } |
1371 | $queryStr = "DELETE FROM `tblDocumentCheckOuts` WHERE `document` = ".$this->_id; |
1372 | $db->getResult($queryStr); |
1373 | return $content; |
1374 | } else { |
1375 | return false; |
1376 | } |
1377 | } else { |
1378 | SeedDMS_Core_File::removeFile($info['filename']); |
1379 | $queryStr = "DELETE FROM `tblDocumentCheckOuts` WHERE `document` = ".$this->_id; |
1380 | $db->getResult($queryStr); |
1381 | return true; |
1382 | } |
1383 | } /* }}} */ |
1384 | |
1385 | /** |
1386 | * Cancel check out of document |
1387 | * |
1388 | * This function will cancel a check out in progress by removing |
1389 | * the check out record from the database and removing the file |
1390 | * from the check out folder. |
1391 | * |
1392 | * @return boolean true if cancelation was successful |
1393 | */ |
1394 | function cancelCheckOut() { /* {{{ */ |
1395 | $db = $this->_dms->getDB(); |
1396 | |
1397 | $infos = self::getCheckOutInfo(); |
1398 | if($infos) { |
1399 | $info = $infos[0]; |
1400 | |
1401 | $db->startTransaction(); |
1402 | $queryStr = "DELETE FROM `tblDocumentCheckOuts` WHERE `document` = ".$this->_id; |
1403 | if (!$db->getResult($queryStr)) { |
1404 | $db->rollbackTransaction(); |
1405 | return false; |
1406 | } |
1407 | if(file_exists($info['filename']) && !SeedDMS_Core_File::removeFile($info['filename'])) { |
1408 | $db->rollbackTransaction(); |
1409 | return false; |
1410 | } |
1411 | $db->commitTransaction(); |
1412 | } |
1413 | |
1414 | return true; |
1415 | |
1416 | } /* }}} */ |
1417 | |
1418 | /** |
1419 | * Return the check out status of the document |
1420 | * |
1421 | * This method returns the checkout status of a previosly checked out |
1422 | * document. |
1423 | * |
1424 | * @return int 1=The checked out file doesn't exists anymore, |
1425 | * 2=The checked out version doesn't exists anymore |
1426 | * 3=The checked out file has not been modified yet |
1427 | * 4=new check out record in database found |
1428 | * 0=The checked out file is modified and check in will create a new version |
1429 | */ |
1430 | function checkOutStatus() { /* {{{ */ |
1431 | $infos = self::getCheckOutInfo(); |
1432 | if(!$infos) |
1433 | return 4; |
1434 | |
1435 | $info = $infos[0]; |
1436 | $lc = self::getLatestContent(); |
1437 | |
1438 | /* If file doesn't exist anymore, then just remove the record from the db */ |
1439 | if(!file_exists($info['filename'])) { |
1440 | return 1; |
1441 | } |
1442 | |
1443 | /* Check if version of checked out file is equal to current version */ |
1444 | if($lc->getVersion() != $info['version']) { |
1445 | return 2; |
1446 | } |
1447 | |
1448 | $checksum = SeedDMS_Core_File::checksum($info['filename']); |
1449 | if($checksum == $lc->getChecksum()) { |
1450 | return 3; |
1451 | } |
1452 | |
1453 | return 0; |
1454 | } /* }}} */ |
1455 | |
1456 | /** |
1457 | * @return float |
1458 | */ |
1459 | function getSequence() { return $this->_sequence; } |
1460 | |
1461 | /** |
1462 | * @param float $seq |
1463 | * @return bool |
1464 | */ |
1465 | function setSequence($seq) { /* {{{ */ |
1466 | $db = $this->_dms->getDB(); |
1467 | |
1468 | $queryStr = "UPDATE `tblDocuments` SET `sequence` = " . $seq . " WHERE `id` = " . $this->_id; |
1469 | if (!$db->getResult($queryStr)) |
1470 | return false; |
1471 | |
1472 | $this->_sequence = $seq; |
1473 | return true; |
1474 | } /* }}} */ |
1475 | |
1476 | /** |
1477 | * Delete all entries for this document from the access control list |
1478 | * |
1479 | * @param boolean $noclean set to true if notifier list shall not be clean up |
1480 | * @return boolean true if operation was successful otherwise false |
1481 | */ |
1482 | function clearAccessList($noclean=false) { /* {{{ */ |
1483 | $db = $this->_dms->getDB(); |
1484 | |
1485 | $queryStr = "DELETE FROM `tblACLs` WHERE `targetType` = " . T_DOCUMENT . " AND `target` = " . $this->_id; |
1486 | if (!$db->getResult($queryStr)) |
1487 | return false; |
1488 | |
1489 | unset($this->_accessList); |
1490 | |
1491 | if(!$noclean) |
1492 | $this->cleanNotifyList(); |
1493 | |
1494 | return true; |
1495 | } /* }}} */ |
1496 | |
1497 | /** |
1498 | * Returns a list of access privileges |
1499 | * |
1500 | * If the document inherits the access privileges from the parent folder |
1501 | * those will be returned. |
1502 | * $mode and $op can be set to restrict the list of returned access |
1503 | * privileges. If $mode is set to M_ANY no restriction will apply |
1504 | * regardless of the value of $op. The returned array contains a list |
1505 | * of {@link SeedDMS_Core_UserAccess} and |
1506 | * {@link SeedDMS_Core_GroupAccess} objects. Even if the document |
1507 | * has no access list the returned array contains the two elements |
1508 | * 'users' and 'groups' which are than empty. The methode returns false |
1509 | * if the function fails. |
1510 | * |
1511 | * @param int $mode access mode (defaults to M_ANY) |
1512 | * @param int|string $op operation (defaults to O_EQ) |
1513 | * @return bool|array |
1514 | */ |
1515 | function getAccessList($mode = M_ANY, $op = O_EQ) { /* {{{ */ |
1516 | $db = $this->_dms->getDB(); |
1517 | |
1518 | if ($this->inheritsAccess()) { |
1519 | $res = $this->getFolder(); |
1520 | if (!$res) return false; |
1521 | $pacl = $res->getAccessList($mode, $op); |
1522 | return $pacl; |
1523 | } else { |
1524 | $pacl = array("groups" => array(), "users" => array()); |
1525 | } |
1526 | |
1527 | if (!isset($this->_accessList[$mode])) { |
1528 | if ($op!=O_GTEQ && $op!=O_LTEQ && $op!=O_EQ) { |
1529 | return false; |
1530 | } |
1531 | $modeStr = ""; |
1532 | if ($mode!=M_ANY) { |
1533 | $modeStr = " AND `mode`".$op.(int)$mode; |
1534 | } |
1535 | $queryStr = "SELECT * FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT. |
1536 | " AND `target` = " . $this->_id . $modeStr . " ORDER BY `targetType`"; |
1537 | $resArr = $db->getResultArray($queryStr); |
1538 | if (is_bool($resArr) && !$resArr) |
1539 | return false; |
1540 | |
1541 | $this->_accessList[$mode] = array("groups" => array(), "users" => array()); |
1542 | foreach ($resArr as $row) { |
1543 | if ($row["userID"] != -1) |
1544 | array_push($this->_accessList[$mode]["users"], new SeedDMS_Core_UserAccess($this->_dms->getUser($row["userID"]), (int) $row["mode"])); |
1545 | else //if ($row["groupID"] != -1) |
1546 | array_push($this->_accessList[$mode]["groups"], new SeedDMS_Core_GroupAccess($this->_dms->getGroup($row["groupID"]), (int) $row["mode"])); |
1547 | } |
1548 | } |
1549 | |
1550 | return $this->_accessList[$mode]; |
1551 | return SeedDMS_Core_DMS::mergeAccessLists($pacl, $this->_accessList[$mode]); |
1552 | } /* }}} */ |
1553 | |
1554 | /** |
1555 | * Add access right to folder |
1556 | * This function may change in the future. Instead of passing a flag |
1557 | * and a user/group id a user or group object will be expected. |
1558 | * Starting with version 5.1.25 this method will first check if there |
1559 | * is already an access right for the user/group. |
1560 | * |
1561 | * @param integer $mode access mode |
1562 | * @param integer $userOrGroupID id of user or group |
1563 | * @param integer $isUser set to 1 if $userOrGroupID is the id of a |
1564 | * user |
1565 | * @return bool |
1566 | */ |
1567 | function addAccess($mode, $userOrGroupID, $isUser) { /* {{{ */ |
1568 | $db = $this->_dms->getDB(); |
1569 | |
1570 | if($mode < M_NONE || $mode > M_ALL) |
1571 | return false; |
1572 | |
1573 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`"; |
1574 | |
1575 | /* Adding a second access right will return false */ |
1576 | $queryStr = "SELECT * FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT. |
1577 | " AND `target` = " . $this->_id . " AND ". $userOrGroup . " = ".$userOrGroupID; |
1578 | $resArr = $db->getResultArray($queryStr); |
1579 | if (is_bool($resArr) || $resArr) |
1580 | return false; |
1581 | |
1582 | $queryStr = "INSERT INTO `tblACLs` (`target`, `targetType`, ".$userOrGroup.", `mode`) VALUES |
1583 | (".$this->_id.", ".T_DOCUMENT.", " . (int) $userOrGroupID . ", " .(int) $mode. ")"; |
1584 | if (!$db->getResult($queryStr)) |
1585 | return false; |
1586 | |
1587 | unset($this->_accessList); |
1588 | |
1589 | // Update the notify list, if necessary. |
1590 | if ($mode == M_NONE) { |
1591 | $this->removeNotify($userOrGroupID, $isUser); |
1592 | } |
1593 | |
1594 | return true; |
1595 | } /* }}} */ |
1596 | |
1597 | /** |
1598 | * Change access right of document |
1599 | * This function may change in the future. Instead of passing the a flag |
1600 | * and a user/group id a user or group object will be expected. |
1601 | * |
1602 | * @param integer $newMode access mode |
1603 | * @param integer $userOrGroupID id of user or group |
1604 | * @param integer $isUser set to 1 if $userOrGroupID is the id of a |
1605 | * user |
1606 | * @return bool |
1607 | */ |
1608 | function changeAccess($newMode, $userOrGroupID, $isUser) { /* {{{ */ |
1609 | $db = $this->_dms->getDB(); |
1610 | |
1611 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`"; |
1612 | |
1613 | /* Get the old access right */ |
1614 | $queryStr = "SELECT * FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT. |
1615 | " AND `target` = " . $this->_id . " AND ". $userOrGroup . " = ". (int) $userOrGroupID; |
1616 | $resArr = $db->getResultArray($queryStr); |
1617 | if (is_bool($resArr) && $resArr == false) |
1618 | return false; |
1619 | |
1620 | $oldmode = $resArr[0]['mode']; |
1621 | |
1622 | $queryStr = "UPDATE `tblACLs` SET `mode` = " . (int) $newMode . " WHERE `targetType` = ".T_DOCUMENT." AND `target` = " . $this->_id . " AND " . $userOrGroup . " = " . (int) $userOrGroupID; |
1623 | if (!$db->getResult($queryStr)) |
1624 | return false; |
1625 | |
1626 | unset($this->_accessList); |
1627 | |
1628 | // Update the notify list, if necessary. |
1629 | if ($newMode == M_NONE) { |
1630 | $this->removeNotify($userOrGroupID, $isUser); |
1631 | } |
1632 | |
1633 | return $oldmode; |
1634 | } /* }}} */ |
1635 | |
1636 | /** |
1637 | * Remove access rights for a user or group |
1638 | * |
1639 | * @param integer $userOrGroupID ID of user or group |
1640 | * @param boolean $isUser true if $userOrGroupID is a user id, false if it |
1641 | * is a group id. |
1642 | * @return boolean true on success, otherwise false |
1643 | */ |
1644 | function removeAccess($userOrGroupID, $isUser) { /* {{{ */ |
1645 | $db = $this->_dms->getDB(); |
1646 | |
1647 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`"; |
1648 | |
1649 | $queryStr = "DELETE FROM `tblACLs` WHERE `targetType` = ".T_DOCUMENT." AND `target` = ".$this->_id." AND ".$userOrGroup." = " . (int) $userOrGroupID; |
1650 | if (!$db->getResult($queryStr)) |
1651 | return false; |
1652 | |
1653 | unset($this->_accessList); |
1654 | |
1655 | // Update the notify list, if the user looses access rights. |
1656 | $mode = ($isUser ? $this->getAccessMode($this->_dms->getUser($userOrGroupID)) : $this->getGroupAccessMode($this->_dms->getGroup($userOrGroupID))); |
1657 | if ($mode == M_NONE) { |
1658 | $this->removeNotify($userOrGroupID, $isUser); |
1659 | } |
1660 | |
1661 | return true; |
1662 | } /* }}} */ |
1663 | |
1664 | /** |
1665 | * Returns the greatest access privilege for a given user |
1666 | * |
1667 | * This function returns the access mode for a given user. An administrator |
1668 | * and the owner of the folder has unrestricted access. A guest user has |
1669 | * read only access or no access if access rights are further limited |
1670 | * by access control lists. All other users have access rights according |
1671 | * to the access control lists or the default access. This function will |
1672 | * recursive check for access rights of parent folders if access rights |
1673 | * are inherited. |
1674 | * |
1675 | * The function searches the access control list for entries of |
1676 | * user $user. If it finds more than one entry it will return the |
1677 | * one allowing the greatest privileges, but user rights will always |
1678 | * precede group rights. If there is no entry in the |
1679 | * access control list, it will return the default access mode. |
1680 | * The function takes inherited access rights into account. |
1681 | * For a list of possible access rights see @file inc.AccessUtils.php |
1682 | * |
1683 | * Having access on a document does not necessarily mean the document |
1684 | * content is accessible too. Accessing the content is checked by |
1685 | * {@link SeedDMS_Core_DocumentContent::getAccessMode()} which calls |
1686 | * a callback function defined by the application. If the callback |
1687 | * function is not set, access on the content is always granted. |
1688 | * |
1689 | * Before checking the access in the method itself a callback 'onCheckAccessDocument' |
1690 | * is called. If it returns a value > 0, then this will be returned by this |
1691 | * method without any further checks. The optional paramater $context |
1692 | * will be passed as a third parameter to the callback. It contains |
1693 | * the operation for which the access mode is retrieved. It is for example |
1694 | * set to 'removeDocument' if the access mode is used to check for sufficient |
1695 | * permission on deleting a document. |
1696 | * |
1697 | * @param $user object instance of class SeedDMS_Core_User |
1698 | * @param string $context context in which the access mode is requested |
1699 | * @return integer access mode |
1700 | */ |
1701 | function getAccessMode($user, $context='') { /* {{{ */ |
1702 | if(!$user) |
1703 | return M_NONE; |
1704 | |
1705 | /* Check if 'onCheckAccessDocument' callback is set */ |
1706 | if(isset($this->_dms->callbacks['onCheckAccessDocument'])) { |
1707 | foreach($this->_dms->callbacks['onCheckAccessDocument'] as $callback) { |
1708 | if(($ret = call_user_func($callback[0], $callback[1], $this, $user, $context)) > 0) { |
1709 | return $ret; |
1710 | } |
1711 | } |
1712 | } |
1713 | |
1714 | /* Administrators have unrestricted access */ |
1715 | if ($user->isAdmin()) return M_ALL; |
1716 | |
1717 | /* The owner of the document has unrestricted access */ |
1718 | if ($user->getID() == $this->_ownerID) return M_ALL; |
1719 | |
1720 | /* Check ACLs */ |
1721 | $accessList = $this->getAccessList(); |
1722 | if (!$accessList) return false; |
1723 | |
1724 | /** @var SeedDMS_Core_UserAccess $userAccess */ |
1725 | foreach ($accessList["users"] as $userAccess) { |
1726 | if ($userAccess->getUserID() == $user->getID()) { |
1727 | $mode = $userAccess->getMode(); |
1728 | if ($user->isGuest()) { |
1729 | if ($mode >= M_READ) $mode = M_READ; |
1730 | } |
1731 | return $mode; |
1732 | } |
1733 | } |
1734 | |
1735 | /* Get the highest right defined by a group */ |
1736 | if($accessList['groups']) { |
1737 | $mode = 0; |
1738 | /** @var SeedDMS_Core_GroupAccess $groupAccess */ |
1739 | foreach ($accessList["groups"] as $groupAccess) { |
1740 | if ($user->isMemberOfGroup($groupAccess->getGroup())) { |
1741 | if ($groupAccess->getMode() > $mode) |
1742 | $mode = $groupAccess->getMode(); |
1743 | } |
1744 | } |
1745 | if($mode) { |
1746 | if ($user->isGuest()) { |
1747 | if ($mode >= M_READ) $mode = M_READ; |
1748 | } |
1749 | return $mode; |
1750 | } |
1751 | } |
1752 | |
1753 | $mode = $this->getDefaultAccess(); |
1754 | if ($user->isGuest()) { |
1755 | if ($mode >= M_READ) $mode = M_READ; |
1756 | } |
1757 | return $mode; |
1758 | } /* }}} */ |
1759 | |
1760 | /** |
1761 | * Returns the greatest access privilege for a given group |
1762 | * |
1763 | * This function searches the access control list for entries of |
1764 | * group $group. If it finds more than one entry it will return the |
1765 | * one allowing the greatest privileges. If there is no entry in the |
1766 | * access control list, it will return the default access mode. |
1767 | * The function takes inherited access rights into account. |
1768 | * For a list of possible access rights see @file inc.AccessUtils.php |
1769 | * |
1770 | * @param SeedDMS_Core_Group $group object instance of class SeedDMS_Core_Group |
1771 | * @return integer access mode |
1772 | */ |
1773 | function getGroupAccessMode($group) { /* {{{ */ |
1774 | $highestPrivileged = M_NONE; |
1775 | |
1776 | //ACLs durchforsten |
1777 | $foundInACL = false; |
1778 | $accessList = $this->getAccessList(); |
1779 | if (!$accessList) |
1780 | return false; |
1781 | |
1782 | /** @var SeedDMS_Core_GroupAccess $groupAccess */ |
1783 | foreach ($accessList["groups"] as $groupAccess) { |
1784 | if ($groupAccess->getGroupID() == $group->getID()) { |
1785 | $foundInACL = true; |
1786 | if ($groupAccess->getMode() > $highestPrivileged) |
1787 | $highestPrivileged = $groupAccess->getMode(); |
1788 | if ($highestPrivileged == M_ALL) // max access right -> skip the rest |
1789 | return $highestPrivileged; |
1790 | } |
1791 | } |
1792 | |
1793 | if ($foundInACL) |
1794 | return $highestPrivileged; |
1795 | |
1796 | //Standard-Berechtigung verwenden |
1797 | return $this->getDefaultAccess(); |
1798 | } /* }}} */ |
1799 | |
1800 | /** |
1801 | * Returns a list of all notifications |
1802 | * |
1803 | * The returned list has two elements called 'users' and 'groups'. Each one |
1804 | * is an array itself countaining objects of class SeedDMS_Core_User and |
1805 | * SeedDMS_Core_Group. |
1806 | * |
1807 | * @param integer $type type of notification (not yet used) |
1808 | * @param bool $incdisabled set to true if disabled user shall be included |
1809 | * @return array|bool |
1810 | */ |
1811 | function getNotifyList($type=0, $incdisabled=false) { /* {{{ */ |
1812 | if (empty($this->_notifyList)) { |
1813 | $db = $this->_dms->getDB(); |
1814 | |
1815 | $queryStr ="SELECT * FROM `tblNotify` WHERE `targetType` = " . T_DOCUMENT . " AND `target` = " . $this->_id; |
1816 | $resArr = $db->getResultArray($queryStr); |
1817 | if (is_bool($resArr) && $resArr == false) |
1818 | return false; |
1819 | |
1820 | $this->_notifyList = array("groups" => array(), "users" => array()); |
1821 | foreach ($resArr as $row) |
1822 | { |
1823 | if ($row["userID"] != -1) { |
1824 | $u = $this->_dms->getUser($row["userID"]); |
1825 | if($u && (!$u->isDisabled() || $incdisabled)) |
1826 | array_push($this->_notifyList["users"], $u); |
1827 | } else { //if ($row["groupID"] != -1) |
1828 | $g = $this->_dms->getGroup($row["groupID"]); |
1829 | if($g) |
1830 | array_push($this->_notifyList["groups"], $g); |
1831 | } |
1832 | } |
1833 | } |
1834 | return $this->_notifyList; |
1835 | } /* }}} */ |
1836 | |
1837 | /** |
1838 | * Make sure only users/groups with read access are in the notify list |
1839 | * |
1840 | */ |
1841 | function cleanNotifyList() { /* {{{ */ |
1842 | // If any of the notification subscribers no longer have read access, |
1843 | // remove their subscription. |
1844 | if (empty($this->_notifyList)) |
1845 | $this->getNotifyList(); |
1846 | |
1847 | /* Make a copy of both notifier lists because removeNotify will empty |
1848 | * $this->_notifyList and the second foreach will not work anymore. |
1849 | */ |
1850 | /** @var SeedDMS_Core_User[] $nusers */ |
1851 | $nusers = $this->_notifyList["users"]; |
1852 | /** @var SeedDMS_Core_Group[] $ngroups */ |
1853 | $ngroups = $this->_notifyList["groups"]; |
1854 | foreach ($nusers as $u) { |
1855 | if ($this->getAccessMode($u) < M_READ) { |
1856 | $this->removeNotify($u->getID(), true); |
1857 | } |
1858 | } |
1859 | foreach ($ngroups as $g) { |
1860 | if ($this->getGroupAccessMode($g) < M_READ) { |
1861 | $this->removeNotify($g->getID(), false); |
1862 | } |
1863 | } |
1864 | } /* }}} */ |
1865 | |
1866 | /** |
1867 | * Add a user/group to the notification list |
1868 | * This function does not check if the currently logged in user |
1869 | * is allowed to add a notification. This must be checked by the calling |
1870 | * application. |
1871 | * |
1872 | * @param $userOrGroupID integer id of user or group to add |
1873 | * @param $isUser integer 1 if $userOrGroupID is a user, |
1874 | * 0 if $userOrGroupID is a group |
1875 | * @return integer 0: Update successful. |
1876 | * -1: Invalid User/Group ID. |
1877 | * -2: Target User / Group does not have read access. |
1878 | * -3: User is already subscribed. |
1879 | * -4: Database / internal error. |
1880 | */ |
1881 | function addNotify($userOrGroupID, $isUser) { /* {{{ */ |
1882 | $db = $this->_dms->getDB(); |
1883 | |
1884 | $userOrGroup = ($isUser ? "`userID`" : "`groupID`"); |
1885 | |
1886 | /* Verify that user / group exists. */ |
1887 | $obj = ($isUser ? $this->_dms->getUser($userOrGroupID) : $this->_dms->getGroup($userOrGroupID)); |
1888 | if (!is_object($obj)) { |
1889 | return -1; |
1890 | } |
1891 | |
1892 | /* Verify that the requesting user has permission to add the target to |
1893 | * the notification system. |
1894 | */ |
1895 | /* |
1896 | * The calling application should enforce the policy on who is allowed |
1897 | * to add someone to the notification system. If is shall remain here |
1898 | * the currently logged in user should be passed to this function |
1899 | * |
1900 | GLOBAL $user; |
1901 | if ($user->isGuest()) { |
1902 | return -2; |
1903 | } |
1904 | if (!$user->isAdmin()) { |
1905 | if ($isUser) { |
1906 | if ($user->getID() != $obj->getID()) { |
1907 | return -2; |
1908 | } |
1909 | } |
1910 | else { |
1911 | if (!$obj->isMember($user)) { |
1912 | return -2; |
1913 | } |
1914 | } |
1915 | } |
1916 | */ |
1917 | |
1918 | /* Verify that target user / group has read access to the document. */ |
1919 | if ($isUser) { |
1920 | // Users are straightforward to check. |
1921 | if ($this->getAccessMode($obj) < M_READ) { |
1922 | return -2; |
1923 | } |
1924 | } |
1925 | else { |
1926 | // Groups are a little more complex. |
1927 | if ($this->getDefaultAccess() >= M_READ) { |
1928 | // If the default access is at least READ-ONLY, then just make sure |
1929 | // that the current group has not been explicitly excluded. |
1930 | $acl = $this->getAccessList(M_NONE, O_EQ); |
1931 | $found = false; |
1932 | /** @var SeedDMS_Core_GroupAccess $group */ |
1933 | foreach ($acl["groups"] as $group) { |
1934 | if ($group->getGroupID() == $userOrGroupID) { |
1935 | $found = true; |
1936 | break; |
1937 | } |
1938 | } |
1939 | if ($found) { |
1940 | return -2; |
1941 | } |
1942 | } |
1943 | else { |
1944 | // The default access is restricted. Make sure that the group has |
1945 | // been explicitly allocated access to the document. |
1946 | $acl = $this->getAccessList(M_READ, O_GTEQ); |
1947 | if (is_bool($acl)) { |
1948 | return -4; |
1949 | } |
1950 | $found = false; |
1951 | /** @var SeedDMS_Core_GroupAccess $group */ |
1952 | foreach ($acl["groups"] as $group) { |
1953 | if ($group->getGroupID() == $userOrGroupID) { |
1954 | $found = true; |
1955 | break; |
1956 | } |
1957 | } |
1958 | if (!$found) { |
1959 | return -2; |
1960 | } |
1961 | } |
1962 | } |
1963 | /* Check to see if user/group is already on the list. */ |
1964 | $queryStr = "SELECT * FROM `tblNotify` WHERE `tblNotify`.`target` = '".$this->_id."' ". |
1965 | "AND `tblNotify`.`targetType` = '".T_DOCUMENT."' ". |
1966 | "AND `tblNotify`.".$userOrGroup." = '".(int) $userOrGroupID."'"; |
1967 | $resArr = $db->getResultArray($queryStr); |
1968 | if (is_bool($resArr)) { |
1969 | return -4; |
1970 | } |
1971 | if (count($resArr)>0) { |
1972 | return -3; |
1973 | } |
1974 | |
1975 | $queryStr = "INSERT INTO `tblNotify` (`target`, `targetType`, " . $userOrGroup . ") VALUES (" . $this->_id . ", " . T_DOCUMENT . ", " . (int) $userOrGroupID . ")"; |
1976 | if (!$db->getResult($queryStr)) |
1977 | return -4; |
1978 | |
1979 | unset($this->_notifyList); |
1980 | return 0; |
1981 | } /* }}} */ |
1982 | |
1983 | /** |
1984 | * Remove a user or group from the notification list |
1985 | * This function does not check if the currently logged in user |
1986 | * is allowed to remove a notification. This must be checked by the calling |
1987 | * application. |
1988 | * |
1989 | * @param integer $userOrGroupID id of user or group |
1990 | * @param boolean $isUser boolean true if a user is passed in $userOrGroupID, false |
1991 | * if a group is passed in $userOrGroupID |
1992 | * @param integer $type type of notification (0 will delete all) Not used yet! |
1993 | * @return integer 0 if operation was succesful |
1994 | * -1 if the userid/groupid is invalid |
1995 | * -3 if the user/group is already subscribed |
1996 | * -4 in case of an internal database error |
1997 | */ |
1998 | function removeNotify($userOrGroupID, $isUser, $type=0) { /* {{{ */ |
1999 | $db = $this->_dms->getDB(); |
2000 | |
2001 | /* Verify that user / group exists. */ |
2002 | /** @var SeedDMS_Core_Group|SeedDMS_Core_User $obj */ |
2003 | $obj = ($isUser ? $this->_dms->getUser($userOrGroupID) : $this->_dms->getGroup($userOrGroupID)); |
2004 | if (!is_object($obj)) { |
2005 | return -1; |
2006 | } |
2007 | |
2008 | $userOrGroup = ($isUser) ? "`userID`" : "`groupID`"; |
2009 | |
2010 | /* Verify that the requesting user has permission to add the target to |
2011 | * the notification system. |
2012 | */ |
2013 | /* |
2014 | * The calling application should enforce the policy on who is allowed |
2015 | * to add someone to the notification system. If is shall remain here |
2016 | * the currently logged in user should be passed to this function |
2017 | * |
2018 | GLOBAL $user; |
2019 | if ($user->isGuest()) { |
2020 | return -2; |
2021 | } |
2022 | if (!$user->isAdmin()) { |
2023 | if ($isUser) { |
2024 | if ($user->getID() != $obj->getID()) { |
2025 | return -2; |
2026 | } |
2027 | } |
2028 | else { |
2029 | if (!$obj->isMember($user)) { |
2030 | return -2; |
2031 | } |
2032 | } |
2033 | } |
2034 | */ |
2035 | |
2036 | /* Check to see if the target is in the database. */ |
2037 | $queryStr = "SELECT * FROM `tblNotify` WHERE `tblNotify`.`target` = '".$this->_id."' ". |
2038 | "AND `tblNotify`.`targetType` = '".T_DOCUMENT."' ". |
2039 | "AND `tblNotify`.".$userOrGroup." = '".(int) $userOrGroupID."'"; |
2040 | $resArr = $db->getResultArray($queryStr); |
2041 | if (is_bool($resArr)) { |
2042 | return -4; |
2043 | } |
2044 | if (count($resArr)==0) { |
2045 | return -3; |
2046 | } |
2047 | |
2048 | $queryStr = "DELETE FROM `tblNotify` WHERE `target` = " . $this->_id . " AND `targetType` = " . T_DOCUMENT . " AND " . $userOrGroup . " = " . (int) $userOrGroupID; |
2049 | /* If type is given then delete only those notifications */ |
2050 | if($type) |
2051 | $queryStr .= " AND `type` = ".(int) $type; |
2052 | if (!$db->getResult($queryStr)) |
2053 | return -4; |
2054 | |
2055 | unset($this->_notifyList); |
2056 | return 0; |
2057 | } /* }}} */ |
2058 | |
2059 | /** |
2060 | * Add content to a document |
2061 | * |
2062 | * Each document may have any number of content elements attached to it. |
2063 | * Each content element has a version number. Newer versions (greater |
2064 | * version number) replace older versions. |
2065 | * |
2066 | * @param string $comment comment |
2067 | * @param object $user user who shall be the owner of this content |
2068 | * @param string $tmpFile file containing the actuall content |
2069 | * @param string $orgFileName original file name |
2070 | * @param string $fileType |
2071 | * @param string $mimeType MimeType of the content |
2072 | * @param array $reviewers list of reviewers |
2073 | * @param array $approvers list of approvers |
2074 | * @param integer $version version number of content or 0 if next higher version shall be used. |
2075 | * @param array $attributes list of version attributes. The element key |
2076 | * must be the id of the attribute definition. |
2077 | * @param object $workflow |
2078 | * @param integer $initstate intial document status |
2079 | * @return bool|SeedDMS_Core_AddContentResultSet |
2080 | */ |
2081 | function addContent($comment, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $reviewers=array(), $approvers=array(), $version=0, $attributes=array(), $workflow=null, $initstate=S_RELEASED) { /* {{{ */ |
2082 | $db = $this->_dms->getDB(); |
2083 | |
2084 | // the doc path is id/version.filetype |
2085 | $dir = $this->getDir(); |
2086 | |
2087 | /* The version field in table tblDocumentContent used to be auto |
2088 | * increment but that requires the field to be primary as well if |
2089 | * innodb is used. That's why the version is now determined here. |
2090 | */ |
2091 | if ((int)$version<1) { |
2092 | $queryStr = "SELECT MAX(`version`) AS m FROM `tblDocumentContent` WHERE `document` = ".$this->_id; |
2093 | $resArr = $db->getResultArray($queryStr); |
2094 | if (is_bool($resArr) && !$resArr) |
2095 | return false; |
2096 | |
2097 | $version = $resArr[0]['m']+1; |
2098 | } |
2099 | |
2100 | if($fileType == '.') |
2101 | $fileType = ''; |
2102 | $filesize = SeedDMS_Core_File::fileSize($tmpFile); |
2103 | $checksum = SeedDMS_Core_File::checksum($tmpFile); |
2104 | |
2105 | $db->startTransaction(); |
2106 | $queryStr = "INSERT INTO `tblDocumentContent` (`document`, `version`, `comment`, `date`, `createdBy`, `dir`, `orgFileName`, `fileType`, `mimeType`, `fileSize`, `checksum`) VALUES ". |
2107 | "(".$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).")"; |
2108 | if (!$db->getResult($queryStr)) { |
2109 | $db->rollbackTransaction(); |
2110 | return false; |
2111 | } |
2112 | |
2113 | $contentID = $db->getInsertID('tblDocumentContent'); |
2114 | |
2115 | // copy file |
2116 | if (!SeedDMS_Core_File::makeDir($this->_dms->contentDir . $dir)) { |
2117 | $db->rollbackTransaction(); |
2118 | return false; |
2119 | } |
2120 | if($this->_dms->forceRename) |
2121 | $err = SeedDMS_Core_File::renameFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType); |
2122 | elseif($this->_dms->forceLink) |
2123 | $err = SeedDMS_Core_File::linkFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType); |
2124 | else |
2125 | $err = SeedDMS_Core_File::copyFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType); |
2126 | if (!$err) { |
2127 | $db->rollbackTransaction(); |
2128 | return false; |
2129 | } |
2130 | |
2131 | $this->_content = null; |
2132 | $this->_latestContent = null; |
2133 | $content = $this->getLatestContent($contentID); /** @todo: Parameter not defined in Funktion */ |
2134 | $docResultSet = new SeedDMS_Core_AddContentResultSet($content); |
2135 | $docResultSet->setDMS($this->_dms); |
2136 | |
2137 | if($attributes) { |
2138 | foreach($attributes as $attrdefid=>$attribute) { |
2139 | /* $attribute can be a string or an array */ |
2140 | if($attribute) { |
2141 | if($attrdef = $this->_dms->getAttributeDefinition($attrdefid)) { |
2142 | if(!$content->setAttributeValue($attrdef, $attribute)) { |
2143 | $this->_removeContent($content); |
2144 | $db->rollbackTransaction(); |
2145 | return false; |
2146 | } |
2147 | } else { |
2148 | $this->_removeContent($content); |
2149 | $db->rollbackTransaction(); |
2150 | return false; |
2151 | } |
2152 | } |
2153 | } |
2154 | } |
2155 | |
2156 | $queryStr = "INSERT INTO `tblDocumentStatus` (`documentID`, `version`) ". |
2157 | "VALUES (". $this->_id .", ". (int) $version .")"; |
2158 | if (!$db->getResult($queryStr)) { |
2159 | $this->_removeContent($content); |
2160 | $db->rollbackTransaction(); |
2161 | return false; |
2162 | } |
2163 | |
2164 | $statusID = $db->getInsertID('tblDocumentStatus', 'statusID'); |
2165 | |
2166 | if($workflow) |
2167 | $content->setWorkflow($workflow, $user); |
2168 | |
2169 | // Add reviewers into the database. Reviewers must review the document |
2170 | // and submit comments, if appropriate. Reviewers can also recommend that |
2171 | // a document be rejected. |
2172 | $pendingReview=false; |
2173 | /** @noinspection PhpUnusedLocalVariableInspection */ |
2174 | foreach (array("i", "g") as $i){ |
2175 | if (isset($reviewers[$i])) { |
2176 | foreach ($reviewers[$i] as $reviewerID) { |
2177 | $reviewer=($i=="i" ?$this->_dms->getUser($reviewerID) : $this->_dms->getGroup($reviewerID)); |
2178 | $res = ($i=="i" ? $docResultSet->getContent()->addIndReviewer($reviewer, $user, true) : $docResultSet->getContent()->addGrpReviewer($reviewer, $user, true)); |
2179 | $docResultSet->addReviewer($reviewer, $i, $res); |
2180 | // If no error is returned, or if the error is just due to email |
2181 | // failure, mark the state as "pending review". |
2182 | // FIXME: There seems to be no error code -4 anymore |
2183 | if ($res==0 || $res=-3 || $res=-4) { |
2184 | $pendingReview=true; |
2185 | } |
2186 | } |
2187 | } |
2188 | } |
2189 | // Add approvers to the database. Approvers must also review the document |
2190 | // and make a recommendation on its release as an approved version. |
2191 | $pendingApproval=false; |
2192 | /** @noinspection PhpUnusedLocalVariableInspection */ |
2193 | foreach (array("i", "g") as $i){ |
2194 | if (isset($approvers[$i])) { |
2195 | foreach ($approvers[$i] as $approverID) { |
2196 | $approver=($i=="i" ? $this->_dms->getUser($approverID) : $this->_dms->getGroup($approverID)); |
2197 | $res=($i=="i" ? $docResultSet->getContent()->addIndApprover($approver, $user, true) : $docResultSet->getContent()->addGrpApprover($approver, $user, !$pendingReview)); |
2198 | $docResultSet->addApprover($approver, $i, $res); |
2199 | // FIXME: There seems to be no error code -4 anymore |
2200 | if ($res==0 || $res=-3 || $res=-4) { |
2201 | $pendingApproval=true; |
2202 | } |
2203 | } |
2204 | } |
2205 | } |
2206 | |
2207 | // If there are no reviewers or approvers, the document is automatically |
2208 | // promoted to the released state. |
2209 | if ($pendingReview) { |
2210 | $status = S_DRAFT_REV; |
2211 | $comment = ""; |
2212 | } |
2213 | elseif ($pendingApproval) { |
2214 | $status = S_DRAFT_APP; |
2215 | $comment = ""; |
2216 | } |
2217 | elseif($workflow) { |
2218 | $status = S_IN_WORKFLOW; |
2219 | $comment = ", workflow: ".$workflow->getName(); |
2220 | } elseif($initstate == S_DRAFT) { |
2221 | $status = $initstate; |
2222 | $comment = ""; |
2223 | } else { |
2224 | $status = S_RELEASED; |
2225 | $comment = ""; |
2226 | } |
2227 | $queryStr = "INSERT INTO `tblDocumentStatusLog` (`statusID`, `status`, `comment`, `date`, `userID`) ". |
2228 | "VALUES ('". $statusID ."', '". $status."', 'New document content submitted". $comment ."', ".$db->getCurrentDatetime().", '". $user->getID() ."')"; |
2229 | if (!$db->getResult($queryStr)) { |
2230 | $db->rollbackTransaction(); |
2231 | return false; |
2232 | } |
2233 | |
2234 | /** @noinspection PhpMethodParametersCountMismatchInspection */ |
2235 | $docResultSet->setStatus($status); |
2236 | |
2237 | $db->commitTransaction(); |
2238 | return $docResultSet; |
2239 | } /* }}} */ |
2240 | |
2241 | /** |
2242 | * Replace a version of a document |
2243 | * |
2244 | * Each document may have any number of content elements attached to it. |
2245 | * This function replaces the file content of a given version. |
2246 | * Using this function is highly discourage, because it undermines the |
2247 | * idea of keeping all versions of a document as originally saved. |
2248 | * Content will only be replaced if the mimetype, filetype, user and |
2249 | * original filename are identical to the version being updated. |
2250 | * |
2251 | * This function was introduced for the webdav server because any saving |
2252 | * of a document created a new version. |
2253 | * |
2254 | * @param object $user user who shall be the owner of this content |
2255 | * @param string $tmpFile file containing the actuall content |
2256 | * @param string $orgFileName original file name |
2257 | * @param string $fileType |
2258 | * @param string $mimeType MimeType of the content |
2259 | * @param integer $version version number of content or 0 if latest version shall be replaced. |
2260 | * @return bool/array false in case of an error or a result set |
2261 | */ |
2262 | function replaceContent($version, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $allowoverride=[]) { /* {{{ */ |
2263 | $db = $this->_dms->getDB(); |
2264 | |
2265 | // the doc path is id/version.filetype |
2266 | $dir = $this->getDir(); |
2267 | |
2268 | /* If $version < 1 than replace the content of the latest version. |
2269 | */ |
2270 | if ((int) $version<1) { |
2271 | $queryStr = "SELECT MAX(`version`) AS m FROM `tblDocumentContent` WHERE `document` = ".$this->_id; |
2272 | $resArr = $db->getResultArray($queryStr); |
2273 | if (is_bool($resArr) && !$resArr) |
2274 | return false; |
2275 | |
2276 | $version = $resArr[0]['m']; |
2277 | } |
2278 | |
2279 | $content = $this->getContentByVersion($version); |
2280 | if(!$content) |
2281 | return false; |
2282 | |
2283 | if($fileType == '.') |
2284 | $fileType = ''; |
2285 | |
2286 | $sql = []; |
2287 | /* Check if $user, $orgFileName, $fileType and $mimeType are the same */ |
2288 | if($user->getID() != $content->getUser()->getID()) { |
2289 | if(!empty($allowoverride['user'])) |
2290 | $sql[] = "`createdBy`=".$user->getID(); |
2291 | else |
2292 | return false; |
2293 | } |
2294 | if($orgFileName != $content->getOriginalFileName()) { |
2295 | if(!empty($allowoverride['orgfilename'])) |
2296 | $sql[] = "`orgFileName`=".$db->qstr($orgFileName); |
2297 | else |
2298 | return false; |
2299 | } |
2300 | if($fileType != $content->getFileType()) { |
2301 | if(!empty($allowoverride['filetype'])) |
2302 | $sql[] = "`fileType`=".$db->qstr($fileType); |
2303 | else |
2304 | return false; |
2305 | } |
2306 | if($mimeType != $content->getMimeType()) { |
2307 | if(!empty($allowoverride['mimetype'])) |
2308 | $sql[] = "`mimeType`=".$db->qstr($mimeType); |
2309 | else |
2310 | return false; |
2311 | } |
2312 | |
2313 | $filesize = SeedDMS_Core_File::fileSize($tmpFile); |
2314 | $checksum = SeedDMS_Core_File::checksum($tmpFile); |
2315 | |
2316 | $db->startTransaction(); |
2317 | $sql[] = "`date`=".$db->getCurrentTimestamp(); |
2318 | $sql[] = "`fileSize`=".$filesize; |
2319 | $sql[] = "`checksum`=".$db->qstr($checksum); |
2320 | $queryStr = "UPDATE `tblDocumentContent` set ".implode(", ", $sql)." WHERE `id`=".$content->getID(); |
2321 | if (!$db->getResult($queryStr)) { |
2322 | $db->rollbackTransaction(); |
2323 | return false; |
2324 | } |
2325 | |
2326 | // copy file |
2327 | if (!SeedDMS_Core_File::copyFile($tmpFile, $this->_dms->contentDir . $dir . $version . $fileType)) { |
2328 | $db->rollbackTransaction(); |
2329 | return false; |
2330 | } |
2331 | |
2332 | $this->_content = null; |
2333 | $this->_latestContent = null; |
2334 | $db->commitTransaction(); |
2335 | |
2336 | return true; |
2337 | } /* }}} */ |
2338 | |
2339 | /** |
2340 | * Return all content elements of a document |
2341 | * |
2342 | * This functions returns an array of content elements ordered by version. |
2343 | * Version which are not accessible because of its status, will be filtered |
2344 | * out. Access rights based on the document status are calculated for the |
2345 | * currently logged in user. |
2346 | * |
2347 | * @return bool|SeedDMS_Core_DocumentContent[] |
2348 | */ |
2349 | function getContent() { /* {{{ */ |
2350 | $db = $this->_dms->getDB(); |
2351 | |
2352 | if (!isset($this->_content)) { |
2353 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." ORDER BY `version`"; |
2354 | $resArr = $db->getResultArray($queryStr); |
2355 | if (is_bool($resArr) && !$resArr) |
2356 | return false; |
2357 | |
2358 | $this->_content = array(); |
2359 | $classname = $this->_dms->getClassname('documentcontent'); |
2360 | $user = $this->_dms->getLoggedInUser(); |
2361 | foreach ($resArr as $row) { |
2362 | /** @var SeedDMS_Core_DocumentContent $content */ |
2363 | $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'], $row['revisiondate']); |
2364 | /* TODO: Better use content id as key in $this->_content. This |
2365 | * would allow to remove a single content object in removeContent(). |
2366 | * Currently removeContent() must clear $this->_content completely |
2367 | */ |
2368 | if($user) { |
2369 | if($content->getAccessMode($user) >= M_READ) |
2370 | array_push($this->_content, $content); |
2371 | } else { |
2372 | array_push($this->_content, $content); |
2373 | } |
2374 | } |
2375 | } |
2376 | |
2377 | return $this->_content; |
2378 | } /* }}} */ |
2379 | |
2380 | /** |
2381 | * Return the content element of a document with a given version number |
2382 | * |
2383 | * This function will check if the version is accessible and return false |
2384 | * if not. Access rights based on the document status are calculated for the |
2385 | * currently logged in user. |
2386 | * |
2387 | * @param integer $version version number of content element |
2388 | * @return SeedDMS_Core_DocumentContent|null|boolean object of class |
2389 | * {@link SeedDMS_Core_DocumentContent}, null if not content was found, |
2390 | * false in case of an error |
2391 | */ |
2392 | function getContentByVersion($version) { /* {{{ */ |
2393 | if (!is_numeric($version)) return false; |
2394 | |
2395 | if (isset($this->_content)) { |
2396 | foreach ($this->_content as $revision) { |
2397 | if ($revision->getVersion() == $version) |
2398 | return $revision; |
2399 | } |
2400 | return null; |
2401 | } |
2402 | |
2403 | $db = $this->_dms->getDB(); |
2404 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." AND `version` = " . (int) $version; |
2405 | $resArr = $db->getResultArray($queryStr); |
2406 | if (is_bool($resArr) && !$resArr) |
2407 | return false; |
2408 | if (count($resArr) != 1) |
2409 | return null; |
2410 | |
2411 | $resArr = $resArr[0]; |
2412 | $classname = $this->_dms->getClassname('documentcontent'); |
2413 | /** @var SeedDMS_Core_DocumentContent $content */ |
2414 | 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'], $resArr['revisiondate'])) { |
2415 | $user = $this->_dms->getLoggedInUser(); |
2416 | /* A user with write access on the document may always see the version */ |
2417 | if($user && $content->getAccessMode($user) == M_NONE) |
2418 | return null; |
2419 | else |
2420 | return $content; |
2421 | } else { |
2422 | return false; |
2423 | } |
2424 | } /* }}} */ |
2425 | |
2426 | /** |
2427 | * Check if a given version is the latest version of the document |
2428 | * |
2429 | * @param integer $version version number of content element |
2430 | * @return SeedDMS_Core_DocumentContent|boolean object of class {@link SeedDMS_Core_DocumentContent} |
2431 | * or false |
2432 | */ |
2433 | function isLatestContent($version) { /* {{{ */ |
2434 | return $this->getLatestContent()->getVersion() == $version; |
2435 | } /* }}} */ |
2436 | |
2437 | /** |
2438 | * @return bool|null|SeedDMS_Core_DocumentContent |
2439 | */ |
2440 | function __getLatestContent() { /* {{{ */ |
2441 | if (!$this->_latestContent) { |
2442 | $db = $this->_dms->getDB(); |
2443 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." ORDER BY `version` DESC LIMIT 1"; |
2444 | $resArr = $db->getResultArray($queryStr); |
2445 | if (is_bool($resArr) && !$resArr) |
2446 | return false; |
2447 | if (count($resArr) != 1) |
2448 | return false; |
2449 | |
2450 | $resArr = $resArr[0]; |
2451 | $classname = $this->_dms->getClassname('documentcontent'); |
2452 | $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'], $resArr['revisiondate']); |
2453 | } |
2454 | return $this->_latestContent; |
2455 | } /* }}} */ |
2456 | |
2457 | /** |
2458 | * Get the latest version of document |
2459 | * |
2460 | * This function returns the latest accessible version of a document. |
2461 | * If content access has been restricted by the role of the user |
2462 | * the function will go |
2463 | * backwards in history until an accessible version is found. If none |
2464 | * is found null will be returned. |
2465 | * Access rights based on the document status are calculated for the |
2466 | * currently logged in user. |
2467 | * |
2468 | * @return bool|SeedDMS_Core_DocumentContent object of class {@link SeedDMS_Core_DocumentContent} |
2469 | */ |
2470 | function getLatestContent() { /* {{{ */ |
2471 | if (!$this->_latestContent) { |
2472 | $db = $this->_dms->getDB(); |
2473 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = ".$this->_id." ORDER BY `version` DESC"; |
2474 | $resArr = $db->getResultArray($queryStr); |
2475 | if (is_bool($resArr) && !$resArr) |
2476 | return false; |
2477 | |
2478 | $classname = $this->_dms->getClassname('documentcontent'); |
2479 | $user = $this->_dms->getLoggedInUser(); |
2480 | foreach ($resArr as $row) { |
2481 | /** @var SeedDMS_Core_DocumentContent $content */ |
2482 | if (!$this->_latestContent) { |
2483 | $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'], $row['revisiondate']); |
2484 | if($user) { |
2485 | /* If the user may even write the document, then also allow to see all content. |
2486 | * This is needed because the user could upload a new version |
2487 | */ |
2488 | if($content->getAccessMode($user) >= M_READ) { |
2489 | $this->_latestContent = $content; |
2490 | } |
2491 | } else { |
2492 | $this->_latestContent = $content; |
2493 | } |
2494 | } |
2495 | } |
2496 | } |
2497 | |
2498 | return $this->_latestContent; |
2499 | } /* }}} */ |
2500 | |
2501 | /** |
2502 | * Remove version of document |
2503 | * |
2504 | * @param SeedDMS_Core_DocumentContent $version version number of content |
2505 | * @return boolean true if successful, otherwise false |
2506 | */ |
2507 | private function _removeContent($version) { /* {{{ */ |
2508 | $db = $this->_dms->getDB(); |
2509 | |
2510 | $db->startTransaction(); |
2511 | |
2512 | $status = $version->getStatus(); |
2513 | $stID = $status["statusID"]; |
2514 | |
2515 | $queryStr = "DELETE FROM `tblDocumentContent` WHERE `document` = " . $this->getID() . " AND `version` = " . $version->getVersion(); |
2516 | if (!$db->getResult($queryStr)) { |
2517 | $db->rollbackTransaction(); |
2518 | return false; |
2519 | } |
2520 | |
2521 | $queryStr = "DELETE FROM `tblDocumentContentAttributes` WHERE `content` = " . $version->getId(); |
2522 | if (!$db->getResult($queryStr)) { |
2523 | $db->rollbackTransaction(); |
2524 | return false; |
2525 | } |
2526 | |
2527 | $queryStr = "DELETE FROM `tblTransmittalItems` WHERE `document` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'"; |
2528 | if (!$db->getResult($queryStr)) { |
2529 | $db->rollbackTransaction(); |
2530 | return false; |
2531 | } |
2532 | |
2533 | $queryStr = "DELETE FROM `tblDocumentStatusLog` WHERE `statusID` = '".$stID."'"; |
2534 | if (!$db->getResult($queryStr)) { |
2535 | $db->rollbackTransaction(); |
2536 | return false; |
2537 | } |
2538 | |
2539 | $queryStr = "DELETE FROM `tblDocumentStatus` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'"; |
2540 | if (!$db->getResult($queryStr)) { |
2541 | $db->rollbackTransaction(); |
2542 | return false; |
2543 | } |
2544 | |
2545 | $status = $version->getReviewStatus(); |
2546 | $stList = ""; |
2547 | foreach ($status as $st) { |
2548 | $stList .= (strlen($stList)==0 ? "" : ", "). "'".$st["reviewID"]."'"; |
2549 | $queryStr = "SELECT * FROM `tblDocumentReviewLog` WHERE `reviewID` = " . $st['reviewID']; |
2550 | $resArr = $db->getResultArray($queryStr); |
2551 | if ((is_bool($resArr) && !$resArr)) { |
2552 | $db->rollbackTransaction(); |
2553 | return false; |
2554 | } |
2555 | foreach($resArr as $res) { |
2556 | $file = $this->_dms->contentDir . $this->getDir().'r'.$res['reviewLogID']; |
2557 | if(SeedDMS_Core_File::file_exists($file)) |
2558 | SeedDMS_Core_File::removeFile($file); |
2559 | } |
2560 | } |
2561 | |
2562 | if (strlen($stList)>0) { |
2563 | $queryStr = "DELETE FROM `tblDocumentReviewLog` WHERE `tblDocumentReviewLog`.`reviewID` IN (".$stList.")"; |
2564 | if (!$db->getResult($queryStr)) { |
2565 | $db->rollbackTransaction(); |
2566 | return false; |
2567 | } |
2568 | } |
2569 | $queryStr = "DELETE FROM `tblDocumentReviewers` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'"; |
2570 | if (!$db->getResult($queryStr)) { |
2571 | $db->rollbackTransaction(); |
2572 | return false; |
2573 | } |
2574 | $status = $version->getApprovalStatus(); |
2575 | $stList = ""; |
2576 | foreach ($status as $st) { |
2577 | $stList .= (strlen($stList)==0 ? "" : ", "). "'".$st["approveID"]."'"; |
2578 | $queryStr = "SELECT * FROM `tblDocumentApproveLog` WHERE `approveID` = " . $st['approveID']; |
2579 | $resArr = $db->getResultArray($queryStr); |
2580 | if ((is_bool($resArr) && !$resArr)) { |
2581 | $db->rollbackTransaction(); |
2582 | return false; |
2583 | } |
2584 | foreach($resArr as $res) { |
2585 | $file = $this->_dms->contentDir . $this->getDir().'a'.$res['approveLogID']; |
2586 | if(SeedDMS_Core_File::file_exists($file)) |
2587 | SeedDMS_Core_File::removeFile($file); |
2588 | } |
2589 | } |
2590 | |
2591 | if (strlen($stList)>0) { |
2592 | $queryStr = "DELETE FROM `tblDocumentApproveLog` WHERE `tblDocumentApproveLog`.`approveID` IN (".$stList.")"; |
2593 | if (!$db->getResult($queryStr)) { |
2594 | $db->rollbackTransaction(); |
2595 | return false; |
2596 | } |
2597 | } |
2598 | $queryStr = "DELETE FROM `tblDocumentApprovers` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'"; |
2599 | if (!$db->getResult($queryStr)) { |
2600 | $db->rollbackTransaction(); |
2601 | return false; |
2602 | } |
2603 | |
2604 | /* Remove all receipts of document version. |
2605 | * This implmentation is different from the above for removing approvals |
2606 | * and reviews. It doesn't use getReceiptStatus() but reads the database |
2607 | */ |
2608 | $queryStr = "SELECT * FROM `tblDocumentRecipients` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'"; |
2609 | $resArr = $db->getResultArray($queryStr); |
2610 | if ((is_bool($resArr) && !$resArr)) { |
2611 | $db->rollbackTransaction(); |
2612 | return false; |
2613 | } |
2614 | |
2615 | $stList = array(); |
2616 | foreach($resArr as $res) { |
2617 | $stList[] = $res['receiptID']; |
2618 | } |
2619 | |
2620 | if ($stList) { |
2621 | $queryStr = "DELETE FROM `tblDocumentReceiptLog` WHERE `receiptID` IN (".implode(',', $stList).")"; |
2622 | if (!$db->getResult($queryStr)) { |
2623 | $db->rollbackTransaction(); |
2624 | return false; |
2625 | } |
2626 | $queryStr = "DELETE FROM `tblDocumentRecipients` WHERE `receiptID` IN (".implode(',', $stList).")"; |
2627 | if (!$db->getResult($queryStr)) { |
2628 | $db->rollbackTransaction(); |
2629 | return false; |
2630 | } |
2631 | } |
2632 | |
2633 | /* Remove all revisions of document version. |
2634 | * This implementation is different from the above for removing approvals |
2635 | * and reviews. It doesn't use getRevisionStatus() but reads the database |
2636 | */ |
2637 | $queryStr = "SELECT * FROM `tblDocumentRevisors` WHERE `documentID` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'"; |
2638 | $resArr = $db->getResultArray($queryStr); |
2639 | if ((is_bool($resArr) && !$resArr)) { |
2640 | $db->rollbackTransaction(); |
2641 | return false; |
2642 | } |
2643 | |
2644 | $stList = array(); |
2645 | foreach($resArr as $res) { |
2646 | $stList[] = $res['revisionID']; |
2647 | } |
2648 | |
2649 | if ($stList) { |
2650 | $queryStr = "DELETE FROM `tblDocumentRevisionLog` WHERE `revisionID` IN (".implode(',', $stList).")"; |
2651 | if (!$db->getResult($queryStr)) { |
2652 | $db->rollbackTransaction(); |
2653 | return false; |
2654 | } |
2655 | $queryStr = "DELETE FROM `tblDocumentRevisors` WHERE `revisionID` IN (".implode(',', $stList).")"; |
2656 | if (!$db->getResult($queryStr)) { |
2657 | $db->rollbackTransaction(); |
2658 | return false; |
2659 | } |
2660 | } |
2661 | |
2662 | $queryStr = "DELETE FROM `tblWorkflowDocumentContent` WHERE `document` = '". $this->getID() ."' AND `version` = '" . $version->getVersion()."'"; |
2663 | if (!$db->getResult($queryStr)) { |
2664 | $db->rollbackTransaction(); |
2665 | return false; |
2666 | } |
2667 | |
2668 | /* Will be deleted automatically when record will be deleted |
2669 | * from tblWorkflowDocumentContent |
2670 | $queryStr = "DELETE FROM `tblWorkflowLog` WHERE `document` = '". $this->getID() ."' AND `version` = '" . $version->getVersion."'"; |
2671 | if (!$db->getResult($queryStr)) { |
2672 | $db->rollbackTransaction(); |
2673 | return false; |
2674 | } |
2675 | */ |
2676 | |
2677 | // remove only those document files attached to version |
2678 | $res = $this->getDocumentFiles($version->getVersion(), false); |
2679 | if (is_bool($res) && !$res) { |
2680 | $db->rollbackTransaction(); |
2681 | return false; |
2682 | } |
2683 | |
2684 | foreach ($res as $documentfile) |
2685 | if(!$this->removeDocumentFile($documentfile->getId())) { |
2686 | $db->rollbackTransaction(); |
2687 | return false; |
2688 | } |
2689 | |
2690 | if (SeedDMS_Core_File::file_exists( $this->_dms->contentDir.$version->getPath() )) |
2691 | if (!SeedDMS_Core_File::removeFile( $this->_dms->contentDir.$version->getPath() )) { |
2692 | $db->rollbackTransaction(); |
2693 | return false; |
2694 | } |
2695 | |
2696 | $db->commitTransaction(); |
2697 | return true; |
2698 | } /* }}} */ |
2699 | |
2700 | /** |
2701 | * Call callback onPreRemoveDocument before deleting content |
2702 | * |
2703 | * @param SeedDMS_Core_DocumentContent $version version number of content |
2704 | * @return bool|mixed |
2705 | */ |
2706 | function removeContent($version) { /* {{{ */ |
2707 | $this->_dms->lasterror = ''; |
2708 | $db = $this->_dms->getDB(); |
2709 | |
2710 | /* Make sure the version exists */ |
2711 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = " . $this->getID() . " AND `version` = " . $version->getVersion(); |
2712 | $resArr = $db->getResultArray($queryStr); |
2713 | if (is_bool($resArr) && !$resArr) |
2714 | return false; |
2715 | if (count($resArr)==0) |
2716 | return false; |
2717 | |
2718 | /* Make sure this is not the last version */ |
2719 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` = " . $this->getID(); |
2720 | $resArr = $db->getResultArray($queryStr); |
2721 | if (is_bool($resArr) && !$resArr) |
2722 | return false; |
2723 | if (count($resArr)==1) |
2724 | return false; |
2725 | |
2726 | /* Check if 'onPreRemoveDocument' callback is set */ |
2727 | if(isset($this->_dms->callbacks['onPreRemoveContent'])) { |
2728 | foreach($this->_dms->callbacks['onPreRemoveContent'] as $callback) { |
2729 | $ret = call_user_func($callback[0], $callback[1], $this, $version); |
2730 | if(is_bool($ret)) |
2731 | return $ret; |
2732 | } |
2733 | } |
2734 | |
2735 | if(false === ($ret = self::_removeContent($version))) { |
2736 | return false; |
2737 | } |
2738 | |
2739 | /* Invalidate the content list and the latest content of this document, |
2740 | * otherwise getContent() and getLatestContent() |
2741 | * will still return the content just deleted. |
2742 | */ |
2743 | $this->_latestContent = null; |
2744 | $this->_content = null; |
2745 | |
2746 | /* Check if 'onPostRemoveDocument' callback is set */ |
2747 | if(isset($this->_dms->callbacks['onPostRemoveContent'])) { |
2748 | foreach($this->_dms->callbacks['onPostRemoveContent'] as $callback) { |
2749 | if(!call_user_func($callback[0], $callback[1], $version)) { |
2750 | } |
2751 | } |
2752 | } |
2753 | |
2754 | return $ret; |
2755 | } /* }}} */ |
2756 | |
2757 | /** |
2758 | * Return a certain document link |
2759 | * |
2760 | * @param integer $linkID id of link |
2761 | * @return SeedDMS_Core_DocumentLink|bool of SeedDMS_Core_DocumentLink or false in case of |
2762 | * an error. |
2763 | */ |
2764 | function getDocumentLink($linkID) { /* {{{ */ |
2765 | $db = $this->_dms->getDB(); |
2766 | |
2767 | if (!is_numeric($linkID)) return false; |
2768 | |
2769 | $queryStr = "SELECT * FROM `tblDocumentLinks` WHERE `document` = " . $this->_id ." AND `id` = " . (int) $linkID; |
2770 | $resArr = $db->getResultArray($queryStr); |
2771 | if (is_bool($resArr) && !$resArr) |
2772 | return false; |
2773 | if (count($resArr)==0) |
2774 | return null; |
2775 | |
2776 | $resArr = $resArr[0]; |
2777 | $document = $this->_dms->getDocument($resArr["document"]); |
2778 | $target = $this->_dms->getDocument($resArr["target"]); |
2779 | $link = new SeedDMS_Core_DocumentLink($resArr["id"], $document, $target, $resArr["userID"], $resArr["public"]); |
2780 | $user = $this->_dms->getLoggedInUser(); |
2781 | if($link->getAccessMode($user, $document, $target) >= M_READ) |
2782 | return $link; |
2783 | return null; |
2784 | } /* }}} */ |
2785 | |
2786 | /** |
2787 | * Return all document links |
2788 | * |
2789 | * The list may contain all links to other documents, even those which |
2790 | * may not be visible by certain users, unless you pass appropriate |
2791 | * parameters to filter out public links and those created by |
2792 | * the given user. The two parameters are or'ed. If $publiconly |
2793 | * is set the method will return all public links disregarding the |
2794 | * user. If $publiconly is not set but a user is set, the method |
2795 | * will return all links of that user (public and none public). |
2796 | * Setting a user and $publiconly to true will *not* return the |
2797 | * public links of that user but all links which are public or |
2798 | * owned by that user. |
2799 | * |
2800 | * The application must call |
2801 | * SeedDMS_Core_DMS::filterDocumentLinks() afterwards to filter out |
2802 | * those links pointing to a document not accessible by a given user. |
2803 | * |
2804 | * @param boolean $publiconly return all publically visible links |
2805 | * @param SeedDMS_Core_User $user return also private links of this user |
2806 | * |
2807 | * @return array list of objects of class {@see SeedDMS_Core_DocumentLink} |
2808 | */ |
2809 | function getDocumentLinks($publiconly=false, $user=null) { /* {{{ */ |
2810 | if (!isset($this->_documentLinks)) { |
2811 | $db = $this->_dms->getDB(); |
2812 | |
2813 | $queryStr = "SELECT * FROM `tblDocumentLinks` WHERE `document` = " . $this->_id; |
2814 | $tmp = array(); |
2815 | if($publiconly) |
2816 | $tmp[] = "`public`=1"; |
2817 | if($user) |
2818 | $tmp[] = "`userID`=".$user->getID(); |
2819 | if($tmp) { |
2820 | $queryStr .= " AND (".implode(" OR ", $tmp).")"; |
2821 | } |
2822 | |
2823 | $resArr = $db->getResultArray($queryStr); |
2824 | if (is_bool($resArr) && !$resArr) |
2825 | return false; |
2826 | $this->_documentLinks = array(); |
2827 | |
2828 | $user = $this->_dms->getLoggedInUser(); |
2829 | foreach ($resArr as $row) { |
2830 | $target = $this->_dms->getDocument($row["target"]); |
2831 | $link = new SeedDMS_Core_DocumentLink($row["id"], $this, $target, $row["userID"], $row["public"]); |
2832 | if($link->getAccessMode($user, $this, $target) >= M_READ) |
2833 | array_push($this->_documentLinks, $link); |
2834 | } |
2835 | } |
2836 | return $this->_documentLinks; |
2837 | } /* }}} */ |
2838 | |
2839 | /** |
2840 | * Return all document having a link on this document |
2841 | * |
2842 | * The list contains all documents which have a link to the current |
2843 | * document. The list contains even those documents which |
2844 | * may not be accessible by the user, unless you pass appropriate |
2845 | * parameters to filter out public links and those created by |
2846 | * the given user. |
2847 | * This functions is basically the reverse of |
2848 | * {@see SeedDMS_Core_Document::getDocumentLinks()} |
2849 | * |
2850 | * The application must call |
2851 | * SeedDMS_Core_DMS::filterDocumentLinks() afterwards to filter out |
2852 | * those links pointing to a document not accessible by a given user. |
2853 | * |
2854 | * @param boolean $publiconly return all publically visible links |
2855 | * @param SeedDMS_Core_User $user return also private links of this user |
2856 | * |
2857 | * @return array list of objects of class SeedDMS_Core_DocumentLink |
2858 | */ |
2859 | function getReverseDocumentLinks($publiconly=false, $user=null) { /* {{{ */ |
2860 | $db = $this->_dms->getDB(); |
2861 | |
2862 | $queryStr = "SELECT * FROM `tblDocumentLinks` WHERE `target` = " . $this->_id; |
2863 | $tmp = array(); |
2864 | if($publiconly) |
2865 | $tmp[] = "`public`=1"; |
2866 | if($user) |
2867 | $tmp[] = "`userID`=".$user->getID(); |
2868 | if($tmp) { |
2869 | $queryStr .= " AND (".implode(" OR ", $tmp).")"; |
2870 | } |
2871 | |
2872 | $resArr = $db->getResultArray($queryStr); |
2873 | if (is_bool($resArr) && !$resArr) |
2874 | return false; |
2875 | |
2876 | $links = array(); |
2877 | foreach ($resArr as $row) { |
2878 | $document = $this->_dms->getDocument($row["document"]); |
2879 | $link = new SeedDMS_Core_DocumentLink($row["id"], $document, $this, $row["userID"], $row["public"]); |
2880 | if($link->getAccessMode($user, $document, $this) >= M_READ) |
2881 | array_push($links, $link); |
2882 | } |
2883 | |
2884 | return $links; |
2885 | } /* }}} */ |
2886 | |
2887 | function addDocumentLink($targetID, $userID, $public) { /* {{{ */ |
2888 | $db = $this->_dms->getDB(); |
2889 | |
2890 | $public = ($public) ? 1 : 0; |
2891 | |
2892 | if (!is_numeric($targetID) || $targetID < 1) |
2893 | return false; |
2894 | |
2895 | if ($targetID == $this->_id) |
2896 | return false; |
2897 | |
2898 | if (!is_numeric($userID) || $userID < 1) |
2899 | return false; |
2900 | |
2901 | if(!($target = $this->_dms->getDocument($targetID))) |
2902 | return false; |
2903 | |
2904 | if(!($user = $this->_dms->getUser($userID))) |
2905 | return false; |
2906 | |
2907 | $queryStr = "INSERT INTO `tblDocumentLinks` (`document`, `target`, `userID`, `public`) VALUES (".$this->_id.", ".(int)$targetID.", ".(int)$userID.", ".$public.")"; |
2908 | if (!$db->getResult($queryStr)) |
2909 | return false; |
2910 | |
2911 | unset($this->_documentLinks); |
2912 | |
2913 | $id = $db->getInsertID('tblDocumentLinks'); |
2914 | $link = new SeedDMS_Core_DocumentLink($id, $this, $target, $user->getId(), $public); |
2915 | return $link; |
2916 | } /* }}} */ |
2917 | |
2918 | function removeDocumentLink($linkID) { /* {{{ */ |
2919 | $db = $this->_dms->getDB(); |
2920 | |
2921 | if (!is_numeric($linkID) || $linkID < 1) |
2922 | return false; |
2923 | |
2924 | $queryStr = "DELETE FROM `tblDocumentLinks` WHERE `document` = " . $this->_id ." AND `id` = " . (int) $linkID; |
2925 | if (!$db->getResult($queryStr)) return false; |
2926 | unset ($this->_documentLinks); |
2927 | return true; |
2928 | } /* }}} */ |
2929 | |
2930 | /** |
2931 | * Get attached file by its id |
2932 | * |
2933 | * @return object instance of SeedDMS_Core_DocumentFile, null if file is not |
2934 | * accessible, false in case of an sql error |
2935 | */ |
2936 | function getDocumentFile($ID) { /* {{{ */ |
2937 | $db = $this->_dms->getDB(); |
2938 | |
2939 | if (!is_numeric($ID)) return false; |
2940 | |
2941 | $queryStr = "SELECT * FROM `tblDocumentFiles` WHERE `document` = " . $this->_id ." AND `id` = " . (int) $ID; |
2942 | $resArr = $db->getResultArray($queryStr); |
2943 | if ((is_bool($resArr) && !$resArr) || count($resArr)==0) return false; |
2944 | |
2945 | $resArr = $resArr[0]; |
2946 | $classname = $this->_dms->getClassname('documentfile'); |
2947 | $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"]); |
2948 | $user = $this->_dms->getLoggedInUser(); |
2949 | if($file->getAccessMode($user) >= M_READ) |
2950 | return $file; |
2951 | return null; |
2952 | } /* }}} */ |
2953 | |
2954 | /** |
2955 | * Get list of files attached to document |
2956 | * |
2957 | * @param integer $version get only attachments for this version |
2958 | * @param boolean $incnoversion include attachments without a version |
2959 | * |
2960 | * @return array list of files, false in case of an sql error |
2961 | */ |
2962 | function getDocumentFiles($version=0, $incnoversion=true) { /* {{{ */ |
2963 | /* use a smarter caching because removing a document will call this function |
2964 | * for each version and the document itself. |
2965 | */ |
2966 | $hash = substr(md5($version.$incnoversion), 0, 4); |
2967 | if (!isset($this->_documentFiles[$hash])) { |
2968 | $db = $this->_dms->getDB(); |
2969 | |
2970 | $queryStr = "SELECT * FROM `tblDocumentFiles` WHERE `document` = " . $this->_id; |
2971 | if($version) { |
2972 | if($incnoversion) |
2973 | $queryStr .= " AND (`version`=0 OR `version`=".(int) $version.")"; |
2974 | else |
2975 | $queryStr .= " AND (`version`=".(int) $version.")"; |
2976 | } |
2977 | $queryStr .= " ORDER BY "; |
2978 | if($version) { |
2979 | $queryStr .= "`version` DESC,"; |
2980 | } |
2981 | $queryStr .= "`date` DESC"; |
2982 | $resArr = $db->getResultArray($queryStr); |
2983 | if (is_bool($resArr) && !$resArr) return false; |
2984 | |
2985 | $this->_documentFiles = array($hash=>array()); |
2986 | |
2987 | $user = $this->_dms->getLoggedInUser(); |
2988 | $classname = $this->_dms->getClassname('documentfile'); |
2989 | foreach ($resArr as $row) { |
2990 | $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"]); |
2991 | if($file->getAccessMode($user) >= M_READ) |
2992 | array_push($this->_documentFiles[$hash], $file); |
2993 | } |
2994 | } |
2995 | return $this->_documentFiles[$hash]; |
2996 | } /* }}} */ |
2997 | |
2998 | function addDocumentFile($name, $comment, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $version=0, $public=1) { /* {{{ */ |
2999 | $db = $this->_dms->getDB(); |
3000 | |
3001 | $dir = $this->getDir(); |
3002 | |
3003 | $db->startTransaction(); |
3004 | $queryStr = "INSERT INTO `tblDocumentFiles` (`comment`, `date`, `dir`, `document`, `fileType`, `mimeType`, `orgFileName`, `userID`, `name`, `version`, `public`) VALUES ". |
3005 | "(".$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).")"; |
3006 | if (!$db->getResult($queryStr)) { |
3007 | $db->rollbackTransaction(); |
3008 | return false; |
3009 | } |
3010 | |
3011 | $id = $db->getInsertID('tblDocumentFiles'); |
3012 | |
3013 | $file = $this->getDocumentFile($id); |
3014 | if (is_bool($file) && !$file) { |
3015 | $db->rollbackTransaction(); |
3016 | return false; |
3017 | } |
3018 | |
3019 | // copy file |
3020 | if (!SeedDMS_Core_File::makeDir($this->_dms->contentDir . $dir)) return false; |
3021 | if($this->_dms->forceRename) |
3022 | $err = SeedDMS_Core_File::renameFile($tmpFile, $this->_dms->contentDir . $file->getPath()); |
3023 | else |
3024 | $err = SeedDMS_Core_File::copyFile($tmpFile, $this->_dms->contentDir . $file->getPath()); |
3025 | if (!$err) { |
3026 | $db->rollbackTransaction(); |
3027 | return false; |
3028 | } |
3029 | |
3030 | $db->commitTransaction(); |
3031 | unset ($this->_documentFiles); |
3032 | return $file; |
3033 | } /* }}} */ |
3034 | |
3035 | function removeDocumentFile($ID) { /* {{{ */ |
3036 | $db = $this->_dms->getDB(); |
3037 | |
3038 | if (!is_numeric($ID) || $ID < 1) |
3039 | return false; |
3040 | |
3041 | $file = $this->getDocumentFile($ID); |
3042 | if (is_bool($file) && !$file) return false; |
3043 | |
3044 | $db->startTransaction(); |
3045 | /* First delete the database record, because that can be undone |
3046 | * if deletion of the file fails. |
3047 | */ |
3048 | $queryStr = "DELETE FROM `tblDocumentFiles` WHERE `document` = " . $this->getID() . " AND `id` = " . (int) $ID; |
3049 | if (!$db->getResult($queryStr)) { |
3050 | $db->rollbackTransaction(); |
3051 | return false; |
3052 | } |
3053 | |
3054 | if (SeedDMS_Core_File::file_exists( $this->_dms->contentDir . $file->getPath() )){ |
3055 | if (!SeedDMS_Core_File::removeFile( $this->_dms->contentDir . $file->getPath() )) { |
3056 | $db->rollbackTransaction(); |
3057 | return false; |
3058 | } |
3059 | } |
3060 | |
3061 | $db->commitTransaction(); |
3062 | unset ($this->_documentFiles); |
3063 | |
3064 | return true; |
3065 | } /* }}} */ |
3066 | |
3067 | /** |
3068 | * Remove a document completly |
3069 | * |
3070 | * This methods calls the callback 'onPreRemoveDocument' before removing |
3071 | * the document. The current document will be passed as the second |
3072 | * parameter to the callback function. After successful deletion the |
3073 | * 'onPostRemoveDocument' callback will be used. The current document id |
3074 | * will be passed as the second parameter. If onPreRemoveDocument fails |
3075 | * the whole function will fail and the document will not be deleted. |
3076 | * The return value of 'onPostRemoveDocument' will be disregarded. |
3077 | * |
3078 | * @return boolean true on success, otherwise false |
3079 | */ |
3080 | function remove() { /* {{{ */ |
3081 | $db = $this->_dms->getDB(); |
3082 | $this->_dms->lasterror = ''; |
3083 | |
3084 | /* Check if 'onPreRemoveDocument' callback is set */ |
3085 | if(isset($this->_dms->callbacks['onPreRemoveDocument'])) { |
3086 | foreach($this->_dms->callbacks['onPreRemoveDocument'] as $callback) { |
3087 | $ret = call_user_func($callback[0], $callback[1], $this); |
3088 | if(is_bool($ret)) |
3089 | return $ret; |
3090 | } |
3091 | } |
3092 | |
3093 | $res = $this->getContent(); |
3094 | if (is_bool($res) && !$res) return false; |
3095 | |
3096 | $db->startTransaction(); |
3097 | |
3098 | // remove content of document |
3099 | foreach ($this->_content as $version) { |
3100 | if (!$this->_removeContent($version)) { |
3101 | $db->rollbackTransaction(); |
3102 | return false; |
3103 | } |
3104 | } |
3105 | |
3106 | // remove all document files |
3107 | $res = $this->getDocumentFiles(); |
3108 | if (is_bool($res) && !$res) { |
3109 | $db->rollbackTransaction(); |
3110 | return false; |
3111 | } |
3112 | |
3113 | foreach ($res as $documentfile) |
3114 | if(!$this->removeDocumentFile($documentfile->getId())) { |
3115 | $db->rollbackTransaction(); |
3116 | return false; |
3117 | } |
3118 | |
3119 | // TODO: versioning file? |
3120 | |
3121 | if (SeedDMS_Core_File::file_exists( $this->_dms->contentDir . $this->getDir() )) |
3122 | if (!SeedDMS_Core_File::removeDir( $this->_dms->contentDir . $this->getDir() )) { |
3123 | $db->rollbackTransaction(); |
3124 | return false; |
3125 | } |
3126 | |
3127 | $queryStr = "DELETE FROM `tblDocuments` WHERE `id` = " . $this->_id; |
3128 | if (!$db->getResult($queryStr)) { |
3129 | $db->rollbackTransaction(); |
3130 | return false; |
3131 | } |
3132 | $queryStr = "DELETE FROM `tblDocumentAttributes` WHERE `document` = " . $this->_id; |
3133 | if (!$db->getResult($queryStr)) { |
3134 | $db->rollbackTransaction(); |
3135 | return false; |
3136 | } |
3137 | $queryStr = "DELETE FROM `tblACLs` WHERE `target` = " . $this->_id . " AND `targetType` = " . T_DOCUMENT; |
3138 | if (!$db->getResult($queryStr)) { |
3139 | $db->rollbackTransaction(); |
3140 | return false; |
3141 | } |
3142 | $queryStr = "DELETE FROM `tblDocumentLinks` WHERE `document` = " . $this->_id . " OR `target` = " . $this->_id; |
3143 | if (!$db->getResult($queryStr)) { |
3144 | $db->rollbackTransaction(); |
3145 | return false; |
3146 | } |
3147 | $queryStr = "DELETE FROM `tblDocumentLocks` WHERE `document` = " . $this->_id; |
3148 | if (!$db->getResult($queryStr)) { |
3149 | $db->rollbackTransaction(); |
3150 | return false; |
3151 | } |
3152 | $queryStr = "DELETE FROM `tblDocumentCheckOuts` WHERE `document` = " . $this->_id; |
3153 | if (!$db->getResult($queryStr)) { |
3154 | $db->rollbackTransaction(); |
3155 | return false; |
3156 | } |
3157 | $queryStr = "DELETE FROM `tblDocumentFiles` WHERE `document` = " . $this->_id; |
3158 | if (!$db->getResult($queryStr)) { |
3159 | $db->rollbackTransaction(); |
3160 | return false; |
3161 | } |
3162 | $queryStr = "DELETE FROM `tblDocumentCategory` WHERE `documentID` = " . $this->_id; |
3163 | if (!$db->getResult($queryStr)) { |
3164 | $db->rollbackTransaction(); |
3165 | return false; |
3166 | } |
3167 | |
3168 | // Delete the notification list. |
3169 | $queryStr = "DELETE FROM `tblNotify` WHERE `target` = " . $this->_id . " AND `targetType` = " . T_DOCUMENT; |
3170 | if (!$db->getResult($queryStr)) { |
3171 | $db->rollbackTransaction(); |
3172 | return false; |
3173 | } |
3174 | |
3175 | $db->commitTransaction(); |
3176 | |
3177 | /* Check if 'onPostRemoveDocument' callback is set */ |
3178 | if(isset($this->_dms->callbacks['onPostRemoveDocument'])) { |
3179 | foreach($this->_dms->callbacks['onPostRemoveDocument'] as $callback) { |
3180 | if(!call_user_func($callback[0], $callback[1], $this)) { |
3181 | } |
3182 | } |
3183 | } |
3184 | |
3185 | return true; |
3186 | } /* }}} */ |
3187 | |
3188 | /** |
3189 | * Get List of users and groups which have read access on the document |
3190 | * The list will not include any guest users, |
3191 | * administrators and the owner of the folder unless $listadmin resp. |
3192 | * $listowner is set to true. |
3193 | * |
3194 | * This function is deprecated. Use |
3195 | * {@see SeedDMS_Core_Document::getReadAccessList()} instead. |
3196 | */ |
3197 | protected function __getApproversList() { /* {{{ */ |
3198 | return $this->getReadAccessList(0, 0, 0); |
3199 | } /* }}} */ |
3200 | |
3201 | /** |
3202 | * Returns a list of groups and users with read access on the document |
3203 | * |
3204 | * @param boolean $listadmin if set to true any admin will be listed too |
3205 | * @param boolean $listowner if set to true the owner will be listed too |
3206 | * @param boolean $listguest if set to true any guest will be listed too |
3207 | * |
3208 | * @return array list of users and groups |
3209 | */ |
3210 | function getReadAccessList($listadmin=0, $listowner=0, $listguest=0) { /* {{{ */ |
3211 | $db = $this->_dms->getDB(); |
3212 | |
3213 | if (!isset($this->_readAccessList)) { |
3214 | $this->_readAccessList = array("groups" => array(), "users" => array()); |
3215 | $userIDs = ""; |
3216 | $groupIDs = ""; |
3217 | $defAccess = $this->getDefaultAccess(); |
3218 | |
3219 | /* Check if the default access is < read access or >= read access. |
3220 | * If default access is less than read access, then create a list |
3221 | * of users and groups with read access. |
3222 | * If default access is equal or greater then read access, then |
3223 | * create a list of users and groups without read access. |
3224 | */ |
3225 | if ($defAccess<M_READ) { |
3226 | // Get the list of all users and groups that are listed in the ACL as |
3227 | // having read access to the document. |
3228 | $tmpList = $this->getAccessList(M_READ, O_GTEQ); |
3229 | } |
3230 | else { |
3231 | // Get the list of all users and groups that DO NOT have read access |
3232 | // to the document. |
3233 | $tmpList = $this->getAccessList(M_NONE, O_LTEQ); |
3234 | } |
3235 | /** @var SeedDMS_Core_GroupAccess $groupAccess */ |
3236 | foreach ($tmpList["groups"] as $groupAccess) { |
3237 | $groupIDs .= (strlen($groupIDs)==0 ? "" : ", ") . $groupAccess->getGroupID(); |
3238 | } |
3239 | |
3240 | /** @var SeedDMS_Core_UserAccess $userAccess */ |
3241 | foreach ($tmpList["users"] as $userAccess) { |
3242 | $user = $userAccess->getUser(); |
3243 | if (!$listadmin && $user->isAdmin()) continue; |
3244 | if (!$listowner && $user->getID() == $this->_ownerID) continue; |
3245 | if (!$listguest && $user->isGuest()) continue; |
3246 | $userIDs .= (strlen($userIDs)==0 ? "" : ", ") . $userAccess->getUserID(); |
3247 | } |
3248 | |
3249 | // Construct a query against the users table to identify those users |
3250 | // that have read access to this document, either directly through an |
3251 | // ACL entry, by virtue of ownership or by having administrative rights |
3252 | // on the database. |
3253 | $queryStr=""; |
3254 | /* If default access is less then read, $userIDs and $groupIDs contains |
3255 | * a list of user with read access |
3256 | */ |
3257 | if ($defAccess < M_READ) { |
3258 | if (strlen($groupIDs)>0) { |
3259 | $queryStr = "SELECT `tblUsers`.* FROM `tblUsers` ". |
3260 | "LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`userID`=`tblUsers`.`id` ". |
3261 | "WHERE `tblGroupMembers`.`groupID` IN (". $groupIDs .") ". |
3262 | "AND `tblUsers`.`role` != ".SeedDMS_Core_User::role_guest." UNION "; |
3263 | } |
3264 | $queryStr .= |
3265 | "SELECT `tblUsers`.* FROM `tblUsers` ". |
3266 | "WHERE (`tblUsers`.`role` != ".SeedDMS_Core_User::role_guest.") ". |
3267 | "AND ((`tblUsers`.`id` = ". $this->_ownerID . ") ". |
3268 | "OR (`tblUsers`.`role` = ".SeedDMS_Core_User::role_admin.")". |
3269 | (strlen($userIDs) == 0 ? "" : " OR (`tblUsers`.`id` IN (". $userIDs ."))"). |
3270 | ") ORDER BY `login`"; |
3271 | } |
3272 | /* If default access is equal or greater than M_READ, $userIDs and |
3273 | * $groupIDs contains a list of user without read access |
3274 | */ |
3275 | else { |
3276 | if (strlen($groupIDs)>0) { |
3277 | $queryStr = "SELECT `tblUsers`.* FROM `tblUsers` ". |
3278 | "LEFT JOIN `tblGroupMembers` ON `tblGroupMembers`.`userID`=`tblUsers`.`id` ". |
3279 | "WHERE `tblGroupMembers`.`groupID` NOT IN (". $groupIDs .")". |
3280 | "AND `tblUsers`.`role` != ".SeedDMS_Core_User::role_guest." ". |
3281 | (strlen($userIDs) == 0 ? "" : " AND (`tblUsers`.`id` NOT IN (". $userIDs ."))")." UNION "; |
3282 | } else { |
3283 | $queryStr .= |
3284 | "SELECT `tblUsers`.* FROM `tblUsers` ". |
3285 | "WHERE `tblUsers`.`role` != ".SeedDMS_Core_User::role_guest." ". |
3286 | (strlen($userIDs) == 0 ? "" : " AND (`tblUsers`.`id` NOT IN (". $userIDs ."))")." UNION "; |
3287 | } |
3288 | $queryStr .= |
3289 | "SELECT `tblUsers`.* FROM `tblUsers` ". |
3290 | "WHERE (`tblUsers`.`id` = ". $this->_ownerID . ") ". |
3291 | "OR (`tblUsers`.`role` = ".SeedDMS_Core_User::role_admin.") ". |
3292 | // "UNION ". |
3293 | // "SELECT `tblUsers`.* FROM `tblUsers` ". |
3294 | // "WHERE `tblUsers`.`role` != ".SeedDMS_Core_User::role_guest." ". |
3295 | // (strlen($userIDs) == 0 ? "" : " AND (`tblUsers`.`id` NOT IN (". $userIDs ."))"). |
3296 | " ORDER BY `login`"; |
3297 | } |
3298 | $resArr = $db->getResultArray($queryStr); |
3299 | if (!is_bool($resArr)) { |
3300 | foreach ($resArr as $row) { |
3301 | $user = $this->_dms->getUser($row['id']); |
3302 | if (!$listadmin && $user->isAdmin()) continue; |
3303 | if (!$listowner && $user->getID() == $this->_ownerID) continue; |
3304 | $this->_readAccessList["users"][] = $user; |
3305 | } |
3306 | } |
3307 | |
3308 | // Assemble the list of groups that have read access to the document. |
3309 | $queryStr=""; |
3310 | if ($defAccess < M_READ) { |
3311 | if (strlen($groupIDs)>0) { |
3312 | $queryStr = "SELECT `tblGroups`.* FROM `tblGroups` ". |
3313 | "WHERE `tblGroups`.`id` IN (". $groupIDs .") ORDER BY `name`"; |
3314 | } |
3315 | } |
3316 | else { |
3317 | if (strlen($groupIDs)>0) { |
3318 | $queryStr = "SELECT `tblGroups`.* FROM `tblGroups` ". |
3319 | "WHERE `tblGroups`.`id` NOT IN (". $groupIDs .") ORDER BY `name`"; |
3320 | } |
3321 | else { |
3322 | $queryStr = "SELECT `tblGroups`.* FROM `tblGroups` ORDER BY `name`"; |
3323 | } |
3324 | } |
3325 | if (strlen($queryStr)>0) { |
3326 | $resArr = $db->getResultArray($queryStr); |
3327 | if (!is_bool($resArr)) { |
3328 | foreach ($resArr as $row) { |
3329 | $group = $this->_dms->getGroup($row["id"]); |
3330 | $this->_readAccessList["groups"][] = $group; |
3331 | } |
3332 | } |
3333 | } |
3334 | } |
3335 | return $this->_readAccessList; |
3336 | } /* }}} */ |
3337 | |
3338 | /** |
3339 | * Get the internally used folderList which stores the ids of folders from |
3340 | * the root folder to the parent folder. |
3341 | * |
3342 | * @return string column separated list of folder ids |
3343 | */ |
3344 | function getFolderList() { /* {{{ */ |
3345 | $db = $this->_dms->getDB(); |
3346 | |
3347 | $queryStr = "SELECT `folderList` FROM `tblDocuments` WHERE id = ".$this->_id; |
3348 | $resArr = $db->getResultArray($queryStr); |
3349 | if (is_bool($resArr) && !$resArr) |
3350 | return false; |
3351 | |
3352 | return $resArr[0]['folderList']; |
3353 | } /* }}} */ |
3354 | |
3355 | /** |
3356 | * Checks the internal data of the document and repairs it. |
3357 | * Currently, this function only repairs an incorrect folderList |
3358 | * |
3359 | * @return boolean true on success, otherwise false |
3360 | */ |
3361 | function repair() { /* {{{ */ |
3362 | $db = $this->_dms->getDB(); |
3363 | |
3364 | $curfolderlist = $this->getFolderList(); |
3365 | |
3366 | // calculate the folderList of the folder |
3367 | $parent = $this->getFolder(); |
3368 | $pathPrefix=""; |
3369 | $path = $parent->getPath(); |
3370 | foreach ($path as $f) { |
3371 | $pathPrefix .= ":".$f->getID(); |
3372 | } |
3373 | if (strlen($pathPrefix)>1) { |
3374 | $pathPrefix .= ":"; |
3375 | } |
3376 | if($curfolderlist != $pathPrefix) { |
3377 | $queryStr = "UPDATE `tblDocuments` SET `folderList`='".$pathPrefix."' WHERE `id` = ". $this->_id; |
3378 | $res = $db->getResult($queryStr); |
3379 | if (!$res) |
3380 | return false; |
3381 | } |
3382 | return true; |
3383 | } /* }}} */ |
3384 | |
3385 | /** |
3386 | * Calculate the disk space including all versions of the document |
3387 | * |
3388 | * This is done by using the internal database field storing the |
3389 | * filesize of a document version. |
3390 | * |
3391 | * @return integer total disk space in Bytes |
3392 | */ |
3393 | function getUsedDiskSpace(): int { /* {{{ */ |
3394 | $db = $this->_dms->getDB(); |
3395 | |
3396 | $queryStr = "SELECT SUM(`fileSize`) sum FROM `tblDocumentContent` WHERE `document` = " . $this->_id; |
3397 | $resArr = $db->getResultArray($queryStr); |
3398 | if (is_bool($resArr) && $resArr == false) |
3399 | return false; |
3400 | |
3401 | return (int) $resArr[0]['sum']; |
3402 | } /* }}} */ |
3403 | |
3404 | /** |
3405 | * Returns a list of events happend during the life of the document |
3406 | * |
3407 | * This includes the creation of new versions, approval and reviews, etc. |
3408 | * |
3409 | * @return array list of events |
3410 | */ |
3411 | function getTimeline() { /* {{{ */ |
3412 | $db = $this->_dms->getDB(); |
3413 | |
3414 | $timeline = array(); |
3415 | |
3416 | $lc=$this->getLatestContent(); |
3417 | $queryStr = "SELECT `revisiondate`, `version` FROM `tblDocumentContent` WHERE `document` = " . $this->_id . " AND `version` = " . $lc->getVersion(); |
3418 | $resArr = $db->getResultArray($queryStr); |
3419 | if (is_bool($resArr) && $resArr == false) |
3420 | return false; |
3421 | |
3422 | foreach ($resArr as $row) { |
3423 | if($row['revisiondate'] && substr($row['revisiondate'], 0, 4) != '0000') |
3424 | $timeline[] = array('date'=>substr($row['revisiondate'], 0, 10)." 00:00:00", 'allday'=>true, 'msg'=>'Scheduled revision of version '.$row['version'], 'type'=>'scheduled_revision', 'version'=>$row['version'], 'document'=>$this, 'params'=>array($row['version'])); |
3425 | } |
3426 | |
3427 | $queryStr = "SELECT * FROM `tblDocumentFiles` WHERE `document` = " . $this->_id; |
3428 | $resArr = $db->getResultArray($queryStr); |
3429 | if (is_bool($resArr) && $resArr == false) |
3430 | return false; |
3431 | |
3432 | foreach ($resArr as $row) { |
3433 | $date = date('Y-m-d H:i:s', (int) $row['date']); |
3434 | $timeline[] = array('date'=>$date, 'msg'=>'Added attachment "'.$row['name'].'"', 'document'=>$this, 'type'=>'add_file', 'fileid'=>$row['id']); |
3435 | } |
3436 | |
3437 | $queryStr= |
3438 | "SELECT `tblDocumentStatus`.*, `tblDocumentStatusLog`.`statusLogID`,`tblDocumentStatusLog`.`status`, ". |
3439 | "`tblDocumentStatusLog`.`comment`, `tblDocumentStatusLog`.`date`, ". |
3440 | "`tblDocumentStatusLog`.`userID` ". |
3441 | "FROM `tblDocumentStatus` ". |
3442 | "LEFT JOIN `tblDocumentStatusLog` USING (`statusID`) ". |
3443 | "WHERE `tblDocumentStatus`.`documentID` = '". $this->_id ."' ". |
3444 | "ORDER BY `tblDocumentStatusLog`.`statusLogID` DESC"; |
3445 | $resArr = $db->getResultArray($queryStr); |
3446 | if (is_bool($resArr) && !$resArr) |
3447 | return false; |
3448 | |
3449 | /* The above query will also contain entries where a document status exists |
3450 | * but no status log entry. Those records will have no date and must be |
3451 | * skipped. |
3452 | */ |
3453 | foreach ($resArr as $row) { |
3454 | if($row['date']) { |
3455 | $date = $row['date']; |
3456 | $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']); |
3457 | } |
3458 | } |
3459 | return $timeline; |
3460 | } /* }}} */ |
3461 | |
3462 | /** |
3463 | * Transfers the document to a new user |
3464 | * |
3465 | * This method not just sets a new owner of the document but also |
3466 | * transfers the document links, attachments and locks to the new user. |
3467 | * |
3468 | * @return boolean true if successful, otherwise false |
3469 | */ |
3470 | function transferToUser($newuser) { /* {{{ */ |
3471 | $db = $this->_dms->getDB(); |
3472 | |
3473 | if($newuser->getId() == $this->_ownerID) |
3474 | return true; |
3475 | |
3476 | $db->startTransaction(); |
3477 | $queryStr = "UPDATE `tblDocuments` SET `owner` = ".$newuser->getId()." WHERE `id` = " . $this->_id; |
3478 | if (!$db->getResult($queryStr)) { |
3479 | $db->rollbackTransaction(); |
3480 | return false; |
3481 | } |
3482 | |
3483 | $queryStr = "UPDATE `tblDocumentLocks` SET `userID` = ".$newuser->getId()." WHERE `document` = " . $this->_id . " AND `userID` = ".$this->_ownerID; |
3484 | if (!$db->getResult($queryStr)) { |
3485 | $db->rollbackTransaction(); |
3486 | return false; |
3487 | } |
3488 | |
3489 | $queryStr = "UPDATE `tblDocumentLinks` SET `userID` = ".$newuser->getId()." WHERE `document` = " . $this->_id . " AND `userID` = ".$this->_ownerID; |
3490 | if (!$db->getResult($queryStr)) { |
3491 | $db->rollbackTransaction(); |
3492 | return false; |
3493 | } |
3494 | |
3495 | $queryStr = "UPDATE `tblDocumentFiles` SET `userID` = ".$newuser->getId()." WHERE `document` = " . $this->_id . " AND `userID` = ".$this->_ownerID; |
3496 | if (!$db->getResult($queryStr)) { |
3497 | $db->rollbackTransaction(); |
3498 | return false; |
3499 | } |
3500 | |
3501 | $this->_ownerID = $newuser->getID(); |
3502 | $this->_owner = $newuser; |
3503 | |
3504 | $db->commitTransaction(); |
3505 | return true; |
3506 | } /* }}} */ |
3507 | |
3508 | } /* }}} */ |
3509 | |
3510 | |
3511 | /** |
3512 | * Class to represent content of a document |
3513 | * |
3514 | * Each document has content attached to it, often called a 'version' of the |
3515 | * document. The document content represents a file on the disk with some |
3516 | * meta data stored in the database. A document content has a version number |
3517 | * which is incremented with each replacement of the old content. Old versions |
3518 | * are kept unless they are explicitly deleted by |
3519 | * {@link SeedDMS_Core_Document::removeContent()}. |
3520 | * |
3521 | * @category DMS |
3522 | * @package SeedDMS_Core |
3523 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli, |
3524 | * Uwe Steinmann <uwe@steinmann.cx> |
3525 | * @copyright Copyright (C) 2002-2005 Markus Westphal, |
3526 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli, |
3527 | * 2010-2022 Uwe Steinmann |
3528 | * @version Release: @package_version@ |
3529 | */ |
3530 | class SeedDMS_Core_DocumentContent extends SeedDMS_Core_Object { /* {{{ */ |
3531 | /** |
3532 | * @var object document |
3533 | */ |
3534 | protected $_document; |
3535 | |
3536 | /** |
3537 | * @var integer version |
3538 | */ |
3539 | protected $_version; |
3540 | |
3541 | /** |
3542 | * @var string comment |
3543 | */ |
3544 | protected $_comment; |
3545 | |
3546 | /** |
3547 | * @var string date |
3548 | */ |
3549 | protected $_date; |
3550 | |
3551 | /** |
3552 | * @var integer $_userID |
3553 | */ |
3554 | protected $_userID; |
3555 | |
3556 | /** |
3557 | * @var object $_user |
3558 | */ |
3559 | protected $_user; |
3560 | |
3561 | /** |
3562 | * @var string dir on disk (deprecated) |
3563 | */ |
3564 | protected $_dir; |
3565 | |
3566 | /** |
3567 | * @var string original file name |
3568 | */ |
3569 | protected $_orgFileName; |
3570 | |
3571 | /** |
3572 | * @var string file type (actually the extension without the leading dot) |
3573 | */ |
3574 | protected $_fileType; |
3575 | |
3576 | /** |
3577 | * @var string mime type |
3578 | */ |
3579 | protected $_mimeType; |
3580 | |
3581 | /** |
3582 | * @var string checksum of content |
3583 | */ |
3584 | protected $_checksum; |
3585 | |
3586 | /** |
3587 | * @var int size of content file |
3588 | */ |
3589 | protected $_fileSize; |
3590 | |
3591 | /** |
3592 | * @var object workflow |
3593 | */ |
3594 | protected $_workflow; |
3595 | |
3596 | /** |
3597 | * @var object workflow state |
3598 | */ |
3599 | protected $_workflowState; |
3600 | |
3601 | /** |
3602 | * @var int $_status state |
3603 | */ |
3604 | protected $_status; |
3605 | |
3606 | /** |
3607 | * @var int $_reviewStatus state |
3608 | */ |
3609 | protected $_reviewStatus; |
3610 | |
3611 | /** |
3612 | * @var int $_approvalStatus state |
3613 | */ |
3614 | protected $_approvalStatus; |
3615 | |
3616 | /** |
3617 | * @var int $_receiptStatus state |
3618 | */ |
3619 | protected $_receiptStatus; |
3620 | |
3621 | /** |
3622 | * @var int $_revisionStatus state |
3623 | */ |
3624 | protected $_revisionStatus; |
3625 | |
3626 | /** |
3627 | * @var string date of revision |
3628 | */ |
3629 | protected $_revisionDate; |
3630 | |
3631 | /** |
3632 | * @var object dms |
3633 | */ |
3634 | public $_dms; |
3635 | |
3636 | /** |
3637 | * Recalculate the status of a document |
3638 | * |
3639 | * The methods checks the review and approval status and sets the |
3640 | * status of the document accordingly. |
3641 | * |
3642 | * If status is S_RELEASED and the version has a workflow, then set |
3643 | * the status to S_IN_WORKFLOW |
3644 | * If status is S_RELEASED and there are reviewers => set status S_DRAFT_REV |
3645 | * If status is S_RELEASED or S_DRAFT_REV and there are approvers => set |
3646 | * status S_DRAFT_APP |
3647 | * If status is draft and there are no approver and no reviewers => set |
3648 | * status to S_RELEASED |
3649 | * The status of a document with the current status S_OBSOLETE, S_REJECTED, |
3650 | * S_NEEDS_CORRECTION or S_EXPIRED will not be changed unless the parameter |
3651 | * $ignorecurrentstatus is set to true. |
3652 | * |
3653 | * This method may not be called after a negative approval or review to |
3654 | * recalculated the status, because |
3655 | * it doesn't take a defeating approval or review into account. This method |
3656 | * does not set the status to S_REJECTED! It will |
3657 | * just check for a pending workflow, approval or review and set the status |
3658 | * accordingly, e.g. after the list of reviewers or appovers has been |
3659 | * modified. If there is not pending workflow, approval or review the |
3660 | * status will be set to S_RELEASED. |
3661 | * |
3662 | * This method will call {@see SeedDMS_Core_DocumentContent::setStatus()} |
3663 | * which checks if the status has actually changed. This is, why this |
3664 | * function can be called at any time without harm to the status log. |
3665 | * The $initialstatus can be set, to define the status set when no other |
3666 | * status is set. This happens if the document has no |
3667 | * |
3668 | * @param boolean $ignorecurrentstatus ignore the current status and |
3669 | * recalculate a new status in any case |
3670 | * @param object $user the user initiating this method |
3671 | * @param string $msg message stored in status log when status is set |
3672 | * @param integer $initialstatus status to be set if no other status is set |
3673 | */ |
3674 | function verifyStatus($ignorecurrentstatus=false, $user=null, $msg='', $initialstatus=S_RELEASED) { /* {{{ */ |
3675 | |
3676 | unset($this->_status); |
3677 | $st=$this->getStatus(); |
3678 | |
3679 | /* Documents already obsoleted, rejected or expired will not change |
3680 | * its status anymore, unless explicitly requested. Be aware, that |
3681 | * this method has an unsufficient check for negative reviews and |
3682 | * approvals. A document in status S_REJECTED may become S_RELEASED |
3683 | * if there is at least one positive review or approval. |
3684 | */ |
3685 | if (!$ignorecurrentstatus && ($st["status"]==S_OBSOLETE || $st["status"]==S_REJECTED || $st["status"]==S_EXPIRED || $st["status"]==S_NEEDS_CORRECTION)) return $st['status']; |
3686 | |
3687 | $this->_workflow = null; // force to be reloaded from DB |
3688 | $hasworkflow = $this->getWorkflow() ? true : false; |
3689 | |
3690 | /* $pendingReview will be set when there are still open reviews */ |
3691 | $pendingReview=false; |
3692 | /* $hasReview will be set if there is at least one positiv review */ |
3693 | $hasReview=false; |
3694 | unset($this->_reviewStatus); // force to be reloaded from DB |
3695 | $reviewStatus=$this->getReviewStatus(); |
3696 | if (is_array($reviewStatus) && count($reviewStatus)>0) { |
3697 | foreach ($reviewStatus as $r){ |
3698 | if ($r["status"]==0){ |
3699 | $pendingReview=true; |
3700 | break; |
3701 | } elseif($r["status"]==1){ |
3702 | $hasReview=true; |
3703 | } |
3704 | } |
3705 | } |
3706 | |
3707 | /* $pendingApproval will be set when there are still open approvals */ |
3708 | $pendingApproval=false; |
3709 | /* $hasApproval will be set if there is at least one positiv review */ |
3710 | $hasApproval=false; |
3711 | unset($this->_approvalStatus); // force to be reloaded from DB |
3712 | $approvalStatus=$this->getApprovalStatus(); |
3713 | if (is_array($approvalStatus) && count($approvalStatus)>0) { |
3714 | foreach ($approvalStatus as $a){ |
3715 | if ($a["status"]==0){ |
3716 | $pendingApproval=true; |
3717 | break; |
3718 | } elseif($a["status"]==1){ |
3719 | $hasApproval=true; |
3720 | } |
3721 | } |
3722 | } |
3723 | $pendingRevision=false; |
3724 | $hasRevision=false; |
3725 | $needsCorrection=false; |
3726 | unset($this->_revisionStatus); // force to be reloaded from DB |
3727 | $revsisionStatus=$this->getRevisionStatus(); |
3728 | if (is_array($revsisionStatus) && count($revsisionStatus)>0) { |
3729 | foreach ($revsisionStatus as $a){ |
3730 | if ($a["status"]==0){ |
3731 | $pendingRevision=true; |
3732 | break; |
3733 | } elseif($a["status"]==1){ |
3734 | $hasRevision=true; |
3735 | } elseif($a["status"]==-1){ |
3736 | $needsCorrection=true; |
3737 | } |
3738 | } |
3739 | } |
3740 | |
3741 | $ret = false; |
3742 | /* First check for a running workflow or open reviews, approvals, revisions. */ |
3743 | if ($hasworkflow) { $newstatus = S_IN_WORKFLOW; $ret = $this->setStatus(S_IN_WORKFLOW,$msg,$user); } |
3744 | elseif ($pendingReview) { $newstatus = S_DRAFT_REV; $ret = $this->setStatus(S_DRAFT_REV,$msg,$user); } |
3745 | elseif ($pendingApproval) { $newstatus = S_DRAFT_APP; $ret = $this->setStatus(S_DRAFT_APP,$msg,$user); } |
3746 | elseif ($pendingRevision) { $newstatus = S_IN_REVISION; $ret = $this->setStatus(S_IN_REVISION,$msg,$user); } |
3747 | /* This point will only be reached if there is no pending workflow, review, |
3748 | * approval or revision but the current status is one of S_DRAFT_REV, |
3749 | * S_DRAFT_APP or S_IN_REVISION. This can happen if formely set reviewers, |
3750 | * approvers, revisors are completly removed. In case of S_DRAFT_REV and |
3751 | * S_DRAFT_APP the document will go back into its initial status. If a |
3752 | * positive review or approval was found the document will be released. |
3753 | * Be aware that negative reviews or approvals are not taken into account, |
3754 | * because in that case the document must have been rejected before calling |
3755 | * this function. FIXME: this is a problem if the parameter $ignorecurrentstatus |
3756 | * was set, because an already rejected document may be released with just |
3757 | * one positive review or approval disregarding any negative reviews or |
3758 | * approvals. |
3759 | * A document in status S_IN_REVISION will be treated differently. |
3760 | * It takes negative revisions into account! |
3761 | * |
3762 | * A document in status S_DRAFT will never go into S_RELEASED and document |
3763 | * already released will never go back at this point into the given |
3764 | * initial status, which can only by S_DRAFT or S_RELEASED |
3765 | */ |
3766 | elseif ($st["status"]!=S_DRAFT && $st["status"]!=S_RELEASED ) { |
3767 | if($st["status"]==S_DRAFT_REV || $st["status"]==S_DRAFT_APP) { |
3768 | if($hasReview || $hasApproval) { $newstatus = S_RELEASED; $ret = $this->setStatus(S_RELEASED,$msg,$user); } |
3769 | else { $newstatus = $initialstatus; $ret = $this->setStatus($initialstatus,$msg,$user); } |
3770 | } elseif($st["status"]==S_IN_REVISION) { |
3771 | if($needsCorrection) { $newstatus = S_NEEDS_CORRECTION; $ret = $this->setStatus(S_NEEDS_CORRECTION,$msg,$user); } |
3772 | else { |
3773 | $newstatus = S_RELEASED; |
3774 | $ret = $this->finishRevision($user, S_RELEASED, 'Finished revision workflow', $msg); |
3775 | } |
3776 | } elseif($st["status"]==S_EXPIRED) { |
3777 | $newstatus = S_RELEASED; $ret = $this->setStatus(S_RELEASED,$msg,$user); |
3778 | } elseif($st["status"]==S_IN_WORKFLOW) { |
3779 | $newstatus = $initialstatus; $ret = $this->setStatus($initialstatus,$msg,$user); |
3780 | } |
3781 | } |
3782 | |
3783 | return $ret ? $newstatus : $ret; |
3784 | } /* }}} */ |
3785 | |
3786 | function __construct($id, $document, $version, $comment, $date, $userID, $dir, $orgFileName, $fileType, $mimeType, $fileSize=0, $checksum='', $revisionDate=null) { /* {{{ */ |
3787 | parent::__construct($id); |
3788 | $this->_document = $document; |
3789 | $this->_version = (int) $version; |
3790 | $this->_comment = $comment; |
3791 | $this->_date = (int) $date; |
3792 | $this->_userID = (int) $userID; |
3793 | $this->_user = null; |
3794 | $this->_dir = $dir; |
3795 | $this->_orgFileName = $orgFileName; |
3796 | $this->_fileType = $fileType; |
3797 | $this->_mimeType = $mimeType; |
3798 | $this->_dms = $document->getDMS(); |
3799 | if(!$fileSize) { |
3800 | $this->_fileSize = SeedDMS_Core_File::fileSize($this->_dms->contentDir . $this->getPath()); |
3801 | } else { |
3802 | $this->_fileSize = $fileSize; |
3803 | } |
3804 | $this->_checksum = $checksum; |
3805 | $this->_workflow = null; |
3806 | $this->_workflowState = null; |
3807 | $this->_revisionDate = $revisionDate; |
3808 | } /* }}} */ |
3809 | |
3810 | /** |
3811 | * Return an document content by its id |
3812 | * |
3813 | * @param integer $id id of document |
3814 | * @param SeedDMS_Core_DMS $dms |
3815 | * @return bool|SeedDMS_Core_DocumentContent instance of SeedDMS_Core_DocumentContent |
3816 | * if document content exists, null if document does not exist, false in case of error |
3817 | */ |
3818 | public static function getInstance($id, $dms) { /* {{{ */ |
3819 | $db = $dms->getDB(); |
3820 | |
3821 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `id` = " . (int) $id; |
3822 | $resArr = $db->getResultArray($queryStr); |
3823 | if (is_bool($resArr) && $resArr == false) |
3824 | return false; |
3825 | if (count($resArr) != 1) |
3826 | return null; |
3827 | $row = $resArr[0]; |
3828 | |
3829 | $classname = $dms->getClassname('documentcontent'); |
3830 | $user = $dms->getLoggedInUser(); |
3831 | $document = $dms->getDocument($row['document']); |
3832 | $document->setDMS($dms); |
3833 | /** @var SeedDMS_Core_DocumentContent $documentcontent */ |
3834 | $content = new $classname($row["id"], $document, $row["version"], $row["comment"], $row["date"], $row["createdBy"], $row["dir"], $row["orgFileName"], $row["fileType"], $row["mimeType"], $row['fileSize'], $row['checksum'], $row['revisiondate']); |
3835 | if($user) { |
3836 | if($content->getAccessMode($user) >= M_READ) |
3837 | return $content; |
3838 | } else { |
3839 | return $content; |
3840 | } |
3841 | return null; |
3842 | } /* }}} */ |
3843 | |
3844 | /** |
3845 | * Check if this object is of type 'documentcontent'. |
3846 | * |
3847 | * @param string $type type of object |
3848 | */ |
3849 | public function isType($type) { /* {{{ */ |
3850 | return $type == 'documentcontent'; |
3851 | } /* }}} */ |
3852 | |
3853 | function getVersion() { return $this->_version; } |
3854 | function getComment() { return $this->_comment; } |
3855 | function getDate() { return $this->_date; } |
3856 | function getOriginalFileName() { return $this->_orgFileName; } |
3857 | function getFileType() { return $this->_fileType; } |
3858 | function getFileName(){ return $this->_version . $this->_fileType; } |
3859 | /** |
3860 | * getDir and the corresponding database table field are deprecated |
3861 | */ |
3862 | function __getDir() { return $this->_dir; } |
3863 | function getMimeType() { return $this->_mimeType; } |
3864 | function getRevisionDate() { return $this->_revisionDate; } |
3865 | function getDocument() { return $this->_document; } |
3866 | |
3867 | function getUser() { /* {{{ */ |
3868 | if (!isset($this->_user)) |
3869 | $this->_user = $this->_document->getDMS()->getUser($this->_userID); |
3870 | return $this->_user; |
3871 | } /* }}} */ |
3872 | |
3873 | /** |
3874 | * Return path of file on disk relative to the content directory |
3875 | * |
3876 | * Since version 5.1.13 a single '.' in the fileType will be skipped. |
3877 | * On Windows a file named 'name.' will be saved as 'name' but the fileType |
3878 | * will contain the a single '.'. |
3879 | * |
3880 | * @return string path of file on disc |
3881 | */ |
3882 | function getPath() { return $this->_document->getDir() . $this->_version . $this->_fileType; } |
3883 | |
3884 | function setRevisionDate($date = false) { /* {{{ */ |
3885 | $db = $this->_document->getDMS()->getDB(); |
3886 | |
3887 | if(!$date) |
3888 | $queryStr = "UPDATE `tblDocumentContent` SET `revisiondate` = null WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
3889 | elseif($date == 'now') |
3890 | $queryStr = "UPDATE `tblDocumentContent` SET `revisiondate` = ".$db->getCurrentDatetime()." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
3891 | else |
3892 | $queryStr = "UPDATE `tblDocumentContent` SET `revisiondate` = ".$db->qstr($date)." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
3893 | if (!$db->getResult($queryStr)) |
3894 | return false; |
3895 | |
3896 | $this->_revisionDate = $date; |
3897 | |
3898 | return true; |
3899 | } /* }}} */ |
3900 | |
3901 | /** |
3902 | * Set upload date of document content |
3903 | * |
3904 | * @param string $date date must be a timestamp or in the format 'Y-m-d H:i:s' |
3905 | * |
3906 | * @return boolean true on success, otherwise false |
3907 | */ |
3908 | function setDate($date = false) { /* {{{ */ |
3909 | $db = $this->_document->getDMS()->getDB(); |
3910 | |
3911 | if(!$date) |
3912 | $date = time(); |
3913 | else { |
3914 | if(is_string($date) && SeedDMS_Core_DMS::checkDate($date, 'Y-m-d H:i:s')) { |
3915 | $date = strtotime($date); |
3916 | } elseif(is_numeric($date)) |
3917 | $date = (int) $date; |
3918 | else |
3919 | return false; |
3920 | } |
3921 | |
3922 | $queryStr = "UPDATE `tblDocumentContent` SET `date` = ". $date." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
3923 | if (!$db->getResult($queryStr)) |
3924 | return false; |
3925 | |
3926 | $this->_date = $date; |
3927 | |
3928 | return true; |
3929 | } /* }}} */ |
3930 | |
3931 | function getFileSize() { /* {{{ */ |
3932 | return $this->_fileSize; |
3933 | } /* }}} */ |
3934 | |
3935 | /** |
3936 | * Set file size by reading the file |
3937 | */ |
3938 | function setFileSize() { /* {{{ */ |
3939 | $filesize = SeedDMS_Core_File::fileSize($this->_dms->contentDir . $this->_document->getDir() . $this->getFileName()); |
3940 | if($filesize === false) |
3941 | return false; |
3942 | |
3943 | $db = $this->_document->getDMS()->getDB(); |
3944 | $queryStr = "UPDATE `tblDocumentContent` SET `fileSize` = ".$filesize." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
3945 | if (!$db->getResult($queryStr)) |
3946 | return false; |
3947 | $this->_fileSize = $filesize; |
3948 | |
3949 | return true; |
3950 | } /* }}} */ |
3951 | |
3952 | function getChecksum() { /* {{{ */ |
3953 | return $this->_checksum; |
3954 | } /* }}} */ |
3955 | |
3956 | /** |
3957 | * Set checksum by reading the file |
3958 | */ |
3959 | function setChecksum() { /* {{{ */ |
3960 | $checksum = SeedDMS_Core_File::checksum($this->_dms->contentDir . $this->_document->getDir() . $this->getFileName()); |
3961 | if($checksum === false) |
3962 | return false; |
3963 | |
3964 | $db = $this->_document->getDMS()->getDB(); |
3965 | $queryStr = "UPDATE `tblDocumentContent` SET `checksum` = ".$db->qstr($checksum)." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
3966 | if (!$db->getResult($queryStr)) |
3967 | return false; |
3968 | $this->_checksum = $checksum; |
3969 | |
3970 | return true; |
3971 | } /* }}} */ |
3972 | |
3973 | /** |
3974 | * Set file type by evaluating the mime type |
3975 | */ |
3976 | function setFileType() { /* {{{ */ |
3977 | $mimetype = $this->getMimeType(); |
3978 | |
3979 | $expect = SeedDMS_Core_File::fileExtension($mimetype); |
3980 | if($expect && '.'.$expect != $this->_fileType) { |
3981 | $db = $this->_document->getDMS()->getDB(); |
3982 | $db->startTransaction(); |
3983 | $queryStr = "UPDATE `tblDocumentContent` SET `fileType`='.".$expect."' WHERE `id` = ". $this->_id; |
3984 | $res = $db->getResult($queryStr); |
3985 | if ($res) { |
3986 | if(!SeedDMS_Core_File::renameFile($this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType, $this->_dms->contentDir.$this->_document->getDir() . $this->_version . '.' . $expect)) { |
3987 | $db->rollbackTransaction(); |
3988 | } else { |
3989 | $this->_fileType = '.'.$expect; |
3990 | $db->commitTransaction(); |
3991 | return true; |
3992 | } |
3993 | } else { |
3994 | $db->rollbackTransaction(); |
3995 | } |
3996 | } |
3997 | |
3998 | return false; |
3999 | } /* }}} */ |
4000 | |
4001 | function setMimeType($newMimetype) { /* {{{ */ |
4002 | $db = $this->_document->getDMS()->getDB(); |
4003 | |
4004 | if(!$newMimetype) |
4005 | return false; |
4006 | |
4007 | $newMimetype = trim($newMimetype); |
4008 | |
4009 | if(!$newMimetype) |
4010 | return false; |
4011 | |
4012 | $queryStr = "UPDATE `tblDocumentContent` SET `mimeType` = ".$db->qstr($newMimetype)." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
4013 | if (!$db->getResult($queryStr)) |
4014 | return false; |
4015 | |
4016 | $this->_mimeType = $newMimetype; |
4017 | |
4018 | return true; |
4019 | } /* }}} */ |
4020 | |
4021 | function setComment($newComment) { /* {{{ */ |
4022 | $db = $this->_document->getDMS()->getDB(); |
4023 | |
4024 | /* Check if 'onPreSetVersionComment' callback is set */ |
4025 | if(isset($this->_dms->callbacks['onPreSetVersionComment'])) { |
4026 | foreach($this->_dms->callbacks['onPreSetVersionComment'] as $callback) { |
4027 | $ret = call_user_func($callback[0], $callback[1], $this, $newComment); |
4028 | if(is_bool($ret)) |
4029 | return $ret; |
4030 | } |
4031 | } |
4032 | |
4033 | $queryStr = "UPDATE `tblDocumentContent` SET `comment` = ".$db->qstr($newComment)." WHERE `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version; |
4034 | if (!$db->getResult($queryStr)) |
4035 | return false; |
4036 | |
4037 | $this->_comment = $newComment; |
4038 | |
4039 | /* Check if 'onPostSetVersionComment' callback is set */ |
4040 | if(isset($this->_dms->callbacks['onPostSetVersionComment'])) { |
4041 | foreach($this->_dms->callbacks['onPostSetVersionComment'] as $callback) { |
4042 | $ret = call_user_func($callback[0], $callback[1], $this, $oldComment); |
4043 | if(is_bool($ret)) |
4044 | return $ret; |
4045 | } |
4046 | } |
4047 | |
4048 | return true; |
4049 | } /* }}} */ |
4050 | |
4051 | /** |
4052 | * Get the latest status of the content |
4053 | * |
4054 | * The status of the content reflects its current review, approval or workflow |
4055 | * state. A status can be a negative or positive number or 0. A negative |
4056 | * numbers indicate a missing approval, review or an obsolete content. |
4057 | * Positive numbers indicate some kind of approval or workflow being |
4058 | * active, but not necessarily a release. |
4059 | * S_DRAFT_REV, 0 |
4060 | * S_DRAFT_APP, 1 |
4061 | * S_RELEASED, 2 |
4062 | * S_IN_WORKFLOW, 3 |
4063 | * S_IN_REVISION, 4 |
4064 | * S_REJECTED, -1 |
4065 | * S_OBSOLETE, -2 |
4066 | * S_EXPIRED, -3 |
4067 | * When a content is inserted and does not need approval nor review, |
4068 | * then its status is set to S_RELEASED immediately. Any change of |
4069 | * the status is monitored in the table tblDocumentStatusLog. This |
4070 | * function will always return the latest entry for the content. |
4071 | * |
4072 | * @return array latest record from tblDocumentStatusLog |
4073 | */ |
4074 | function getStatus($limit=1) { /* {{{ */ |
4075 | $db = $this->_document->getDMS()->getDB(); |
4076 | |
4077 | if (!is_numeric($limit)) return false; |
4078 | |
4079 | // Retrieve the current overall status of the content represented by |
4080 | // this object. |
4081 | if (!isset($this->_status)) { |
4082 | $queryStr= |
4083 | "SELECT `tblDocumentStatus`.*, `tblDocumentStatusLog`.`status`, ". |
4084 | "`tblDocumentStatusLog`.`comment`, `tblDocumentStatusLog`.`date`, ". |
4085 | "`tblDocumentStatusLog`.`userID` ". |
4086 | "FROM `tblDocumentStatus` ". |
4087 | "LEFT JOIN `tblDocumentStatusLog` USING (`statusID`) ". |
4088 | "WHERE `tblDocumentStatus`.`documentID` = '". $this->_document->getID() ."' ". |
4089 | "AND `tblDocumentStatus`.`version` = '". $this->_version ."' ". |
4090 | "ORDER BY `tblDocumentStatusLog`.`statusLogID` DESC LIMIT ".(int) $limit; |
4091 | |
4092 | $res = $db->getResultArray($queryStr); |
4093 | if (is_bool($res) && !$res) |
4094 | return false; |
4095 | if (count($res)!=1) |
4096 | return false; |
4097 | $this->_status = $res[0]; |
4098 | } |
4099 | return $this->_status; |
4100 | } /* }}} */ |
4101 | |
4102 | /** |
4103 | * Get current and former states of the document content |
4104 | * |
4105 | * @param integer $limit if not set all log entries will be returned |
4106 | * @return array list of status changes |
4107 | */ |
4108 | function getStatusLog($limit=0) { /* {{{ */ |
4109 | $db = $this->_document->getDMS()->getDB(); |
4110 | |
4111 | if (!is_numeric($limit)) return false; |
4112 | |
4113 | $queryStr= |
4114 | "SELECT `tblDocumentStatus`.*, `tblDocumentStatusLog`.`status`, ". |
4115 | "`tblDocumentStatusLog`.`comment`, `tblDocumentStatusLog`.`date`, ". |
4116 | "`tblDocumentStatusLog`.`userID` ". |
4117 | "FROM `tblDocumentStatus` ". |
4118 | "LEFT JOIN `tblDocumentStatusLog` USING (`statusID`) ". |
4119 | "WHERE `tblDocumentStatus`.`documentID` = '". $this->_document->getID() ."' ". |
4120 | "AND `tblDocumentStatus`.`version` = '". $this->_version ."' ". |
4121 | "ORDER BY `tblDocumentStatusLog`.`statusLogID` DESC "; |
4122 | if($limit) |
4123 | $queryStr .= "LIMIT ".(int) $limit; |
4124 | |
4125 | $res = $db->getResultArray($queryStr); |
4126 | if (is_bool($res) && !$res) |
4127 | return false; |
4128 | |
4129 | return $res; |
4130 | } /* }}} */ |
4131 | |
4132 | /** |
4133 | * Set the status of the content |
4134 | * Setting the status means to add another entry into the table |
4135 | * tblDocumentStatusLog. The method returns also false if the status |
4136 | * is already set on the value passed to the method. |
4137 | * |
4138 | * @param integer $status new status of content |
4139 | * @param string $comment comment for this status change |
4140 | * @param object $updateUser user initiating the status change |
4141 | * @param string $date date in the format 'Y-m-d H:i:s' |
4142 | * |
4143 | * @return boolean true on success, otherwise false |
4144 | */ |
4145 | function setStatus($status, $comment, $updateUser, $date='') { /* {{{ */ |
4146 | $db = $this->_document->getDMS()->getDB(); |
4147 | |
4148 | if (!is_numeric($status)) return false; |
4149 | |
4150 | /* return an error if $updateuser is not set */ |
4151 | if(!$updateUser || !$updateUser->isType('user')) |
4152 | return false; |
4153 | |
4154 | // If the supplied value lies outside of the accepted range, return an |
4155 | // error. |
4156 | if ($status < S_LOWEST_STATUS || $status > S_HIGHEST_STATUS) { |
4157 | return false; |
4158 | } |
4159 | |
4160 | // Retrieve the current overall status of the content represented by |
4161 | // this object, if it hasn't been done already. |
4162 | if (!isset($this->_status)) { |
4163 | $this->getStatus(); |
4164 | } |
4165 | if ($this->_status["status"]==$status) { |
4166 | return true; |
4167 | } |
4168 | if($date) { |
4169 | if(!SeedDMS_Core_DMS::checkDate($date, 'Y-m-d H:i:s')) |
4170 | return false; |
4171 | $ddate = $db->qstr($date); |
4172 | } else |
4173 | $ddate = $db->getCurrentDatetime(); |
4174 | $db->startTransaction(); |
4175 | $queryStr = "INSERT INTO `tblDocumentStatusLog` (`statusID`, `status`, `comment`, `date`, `userID`) ". |
4176 | "VALUES ('". $this->_status["statusID"] ."', '". (int) $status ."', ".$db->qstr($comment).", ".$ddate.", '". $updateUser->getID() ."')"; |
4177 | $res = $db->getResult($queryStr); |
4178 | if (is_bool($res) && !$res) { |
4179 | $db->rollbackTransaction(); |
4180 | return false; |
4181 | } |
4182 | |
4183 | /* Check if 'onSetStatus' callback is set */ |
4184 | if(isset($this->_dms->callbacks['onSetStatus'])) { |
4185 | foreach($this->_dms->callbacks['onSetStatus'] as $callback) { |
4186 | $ret = call_user_func($callback[0], $callback[1], $this, $updateUser, $this->_status["status"], $status); |
4187 | if(is_bool($ret)) { |
4188 | unset($this->_status); |
4189 | if($ret) |
4190 | $db->commitTransaction(); |
4191 | else |
4192 | $db->rollbackTransaction(); |
4193 | return $ret; |
4194 | } |
4195 | } |
4196 | } |
4197 | |
4198 | $db->commitTransaction(); |
4199 | unset($this->_status); |
4200 | return true; |
4201 | } /* }}} */ |
4202 | |
4203 | /** |
4204 | * Rewrites the complete status log |
4205 | * |
4206 | * Attention: this function is highly dangerous. |
4207 | * It removes an existing status log and rewrites it. |
4208 | * This method was added for importing an xml dump. |
4209 | * |
4210 | * @param array $statuslog new status log with the newest log entry first. |
4211 | * @return boolean true on success, otherwise false |
4212 | */ |
4213 | function rewriteStatusLog($statuslog) { /* {{{ */ |
4214 | $db = $this->_document->getDMS()->getDB(); |
4215 | |
4216 | $queryStr= "SELECT `tblDocumentStatus`.* FROM `tblDocumentStatus` WHERE `tblDocumentStatus`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentStatus`.`version` = '". $this->_version ."' "; |
4217 | $res = $db->getResultArray($queryStr); |
4218 | if (is_bool($res) && !$res) |
4219 | return false; |
4220 | |
4221 | $statusID = $res[0]['statusID']; |
4222 | |
4223 | $db->startTransaction(); |
4224 | |
4225 | /* First, remove the old entries */ |
4226 | $queryStr = "DELETE FROM `tblDocumentStatusLog` WHERE `statusID`=".$statusID; |
4227 | if (!$db->getResult($queryStr)) { |
4228 | $db->rollbackTransaction(); |
4229 | return false; |
4230 | } |
4231 | |
4232 | /* Second, insert the new entries */ |
4233 | $statuslog = array_reverse($statuslog); |
4234 | foreach($statuslog as $log) { |
4235 | if(!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) { |
4236 | $db->rollbackTransaction(); |
4237 | return false; |
4238 | } |
4239 | $queryStr = "INSERT INTO `tblDocumentStatusLog` (`statusID`, `status`, `comment`, `date`, `userID`) ". |
4240 | "VALUES ('".$statusID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".$log['user']->getID().")"; |
4241 | if (!$db->getResult($queryStr)) { |
4242 | $db->rollbackTransaction(); |
4243 | return false; |
4244 | } |
4245 | } |
4246 | |
4247 | $db->commitTransaction(); |
4248 | return true; |
4249 | } /* }}} */ |
4250 | |
4251 | |
4252 | /** |
4253 | * Returns the access mode similar to a document |
4254 | * |
4255 | * There is no real access mode for document content, so this is more |
4256 | * like a virtual access mode, derived from the status of the document |
4257 | * content. The function checks if {@link SeedDMS_Core_DMS::noReadForStatus} |
4258 | * contains the status of the version and returns M_NONE if it exists and |
4259 | * the user is not involved in a workflow or review/approval/revision. |
4260 | * This method is called by all functions that returns the content e.g. |
4261 | * {@link SeedDMS_Core_Document::getLatestContent()} |
4262 | * It is also used by {@link SeedDMS_Core_Document::getAccessMode()} to |
4263 | * prevent access on the whole document if there is no accessible version. |
4264 | * |
4265 | * FIXME: This function only works propperly if $u is the currently logged in |
4266 | * user, because noReadForStatus will be set for this user. |
4267 | * FIXED: instead of using $dms->noReadForStatus it is take from the user's role |
4268 | * |
4269 | * @param object $u user |
4270 | * @return integer either M_NONE or M_READ |
4271 | */ |
4272 | function getAccessMode($u) { /* {{{ */ |
4273 | $dms = $this->_document->getDMS(); |
4274 | |
4275 | /* Check if 'onCheckAccessDocumentContent' callback is set */ |
4276 | if(isset($this->_dms->callbacks['onCheckAccessDocumentContent'])) { |
4277 | foreach($this->_dms->callbacks['onCheckAccessDocumentContent'] as $callback) { |
4278 | if(($ret = call_user_func($callback[0], $callback[1], $this, $u)) > 0) { |
4279 | return $ret; |
4280 | } |
4281 | } |
4282 | } |
4283 | |
4284 | // return M_READ; |
4285 | |
4286 | if(!$u) |
4287 | return M_NONE; |
4288 | |
4289 | /* If read access isn't further restricted by status, than grant read access */ |
4290 | /* Old code |
4291 | if(!$dms->noReadForStatus) |
4292 | return M_READ; |
4293 | $noReadForStatus = $dms->noReadForStatus; |
4294 | */ |
4295 | $noReadForStatus = $u->getRole()->getNoAccess(); |
4296 | if(!$noReadForStatus) |
4297 | return M_READ; |
4298 | |
4299 | /* If the current status is not in list of status without read access, then grant read access */ |
4300 | if(!in_array($this->getStatus()['status'], $noReadForStatus)) |
4301 | return M_READ; |
4302 | |
4303 | /* Administrators have unrestricted access */ |
4304 | if ($u->isAdmin()) return M_READ; |
4305 | |
4306 | /* The owner of the document has unrestricted access */ |
4307 | $owner = $this->_document->getOwner(); |
4308 | if ($u->getID() == $owner->getID()) return M_READ; |
4309 | |
4310 | /* Read/Write access on the document will also grant access on the version */ |
4311 | if($this->_document->getAccessMode($u) >= M_READWRITE) return M_READ; |
4312 | |
4313 | /* At this point the current status is in the list of status without read access. |
4314 | * The only way to still gain read access is, if the user is involved in the |
4315 | * process, e.g. is a reviewer, approver or an active person in the workflow. |
4316 | */ |
4317 | $s = $this->getStatus(); |
4318 | switch($s['status']) { |
4319 | case S_DRAFT_REV: |
4320 | $status = $this->getReviewStatus(); |
4321 | foreach ($status as $r) { |
4322 | if($r['status'] != -2) // Check if reviewer was removed |
4323 | switch ($r["type"]) { |
4324 | case 0: // Reviewer is an individual. |
4325 | if($u->getId() == $r["required"]) |
4326 | return M_READ; |
4327 | break; |
4328 | case 1: // Reviewer is a group. |
4329 | $required = $dms->getGroup($r["required"]); |
4330 | if (is_object($required) && $required->isMember($u)) |
4331 | return M_READ; |
4332 | break; |
4333 | } |
4334 | } |
4335 | break; |
4336 | case S_DRAFT_APP: |
4337 | $status = $this->getApprovalStatus(); |
4338 | foreach ($status as $r) { |
4339 | if($r['status'] != -2) // Check if approver was removed |
4340 | switch ($r["type"]) { |
4341 | case 0: // Reviewer is an individual. |
4342 | if($u->getId() == $r["required"]) |
4343 | return M_READ; |
4344 | break; |
4345 | case 1: // Reviewer is a group. |
4346 | $required = $dms->getGroup($r["required"]); |
4347 | if (is_object($required) && $required->isMember($u)) |
4348 | return M_READ; |
4349 | break; |
4350 | } |
4351 | } |
4352 | break; |
4353 | case S_RELEASED: |
4354 | break; |
4355 | case S_IN_WORKFLOW: |
4356 | if(!$this->_workflow) |
4357 | $this->getWorkflow(); |
4358 | |
4359 | if($this->_workflow) { |
4360 | if (!$this->_workflowState) |
4361 | $this->getWorkflowState(); |
4362 | $transitions = $this->_workflow['workflow']->getNextTransitions($this->_workflowState); |
4363 | foreach($transitions as $transition) { |
4364 | if($this->triggerWorkflowTransitionIsAllowed($u, $transition)) |
4365 | return M_READ; |
4366 | } |
4367 | } |
4368 | break; |
4369 | case S_IN_REVISION: |
4370 | $status = $this->getRevisionStatus(); |
4371 | foreach ($status as $r) { |
4372 | if($r['status'] != -2) // Check if reviewer was removed |
4373 | switch ($r["type"]) { |
4374 | case 0: // Revisor is an individual. |
4375 | if($u->getId() == $r["required"]) |
4376 | return M_READ; |
4377 | break; |
4378 | case 1: // Revisor is a group. |
4379 | $required = $dms->getGroup($r["required"]); |
4380 | if (is_object($required) && $required->isMember($u)) |
4381 | return M_READ; |
4382 | break; |
4383 | } |
4384 | } |
4385 | break; |
4386 | case S_REJECTED: |
4387 | break; |
4388 | case S_OBSOLETE: |
4389 | break; |
4390 | case S_EXPIRED: |
4391 | break; |
4392 | } |
4393 | |
4394 | return M_NONE; |
4395 | } /* }}} */ |
4396 | |
4397 | /** |
4398 | * Return a list of all reviewers separated by individuals and groups |
4399 | * This list will not take the review log into account. Therefore it |
4400 | * can contain reviewers which has actually been deleted as a reviewer. |
4401 | * |
4402 | * @return array|bool|null |
4403 | */ |
4404 | function getReviewers() { /* {{{ */ |
4405 | $dms = $this->_document->getDMS(); |
4406 | $db = $dms->getDB(); |
4407 | |
4408 | $queryStr= |
4409 | "SELECT * FROM `tblDocumentReviewers` WHERE `version`='".$this->_version |
4410 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4411 | |
4412 | $recs = $db->getResultArray($queryStr); |
4413 | if (is_bool($recs)) |
4414 | return false; |
4415 | $reviewers = array('i'=>array(), 'g'=>array()); |
4416 | foreach($recs as $rec) { |
4417 | if($rec['type'] == 0) { |
4418 | if($u = $dms->getUser($rec['required'])) |
4419 | $reviewers['i'][] = $u; |
4420 | } elseif($rec['type'] == 1) { |
4421 | if($g = $dms->getGroup($rec['required'])) |
4422 | $reviewers['g'][] = $g; |
4423 | } |
4424 | } |
4425 | return $reviewers; |
4426 | } /* }}} */ |
4427 | |
4428 | /** |
4429 | * Get the current review status of the document content |
4430 | * The review status is a list of reviewers and its current status |
4431 | * |
4432 | * @param integer $limit the number of recent status changes per reviewer |
4433 | * @return array list of review status |
4434 | */ |
4435 | function getReviewStatus($limit=1) { /* {{{ */ |
4436 | $db = $this->_document->getDMS()->getDB(); |
4437 | |
4438 | if (!is_numeric($limit)) return false; |
4439 | |
4440 | // Retrieve the current status of each assigned reviewer for the content |
4441 | // represented by this object. |
4442 | // FIXME: caching was turned off to make list of review log in ViewDocument |
4443 | // possible |
4444 | if (1 || !isset($this->_reviewStatus)) { |
4445 | /* First get a list of all reviews for this document content */ |
4446 | $queryStr= |
4447 | "SELECT `reviewID` FROM `tblDocumentReviewers` WHERE `version`='".$this->_version |
4448 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4449 | $recs = $db->getResultArray($queryStr); |
4450 | if (is_bool($recs) && !$recs) |
4451 | return false; |
4452 | $this->_reviewStatus = array(); |
4453 | if($recs) { |
4454 | foreach($recs as $rec) { |
4455 | $queryStr= |
4456 | "SELECT `tblDocumentReviewers`.*, `tblDocumentReviewLog`.`reviewLogID`, `tblDocumentReviewLog`.`status`, ". |
4457 | "`tblDocumentReviewLog`.`comment`, `tblDocumentReviewLog`.`date`, ". |
4458 | "`tblDocumentReviewLog`.`userID`, `tblUsers`.`fullName`, `tblGroups`.`name` AS `groupName` ". |
4459 | "FROM `tblDocumentReviewers` ". |
4460 | "LEFT JOIN `tblDocumentReviewLog` USING (`reviewID`) ". |
4461 | "LEFT JOIN `tblUsers` on `tblUsers`.`id` = `tblDocumentReviewers`.`required`". |
4462 | "LEFT JOIN `tblGroups` on `tblGroups`.`id` = `tblDocumentReviewers`.`required`". |
4463 | "WHERE `tblDocumentReviewers`.`reviewID` = '". $rec['reviewID'] ."' ". |
4464 | "ORDER BY `tblDocumentReviewLog`.`reviewLogID` DESC LIMIT ".(int) $limit; |
4465 | |
4466 | $res = $db->getResultArray($queryStr); |
4467 | if (is_bool($res) && !$res) { |
4468 | unset($this->_reviewStatus); |
4469 | return false; |
4470 | } |
4471 | foreach($res as &$t) { |
4472 | $filename = $this->_dms->contentDir . $this->_document->getDir().'r'.$t['reviewLogID']; |
4473 | if(SeedDMS_Core_File::file_exists($filename)) |
4474 | $t['file'] = $filename; |
4475 | else |
4476 | $t['file'] = ''; |
4477 | } |
4478 | $this->_reviewStatus = array_merge($this->_reviewStatus, $res); |
4479 | } |
4480 | } |
4481 | } |
4482 | return $this->_reviewStatus; |
4483 | } /* }}} */ |
4484 | |
4485 | /** |
4486 | * Get the latest entries from the review log of the document content |
4487 | * |
4488 | * @param integer $limit the number of log entries returned, defaults to 1 |
4489 | * @return array list of review log entries |
4490 | */ |
4491 | function getReviewLog($limit=1) { /* {{{ */ |
4492 | $db = $this->_document->getDMS()->getDB(); |
4493 | |
4494 | if (!is_numeric($limit)) return false; |
4495 | |
4496 | $queryStr= |
4497 | "SELECT * FROM `tblDocumentReviewLog` LEFT JOIN `tblDocumentReviewers` ON `tblDocumentReviewLog`.`reviewID` = `tblDocumentReviewers`.`reviewID` WHERE `version`='".$this->_version |
4498 | ."' AND `documentID` = '". $this->_document->getID() ."' " |
4499 | ."ORDER BY `tblDocumentReviewLog`.`reviewLogID` DESC LIMIT ".(int) $limit; |
4500 | $recs = $db->getResultArray($queryStr); |
4501 | if (is_bool($recs) && !$recs) |
4502 | return false; |
4503 | return($recs); |
4504 | } /* }}} */ |
4505 | |
4506 | /** |
4507 | * Rewrites the complete review log |
4508 | * |
4509 | * Attention: this function is highly dangerous. |
4510 | * It removes an existing review log and rewrites it. |
4511 | * This method was added for importing an xml dump. |
4512 | * |
4513 | * @param array $reviewlog new status log with the newest log entry first. |
4514 | * @return boolean true on success, otherwise false |
4515 | */ |
4516 | function rewriteReviewLog($reviewers) { /* {{{ */ |
4517 | $db = $this->_document->getDMS()->getDB(); |
4518 | |
4519 | $queryStr= "SELECT `tblDocumentReviewers`.* FROM `tblDocumentReviewers` WHERE `tblDocumentReviewers`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentReviewers`.`version` = '". $this->_version ."' "; |
4520 | $res = $db->getResultArray($queryStr); |
4521 | if (is_bool($res) && !$res) |
4522 | return false; |
4523 | |
4524 | $db->startTransaction(); |
4525 | |
4526 | if($res) { |
4527 | foreach($res as $review) { |
4528 | $reviewID = $review['reviewID']; |
4529 | |
4530 | /* First, remove the old entries */ |
4531 | $queryStr = "DELETE FROM `tblDocumentReviewLog` WHERE `reviewID`=".$reviewID; |
4532 | if (!$db->getResult($queryStr)) { |
4533 | $db->rollbackTransaction(); |
4534 | return false; |
4535 | } |
4536 | |
4537 | $queryStr = "DELETE FROM `tblDocumentReviewers` WHERE `reviewID`=".$reviewID; |
4538 | if (!$db->getResult($queryStr)) { |
4539 | $db->rollbackTransaction(); |
4540 | return false; |
4541 | } |
4542 | } |
4543 | } |
4544 | |
4545 | /* Second, insert the new entries */ |
4546 | foreach($reviewers as $review) { |
4547 | $queryStr = "INSERT INTO `tblDocumentReviewers` (`documentID`, `version`, `type`, `required`) ". |
4548 | "VALUES ('".$this->_document->getID()."', '".$this->_version."', ".$review['type'] .", ".(is_object($review['required']) ? $review['required']->getID() : (int) $review['required']).")"; |
4549 | if (!$db->getResult($queryStr)) { |
4550 | $db->rollbackTransaction(); |
4551 | return false; |
4552 | } |
4553 | $reviewID = $db->getInsertID('tblDocumentReviewers', 'reviewID'); |
4554 | $reviewlog = array_reverse($review['logs']); |
4555 | foreach($reviewlog as $log) { |
4556 | if(!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) { |
4557 | $db->rollbackTransaction(); |
4558 | return false; |
4559 | } |
4560 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ". |
4561 | "VALUES ('".$reviewID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".(is_object($log['user']) ? $log['user']->getID() : (int) $log['user']).")"; |
4562 | if (!$db->getResult($queryStr)) { |
4563 | $db->rollbackTransaction(); |
4564 | return false; |
4565 | } |
4566 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID'); |
4567 | if(!empty($log['file'])) { |
4568 | SeedDMS_Core_File::copyFile($log['file'], $this->_dms->contentDir . $this->_document->getDir() . 'r' . $reviewLogID); |
4569 | } |
4570 | } |
4571 | } |
4572 | |
4573 | $db->commitTransaction(); |
4574 | return true; |
4575 | } /* }}} */ |
4576 | |
4577 | /** |
4578 | * Return a list of all approvers separated by individuals and groups |
4579 | * This list will not take the approval log into account. Therefore it |
4580 | * can contain approvers which has actually been deleted as an approver. |
4581 | * |
4582 | * @return array|bool|null |
4583 | */ |
4584 | function getApprovers() { /* {{{ */ |
4585 | $dms = $this->_document->getDMS(); |
4586 | $db = $dms->getDB(); |
4587 | |
4588 | $queryStr= |
4589 | "SELECT * FROM `tblDocumentApprovers` WHERE `version`='".$this->_version |
4590 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4591 | |
4592 | $recs = $db->getResultArray($queryStr); |
4593 | if (is_bool($recs)) |
4594 | return false; |
4595 | $approvers = array('i'=>array(), 'g'=>array()); |
4596 | foreach($recs as $rec) { |
4597 | if($rec['type'] == 0) { |
4598 | if($u = $dms->getUser($rec['required'])) |
4599 | $approvers['i'][] = $u; |
4600 | } elseif($rec['type'] == 1) { |
4601 | if($g = $dms->getGroup($rec['required'])) |
4602 | $approvers['g'][] = $g; |
4603 | } |
4604 | } |
4605 | return $approvers; |
4606 | } /* }}} */ |
4607 | |
4608 | /** |
4609 | * Get the current approval status of the document content |
4610 | * The approval status is a list of approvers and its current status |
4611 | * |
4612 | * @param integer $limit the number of recent status changes per approver |
4613 | * @return array list of approval status |
4614 | */ |
4615 | function getApprovalStatus($limit=1) { /* {{{ */ |
4616 | $db = $this->_document->getDMS()->getDB(); |
4617 | |
4618 | if (!is_numeric($limit)) return false; |
4619 | |
4620 | // Retrieve the current status of each assigned approver for the content |
4621 | // represented by this object. |
4622 | // FIXME: caching was turned off to make list of approval log in ViewDocument |
4623 | // possible |
4624 | if (1 || !isset($this->_approvalStatus)) { |
4625 | /* First get a list of all approvals for this document content */ |
4626 | $queryStr= |
4627 | "SELECT `approveID` FROM `tblDocumentApprovers` WHERE `version`='".$this->_version |
4628 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4629 | $recs = $db->getResultArray($queryStr); |
4630 | if (is_bool($recs) && !$recs) |
4631 | return false; |
4632 | $this->_approvalStatus = array(); |
4633 | if($recs) { |
4634 | foreach($recs as $rec) { |
4635 | $queryStr= |
4636 | "SELECT `tblDocumentApprovers`.*, `tblDocumentApproveLog`.`approveLogID`, `tblDocumentApproveLog`.`status`, ". |
4637 | "`tblDocumentApproveLog`.`comment`, `tblDocumentApproveLog`.`date`, ". |
4638 | "`tblDocumentApproveLog`.`userID`, `tblUsers`.`fullName`, `tblGroups`.`name` AS `groupName` ". |
4639 | "FROM `tblDocumentApprovers` ". |
4640 | "LEFT JOIN `tblDocumentApproveLog` USING (`approveID`) ". |
4641 | "LEFT JOIN `tblUsers` on `tblUsers`.`id` = `tblDocumentApprovers`.`required` ". |
4642 | "LEFT JOIN `tblGroups` on `tblGroups`.`id` = `tblDocumentApprovers`.`required`". |
4643 | "WHERE `tblDocumentApprovers`.`approveID` = '". $rec['approveID'] ."' ". |
4644 | "ORDER BY `tblDocumentApproveLog`.`approveLogID` DESC LIMIT ".(int) $limit; |
4645 | |
4646 | $res = $db->getResultArray($queryStr); |
4647 | if (is_bool($res) && !$res) { |
4648 | unset($this->_approvalStatus); |
4649 | return false; |
4650 | } |
4651 | foreach($res as &$t) { |
4652 | $filename = $this->_dms->contentDir . $this->_document->getDir().'a'.$t['approveLogID']; |
4653 | if(SeedDMS_Core_File::file_exists($filename)) |
4654 | $t['file'] = $filename; |
4655 | else |
4656 | $t['file'] = ''; |
4657 | } |
4658 | $this->_approvalStatus = array_merge($this->_approvalStatus, $res); |
4659 | } |
4660 | } |
4661 | } |
4662 | return $this->_approvalStatus; |
4663 | } /* }}} */ |
4664 | |
4665 | /** |
4666 | * Get the latest entries from the approval log of the document content |
4667 | * |
4668 | * @param integer $limit the number of log entries returned, defaults to 1 |
4669 | * @return array list of approval log entries |
4670 | */ |
4671 | function getApproveLog($limit=1) { /* {{{ */ |
4672 | $db = $this->_document->getDMS()->getDB(); |
4673 | |
4674 | if (!is_numeric($limit)) return false; |
4675 | |
4676 | $queryStr= |
4677 | "SELECT * FROM `tblDocumentApproveLog` LEFT JOIN `tblDocumentApprovers` ON `tblDocumentApproveLog`.`approveID` = `tblDocumentApprovers`.`approveID` WHERE `version`='".$this->_version |
4678 | ."' AND `documentID` = '". $this->_document->getID() ."' " |
4679 | ."ORDER BY `tblDocumentApproveLog`.`approveLogID` DESC LIMIT ".(int) $limit; |
4680 | $recs = $db->getResultArray($queryStr); |
4681 | if (is_bool($recs) && !$recs) |
4682 | return false; |
4683 | return($recs); |
4684 | } /* }}} */ |
4685 | |
4686 | /** |
4687 | * Rewrites the complete approval log |
4688 | * |
4689 | * Attention: this function is highly dangerous. |
4690 | * It removes an existing review log and rewrites it. |
4691 | * This method was added for importing an xml dump. |
4692 | * |
4693 | * @param array $reviewlog new status log with the newest log entry first. |
4694 | * @return boolean true on success, otherwise false |
4695 | */ |
4696 | function rewriteApprovalLog($reviewers) { /* {{{ */ |
4697 | $db = $this->_document->getDMS()->getDB(); |
4698 | |
4699 | $queryStr= "SELECT `tblDocumentApprovers`.* FROM `tblDocumentApprovers` WHERE `tblDocumentApprovers`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentApprovers`.`version` = '". $this->_version ."' "; |
4700 | $res = $db->getResultArray($queryStr); |
4701 | if (is_bool($res) && !$res) |
4702 | return false; |
4703 | |
4704 | $db->startTransaction(); |
4705 | |
4706 | if($res) { |
4707 | foreach($res as $review) { |
4708 | $reviewID = $review['reviewID']; |
4709 | |
4710 | /* First, remove the old entries */ |
4711 | $queryStr = "DELETE FROM `tblDocumentApproveLog` WHERE `approveID`=".$reviewID; |
4712 | if (!$db->getResult($queryStr)) { |
4713 | $db->rollbackTransaction(); |
4714 | return false; |
4715 | } |
4716 | |
4717 | $queryStr = "DELETE FROM `tblDocumentApprovers` WHERE `approveID`=".$reviewID; |
4718 | if (!$db->getResult($queryStr)) { |
4719 | $db->rollbackTransaction(); |
4720 | return false; |
4721 | } |
4722 | } |
4723 | } |
4724 | |
4725 | /* Second, insert the new entries */ |
4726 | foreach($reviewers as $review) { |
4727 | $queryStr = "INSERT INTO `tblDocumentApprovers` (`documentID`, `version`, `type`, `required`) ". |
4728 | "VALUES ('".$this->_document->getID()."', '".$this->_version."', ".$review['type'] .", ".(is_object($review['required']) ? $review['required']->getID() : (int) $review['required']).")"; |
4729 | if (!$db->getResult($queryStr)) { |
4730 | $db->rollbackTransaction(); |
4731 | return false; |
4732 | } |
4733 | $reviewID = $db->getInsertID('tblDocumentApprovers', 'approveID'); |
4734 | $reviewlog = array_reverse($review['logs']); |
4735 | foreach($reviewlog as $log) { |
4736 | if(!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) { |
4737 | $db->rollbackTransaction(); |
4738 | return false; |
4739 | } |
4740 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ". |
4741 | "VALUES ('".$reviewID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".(is_object($log['user']) ? $log['user']->getID() : (int) $log['user']).")"; |
4742 | if (!$db->getResult($queryStr)) { |
4743 | $db->rollbackTransaction(); |
4744 | return false; |
4745 | } |
4746 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID'); |
4747 | if(!empty($log['file'])) { |
4748 | SeedDMS_Core_File::copyFile($log['file'], $this->_dms->contentDir . $this->_document->getDir() . 'a' . $approveLogID); |
4749 | } |
4750 | } |
4751 | } |
4752 | |
4753 | $db->commitTransaction(); |
4754 | return true; |
4755 | } /* }}} */ |
4756 | |
4757 | /** |
4758 | * Return a list of all recipients separated by individuals and groups |
4759 | * This list will not take the receipt log into account. Therefore it |
4760 | * can contain recipients which has actually been deleted as a recipient. |
4761 | * |
4762 | * @return array|bool|null |
4763 | */ |
4764 | function getRecipients() { /* {{{ */ |
4765 | $dms = $this->_document->getDMS(); |
4766 | $db = $dms->getDB(); |
4767 | |
4768 | $queryStr= |
4769 | "SELECT * FROM `tblDocumentRecipients` WHERE `version`='".$this->_version |
4770 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4771 | |
4772 | $recs = $db->getResultArray($queryStr); |
4773 | if (is_bool($recs)) |
4774 | return false; |
4775 | $recipients = array('i'=>array(), 'g'=>array()); |
4776 | foreach($recs as $rec) { |
4777 | if($rec['type'] == 0) { |
4778 | if($u = $dms->getUser($rec['required'])) |
4779 | $recipients['i'][] = $u; |
4780 | } elseif($rec['type'] == 1) { |
4781 | if($g = $dms->getGroup($rec['required'])) |
4782 | $recipients['g'][] = $g; |
4783 | } |
4784 | } |
4785 | return $recipients; |
4786 | } /* }}} */ |
4787 | |
4788 | /** |
4789 | * Get the current receipt status of the document content |
4790 | * The receipt status is a list of receipts |
4791 | * |
4792 | * @param integer $limit maximum number of status changes per receiver |
4793 | * @return array list of receipts |
4794 | */ |
4795 | function getReceiptStatus($limit=1) { /* {{{ */ |
4796 | $db = $this->_document->getDMS()->getDB(); |
4797 | |
4798 | if (!is_numeric($limit)) return false; |
4799 | |
4800 | // Retrieve the current status of each assigned reviewer for the content |
4801 | // represented by this object. |
4802 | // When just the last log entry for each recipient is needed then a single |
4803 | // sql statement is much faster than the code below which first retrieves |
4804 | // all receivers and than the logs |
4805 | // FIXME: caching was turned off to make list of review log in ViewDocument |
4806 | // possible |
4807 | if($limit == 1) { |
4808 | /* The following sql statement is somewhat optimized. The first join is |
4809 | * crucial because it should first take the table with the least number |
4810 | * of records and join the other tables. ttreceiptid join tblDocumentRecipients |
4811 | * is faster than tblDocumentRecipients join ttreceiptid |
4812 | */ |
4813 | if (!$db->createTemporaryTable("ttreceiptid")) { |
4814 | return false; |
4815 | } |
4816 | $queryStr= |
4817 | "SELECT `tblDocumentRecipients`.*, `tblDocumentReceiptLog`.`receiptLogID`, `tblDocumentReceiptLog`.`status`, `tblDocumentReceiptLog`.`comment`, `tblDocumentReceiptLog`.`date`, `tblDocumentReceiptLog`.`userID`, `tblUsers`.`fullName`, `tblGroups`.`name` FROM `ttreceiptid` LEFT JOIN `tblDocumentRecipients` ON `tblDocumentRecipients`.`receiptID`=`ttreceiptid`.`receiptID` LEFT JOIN `tblDocumentReceiptLog` ON `ttreceiptid`.`maxLogID`=`tblDocumentReceiptLog`.`receiptLogID` LEFT JOIN `tblUsers` ON `tblDocumentRecipients`.`required`=`tblUsers`.`id` LEFT JOIN `tblGroups` ON `tblDocumentRecipients`.`required`=`tblGroups`.`id` WHERE `version`='".$this->_version |
4818 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4819 | $recs = $db->getResultArray($queryStr); |
4820 | if (is_bool($recs) && !$recs) { |
4821 | unset($this->_receiptStatus); |
4822 | return false; |
4823 | } |
4824 | $this->_receiptStatus = $recs; |
4825 | } elseif (1 || !isset($this->_receiptStatus)) { |
4826 | /* First get a list of all receipts for this document content */ |
4827 | $queryStr= |
4828 | "SELECT `receiptID` FROM `tblDocumentRecipients` WHERE `version`='".$this->_version |
4829 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4830 | $recs = $db->getResultArray($queryStr); |
4831 | if (is_bool($recs) && !$recs) |
4832 | return false; |
4833 | $this->_receiptStatus = array(); |
4834 | if($recs) { |
4835 | foreach($recs as $rec) { |
4836 | $queryStr= |
4837 | "SELECT `tblDocumentRecipients`.*, `tblDocumentReceiptLog`.`receiptLogID`, ". |
4838 | "`tblDocumentReceiptLog`.`status`, ". |
4839 | "`tblDocumentReceiptLog`.`comment`, ". |
4840 | "`tblDocumentReceiptLog`.`date`, ". |
4841 | "`tblDocumentReceiptLog`.`userID`, `tblUsers`.`fullName`, `tblGroups`.`name` AS `groupName` ". |
4842 | "FROM `tblDocumentRecipients` ". |
4843 | "LEFT JOIN `tblDocumentReceiptLog` USING (`receiptID`) ". |
4844 | "LEFT JOIN `tblUsers` on `tblUsers`.`id` = `tblDocumentRecipients`.`required` ". |
4845 | "LEFT JOIN `tblGroups` on `tblGroups`.`id` = `tblDocumentRecipients`.`required` ". |
4846 | "WHERE `tblDocumentRecipients`.`receiptID` = '". $rec['receiptID'] ."' ". |
4847 | "ORDER BY `tblDocumentReceiptLog`.`receiptLogID` DESC LIMIT ".(int) $limit; |
4848 | |
4849 | $res = $db->getResultArray($queryStr); |
4850 | if (is_bool($res) && !$res) { |
4851 | unset($this->_receiptStatus); |
4852 | return false; |
4853 | } |
4854 | $this->_receiptStatus = array_merge($this->_receiptStatus, $res); |
4855 | } |
4856 | } |
4857 | } |
4858 | return $this->_receiptStatus; |
4859 | } /* }}} */ |
4860 | |
4861 | /** |
4862 | * Get the latest entries from the receipt log of the document content |
4863 | * |
4864 | * @param integer $limit the number of log entries returned, defaults to 1 |
4865 | * @return array list of receiptlog entries |
4866 | */ |
4867 | function getReceiptLog($limit=1) { /* {{{ */ |
4868 | $db = $this->_document->getDMS()->getDB(); |
4869 | |
4870 | if (!is_numeric($limit)) return false; |
4871 | |
4872 | $queryStr= |
4873 | "SELECT * FROM `tblDocumentReceiptLog` LEFT JOIN `tblDocumentRecipients` ON `tblDocumentReceiptLog`.`receiptID` = `tblDocumentRecipients`.`receiptID` WHERE `version`='".$this->_version |
4874 | ."' AND `documentID` = '". $this->_document->getID() ."' " |
4875 | ."ORDER BY `tblDocumentReceiptLog`.`receiptLogID` DESC LIMIT ".(int) $limit; |
4876 | $recs = $db->getResultArray($queryStr); |
4877 | if (is_bool($recs) && !$recs) |
4878 | return false; |
4879 | return($recs); |
4880 | } /* }}} */ |
4881 | |
4882 | /** |
4883 | * Rewrites the complete receipt log |
4884 | * |
4885 | * Attention: this function is highly dangerous. |
4886 | * It removes an existing receipt log and rewrites it. |
4887 | * This method was added for importing an xml dump. |
4888 | * |
4889 | * @param array $receiptlog new status log with the newest log entry first. |
4890 | * @return boolean true on success, otherwise false |
4891 | */ |
4892 | function rewriteReceiptLog($recipients) { /* {{{ */ |
4893 | $db = $this->_document->getDMS()->getDB(); |
4894 | |
4895 | $queryStr= "SELECT `tblDocumentRecipients`.* FROM `tblDocumentRecipients` WHERE `tblDocumentRecipients`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentRecipients`.`version` = '". $this->_version ."' "; |
4896 | $res = $db->getResultArray($queryStr); |
4897 | if (is_bool($res) && !$res) |
4898 | return false; |
4899 | |
4900 | $db->startTransaction(); |
4901 | |
4902 | if($res) { |
4903 | foreach($res as $receipt) { |
4904 | $receiptID = $receipt['receiptID']; |
4905 | |
4906 | /* First, remove the old entries */ |
4907 | $queryStr = "DELETE from `tblDocumentReceiptLog` where `receiptID`=".$receiptID; |
4908 | if (!$db->getResult($queryStr)) { |
4909 | $db->rollbackTransaction(); |
4910 | return false; |
4911 | } |
4912 | |
4913 | $queryStr = "DELETE from `tblDocumentRecipients` where `receiptID`=".$receiptID; |
4914 | if (!$db->getResult($queryStr)) { |
4915 | $db->rollbackTransaction(); |
4916 | return false; |
4917 | } |
4918 | } |
4919 | } |
4920 | |
4921 | /* Second, insert the new entries */ |
4922 | foreach($recipients as $receipt) { |
4923 | $queryStr = "INSERT INTO `tblDocumentRecipients` (`documentID`, `version`, `type`, `required`) ". |
4924 | "VALUES ('".$this->_document->getID()."', '".$this->_version."', ".$receipt['type'] .", ".(is_object($receipt['required']) ? $receipt['required']->getID() : (int) $receipt['required']).")"; |
4925 | if (!$db->getResult($queryStr)) { |
4926 | $db->rollbackTransaction(); |
4927 | return false; |
4928 | } |
4929 | $receiptID = $db->getInsertID('tblDocumentRecipients', 'receiptID'); |
4930 | $receiptlog = array_reverse($receipt['logs']); |
4931 | foreach($receiptlog as $log) { |
4932 | if(!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) { |
4933 | $db->rollbackTransaction(); |
4934 | return false; |
4935 | } |
4936 | $queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, `comment`, `date`, `userID`) ". |
4937 | "VALUES ('".$receiptID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".(is_object($log['user']) ? $log['user']->getID() : (int) $log['user']).")"; |
4938 | if (!$db->getResult($queryStr)) { |
4939 | $db->rollbackTransaction(); |
4940 | return false; |
4941 | } |
4942 | $receiptLogID = $db->getInsertID('tblDocumentReceiptLog', 'receiptLogID'); |
4943 | if(!empty($log['file'])) { |
4944 | SeedDMS_Core_File::copyFile($log['file'], $this->_dms->contentDir . $this->_document->getDir() . 'r' . $receiptLogID); |
4945 | } |
4946 | } |
4947 | } |
4948 | |
4949 | $db->commitTransaction(); |
4950 | return true; |
4951 | } /* }}} */ |
4952 | |
4953 | /** |
4954 | * Return a list of all revisors separated by individuals and groups |
4955 | * This list will not take the revision log into account. Therefore it |
4956 | * can contain revisors which has actually been deleted as a revisor. |
4957 | * |
4958 | * @return array|bool|null |
4959 | */ |
4960 | function getRevisors() { /* {{{ */ |
4961 | $dms = $this->_document->getDMS(); |
4962 | $db = $dms->getDB(); |
4963 | |
4964 | $queryStr= |
4965 | "SELECT * FROM `tblDocumentRevisors` WHERE `version`='".$this->_version |
4966 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
4967 | |
4968 | $recs = $db->getResultArray($queryStr); |
4969 | if (is_bool($recs)) |
4970 | return false; |
4971 | $revisors = array('i'=>array(), 'g'=>array()); |
4972 | foreach($recs as $rec) { |
4973 | if($rec['type'] == 0) { |
4974 | if($u = $dms->getUser($rec['required'])) |
4975 | $revisors['i'][] = $u; |
4976 | } elseif($rec['type'] == 1) { |
4977 | if($g = $dms->getGroup($rec['required'])) |
4978 | $revisors['g'][] = $g; |
4979 | } |
4980 | } |
4981 | return $revisors; |
4982 | } /* }}} */ |
4983 | |
4984 | /** |
4985 | * Get the current revision status of the document content |
4986 | * The revision status is a list of revisions |
4987 | * If $limit is 1 it will return just the last log entry for each |
4988 | * revisor. |
4989 | * Keep in mind that a revision log may contain repeating revisions. |
4990 | * |
4991 | * @param integer $limit maximum number of records per revisor |
4992 | * @return array list of revisions |
4993 | */ |
4994 | function getRevisionStatus($limit=1) { /* {{{ */ |
4995 | $db = $this->_document->getDMS()->getDB(); |
4996 | |
4997 | if (!is_numeric($limit)) return false; |
4998 | |
4999 | // Retrieve the current status of each assigned reviewer for the content |
5000 | // represented by this object. |
5001 | // FIXME: caching was turned off to make list of review log in ViewDocument |
5002 | // possible |
5003 | if (1 || !isset($this->_revisionStatus)) { |
5004 | /* First get a list of all revisions for this document content */ |
5005 | $queryStr= |
5006 | "SELECT `revisionID` FROM `tblDocumentRevisors` WHERE `version`='".$this->_version |
5007 | ."' AND `documentID` = '". $this->_document->getID() ."' "; |
5008 | $recs = $db->getResultArray($queryStr); |
5009 | if (is_bool($recs) && !$recs) |
5010 | return false; |
5011 | $this->_revisionStatus = array(); |
5012 | if($recs) { |
5013 | foreach($recs as $rec) { |
5014 | $queryStr= |
5015 | "SELECT `tblDocumentRevisors`.*, `tblDocumentRevisionLog`.`revisionLogID`, ". |
5016 | "`tblDocumentRevisionLog`.`status`, ". |
5017 | "`tblDocumentRevisionLog`.`comment`, ". |
5018 | "`tblDocumentRevisionLog`.`date`, ". |
5019 | "`tblDocumentRevisionLog`.`userID`, `tblUsers`.`fullName`, `tblGroups`.`name` AS `groupName` ". |
5020 | "FROM `tblDocumentRevisors` ". |
5021 | "LEFT JOIN `tblDocumentRevisionLog` USING (`revisionID`) ". |
5022 | "LEFT JOIN `tblUsers` on `tblUsers`.`id` = `tblDocumentRevisors`.`required` ". |
5023 | "LEFT JOIN `tblGroups` on `tblGroups`.`id` = `tblDocumentRevisors`.`required` ". |
5024 | "WHERE `tblDocumentRevisors`.`revisionID` = '". $rec['revisionID'] ."' ". |
5025 | "ORDER BY `tblDocumentRevisionLog`.`revisionLogID` DESC LIMIT ".(int) $limit; |
5026 | |
5027 | $res = $db->getResultArray($queryStr); |
5028 | if (is_bool($res) && !$res) { |
5029 | unset($this->_revisionStatus); |
5030 | return false; |
5031 | } |
5032 | $this->_revisionStatus = array_merge($this->_revisionStatus, $res); |
5033 | } |
5034 | } |
5035 | } |
5036 | return $this->_revisionStatus; |
5037 | } /* }}} */ |
5038 | |
5039 | /** |
5040 | * Get the latest entries from the revision log of the document content |
5041 | * |
5042 | * @param integer $limit the number of log entries returned, defaults to 1 |
5043 | * @return array list of revisionlog entries |
5044 | */ |
5045 | function getRevisionLog($limit=1) { /* {{{ */ |
5046 | $db = $this->_document->getDMS()->getDB(); |
5047 | |
5048 | if (!is_numeric($limit)) return false; |
5049 | |
5050 | $queryStr= |
5051 | "SELECT * FROM `tblDocumentRevisionLog` LEFT JOIN `tblDocumentRevisors` ON `tblDocumentRevisionLog`.`revisionID` = `tblDocumentRevisors`.`revisionID` WHERE `version`='".$this->_version |
5052 | ."' AND `documentID` = '". $this->_document->getID() ."' " |
5053 | ."ORDER BY `tblDocumentRevisionLog`.`revisionLogID` DESC LIMIT ".(int) $limit; |
5054 | $recs = $db->getResultArray($queryStr); |
5055 | if (is_bool($recs) && !$recs) |
5056 | return false; |
5057 | return($recs); |
5058 | } /* }}} */ |
5059 | |
5060 | /** |
5061 | * Rewrites the complete revision log |
5062 | * |
5063 | * Attention: this function is highly dangerous. |
5064 | * It removes an existing revision log and rewrites it. |
5065 | * This method was added for importing an xml dump. |
5066 | * |
5067 | * @param array $revisionlog new status log with the newest log entry first. |
5068 | * @return boolean 0 on success, otherwise a negativ error number |
5069 | */ |
5070 | function rewriteRevisionLog($revisions) { /* {{{ */ |
5071 | $db = $this->_document->getDMS()->getDB(); |
5072 | |
5073 | $queryStr= "SELECT `tblDocumentRevisors`.* FROM `tblDocumentRevisors` WHERE `tblDocumentRevisors`.`documentID` = '". $this->_document->getID() ."' AND `tblDocumentRevisors`.`version` = '". $this->_version ."' "; |
5074 | $res = $db->getResultArray($queryStr); |
5075 | if (is_bool($res) && !$res) |
5076 | return false; |
5077 | |
5078 | $db->startTransaction(); |
5079 | |
5080 | if($res) { |
5081 | foreach($res as $revision) { |
5082 | $revisionID = $revision['revisionID']; |
5083 | |
5084 | /* First, remove the old entries */ |
5085 | $queryStr = "DELETE from `tblDocumentRevisionLog` where `revisionID`=".$revisionID; |
5086 | if (!$db->getResult($queryStr)) { |
5087 | $db->rollbackTransaction(); |
5088 | return false; |
5089 | } |
5090 | |
5091 | $queryStr = "DELETE from `tblDocumentRevisors` where `revisionID`=".$revisionID; |
5092 | if (!$db->getResult($queryStr)) { |
5093 | $db->rollbackTransaction(); |
5094 | return false; |
5095 | } |
5096 | } |
5097 | } |
5098 | |
5099 | /* Second, insert the new entries */ |
5100 | foreach($revisions as $revision) { |
5101 | $queryStr = "INSERT INTO `tblDocumentRevisors` (`documentID`, `version`, `type`, `required`) ". |
5102 | "VALUES ('".$this->_document->getID()."', '".$this->_version."', ".$revision['type'] .", ".(is_object($revision['required']) ? $revision['required']->getID() : (int) $revision['required']).")"; |
5103 | if (!$db->getResult($queryStr)) { |
5104 | $db->rollbackTransaction(); |
5105 | return false; |
5106 | } |
5107 | $revisionID = $db->getInsertID('tblDocumentRevisors', 'revisionID'); |
5108 | $revisionlog = array_reverse($revision['logs']); |
5109 | foreach($revisionlog as $log) { |
5110 | if(!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) { |
5111 | $db->rollbackTransaction(); |
5112 | return false; |
5113 | } |
5114 | $queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, `comment`, `date`, `userID`) ". |
5115 | "VALUES ('".$revisionID ."', '".(int) $log['status']."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".(is_object($log['user']) ? $log['user']->getID() : (int) $log['user']).")"; |
5116 | if (!$db->getResult($queryStr)) { |
5117 | $db->rollbackTransaction(); |
5118 | return false; |
5119 | } |
5120 | $revisionLogID = $db->getInsertID('tblDocumentRevisionLog', 'revisionLogID'); |
5121 | if(!empty($log['file'])) { |
5122 | SeedDMS_Core_File::copyFile($log['file'], $this->_dms->contentDir . $this->_document->getDir() . 'r' . $revisionLogID); |
5123 | } |
5124 | } |
5125 | } |
5126 | |
5127 | $db->commitTransaction(); |
5128 | return true; |
5129 | } /* }}} */ |
5130 | |
5131 | /** |
5132 | * Check if document version has a scheduled revision workflow. |
5133 | * The method will update the document status log database table |
5134 | * if needed and set the revisiondate of the content to $next. |
5135 | * |
5136 | * FIXME: This method does not check if there are any revisors left. Even |
5137 | * if all revisors have been removed, it will still start the revision workflow! |
5138 | * NOTE: This seems not the case anymore. The status of each revision is |
5139 | * checked. Only if at least one status is S_LOG_SLEEPING the revision will be |
5140 | * started. This wouldn't be the case if all revisors had been removed. |
5141 | * |
5142 | * @param object $user user requesting the possible automatic change |
5143 | * @param string $next next date for review |
5144 | * @return boolean true if status has changed |
5145 | */ |
5146 | function checkForDueRevisionWorkflow($user, $next=''){ /* {{{ */ |
5147 | $st=$this->getStatus(); |
5148 | |
5149 | /* A revision workflow will only be started if the document version is released */ |
5150 | if($st["status"] == S_RELEASED) { |
5151 | /* First check if there are any scheduled revisions currently sleeping */ |
5152 | $pendingRevision=false; |
5153 | unset($this->_revisionStatus); // force to be reloaded from DB |
5154 | $revisionStatus=$this->getRevisionStatus(); |
5155 | if (is_array($revisionStatus) && count($revisionStatus)>0) { |
5156 | foreach ($revisionStatus as $a){ |
5157 | if ($a["status"]==S_LOG_SLEEPING || $a["status"]==S_LOG_SLEEPING){ |
5158 | $pendingRevision=true; |
5159 | break; |
5160 | } |
5161 | } |
5162 | } |
5163 | if(!$pendingRevision) |
5164 | return false; |
5165 | |
5166 | /* We have sleeping revision, next check if the revision is already due */ |
5167 | if($this->getRevisionDate() && $this->getRevisionDate() <= date('Y-m-d 00:00:00')) { |
5168 | if($this->startRevision($user, 'Automatic start of revision workflow scheduled for '.$this->getRevisionDate())) { |
5169 | if($next) { |
5170 | $tmp = explode('-', substr($next, 0, 10)); |
5171 | if(checkdate($tmp[1], $tmp[2], $tmp[0])) |
5172 | $this->setRevisionDate($next); |
5173 | } else { |
5174 | $this->setRevisionDate(false); |
5175 | } |
5176 | return true; |
5177 | } |
5178 | } |
5179 | } |
5180 | return false; |
5181 | } /* }}} */ |
5182 | |
5183 | /** |
5184 | * Add user as new reviewer |
5185 | * |
5186 | * @param object $user user in charge for the review |
5187 | * @param object $requestUser user requesting the operation (usually the |
5188 | * currently logged in user) |
5189 | * |
5190 | * @return integer|false if > 0 the id of the review log, if < 0 the error |
5191 | * code, false in case of an sql error |
5192 | */ |
5193 | function addIndReviewer($user, $requestUser) { /* {{{ */ |
5194 | if(!$user || !$requestUser) |
5195 | return -1; |
5196 | |
5197 | $db = $this->_document->getDMS()->getDB(); |
5198 | |
5199 | if(!$user->isType('user')) |
5200 | return -1; |
5201 | |
5202 | $userID = $user->getID(); |
5203 | |
5204 | // Get the list of users and groups with read access to this document. |
5205 | if($this->_document->getAccessMode($user) < M_READ) { |
5206 | return -2; |
5207 | } |
5208 | |
5209 | // Check to see if the user has already been added to the review list. |
5210 | $reviewStatus = $user->getReviewStatus($this->_document->getID(), $this->_version); |
5211 | if (is_bool($reviewStatus) && !$reviewStatus) { |
5212 | return false; |
5213 | } |
5214 | $indstatus = false; |
5215 | if (count($reviewStatus["indstatus"]) > 0) { |
5216 | $indstatus = array_pop($reviewStatus["indstatus"]); |
5217 | if($indstatus["status"]!=-2) { |
5218 | // User is already on the list of reviewers; return an error. |
5219 | return -3; |
5220 | } |
5221 | } |
5222 | |
5223 | // Add the user into the review database. |
5224 | if (!$indstatus || ($indstatus && $indstatus["status"]!=-2)) { |
5225 | $queryStr = "INSERT INTO `tblDocumentReviewers` (`documentID`, `version`, `type`, `required`) ". |
5226 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '0', '". $userID ."')"; |
5227 | $res = $db->getResult($queryStr); |
5228 | if (is_bool($res) && !$res) { |
5229 | return false; |
5230 | } |
5231 | $reviewID = $db->getInsertID('tblDocumentReviewers', 'reviewID'); |
5232 | } |
5233 | else { |
5234 | $reviewID = isset($indstatus["reviewID"]) ? $indstatus["reviewID"] : NULL; |
5235 | } |
5236 | |
5237 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ". |
5238 | "VALUES ('". $reviewID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
5239 | $res = $db->getResult($queryStr); |
5240 | if (is_bool($res) && !$res) { |
5241 | return false; |
5242 | } |
5243 | |
5244 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID'); |
5245 | $db->dropTemporaryTable('ttreviewid'); |
5246 | return $reviewLogID; |
5247 | } /* }}} */ |
5248 | |
5249 | /** |
5250 | * Add group as new reviewer |
5251 | * |
5252 | * @param object $group group in charge for the review |
5253 | * @param object $requestUser user requesting the operation (usually the |
5254 | * currently logged in user) |
5255 | * |
5256 | * @return integer|false if > 0 the id of the review log, if < 0 the error |
5257 | * code, false in case of an sql error |
5258 | */ |
5259 | function addGrpReviewer($group, $requestUser) { /* {{{ */ |
5260 | if(!$group || !$requestUser) |
5261 | return -1; |
5262 | |
5263 | $db = $this->_document->getDMS()->getDB(); |
5264 | |
5265 | if(!$group->isType('group')) |
5266 | return -1; |
5267 | |
5268 | $groupID = $group->getID(); |
5269 | |
5270 | // Get the list of users and groups with read access to this document. |
5271 | if (!isset($this->_readAccessList)) { |
5272 | // TODO: error checking. |
5273 | $this->_readAccessList = $this->_document->getReadAccessList(); |
5274 | } |
5275 | $approved = false; |
5276 | foreach ($this->_readAccessList["groups"] as $appGroup) { |
5277 | if ($groupID == $appGroup->getID()) { |
5278 | $approved = true; |
5279 | break; |
5280 | } |
5281 | } |
5282 | if (!$approved) { |
5283 | return -2; |
5284 | } |
5285 | |
5286 | // Check to see if the group has already been added to the review list. |
5287 | $reviewStatus = $group->getReviewStatus($this->_document->getID(), $this->_version); |
5288 | if (is_bool($reviewStatus) && !$reviewStatus) { |
5289 | return false; |
5290 | } |
5291 | if (count($reviewStatus) > 0 && $reviewStatus[0]["status"]!=-2) { |
5292 | // Group is already on the list of reviewers; return an error. |
5293 | return -3; |
5294 | } |
5295 | |
5296 | // Add the group into the review database. |
5297 | if (!isset($reviewStatus[0]["status"]) || (isset($reviewStatus[0]["status"]) && $reviewStatus[0]["status"]!=-2)) { |
5298 | $queryStr = "INSERT INTO `tblDocumentReviewers` (`documentID`, `version`, `type`, `required`) ". |
5299 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '1', '". $groupID ."')"; |
5300 | $res = $db->getResult($queryStr); |
5301 | if (is_bool($res) && !$res) { |
5302 | return false; |
5303 | } |
5304 | $reviewID = $db->getInsertID('tblDocumentReviewers', 'reviewID'); |
5305 | } |
5306 | else { |
5307 | $reviewID = isset($reviewStatus[0]["reviewID"])?$reviewStatus[0]["reviewID"]:NULL; |
5308 | } |
5309 | |
5310 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ". |
5311 | "VALUES ('". $reviewID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
5312 | $res = $db->getResult($queryStr); |
5313 | if (is_bool($res) && !$res) { |
5314 | return false; |
5315 | } |
5316 | |
5317 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID'); |
5318 | $db->dropTemporaryTable('ttreviewid'); |
5319 | return $reviewLogID; |
5320 | } /* }}} */ |
5321 | |
5322 | /** |
5323 | * Add a review to the document content |
5324 | * |
5325 | * This method will add an entry to the table tblDocumentReviewLog. |
5326 | * It will first check if the user is ment to review the document version. |
5327 | * It not the return value is -3. |
5328 | * Next it will check if the users has been removed from the list of |
5329 | * reviewers. In that case -4 will be returned. |
5330 | * If the given review status has been set by the user before, it cannot |
5331 | * be set again and 0 will be returned. Іf the review could be succesfully |
5332 | * added, the review log id will be returned. |
5333 | * |
5334 | * @see SeedDMS_Core_DocumentContent::setApprovalByInd() |
5335 | * |
5336 | * @param object $user user doing the review |
5337 | * @param object $requestUser user asking for the review, this is mostly |
5338 | * the user currently logged in. |
5339 | * @param integer $status status of review |
5340 | * @param string $comment comment for review |
5341 | * |
5342 | * @return integer|bool new review log id, error code 0 till -4, |
5343 | * false in case of an sql error |
5344 | */ |
5345 | function setReviewByInd($user, $requestUser, $status, $comment, $file='') { /* {{{ */ |
5346 | if(!$user || !$requestUser) |
5347 | return -1; |
5348 | |
5349 | $db = $this->_document->getDMS()->getDB(); |
5350 | |
5351 | if(!$user->isType('user')) |
5352 | return -1; |
5353 | |
5354 | // Check if the user is on the review list at all. |
5355 | $reviewStatus = $user->getReviewStatus($this->_document->getID(), $this->_version); |
5356 | if (is_bool($reviewStatus) && !$reviewStatus) { |
5357 | return false; |
5358 | } |
5359 | if (count($reviewStatus["indstatus"])==0) { |
5360 | // User is not assigned to review this document. No action required. |
5361 | // Return an error. |
5362 | return -3; |
5363 | } |
5364 | $indstatus = array_pop($reviewStatus["indstatus"]); |
5365 | if ($indstatus["status"]==-2) { |
5366 | // User has been deleted from reviewers |
5367 | return -4; |
5368 | } |
5369 | // Check if the status is really different from the current status |
5370 | if ($indstatus["status"] == $status) |
5371 | return 0; |
5372 | |
5373 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, |
5374 | `comment`, `date`, `userID`) ". |
5375 | "VALUES ('". $indstatus["reviewID"] ."', '". |
5376 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
5377 | $requestUser->getID() ."')"; |
5378 | $res=$db->getResult($queryStr); |
5379 | if (is_bool($res) && !$res) |
5380 | return false; |
5381 | |
5382 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID'); |
5383 | if($file) { |
5384 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'r' . $reviewLogID); |
5385 | } |
5386 | return $reviewLogID; |
5387 | } /* }}} */ |
5388 | |
5389 | /** |
5390 | * Add another entry to review log which resets the status |
5391 | * |
5392 | * This method will not delete anything from the database, but will add |
5393 | * a new review log entry which sets the status to 0. This is only allowed |
5394 | * if the current status is either 1 (reviewed) or -1 (rejected). |
5395 | * |
5396 | * After calling this method SeedDMS_Core_DocumentCategory::verifyStatus() |
5397 | * should be called to recalculate the document status. |
5398 | * |
5399 | * @param integer $reviewid id of review |
5400 | * @param SeedDMS_Core_User $requestUser user requesting the removal |
5401 | * @param string $comment comment |
5402 | * |
5403 | * @return integer|bool true if successful, error code < 0, |
5404 | * false in case of an sql error |
5405 | */ |
5406 | public function removeReview($reviewid, $requestUser, $comment='') { /* {{{ */ |
5407 | $db = $this->_document->getDMS()->getDB(); |
5408 | |
5409 | // Check to see if the user can be removed from the review list. |
5410 | $reviews = $this->getReviewStatus(); |
5411 | if (is_bool($reviews) && !$reviews) { |
5412 | return false; |
5413 | } |
5414 | $reviewStatus = null; |
5415 | foreach($reviews as $review) { |
5416 | if($review['reviewID'] == $reviewid) { |
5417 | $reviewStatus = $review; |
5418 | break; |
5419 | } |
5420 | } |
5421 | if(!$reviewStatus) |
5422 | return -2; |
5423 | |
5424 | // The review log entry may only be removed if the status is 1 or -1 |
5425 | if ($reviewStatus["status"] != 1 && $reviewStatus["status"] != -1) |
5426 | return -3; |
5427 | |
5428 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, |
5429 | `comment`, `date`, `userID`) ". |
5430 | "VALUES ('". $reviewStatus["reviewID"] ."', '0', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
5431 | $requestUser->getID() ."')"; |
5432 | $res=$db->getResult($queryStr); |
5433 | if (is_bool($res) && !$res) |
5434 | return false; |
5435 | |
5436 | return true; |
5437 | } /* }}} */ |
5438 | |
5439 | /** |
5440 | * Add a review to the document content |
5441 | * |
5442 | * This method is similar to |
5443 | * {@see SeedDMS_Core_DocumentContent::setReviewByInd()} but adds a review |
5444 | * for a group instead of a user. |
5445 | * |
5446 | * @param object $group group doing the review |
5447 | * @param object $requestUser user asking for the review, this is mostly |
5448 | * the user currently logged in. |
5449 | * @param integer $status status of review |
5450 | * @param string $comment comment for review |
5451 | * |
5452 | * @return integer|bool new review log id, error code 0 till -4, |
5453 | * false in case of an sql error |
5454 | */ |
5455 | function setReviewByGrp($group, $requestUser, $status, $comment, $file='') { /* {{{ */ |
5456 | if(!$group || !$requestUser) |
5457 | return -1; |
5458 | |
5459 | $db = $this->_document->getDMS()->getDB(); |
5460 | |
5461 | if(!$group->isType('group')) |
5462 | return -1; |
5463 | |
5464 | // Check if the group is on the review list at all. |
5465 | $reviewStatus = $group->getReviewStatus($this->_document->getID(), $this->_version); |
5466 | if (is_bool($reviewStatus) && !$reviewStatus) { |
5467 | return false; |
5468 | } |
5469 | if (count($reviewStatus)==0) { |
5470 | // User is not assigned to review this document. No action required. |
5471 | // Return an error. |
5472 | return -3; |
5473 | } |
5474 | if ((int) $reviewStatus[0]["status"]==-2) { |
5475 | // Group has been deleted from reviewers |
5476 | return -4; |
5477 | } |
5478 | |
5479 | // Check if the status is really different from the current status |
5480 | if ($reviewStatus[0]["status"] == $status) |
5481 | return 0; |
5482 | |
5483 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, |
5484 | `comment`, `date`, `userID`) ". |
5485 | "VALUES ('". $reviewStatus[0]["reviewID"] ."', '". |
5486 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
5487 | $requestUser->getID() ."')"; |
5488 | $res=$db->getResult($queryStr); |
5489 | if (is_bool($res) && !$res) |
5490 | return false; |
5491 | |
5492 | $reviewLogID = $db->getInsertID('tblDocumentReviewLog', 'reviewLogID'); |
5493 | if($file) { |
5494 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'r' . $reviewLogID); |
5495 | } |
5496 | return $reviewLogID; |
5497 | } /* }}} */ |
5498 | |
5499 | /** |
5500 | * Add user as new approver |
5501 | * |
5502 | * @param object $user user in charge for the approval |
5503 | * @param object $requestUser user requesting the operation (usually the |
5504 | * currently logged in user) |
5505 | * |
5506 | * @return integer|false if > 0 the id of the approval log, if < 0 the error |
5507 | * code, false in case of an sql error |
5508 | */ |
5509 | function addIndApprover($user, $requestUser) { /* {{{ */ |
5510 | if(!$user || !$requestUser) |
5511 | return -1; |
5512 | |
5513 | $db = $this->_document->getDMS()->getDB(); |
5514 | |
5515 | if(!$user->isType('user')) |
5516 | return -1; |
5517 | |
5518 | $userID = $user->getID(); |
5519 | |
5520 | // Get the list of users and groups with read access to this document. |
5521 | if($this->_document->getAccessMode($user) < M_READ) { |
5522 | return -2; |
5523 | } |
5524 | |
5525 | // Check if the user has already been added to the approvers list. |
5526 | $approvalStatus = $user->getApprovalStatus($this->_document->getID(), $this->_version); |
5527 | if (is_bool($approvalStatus) && !$approvalStatus) { |
5528 | return false; |
5529 | } |
5530 | $indstatus = false; |
5531 | if (count($approvalStatus["indstatus"]) > 0) { |
5532 | $indstatus = array_pop($approvalStatus["indstatus"]); |
5533 | if($indstatus["status"]!=-2) { |
5534 | // User is already on the list of approverss; return an error. |
5535 | return -3; |
5536 | } |
5537 | } |
5538 | |
5539 | if ( !$indstatus || (isset($indstatus["status"]) && $indstatus["status"]!=-2)) { |
5540 | // Add the user into the approvers database. |
5541 | $queryStr = "INSERT INTO `tblDocumentApprovers` (`documentID`, `version`, `type`, `required`) ". |
5542 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '0', '". $userID ."')"; |
5543 | $res = $db->getResult($queryStr); |
5544 | if (is_bool($res) && !$res) { |
5545 | return false; |
5546 | } |
5547 | $approveID = $db->getInsertID('tblDocumentApprovers', 'approveID'); |
5548 | } |
5549 | else { |
5550 | $approveID = isset($indstatus["approveID"]) ? $indstatus["approveID"] : NULL; |
5551 | } |
5552 | |
5553 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ". |
5554 | "VALUES ('". $approveID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
5555 | $res = $db->getResult($queryStr); |
5556 | if (is_bool($res) && !$res) { |
5557 | return false; |
5558 | } |
5559 | |
5560 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID'); |
5561 | $db->dropTemporaryTable('ttapproveid'); |
5562 | return $approveLogID; |
5563 | } /* }}} */ |
5564 | |
5565 | /** |
5566 | * Add group as new approver |
5567 | * |
5568 | * @param object $group group in charge for the approval |
5569 | * @param object $requestUser user requesting the operation (usually the |
5570 | * currently logged in user) |
5571 | * |
5572 | * @return integer|false if > 0 the id of the approval log, if < 0 the error |
5573 | * code, false in case of an sql error |
5574 | */ |
5575 | function addGrpApprover($group, $requestUser) { /* {{{ */ |
5576 | if(!$group || !$requestUser) |
5577 | return -1; |
5578 | |
5579 | $db = $this->_document->getDMS()->getDB(); |
5580 | |
5581 | if(!$group->isType('group')) |
5582 | return -1; |
5583 | |
5584 | $groupID = $group->getID(); |
5585 | |
5586 | // Get the list of users and groups with read access to this document. |
5587 | if (!isset($this->_readAccessList)) { |
5588 | // TODO: error checking. |
5589 | $this->_readAccessList = $this->_document->getReadAccessList(); |
5590 | } |
5591 | $approved = false; |
5592 | foreach ($this->_readAccessList["groups"] as $appGroup) { |
5593 | if ($groupID == $appGroup->getID()) { |
5594 | $approved = true; |
5595 | break; |
5596 | } |
5597 | } |
5598 | if (!$approved) { |
5599 | return -2; |
5600 | } |
5601 | |
5602 | // Check if the group has already been added to the approver list. |
5603 | $approvalStatus = $group->getApprovalStatus($this->_document->getID(), $this->_version); |
5604 | if (is_bool($approvalStatus) && !$approvalStatus) { |
5605 | return false; |
5606 | } |
5607 | if (count($approvalStatus) > 0 && $approvalStatus[0]["status"]!=-2) { |
5608 | // Group is already on the list of approvers; return an error. |
5609 | return -3; |
5610 | } |
5611 | |
5612 | // Add the group into the approver database. |
5613 | if (!isset($approvalStatus[0]["status"]) || (isset($approvalStatus[0]["status"]) && $approvalStatus[0]["status"]!=-2)) { |
5614 | $queryStr = "INSERT INTO `tblDocumentApprovers` (`documentID`, `version`, `type`, `required`) ". |
5615 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '1', '". $groupID ."')"; |
5616 | $res = $db->getResult($queryStr); |
5617 | if (is_bool($res) && !$res) { |
5618 | return false; |
5619 | } |
5620 | $approveID = $db->getInsertID('tblDocumentApprovers', 'approveID'); |
5621 | } |
5622 | else { |
5623 | $approveID = isset($approvalStatus[0]["approveID"])?$approvalStatus[0]["approveID"]:NULL; |
5624 | } |
5625 | |
5626 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ". |
5627 | "VALUES ('". $approveID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
5628 | $res = $db->getResult($queryStr); |
5629 | if (is_bool($res) && !$res) { |
5630 | return false; |
5631 | } |
5632 | |
5633 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID'); |
5634 | $db->dropTemporaryTable('ttapproveid'); |
5635 | return $approveLogID; |
5636 | } /* }}} */ |
5637 | |
5638 | /** |
5639 | * Sets approval status of a document content for a user |
5640 | * |
5641 | * This function can be used to approve or reject a document content, or |
5642 | * to reset its approval state. In most cases this function will be |
5643 | * called by an user, but an admin may set the approval for |
5644 | * somebody else. |
5645 | * It is first checked if the user is in the list of approvers at all. |
5646 | * Then it is check if the approval status is already -2. In both cases |
5647 | * the function returns with an error. |
5648 | * |
5649 | * @see SeedDMS_Core_DocumentContent::setReviewByInd() |
5650 | * |
5651 | * @param object $user user in charge for doing the approval |
5652 | * @param object $requestUser user actually calling this function |
5653 | * @param integer $status the status of the approval, possible values are |
5654 | * 0=unprocessed (maybe used to reset a status) |
5655 | * 1=approved, |
5656 | * -1=rejected, |
5657 | * -2=user is deleted (use {link |
5658 | * SeedDMS_Core_DocumentContent::delIndApprover} instead) |
5659 | * @param string $comment approval comment |
5660 | * |
5661 | * @return integer|bool new review log id, error code 0 till -4, |
5662 | * false in case of an sql error |
5663 | */ |
5664 | function setApprovalByInd($user, $requestUser, $status, $comment, $file='') { /* {{{ */ |
5665 | if(!$user || !$requestUser) |
5666 | return -1; |
5667 | |
5668 | $db = $this->_document->getDMS()->getDB(); |
5669 | |
5670 | if(!$user->isType('user')) |
5671 | return -1; |
5672 | |
5673 | // Check if the user is on the approval list at all. |
5674 | $approvalStatus = $user->getApprovalStatus($this->_document->getID(), $this->_version); |
5675 | if (is_bool($approvalStatus) && !$approvalStatus) { |
5676 | return false; |
5677 | } |
5678 | if (count($approvalStatus["indstatus"])==0) { |
5679 | // User is not assigned to approve this document. No action required. |
5680 | // Return an error. |
5681 | return -3; |
5682 | } |
5683 | $indstatus = array_pop($approvalStatus["indstatus"]); |
5684 | if ($indstatus["status"]==-2) { |
5685 | // User has been deleted from approvers |
5686 | return -4; |
5687 | } |
5688 | // Check if the status is really different from the current status |
5689 | if ($indstatus["status"] == $status) |
5690 | return 0; |
5691 | |
5692 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, |
5693 | `comment`, `date`, `userID`) ". |
5694 | "VALUES ('". $indstatus["approveID"] ."', '". |
5695 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
5696 | $requestUser->getID() ."')"; |
5697 | $res=$db->getResult($queryStr); |
5698 | if (is_bool($res) && !$res) |
5699 | return false; |
5700 | |
5701 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID'); |
5702 | if($file) { |
5703 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'a' . $approveLogID); |
5704 | } |
5705 | return $approveLogID; |
5706 | } /* }}} */ |
5707 | |
5708 | /** |
5709 | * Add another entry to approval log which resets the status |
5710 | * |
5711 | * This method will not delete anything from the database, but will add |
5712 | * a new approval log entry which sets the status to 0. This is only allowed |
5713 | * if the current status is either 1 (approved) or -1 (rejected). |
5714 | * |
5715 | * After calling this method SeedDMS_Core_DocumentCategory::verifyStatus() |
5716 | * should be called to recalculate the document status. |
5717 | * |
5718 | * @param integer $approveid id of approval |
5719 | * @param SeedDMS_Core_User $requestUser user requesting the removal |
5720 | * @param string $comment comment |
5721 | * |
5722 | * @return integer|bool true if successful, error code < 0, |
5723 | * false in case of an sql error |
5724 | */ |
5725 | public function removeApproval($approveid, $requestUser, $comment='') { /* {{{ */ |
5726 | $db = $this->_document->getDMS()->getDB(); |
5727 | |
5728 | // Check to see if the user can be removed from the approval list. |
5729 | $approvals = $this->getApprovalStatus(); |
5730 | if (is_bool($approvals) && !$approvals) { |
5731 | return false; |
5732 | } |
5733 | $approvalStatus = null; |
5734 | foreach($approvals as $approval) { |
5735 | if($approval['approveID'] == $approveid) { |
5736 | $approvalStatus = $approval; |
5737 | break; |
5738 | } |
5739 | } |
5740 | if(!$approvalStatus) |
5741 | return -2; |
5742 | |
5743 | // The approval log entry may only be removed if the status is 1 or -1 |
5744 | if ($approvalStatus["status"] != 1 && $approvalStatus["status"] != -1) |
5745 | return -3; |
5746 | |
5747 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, |
5748 | `comment`, `date`, `userID`) ". |
5749 | "VALUES ('". $approvalStatus["approveID"] ."', '0', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
5750 | $requestUser->getID() ."')"; |
5751 | $res=$db->getResult($queryStr); |
5752 | if (is_bool($res) && !$res) |
5753 | return false; |
5754 | |
5755 | return true; |
5756 | } /* }}} */ |
5757 | |
5758 | /** |
5759 | * Sets approval status of a document content for a group |
5760 | * |
5761 | * The functions behaves like |
5762 | * {link SeedDMS_Core_DocumentContent::setApprovalByInd} but does it for |
5763 | * a group instead of a user |
5764 | */ |
5765 | function setApprovalByGrp($group, $requestUser, $status, $comment, $file='') { /* {{{ */ |
5766 | if(!$group || !$requestUser) |
5767 | return -1; |
5768 | |
5769 | $db = $this->_document->getDMS()->getDB(); |
5770 | |
5771 | if(!$group->isType('group')) |
5772 | return -1; |
5773 | |
5774 | // Check if the group is on the approval list at all. |
5775 | $approvalStatus = $group->getApprovalStatus($this->_document->getID(), $this->_version); |
5776 | if (is_bool($approvalStatus) && !$approvalStatus) { |
5777 | return false; |
5778 | } |
5779 | if (count($approvalStatus)==0) { |
5780 | // User is not assigned to approve this document. No action required. |
5781 | // Return an error. |
5782 | return -3; |
5783 | } |
5784 | if ($approvalStatus[0]["status"]==-2) { |
5785 | // Group has been deleted from approvers |
5786 | return -4; |
5787 | } |
5788 | |
5789 | // Check if the status is really different from the current status |
5790 | if ($approvalStatus[0]["status"] == $status) |
5791 | return 0; |
5792 | |
5793 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, |
5794 | `comment`, `date`, `userID`) ". |
5795 | "VALUES ('". $approvalStatus[0]["approveID"] ."', '". |
5796 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
5797 | $requestUser->getID() ."')"; |
5798 | $res=$db->getResult($queryStr); |
5799 | if (is_bool($res) && !$res) |
5800 | return false; |
5801 | |
5802 | $approveLogID = $db->getInsertID('tblDocumentApproveLog', 'approveLogID'); |
5803 | if($file) { |
5804 | SeedDMS_Core_File::copyFile($file, $this->_dms->contentDir . $this->_document->getDir() . 'a' . $approveLogID); |
5805 | } |
5806 | return $approveLogID; |
5807 | } /* }}} */ |
5808 | |
5809 | function addIndRecipient($user, $requestUser) { /* {{{ */ |
5810 | $db = $this->_document->getDMS()->getDB(); |
5811 | |
5812 | if(!$user || !$requestUser) |
5813 | return -1; |
5814 | |
5815 | if(!$user->isType('user')) |
5816 | return -1; |
5817 | |
5818 | $userID = $user->getID(); |
5819 | |
5820 | // Get the list of users and groups with read access to this document. |
5821 | if($this->_document->getAccessMode($user) < M_READ) { |
5822 | return -2; |
5823 | } |
5824 | |
5825 | // Check to see if the user has already been added to the receipt list. |
5826 | $receiptStatus = $user->getReceiptStatus($this->_document->getID(), $this->_version); |
5827 | if (is_bool($receiptStatus) && !$receiptStatus) { |
5828 | return -1; |
5829 | } |
5830 | $indstatus = false; |
5831 | if (count($receiptStatus["indstatus"]) > 0) { |
5832 | $indstatus = array_pop($receiptStatus["indstatus"]); |
5833 | if($indstatus["status"]!=-2) { |
5834 | // User is already on the list of recipients; return an error. |
5835 | return -3; |
5836 | } |
5837 | } |
5838 | |
5839 | // Add the user into the recipients database. |
5840 | if (!$indstatus || ($indstatus && $indstatus["status"]!=-2)) { |
5841 | $queryStr = "INSERT INTO `tblDocumentRecipients` (`documentID`, `version`, `type`, `required`) ". |
5842 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '0', '". $userID ."')"; |
5843 | $res = $db->getResult($queryStr); |
5844 | if (is_bool($res) && !$res) { |
5845 | return -1; |
5846 | } |
5847 | $receiptID = $db->getInsertID('tblDocumentRecipients', 'receiptID'); |
5848 | } |
5849 | else { |
5850 | $receiptID = isset($indstatus["receiptID"]) ? $indstatus["receiptID"] : NULL; |
5851 | } |
5852 | |
5853 | $queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, `comment`, `date`, `userID`) ". |
5854 | "VALUES ('". $receiptID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
5855 | $res = $db->getResult($queryStr); |
5856 | if (is_bool($res) && !$res) { |
5857 | return -1; |
5858 | } |
5859 | |
5860 | // Add recipient to event notification table. |
5861 | //$this->_document->addNotify($userID, true); |
5862 | |
5863 | $receiptLogID = $db->getInsertID('tblDocumentReceiptLog', 'receiptLogID'); |
5864 | $db->dropTemporaryTable('ttreceiptid'); |
5865 | return $receiptLogID; |
5866 | } /* }}} */ |
5867 | |
5868 | function addGrpRecipient($group, $requestUser) { /* {{{ */ |
5869 | $db = $this->_document->getDMS()->getDB(); |
5870 | |
5871 | if(!$group || !$requestUser) |
5872 | return -1; |
5873 | |
5874 | if(!$group->isType('group')) |
5875 | return -1; |
5876 | |
5877 | $groupID = $group->getID(); |
5878 | |
5879 | // Get the list of users and groups with read access to this document. |
5880 | if (!isset($this->_readAccessList)) { |
5881 | // TODO: error checking. |
5882 | $this->_readAccessList = $this->_document->getReadAccessList(); |
5883 | } |
5884 | $approved = false; |
5885 | foreach ($this->_readAccessList["groups"] as $appGroup) { |
5886 | if ($groupID == $appGroup->getID()) { |
5887 | $approved = true; |
5888 | break; |
5889 | } |
5890 | } |
5891 | if (!$approved) { |
5892 | return -2; |
5893 | } |
5894 | |
5895 | // Check to see if the group has already been added to the review list. |
5896 | $receiptStatus = $group->getReceiptStatus($this->_document->getID(), $this->_version); |
5897 | if (is_bool($receiptStatus) && !$receiptStatus) { |
5898 | return -1; |
5899 | } |
5900 | $status = false; |
5901 | if (count($receiptStatus) > 0) { |
5902 | $status = array_pop($receiptStatus); |
5903 | if($status["status"]!=-2) { |
5904 | // User is already on the list of recipients; return an error. |
5905 | return -3; |
5906 | } |
5907 | } |
5908 | |
5909 | // Add the group into the recipients database. |
5910 | if (!$status || ($status && $status["status"]!=-2)) { |
5911 | $queryStr = "INSERT INTO `tblDocumentRecipients` (`documentID`, `version`, `type`, `required`) ". |
5912 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '1', '". $groupID ."')"; |
5913 | $res = $db->getResult($queryStr); |
5914 | if (is_bool($res) && !$res) { |
5915 | return -1; |
5916 | } |
5917 | $receiptID = $db->getInsertID('tblDocumentRecipients', 'receiptID'); |
5918 | } |
5919 | else { |
5920 | $receiptID = isset($status["receiptID"]) ? $status["receiptID"] : NULL; |
5921 | } |
5922 | |
5923 | $queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, `comment`, `date`, `userID`) ". |
5924 | "VALUES ('". $receiptID ."', '0', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
5925 | $res = $db->getResult($queryStr); |
5926 | if (is_bool($res) && !$res) { |
5927 | return -1; |
5928 | } |
5929 | |
5930 | $receiptLogID = $db->getInsertID('tblDocumentReceiptLog', 'receiptLogID'); |
5931 | $db->dropTemporaryTable('ttreceiptid'); |
5932 | return $receiptLogID; |
5933 | } /* }}} */ |
5934 | |
5935 | /** |
5936 | * Add an individual revisor to the document content |
5937 | * |
5938 | * This function adds a user as a revisor but doesn't start the |
5939 | * revision workflow by default. This behaviour is different from all |
5940 | * other workflows (approval, review, receipt), because it adds |
5941 | * an initial entry in the revision log, which marks the revision as |
5942 | * 'sleeping'. The workflow is started at a later point in time by adding |
5943 | * the second entry in the revision log which puts it into 'waiting'. |
5944 | * |
5945 | * @param object $user user to be added as a revisor |
5946 | * @param object $requestUser user requesting the addition |
5947 | * @return integer 0 if successful otherwise a value < 0 |
5948 | */ |
5949 | function addRevisor($object, $requestUser) { /* {{{ */ |
5950 | $dms = $this->_document->getDMS(); |
5951 | $db = $dms->getDB(); |
5952 | |
5953 | if(!$object || !$requestUser) |
5954 | return -1; |
5955 | |
5956 | // Check to see if the user has already been added to the revisor list. |
5957 | $revisionStatus = $object->getRevisionStatus($this->_document->getID(), $this->_version); |
5958 | if (is_bool($revisionStatus) && !$revisionStatus) { |
5959 | return -1; |
5960 | } |
5961 | |
5962 | /* getRevisionStatus() returns an array with either an element |
5963 | * 'indstatus' (user) or no element (group) containing the revision log |
5964 | */ |
5965 | if(get_class($object) == $dms->getClassname('user')) { |
5966 | $revisionStatus = $revisionStatus['indstatus']; |
5967 | $type = 0; |
5968 | |
5969 | // Get the list of users and groups with read access to this document. |
5970 | if($this->_document->getAccessMode($object) < M_READ) { |
5971 | return -2; |
5972 | } |
5973 | } elseif(get_class($object) == $dms->getClassname('group')) { |
5974 | $type = 1; |
5975 | |
5976 | // Get the list of users and groups with read access to this document. |
5977 | if($this->_document->getGroupAccessMode($object) < M_READ) { |
5978 | return -2; |
5979 | } |
5980 | } else { |
5981 | return -1; |
5982 | } |
5983 | |
5984 | /* There are two cases: 1. the user has not been added at all or 2. |
5985 | * the user was added before but has been removed later. In both |
5986 | * cases the user may be added. In case 2. 'indstatus' will be set |
5987 | * and the last status is -2. If it is not -2, then the user is still |
5988 | * in the process and cannot be added again. |
5989 | */ |
5990 | $indstatus = false; |
5991 | if($revisionStatus) { |
5992 | if (count($revisionStatus) > 0) { |
5993 | $indstatus = array_pop($revisionStatus); |
5994 | if($indstatus["status"] != S_LOG_USER_REMOVED) { |
5995 | // User is already on the list of recipients; return an error. |
5996 | return -3; |
5997 | } |
5998 | } |
5999 | } |
6000 | |
6001 | // Add the user into the revisors database. |
6002 | if (!$indstatus) { |
6003 | $queryStr = "INSERT INTO `tblDocumentRevisors` (`documentID`, `version`, `type`, `required`) ". |
6004 | "VALUES ('". $this->_document->getID() ."', '". $this->_version ."', '". $type ."', '". $object->getID() ."')"; |
6005 | $res = $db->getResult($queryStr); |
6006 | if (is_bool($res) && !$res) { |
6007 | return -1; |
6008 | } |
6009 | $revisionID = $db->getInsertID('tblDocumentRevisors', 'revisionID'); |
6010 | } else { |
6011 | $revisionID = isset($indstatus["revisionID"]) ? $indstatus["revisionID"] : NULL; |
6012 | } |
6013 | |
6014 | /* If a user is added when the revision has already been startet, then |
6015 | * put it into S_LOG_WAITING otherwise into S_LOG_SLEEPING. Attention, if a |
6016 | * document content is in any other status but S_IN_REVISION, then it will |
6017 | * end up in S_LOG_SLEEPING. As this method is also called by removeFromProcesses() |
6018 | * when another user takes over the processes, it may happen that revisions |
6019 | * of document contents in status e.g. S_OBSOLETE, S_EXPIRED will change its |
6020 | * status from S_LOG_WAITING to S_LOG_SLEEPING. |
6021 | * This could only be fixed if this method could set an initial revision status |
6022 | * by possibly passing it as another parameter to the method. |
6023 | */ |
6024 | $st=$this->getStatus(); |
6025 | $queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, `comment`, `date`, `userID`) ". |
6026 | "VALUES ('". $revisionID ."', '".($st["status"] == S_IN_REVISION ? S_LOG_WAITING : S_LOG_SLEEPING)."', '', ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6027 | $res = $db->getResult($queryStr); |
6028 | if (is_bool($res) && !$res) { |
6029 | return -1; |
6030 | } |
6031 | |
6032 | $revisionLogID = $db->getInsertID('tblDocumentRevisionLog', 'revisionLogID'); |
6033 | $db->dropTemporaryTable('ttrevisionid'); |
6034 | return $revisionLogID; |
6035 | } /* }}} */ |
6036 | |
6037 | function addIndRevisor($user, $requestUser, $donotstart=true) { /* {{{ */ |
6038 | if($user && !$user->isType('user')) |
6039 | return -1; |
6040 | |
6041 | return self::addRevisor($user, $requestUser, $donotstart); |
6042 | } /* }}} */ |
6043 | |
6044 | function addGrpRevisor($group, $requestUser, $donotstart=true) { /* {{{ */ |
6045 | if($group && !$group->isType('group')) |
6046 | return -1; |
6047 | |
6048 | return self::addRevisor($group, $requestUser, $donotstart); |
6049 | } /* }}} */ |
6050 | |
6051 | /** |
6052 | * Add a receipt to the document content |
6053 | * |
6054 | * This method will add an entry to the table tblDocumentReceiptLog. |
6055 | * It will first check if the user is ment to receipt the document version. |
6056 | * If not the return value is -3. |
6057 | * Next it will check if the user has been removed from the list of |
6058 | * recipients. In that case -4 will be returned. |
6059 | * If the given receipt has been set by the user before, it cannot |
6060 | * be set again and 0 will be returned. Іf the receipt could be succesfully |
6061 | * added, the receiptview log id will be returned. |
6062 | * |
6063 | * @see SeedDMS_Core_DocumentContent::setApprovalByInd() |
6064 | * @param object $user user doing the receipt |
6065 | * @param object $requestUser user asking for the receipt, this is mostly |
6066 | * @param integer $status the status of the receipt, possible values are |
6067 | * 0=unprocessed (may be used to reset a status) |
6068 | * 1=received, |
6069 | * -1=rejected, |
6070 | * -2=user is deleted (use {link |
6071 | * SeedDMS_Core_DocumentContent::delIndRecipient} instead) |
6072 | * the user currently logged in. |
6073 | * @return integer new receipt log id |
6074 | */ |
6075 | function setReceiptByInd($user, $requestUser, $status, $comment) { /* {{{ */ |
6076 | $db = $this->_document->getDMS()->getDB(); |
6077 | |
6078 | if(!$user || !$requestUser) |
6079 | return -1; |
6080 | |
6081 | if(!$user->isType('user')) |
6082 | return -1; |
6083 | |
6084 | // Check to see if the user can be removed from the review list. |
6085 | $receiptStatus = $user->getReceiptStatus($this->_document->getID(), $this->_version); |
6086 | if (is_bool($receiptStatus) && !$receiptStatus) { |
6087 | return -1; |
6088 | } |
6089 | if (count($receiptStatus["indstatus"])==0) { |
6090 | // User is not assigned to receipt this document. No action required. |
6091 | // Return an error. |
6092 | return -3; |
6093 | } |
6094 | $indstatus = array_pop($receiptStatus["indstatus"]); |
6095 | if ($indstatus["status"] == S_LOG_USER_REMOVED) { |
6096 | // User has been deleted from recipients |
6097 | return -4; |
6098 | } |
6099 | // Check if the status is really different from the current status |
6100 | if ($indstatus["status"] == $status) |
6101 | return 0; |
6102 | |
6103 | $queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, |
6104 | `comment`, `date`, `userID`) ". |
6105 | "VALUES ('". $indstatus["receiptID"] ."', '". |
6106 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
6107 | $requestUser->getID() ."')"; |
6108 | $res=$db->getResult($queryStr); |
6109 | if (is_bool($res) && !$res) |
6110 | return -1; |
6111 | else { |
6112 | $receiptLogID = $db->getInsertID('tblDocumentReceiptLog', 'receiptLogID'); |
6113 | return $receiptLogID; |
6114 | } |
6115 | } /* }}} */ |
6116 | |
6117 | /** |
6118 | * Add a receipt to the document content |
6119 | * |
6120 | * This method is similar to |
6121 | * {@see SeedDMS_Core_DocumentContent::setReceiptByInd()} but adds a receipt |
6122 | * for a group instead of a user. |
6123 | * |
6124 | * @param object $group group doing the receipt |
6125 | * @param object $requestUser user asking for the receipt, this is mostly |
6126 | * the user currently logged in. |
6127 | * @return integer new receipt log id |
6128 | */ |
6129 | function setReceiptByGrp($group, $requestUser, $status, $comment) { /* {{{ */ |
6130 | $db = $this->_document->getDMS()->getDB(); |
6131 | |
6132 | if(!$group || !$requestUser) |
6133 | return -1; |
6134 | |
6135 | if(!$group->isType('group')) |
6136 | return -1; |
6137 | |
6138 | // Check to see if the user can be removed from the recipient list. |
6139 | $receiptStatus = $group->getReceiptStatus($this->_document->getID(), $this->_version); |
6140 | if (is_bool($receiptStatus) && !$receiptStatus) { |
6141 | return -1; |
6142 | } |
6143 | if (count($receiptStatus)==0) { |
6144 | // User is not assigned to receipt this document. No action required. |
6145 | // Return an error. |
6146 | return -3; |
6147 | } |
6148 | $grpstatus = array_pop($receiptStatus); |
6149 | if ($grpstatus["status"] == S_LOG_USER_REMOVED) { |
6150 | // Group has been deleted from recipients |
6151 | return -4; |
6152 | } |
6153 | |
6154 | // Check if the status is really different from the current status |
6155 | if ($grpstatus["status"] == $status) |
6156 | return 0; |
6157 | |
6158 | $queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, |
6159 | `comment`, `date`, `userID`) ". |
6160 | "VALUES ('". $grpstatus["receiptID"] ."', '". |
6161 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
6162 | $requestUser->getID() ."')"; |
6163 | $res=$db->getResult($queryStr); |
6164 | if (is_bool($res) && !$res) |
6165 | return -1; |
6166 | else { |
6167 | $receiptLogID = $db->getInsertID('tblDocumentReceiptLog', 'receiptLogID'); |
6168 | return $receiptLogID; |
6169 | } |
6170 | } /* }}} */ |
6171 | |
6172 | /** |
6173 | * Add a revision to the document content |
6174 | * |
6175 | * This method will add an entry to the table tblDocumentRevisionLog. |
6176 | * It will first check if the user is ment to revision the document version. |
6177 | * If not the return value is -3. |
6178 | * Next it will check if the user has been removed from the list of |
6179 | * recipients. In that case -4 will be returned. |
6180 | * If the given revision has been set by the user before, it cannot |
6181 | * be set again and 0 will be returned. Іf the revision could be succesfully |
6182 | * added, the revision log id will be returned. |
6183 | * |
6184 | * @see SeedDMS_Core_DocumentContent::setApprovalByInd() |
6185 | * @param object $user user doing the revision |
6186 | * @param object $requestUser user asking for the revision, this is mostly |
6187 | * the user currently logged in. |
6188 | * @param integer $status the status of the revision, possible values are |
6189 | * 0=unprocessed (may be used to reset a status) |
6190 | * 1=revised, |
6191 | * -2=user is deleted (use {link |
6192 | * SeedDMS_Core_DocumentContent::delIndRecipient} instead) |
6193 | * -3=workflow revision is sleeping |
6194 | * @return integer new revision log id, 0, or a value < 0. 0 means the |
6195 | * status has not changed because the new status is equal the current |
6196 | * status. A value < 0 indicate |
6197 | * an error. -1: internal error, -3: user may not revise this document |
6198 | * -4: the user has been removed from the list of revisors, |
6199 | * -5: the revision has not been started at all. |
6200 | */ |
6201 | function setRevision($object, $requestUser, $status, $comment) { /* {{{ */ |
6202 | $dms = $this->_document->getDMS(); |
6203 | $db = $dms->getDB(); |
6204 | |
6205 | if(!$object || !$requestUser) |
6206 | return -1; |
6207 | |
6208 | // Check to see if the user/group can be removed from the review list. |
6209 | $revisionStatus = $object->getRevisionStatus($this->_document->getID(), $this->_version); |
6210 | if (is_bool($revisionStatus) && !$revisionStatus) { |
6211 | return -1; |
6212 | } |
6213 | |
6214 | /* getRevisionStatus() returns an array with either an element |
6215 | * 'indstatus' (user) or 'status' (group) containing the revision log |
6216 | */ |
6217 | if(get_class($object) == $dms->getClassname('user')) { |
6218 | $revisionStatus = $revisionStatus['indstatus']; |
6219 | } elseif(get_class($object) == $dms->getClassname('group')) { |
6220 | } else { |
6221 | return -1; |
6222 | } |
6223 | |
6224 | if (!$revisionStatus) { |
6225 | // User is not assigned to revision this document. No action required. |
6226 | // Return an error. |
6227 | return -3; |
6228 | } |
6229 | $indstatus = array_pop($revisionStatus); |
6230 | |
6231 | /* check if revision workflow has been started already */ |
6232 | if($indstatus['status'] == S_LOG_SLEEPING && ($status == S_LOG_REJECTED || $status == S_LOG_ACCEPTED)) |
6233 | return -5; |
6234 | |
6235 | if ($indstatus["status"] == -2) { |
6236 | // User has been deleted from recipients |
6237 | return -4; |
6238 | } |
6239 | // Check if the status is really different from the current status |
6240 | if ($indstatus["status"] == $status) |
6241 | return 0; |
6242 | |
6243 | $queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, |
6244 | `comment`, `date`, `userID`) ". |
6245 | "VALUES ('". $indstatus["revisionID"] ."', '". |
6246 | (int) $status ."', ".$db->qstr($comment).", ".$db->getCurrentDatetime().", '". |
6247 | $requestUser->getID() ."')"; |
6248 | $res=$db->getResult($queryStr); |
6249 | if (is_bool($res) && !$res) |
6250 | return -1; |
6251 | else { |
6252 | $revisionLogID = $db->getInsertID('tblDocumentRevisionLog', 'revisionLogID'); |
6253 | return $revisionLogID; |
6254 | } |
6255 | } /* }}} */ |
6256 | |
6257 | function setRevisionByInd($user, $requestUser, $status, $comment) { /* {{{ */ |
6258 | if(!$user || !$user->isType('user')) |
6259 | return -1; |
6260 | |
6261 | return self::setRevision($user, $requestUser, $status, $comment); |
6262 | } /* }}} */ |
6263 | |
6264 | function setRevisionByGrp($group, $requestUser, $status, $comment) { /* {{{ */ |
6265 | if(!$group || !$group->isType('group')) |
6266 | return -1; |
6267 | |
6268 | return self::setRevision($group, $requestUser, $status, $comment); |
6269 | } /* }}} */ |
6270 | |
6271 | function delIndReviewer($user, $requestUser, $msg='') { /* {{{ */ |
6272 | $db = $this->_document->getDMS()->getDB(); |
6273 | |
6274 | if(!$user || !$user->isType('user')) |
6275 | return -1; |
6276 | |
6277 | // Check to see if the user can be removed from the review list. |
6278 | $reviewStatus = $user->getReviewStatus($this->_document->getID(), $this->_version); |
6279 | if (is_bool($reviewStatus) && !$reviewStatus) { |
6280 | return false; |
6281 | } |
6282 | if (count($reviewStatus["indstatus"])==0) { |
6283 | // User is not assigned to review this document. No action required. |
6284 | // Return an error. |
6285 | return -2; |
6286 | } |
6287 | $indstatus = array_pop($reviewStatus["indstatus"]); |
6288 | if ($indstatus["status"]!=0) { |
6289 | // User has already submitted a review or has already been deleted; |
6290 | // return an error. |
6291 | return -3; |
6292 | } |
6293 | |
6294 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ". |
6295 | "VALUES ('". $indstatus["reviewID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6296 | $res = $db->getResult($queryStr); |
6297 | if (is_bool($res) && !$res) { |
6298 | return false; |
6299 | } |
6300 | |
6301 | return 0; |
6302 | } /* }}} */ |
6303 | |
6304 | function delGrpReviewer($group, $requestUser, $msg='') { /* {{{ */ |
6305 | $db = $this->_document->getDMS()->getDB(); |
6306 | |
6307 | if(!$group->isType('group')) |
6308 | return -1; |
6309 | |
6310 | $groupID = $group->getID(); |
6311 | |
6312 | // Check to see if the user can be removed from the review list. |
6313 | $reviewStatus = $group->getReviewStatus($this->_document->getID(), $this->_version); |
6314 | if (is_bool($reviewStatus) && !$reviewStatus) { |
6315 | return false; |
6316 | } |
6317 | if (count($reviewStatus)==0) { |
6318 | // User is not assigned to review this document. No action required. |
6319 | // Return an error. |
6320 | return -2; |
6321 | } |
6322 | if ($reviewStatus[0]["status"]!=0) { |
6323 | // User has already submitted a review or has already been deleted; |
6324 | // return an error. |
6325 | return -3; |
6326 | } |
6327 | |
6328 | $queryStr = "INSERT INTO `tblDocumentReviewLog` (`reviewID`, `status`, `comment`, `date`, `userID`) ". |
6329 | "VALUES ('". $reviewStatus[0]["reviewID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6330 | $res = $db->getResult($queryStr); |
6331 | if (is_bool($res) && !$res) { |
6332 | return false; |
6333 | } |
6334 | |
6335 | return 0; |
6336 | } /* }}} */ |
6337 | |
6338 | function delIndApprover($user, $requestUser, $msg='') { /* {{{ */ |
6339 | $db = $this->_document->getDMS()->getDB(); |
6340 | |
6341 | if(!$user->isType('user')) |
6342 | return -1; |
6343 | |
6344 | $userID = $user->getID(); |
6345 | |
6346 | // Check if the user is on the approval list at all. |
6347 | $approvalStatus = $user->getApprovalStatus($this->_document->getID(), $this->_version); |
6348 | if (is_bool($approvalStatus) && !$approvalStatus) { |
6349 | return false; |
6350 | } |
6351 | if (count($approvalStatus["indstatus"])==0) { |
6352 | // User is not assigned to approve this document. No action required. |
6353 | // Return an error. |
6354 | return -2; |
6355 | } |
6356 | $indstatus = array_pop($approvalStatus["indstatus"]); |
6357 | if ($indstatus["status"]!=0) { |
6358 | // User has already submitted an approval or has already been deleted; |
6359 | // return an error. |
6360 | return -3; |
6361 | } |
6362 | |
6363 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ". |
6364 | "VALUES ('". $indstatus["approveID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6365 | $res = $db->getResult($queryStr); |
6366 | if (is_bool($res) && !$res) { |
6367 | return false; |
6368 | } |
6369 | |
6370 | return 0; |
6371 | } /* }}} */ |
6372 | |
6373 | function delGrpApprover($group, $requestUser, $msg='') { /* {{{ */ |
6374 | $db = $this->_document->getDMS()->getDB(); |
6375 | |
6376 | if(!$group->isType('group')) |
6377 | return -1; |
6378 | |
6379 | $groupID = $group->getID(); |
6380 | |
6381 | // Check if the group is on the approval list at all. |
6382 | $approvalStatus = $group->getApprovalStatus($this->_document->getID(), $this->_version); |
6383 | if (is_bool($approvalStatus) && !$approvalStatus) { |
6384 | return false; |
6385 | } |
6386 | if (count($approvalStatus)==0) { |
6387 | // User is not assigned to approve this document. No action required. |
6388 | // Return an error. |
6389 | return -2; |
6390 | } |
6391 | if ($approvalStatus[0]["status"]!=0) { |
6392 | // User has already submitted an approval or has already been deleted; |
6393 | // return an error. |
6394 | return -3; |
6395 | } |
6396 | |
6397 | $queryStr = "INSERT INTO `tblDocumentApproveLog` (`approveID`, `status`, `comment`, `date`, `userID`) ". |
6398 | "VALUES ('". $approvalStatus[0]["approveID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6399 | $res = $db->getResult($queryStr); |
6400 | if (is_bool($res) && !$res) { |
6401 | return false; |
6402 | } |
6403 | |
6404 | return 0; |
6405 | } /* }}} */ |
6406 | |
6407 | function delIndRecipient($user, $requestUser, $msg='') { /* {{{ */ |
6408 | $db = $this->_document->getDMS()->getDB(); |
6409 | |
6410 | $userID = $user->getID(); |
6411 | |
6412 | // Check to see if the user can be removed from the recipient list. |
6413 | $receiptStatus = $user->getReceiptStatus($this->_document->getID(), $this->_version); |
6414 | if (is_bool($receiptStatus) && !$receiptStatus) { |
6415 | return -1; |
6416 | } |
6417 | if (count($receiptStatus["indstatus"])==0) { |
6418 | // User is not assigned to receipt this document. No action required. |
6419 | // Return an error. |
6420 | return -2; |
6421 | } |
6422 | $indstatus = array_pop($receiptStatus["indstatus"]); |
6423 | if ($indstatus["status"]!=0) { |
6424 | // User has already submitted a receipt or has already been deleted; |
6425 | // return an error. |
6426 | return -3; |
6427 | } |
6428 | |
6429 | $queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, `comment`, `date`, `userID`) ". |
6430 | "VALUES ('". $indstatus["receiptID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6431 | $res = $db->getResult($queryStr); |
6432 | if (is_bool($res) && !$res) { |
6433 | return -1; |
6434 | } |
6435 | |
6436 | return 0; |
6437 | } /* }}} */ |
6438 | |
6439 | function delGrpRecipient($group, $requestUser, $msg='') { /* {{{ */ |
6440 | $db = $this->_document->getDMS()->getDB(); |
6441 | |
6442 | $groupID = $group->getID(); |
6443 | |
6444 | // Check to see if the user can be removed from the recipient list. |
6445 | $receiptStatus = $group->getReceiptStatus($this->_document->getID(), $this->_version); |
6446 | if (is_bool($receiptStatus) && !$receiptStatus) { |
6447 | return -1; |
6448 | } |
6449 | if (count($receiptStatus)==0) { |
6450 | // User is not assigned to receipt this document. No action required. |
6451 | // Return an error. |
6452 | return -2; |
6453 | } |
6454 | $status = array_pop($receiptStatus); |
6455 | if ($status["status"]!=0) { |
6456 | // User has already submitted a receipt or has already been deleted; |
6457 | // return an error. |
6458 | return -3; |
6459 | } |
6460 | |
6461 | $queryStr = "INSERT INTO `tblDocumentReceiptLog` (`receiptID`, `status`, `comment`, `date`, `userID`) ". |
6462 | "VALUES ('". $status["receiptID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6463 | $res = $db->getResult($queryStr); |
6464 | if (is_bool($res) && !$res) { |
6465 | return -1; |
6466 | } |
6467 | |
6468 | return 0; |
6469 | } /* }}} */ |
6470 | |
6471 | /** |
6472 | * Removes a user from the revision workflow |
6473 | * |
6474 | * This methods behaves differently from one in the other workflows, e.g. |
6475 | * {@see SeedDMS_Core_DocumentContent::delIndReviewer}, because it |
6476 | * also takes into account if the workflow has been started already. |
6477 | * A workflow has been started, when there are entries in the revision log. |
6478 | * If the revision workflow has not been started, then the user will |
6479 | * be silently removed from the list of revisors. If the workflow has |
6480 | * started already, then log entry will indicated the removal of the |
6481 | * user (just as it is done with the other workflows) |
6482 | * |
6483 | * @param object $object user/group which is to be removed |
6484 | * @param object $requestUser user requesting the removal |
6485 | * @return integer 0 if removal was successfull, -1 if an internal error |
6486 | * occured, -3 if the user is not in the list of revisors |
6487 | * |
6488 | */ |
6489 | function delRevisor($object, $requestUser, $msg='') { /* {{{ */ |
6490 | $dms = $this->_document->getDMS(); |
6491 | $db = $dms->getDB(); |
6492 | |
6493 | // Check to see if the user/group can be removed from the revisor list. |
6494 | $revisionStatus = $object->getRevisionStatus($this->_document->getID(), $this->_version); |
6495 | if (is_bool($revisionStatus) && !$revisionStatus) { |
6496 | return -1; |
6497 | } |
6498 | |
6499 | /* getRevisionStatus() returns an array with either an element |
6500 | * 'indstatus' (user) or no element (group) containing the revision log |
6501 | */ |
6502 | if(get_class($object) == $dms->getClassname('user')) { |
6503 | $revisionStatus = $revisionStatus['indstatus']; |
6504 | $type = 0; |
6505 | } elseif(get_class($object) == $dms->getClassname('group')) { |
6506 | $type = 1; |
6507 | } else { |
6508 | return -1; |
6509 | } |
6510 | |
6511 | if (!$revisionStatus) { |
6512 | // User is not assigned to revision this document. No action required. |
6513 | // Return an error. |
6514 | return -2; |
6515 | } |
6516 | |
6517 | /* If the revision log doesn't contain an entry yet, then remove the |
6518 | * user/group from the list of revisors. The first case should not happen. |
6519 | */ |
6520 | if(count($revisionStatus) == 0) { |
6521 | $queryStr = "DELETE from `tblDocumentRevisors` WHERE `documentID` = ". $this->_document->getID() ." AND `version` = ".$this->_version." AND `type` = ". $type ." AND `required` = ".$object->getID(); |
6522 | if (!$db->getResult($queryStr)) { |
6523 | return -1; |
6524 | } |
6525 | } else { |
6526 | $indstatus = array_pop($revisionStatus); |
6527 | if ($indstatus["status"] != S_LOG_WAITING && $indstatus["status"] != S_LOG_SLEEPING) { |
6528 | // User has already submitted a revision or has already been deleted; |
6529 | // return an error. |
6530 | if($indstatus["status"] == S_LOG_USER_REMOVED) |
6531 | return -3; |
6532 | else |
6533 | return -4; |
6534 | } |
6535 | |
6536 | $queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, `comment`, `date`, `userID`) ". |
6537 | "VALUES ('". $indstatus["revisionID"] ."', '".S_LOG_USER_REMOVED."', ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". $requestUser->getID() ."')"; |
6538 | $res = $db->getResult($queryStr); |
6539 | if (is_bool($res) && !$res) { |
6540 | return -1; |
6541 | } |
6542 | } |
6543 | |
6544 | return 0; |
6545 | } /* }}} */ |
6546 | |
6547 | function delIndRevisor($user, $requestUser, $msg='') { /* {{{ */ |
6548 | return self::delRevisor($user, $requestUser, $msg); |
6549 | } /* }}} */ |
6550 | |
6551 | function delGrpRevisor($group, $requestUser, $msg='') { /* {{{ */ |
6552 | return self::delRevisor($group, $requestUser, $msg); |
6553 | } /* }}} */ |
6554 | |
6555 | /** |
6556 | * Start a new revision workflow |
6557 | * |
6558 | * This function starts a new revision unless there are users/groups |
6559 | * having finished the previous revision. This means the log status |
6560 | * must be S_LOG_SLEEPING or the user/group was removed (S_LOG_USER_REMOVED) |
6561 | * |
6562 | * @param object $requestUser user requesting the revision start |
6563 | * @param string $msg message saved for the initial log message |
6564 | */ |
6565 | function startRevision($requestUser, $msg='') { /* {{{ */ |
6566 | $dms = $this->_document->getDMS(); |
6567 | $db = $dms->getDB(); |
6568 | |
6569 | $revisionStatus = self::getRevisionStatus(); |
6570 | if(!$revisionStatus) |
6571 | return false; |
6572 | |
6573 | /* A new revision may only be started if we are not in the middle of |
6574 | * revision or the user/group has been removed from the workflow |
6575 | */ |
6576 | /* Taken out, because it happened that a revision wasn't started for each revisor |
6577 | * but just for some. |
6578 | * Checking for each revisor not being sleeping prevented a second start of the |
6579 | * revision for the remaining revisors still sleeping. |
6580 | foreach($revisionStatus as $status) { |
6581 | if($status['status'] != S_LOG_SLEEPING && $status['status'] != S_LOG_USER_REMOVED) |
6582 | return false; |
6583 | } |
6584 | */ |
6585 | |
6586 | /* Make sure all Logs will be set to the right status, in order to |
6587 | * prevent inconsistent states. Actually it could be a feature to |
6588 | * force only some users/groups to revise the document, but for now |
6589 | * this may not be possible. |
6590 | */ |
6591 | $db->startTransaction(); |
6592 | $startedrev = false; |
6593 | foreach($revisionStatus as $status) { |
6594 | if($status['status'] == S_LOG_SLEEPING) { |
6595 | $queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, |
6596 | `comment`, `date`, `userID`) ". |
6597 | "VALUES ('". $status["revisionID"] ."', ". |
6598 | S_LOG_WAITING.", ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". |
6599 | $requestUser->getID() ."')"; |
6600 | $res=$db->getResult($queryStr); |
6601 | if (is_bool($res) && !$res) { |
6602 | $db->rollbackTransaction(); |
6603 | return false; |
6604 | } |
6605 | $startedrev = true; |
6606 | } |
6607 | } |
6608 | /* Set status only if at least one revision was started */ |
6609 | if($startedrev) |
6610 | if(!$this->setStatus(S_IN_REVISION, "Started revision scheduled for ".$this->getRevisionDate(), $requestUser)) { |
6611 | $db->rollbackTransaction(); |
6612 | return false; |
6613 | } |
6614 | $db->commitTransaction(); |
6615 | return true; |
6616 | |
6617 | } /* }}} */ |
6618 | |
6619 | /** |
6620 | * Finish a revision workflow |
6621 | * |
6622 | * This function ends a revision This means the log status |
6623 | * is set back S_LOG_SLEEPING and the document status is set as |
6624 | * passed to the method. The function doesn't not check if all |
6625 | * users/groups has made it vote already. |
6626 | * |
6627 | * @param object $requestUser user requesting the revision start |
6628 | * @param integer $docstatus document status |
6629 | * @param string $msg message saved in revision log |
6630 | * @param string $msg message saved in document status log |
6631 | */ |
6632 | function finishRevision($requestUser, $docstatus, $msg='', $docmsg='') { /* {{{ */ |
6633 | $dms = $this->_document->getDMS(); |
6634 | $db = $dms->getDB(); |
6635 | |
6636 | $revisionStatus = self::getRevisionStatus(); |
6637 | if(!$revisionStatus) |
6638 | return false; |
6639 | |
6640 | /* A revision may only be finished if it wasn't finished already |
6641 | */ |
6642 | foreach($revisionStatus as $status) { |
6643 | if($status['status'] == S_LOG_SLEEPING) |
6644 | return false; |
6645 | } |
6646 | |
6647 | /* Make sure all Logs will be set to the right status, in order to |
6648 | * prevent inconsistent states. Actually it could be a feature to |
6649 | * end only some users/groups to revise the document, but for now |
6650 | * this may not be possible. |
6651 | */ |
6652 | $db->startTransaction(); |
6653 | /* Does it make sense to put all revisions into sleeping mode? I guess |
6654 | * not. If a document was released or rejected the revision are useless |
6655 | * anyway |
6656 | */ |
6657 | foreach($revisionStatus as $status) { |
6658 | if($status['status'] != S_LOG_SLEEPING && $status['status'] != S_LOG_USER_REMOVED) { |
6659 | $queryStr = "INSERT INTO `tblDocumentRevisionLog` (`revisionID`, `status`, |
6660 | `comment`, `date`, `userID`) ". |
6661 | "VALUES ('". $status["revisionID"] ."', ". |
6662 | S_LOG_SLEEPING.", ".$db->qstr($msg).", ".$db->getCurrentDatetime().", '". |
6663 | $requestUser->getID() ."')"; |
6664 | $res=$db->getResult($queryStr); |
6665 | if (is_bool($res) && !$res) { |
6666 | $db->rollbackTransaction(); |
6667 | return false; |
6668 | } |
6669 | } |
6670 | } |
6671 | if(!$this->setStatus($docstatus, $docmsg, $requestUser)) { |
6672 | $db->rollbackTransaction(); |
6673 | return false; |
6674 | } |
6675 | $db->commitTransaction(); |
6676 | return true; |
6677 | |
6678 | } /* }}} */ |
6679 | |
6680 | /** |
6681 | * Set state of workflow assigned to the document content |
6682 | * |
6683 | * @param object $state |
6684 | */ |
6685 | function setWorkflowState($state) { /* {{{ */ |
6686 | $db = $this->_document->getDMS()->getDB(); |
6687 | |
6688 | if($this->_workflow) { |
6689 | $queryStr = "UPDATE `tblWorkflowDocumentContent` set `state`=". $state->getID() ." WHERE `id`=". $this->_workflow['id']; |
6690 | if (!$db->getResult($queryStr)) { |
6691 | return false; |
6692 | } |
6693 | $this->_workflowState = $state; |
6694 | return true; |
6695 | } |
6696 | return false; |
6697 | } /* }}} */ |
6698 | |
6699 | /** |
6700 | * Get state of workflow assigned to the document content |
6701 | * |
6702 | * @return object/boolean an object of class SeedDMS_Core_Workflow_State |
6703 | * or false in case of error, e.g. the version has not a workflow |
6704 | */ |
6705 | function getWorkflowState() { /* {{{ */ |
6706 | $db = $this->_document->getDMS()->getDB(); |
6707 | |
6708 | if(!$this->_workflow) |
6709 | $this->getWorkflow(); |
6710 | |
6711 | if(!$this->_workflow) |
6712 | return false; |
6713 | |
6714 | if (!$this->_workflowState) { |
6715 | $queryStr= |
6716 | "SELECT b.* FROM `tblWorkflowDocumentContent` a LEFT JOIN `tblWorkflowStates` b ON a.`state` = b.id WHERE a.`state` IS NOT NULL AND `a`.`id`=". $this->_workflow['id']; |
6717 | $recs = $db->getResultArray($queryStr); |
6718 | if (!$recs) |
6719 | return false; |
6720 | $this->_workflowState = new SeedDMS_Core_Workflow_State($recs[0]['id'], $recs[0]['name'], $recs[0]['maxtime'], $recs[0]['precondfunc'], $recs[0]['documentstatus']); |
6721 | $this->_workflowState->setDMS($this->_document->getDMS()); |
6722 | } |
6723 | return $this->_workflowState; |
6724 | } /* }}} */ |
6725 | |
6726 | /** |
6727 | * Assign a workflow to a document content |
6728 | * |
6729 | * @param object $workflow |
6730 | */ |
6731 | function setWorkflow($workflow, $user) { /* {{{ */ |
6732 | $db = $this->_document->getDMS()->getDB(); |
6733 | |
6734 | $this->getWorkflow(); |
6735 | if($this->_workflow) |
6736 | return false; |
6737 | |
6738 | if($workflow && is_object($workflow)) { |
6739 | $db->startTransaction(); |
6740 | $initstate = $workflow->getInitState(); |
6741 | $queryStr = "INSERT INTO `tblWorkflowDocumentContent` (`workflow`, `document`, `version`, `state`, `date`) VALUES (". $workflow->getID(). ", ". $this->_document->getID() .", ". $this->_version .", ".$initstate->getID().", ".$db->getCurrentDatetime().")"; |
6742 | if (!$db->getResult($queryStr)) { |
6743 | $db->rollbackTransaction(); |
6744 | return false; |
6745 | } |
6746 | $this->getWorkflow(); |
6747 | if($workflow->getID() != $this->_workflow['workflow']->getID()) { |
6748 | $db->rollbackTransaction(); |
6749 | return false; |
6750 | } |
6751 | if(!$this->setStatus(S_IN_WORKFLOW, "Added workflow '".$workflow->getName()."'", $user)) { |
6752 | $db->rollbackTransaction(); |
6753 | return false; |
6754 | } |
6755 | $db->commitTransaction(); |
6756 | return true; |
6757 | } |
6758 | return false; |
6759 | } /* }}} */ |
6760 | |
6761 | /** |
6762 | * Get workflow assigned to the document content |
6763 | * |
6764 | * The method returns the last workflow if one was assigned. |
6765 | * If the document version is in a sub workflow, it will have |
6766 | * a never date and therefore will be found first. |
6767 | * The methods also sets $this->_workflow['id'] and |
6768 | * $this->_workflow['parent']. $this->_workflow['id'] is the |
6769 | * id from table tblWorkflowDocumentContent which is used to |
6770 | * get log entries for this workflow. |
6771 | * This method will only get a currently running workflow in |
6772 | * a state. Once a |
6773 | * workflow has ended, the current state of the workflow was |
6774 | * set to null. |
6775 | * |
6776 | * @param bool $full return not just workflow but the data from |
6777 | * tblWorkflowDocumentContent too |
6778 | * @return object/boolean an object of class SeedDMS_Core_Workflow |
6779 | * or false in case of error, e.g. the version has not a workflow |
6780 | */ |
6781 | function getWorkflow($full = false) { /* {{{ */ |
6782 | $db = $this->_document->getDMS()->getDB(); |
6783 | |
6784 | if (!$this->_workflow) { |
6785 | $queryStr= |
6786 | "SELECT a.`id` as `wdcid`, a.`parent`, a.`date`, b.* FROM `tblWorkflowDocumentContent` a LEFT JOIN `tblWorkflows` b ON a.`workflow` = b.`id` WHERE a.`version`='".$this->_version |
6787 | ."' AND a.`document` = '". $this->_document->getID() ."' " |
6788 | ." AND a.`state` IS NOT NULL" |
6789 | ." ORDER BY `date` DESC LIMIT 1"; |
6790 | $recs = $db->getResultArray($queryStr); |
6791 | if (is_bool($recs) && !$recs) |
6792 | return false; |
6793 | if(!$recs) |
6794 | return false; |
6795 | $this->_workflow = array('id'=>(int)$recs[0]['wdcid'], 'parent'=>(int)$recs[0]['parent'], 'date'=>$recs[0]['date'], 'workflow'=>new SeedDMS_Core_Workflow($recs[0]['id'], $recs[0]['name'], $this->_document->getDMS()->getWorkflowState($recs[0]['initstate']), $recs[0]["layoutdata"])); |
6796 | $this->_workflow['workflow']->setDMS($this->_document->getDMS()); |
6797 | } |
6798 | if($full) |
6799 | return $this->_workflow; |
6800 | else |
6801 | return $this->_workflow['workflow']; |
6802 | } /* }}} */ |
6803 | |
6804 | /** |
6805 | * Rewrites the complete workflow log |
6806 | * |
6807 | * Attention: this function is highly dangerous. |
6808 | * It removes an existing workflow log and rewrites it. |
6809 | * This method was added for importing an xml dump. |
6810 | * |
6811 | * @param array $workflowlog new workflow log with the newest log entry first. |
6812 | * @return boolean true on success, otherwise false |
6813 | */ |
6814 | function rewriteWorkflowLog($workflowlog) { /* {{{ */ |
6815 | $db = $this->_document->getDMS()->getDB(); |
6816 | |
6817 | /* Get the workflowdocumentcontent */ |
6818 | $queryStr = "SELECT `id` FROM `tblWorkflowDocumentContent` WHERE `tblWorkflowDocumentContent`.`document` = '". $this->_document->getID() ."' AND `tblWorkflowDocumentContent`.`version` = '". $this->_version ."'"; |
6819 | $recs = $db->getResultArray($queryStr); |
6820 | if (is_bool($recs) && !$recs) |
6821 | return false; |
6822 | if (!$recs) |
6823 | return false; |
6824 | |
6825 | $db->startTransaction(); |
6826 | |
6827 | /* First, remove the old entries */ |
6828 | $queryStr = "DELETE FROM `tblWorkflowLog` WHERE `tblWorkflowLog`.`workflowdocumentcontent` IN (SELECT `id` FROM `tblWorkflowDocumentContent` WHERE `tblWorkflowDocumentContent`.`document` = '". $this->_document->getID() ."' AND `tblWorkflowDocumentContent`.`version` = '". $this->_version ."')"; |
6829 | if (!$db->getResult($queryStr)) { |
6830 | $db->rollbackTransaction(); |
6831 | return false; |
6832 | } |
6833 | |
6834 | /* Second, insert the new entries */ |
6835 | $workflowlog = array_reverse($workflowlog); |
6836 | foreach($workflowlog as $log) { |
6837 | if(!SeedDMS_Core_DMS::checkDate($log['date'], 'Y-m-d H:i:s')) { |
6838 | $db->rollbackTransaction(); |
6839 | return false; |
6840 | } |
6841 | $queryStr = "INSERT INTO `tblWorkflowLog` (`workflowdocumentcontent`, `transition`, `comment`, `date`, `userid`) ". |
6842 | "VALUES ('".$recs[0]['id'] ."', '".(int) $log['transition']->getID()."', ".$db->qstr($log['comment']) .", ".$db->qstr($log['date']).", ".$log['user']->getID().")"; |
6843 | if (!$db->getResult($queryStr)) { |
6844 | $db->rollbackTransaction(); |
6845 | return false; |
6846 | } |
6847 | } |
6848 | |
6849 | $db->commitTransaction(); |
6850 | return true; |
6851 | } /* }}} */ |
6852 | |
6853 | /** |
6854 | * Restart workflow from its initial state |
6855 | * |
6856 | * @return boolean true if workflow could be restarted |
6857 | * or false in case of error |
6858 | */ |
6859 | function rewindWorkflow() { /* {{{ */ |
6860 | $db = $this->_document->getDMS()->getDB(); |
6861 | |
6862 | $this->getWorkflow(); |
6863 | |
6864 | if (!$this->_workflow) { |
6865 | return true; |
6866 | } |
6867 | $workflow = $this->_workflow['workflow']; |
6868 | |
6869 | $db->startTransaction(); |
6870 | $queryStr = "DELETE from `tblWorkflowLog` WHERE `workflowdocumentcontent` = ".$this->_workflow['id']; |
6871 | if (!$db->getResult($queryStr)) { |
6872 | $db->rollbackTransaction(); |
6873 | return false; |
6874 | } |
6875 | |
6876 | $this->setWorkflowState($workflow->getInitState()); |
6877 | $db->commitTransaction(); |
6878 | |
6879 | return true; |
6880 | } /* }}} */ |
6881 | |
6882 | /** |
6883 | * Remove workflow |
6884 | * |
6885 | * Fully removing a workflow including entries in the workflow log is |
6886 | * only allowed if the workflow is still its initial state. |
6887 | * At a later point of time only unlinking the document from the |
6888 | * workflow is allowed. It will keep any log entries and set the state |
6889 | * to NULL. |
6890 | * A workflow is unlinked from a document when enterNextState() |
6891 | * succeeds. |
6892 | * |
6893 | * @param object $user user doing initiating the removal |
6894 | * @param boolean $unlink if true, just unlink the workflow from the |
6895 | * document but do not remove the workflow log. The $unlink |
6896 | * flag has been added to detach the workflow from the document |
6897 | * when it has reached a valid end state |
6898 | (see SeedDMS_Core_DocumentContent::enterNextState()) |
6899 | * @return boolean true if workflow could be removed |
6900 | * or false in case of error |
6901 | */ |
6902 | function removeWorkflow($user, $unlink=false) { /* {{{ */ |
6903 | $db = $this->_document->getDMS()->getDB(); |
6904 | |
6905 | $this->getWorkflow(); |
6906 | |
6907 | if (!$this->_workflow) { |
6908 | return true; |
6909 | } |
6910 | |
6911 | $workflow = $this->_workflow['workflow']; |
6912 | |
6913 | /* A workflow should always be in a state, but in case it isn't, the |
6914 | * at least allow to remove the workflow. |
6915 | */ |
6916 | $currentstate = $this->getWorkflowState(); |
6917 | if(!$currentstate || SeedDMS_Core_DMS::checkIfEqual($workflow->getInitState(), $currentstate) || $unlink == true) { |
6918 | $db->startTransaction(); |
6919 | if($unlink) { |
6920 | $queryStr= |
6921 | "UPDATE `tblWorkflowDocumentContent` SET `state` = NULL WHERE `id`=".$this->_workflow['id']; |
6922 | if (!$db->getResult($queryStr)) { |
6923 | $db->rollbackTransaction(); |
6924 | return false; |
6925 | } |
6926 | } else { |
6927 | $queryStr= |
6928 | "DELETE FROM `tblWorkflowDocumentContent` WHERE `id`=".$this->_workflow['id']; |
6929 | if (!$db->getResult($queryStr)) { |
6930 | $db->rollbackTransaction(); |
6931 | return false; |
6932 | } |
6933 | /* will be deleted automatically when tblWorkflowDocumentContent is deleted |
6934 | $queryStr= |
6935 | "DELETE FROM `tblWorkflowLog` WHERE " |
6936 | ."`version`='".$this->_version."' " |
6937 | ." AND `document` = '". $this->_document->getID() ."' " |
6938 | ." AND `workflow` = '". $workflow->getID() ."' "; |
6939 | if (!$db->getResult($queryStr)) { |
6940 | $db->rollbackTransaction(); |
6941 | return false; |
6942 | } |
6943 | */ |
6944 | } |
6945 | $this->_workflow = null; |
6946 | $this->_workflowState = null; |
6947 | $this->verifyStatus(false, $user, 'Workflow removed'); |
6948 | $db->commitTransaction(); |
6949 | } |
6950 | |
6951 | return true; |
6952 | } /* }}} */ |
6953 | |
6954 | /** |
6955 | * Run a sub workflow |
6956 | * |
6957 | * @param object $subworkflow |
6958 | */ |
6959 | function getParentWorkflow() { /* {{{ */ |
6960 | $db = $this->_document->getDMS()->getDB(); |
6961 | |
6962 | /* document content must be in a workflow */ |
6963 | $this->getWorkflow(); |
6964 | if(!$this->_workflow) |
6965 | return false; |
6966 | |
6967 | if(!$this->_workflow['parent']) |
6968 | return false; |
6969 | |
6970 | $queryStr= |
6971 | "SELECT * FROM `tblWorkflowDocumentContent` WHERE `parent`=".$this->_workflow['parent']; |
6972 | $recs = $db->getResultArray($queryStr); |
6973 | if (is_bool($recs) && !$recs) |
6974 | return false; |
6975 | if(!$recs) |
6976 | return false; |
6977 | |
6978 | if($recs[0]['workflow']) |
6979 | return $this->_document->getDMS()->getWorkflow((int)$recs[0]['workflow']); |
6980 | |
6981 | return false; |
6982 | } /* }}} */ |
6983 | |
6984 | /** |
6985 | * Run a sub workflow |
6986 | * |
6987 | * @param object $subworkflow |
6988 | */ |
6989 | function runSubWorkflow($subworkflow) { /* {{{ */ |
6990 | $db = $this->_document->getDMS()->getDB(); |
6991 | |
6992 | /* document content must be in a workflow */ |
6993 | $this->getWorkflow(); |
6994 | if(!$this->_workflow) |
6995 | return false; |
6996 | |
6997 | /* The current workflow state must match the sub workflows initial state */ |
6998 | if($subworkflow->getInitState()->getID() != $this->_workflowState->getID()) |
6999 | return false; |
7000 | |
7001 | if($subworkflow) { |
7002 | $initstate = $subworkflow->getInitState(); |
7003 | $queryStr = "INSERT INTO `tblWorkflowDocumentContent` (`parent`, `workflow`, `document`, `version`, `state`, `date`) VALUES (". $this->_workflow['id']. ", ". $subworkflow->getID(). ", ". $this->_document->getID() .", ". $this->_version .", ".$initstate->getID().", ".$db->getCurrentDatetime().")"; |
7004 | if (!$db->getResult($queryStr)) { |
7005 | return false; |
7006 | } |
7007 | $this->_workflow = array('id'=>$db->getInsertID('tblWorkflowDocumentContent'), 'parent'=>$this->_workflow['id'], 'workflow'=>$subworkflow); |
7008 | return true; |
7009 | } |
7010 | return true; |
7011 | } /* }}} */ |
7012 | |
7013 | /** |
7014 | * Return from sub workflow to parent workflow. |
7015 | * The method will trigger the given transition |
7016 | * |
7017 | * FIXME: Needs much better checking if this is allowed |
7018 | * |
7019 | * @param object $user intiating the return |
7020 | * @param object $transtion to trigger |
7021 | * @param string comment for the transition trigger |
7022 | */ |
7023 | function returnFromSubWorkflow($user, $transition=null, $comment='') { /* {{{ */ |
7024 | $db = $this->_document->getDMS()->getDB(); |
7025 | |
7026 | /* document content must be in a workflow */ |
7027 | $this->getWorkflow(); |
7028 | if(!$this->_workflow) |
7029 | return false; |
7030 | |
7031 | if ($this->_workflow) { |
7032 | $db->startTransaction(); |
7033 | |
7034 | $queryStr = "UPDATE `tblWorkflowDocumentContent` SET `state` = NULL WHERE `id` = '" . $this->_workflow['id']."'"; |
7035 | if (!$db->getResult($queryStr)) { |
7036 | $db->rollbackTransaction(); |
7037 | return false; |
7038 | } |
7039 | |
7040 | /* Calling getWorkflow() should find the parent workflow, better check */ |
7041 | $parent = $this->_workflow['parent']; |
7042 | $this->_workflow = null; |
7043 | $this->getWorkflow(); |
7044 | if($this->_workflow['id'] != $parent) { |
7045 | $db->rollbackTransaction(); |
7046 | return false; |
7047 | } |
7048 | |
7049 | if($transition) { |
7050 | if(false === $this->triggerWorkflowTransition($user, $transition, $comment)) { |
7051 | $db->rollbackTransaction(); |
7052 | return false; |
7053 | } |
7054 | } |
7055 | |
7056 | $db->commitTransaction(); |
7057 | } |
7058 | return $this->_workflow['workflow']; |
7059 | } /* }}} */ |
7060 | |
7061 | /** |
7062 | * Check if the user is allowed to trigger the transition |
7063 | * A user is allowed if either the user itself or |
7064 | * a group of which the user is a member of is registered for |
7065 | * triggering a transition. This method does not change the workflow |
7066 | * state of the document content. |
7067 | * |
7068 | * @param object $user |
7069 | * @return boolean true if user may trigger transaction |
7070 | */ |
7071 | function triggerWorkflowTransitionIsAllowed($user, $transition) { /* {{{ */ |
7072 | $db = $this->_document->getDMS()->getDB(); |
7073 | |
7074 | if(!$this->_workflow) |
7075 | $this->getWorkflow(); |
7076 | |
7077 | if(!$this->_workflow) |
7078 | return false; |
7079 | |
7080 | if(!$this->_workflowState) |
7081 | $this->getWorkflowState(); |
7082 | |
7083 | /* Check if the user has already triggered the transition */ |
7084 | $queryStr= |
7085 | "SELECT * FROM `tblWorkflowLog` WHERE `workflowdocumentcontent` = ".$this->_workflow['id']." AND userid = ".$user->getID(); |
7086 | $queryStr .= " AND `transition` = ".$transition->getID(); |
7087 | $resArr = $db->getResultArray($queryStr); |
7088 | if (is_bool($resArr) && !$resArr) |
7089 | return false; |
7090 | |
7091 | if(count($resArr)) |
7092 | return false; |
7093 | |
7094 | /* Get all transition users allowed to trigger the transition */ |
7095 | $transusers = $transition->getUsers(); |
7096 | if($transusers) { |
7097 | foreach($transusers as $transuser) { |
7098 | if($user->getID() == $transuser->getUser()->getID()) |
7099 | return true; |
7100 | } |
7101 | } |
7102 | |
7103 | /* Get all transition groups whose members are allowed to trigger |
7104 | * the transition */ |
7105 | $transgroups = $transition->getGroups(); |
7106 | if($transgroups) { |
7107 | foreach($transgroups as $transgroup) { |
7108 | $group = $transgroup->getGroup(); |
7109 | if($group->isMember($user)) |
7110 | return true; |
7111 | } |
7112 | } |
7113 | |
7114 | return false; |
7115 | } /* }}} */ |
7116 | |
7117 | /** |
7118 | * Check if all conditions are met to change the workflow state |
7119 | * of a document content (run the transition). |
7120 | * The conditions are met if all explicitly set users and a sufficient |
7121 | * number of users of the groups have acknowledged the content. |
7122 | * |
7123 | * @return boolean true if transaction maybe executed |
7124 | */ |
7125 | function executeWorkflowTransitionIsAllowed($transition) { /* {{{ */ |
7126 | if(!$this->_workflow) |
7127 | $this->getWorkflow(); |
7128 | |
7129 | if(!$this->_workflow) |
7130 | return false; |
7131 | |
7132 | if(!$this->_workflowState) |
7133 | $this->getWorkflowState(); |
7134 | |
7135 | /* Get the Log of transition triggers */ |
7136 | $entries = $this->getWorkflowLog($transition); |
7137 | if(!$entries) |
7138 | return false; |
7139 | |
7140 | /* Get all transition users allowed to trigger the transition |
7141 | * $allowedusers is a list of all users allowed to trigger the |
7142 | * transition |
7143 | */ |
7144 | $transusers = $transition->getUsers(); |
7145 | $allowedusers = array(); |
7146 | foreach($transusers as $transuser) { |
7147 | $a = $transuser->getUser(); |
7148 | $allowedusers[$a->getID()] = $a; |
7149 | } |
7150 | |
7151 | /* Get all transition groups whose members are allowed to trigger |
7152 | * the transition */ |
7153 | $transgroups = $transition->getGroups(); |
7154 | foreach($entries as $entry) { |
7155 | $loguser = $entry->getUser(); |
7156 | /* Unset each allowed user if it was found in the log */ |
7157 | if(isset($allowedusers[$loguser->getID()])) |
7158 | unset($allowedusers[$loguser->getID()]); |
7159 | /* Also check groups if required. Count the group membership of |
7160 | * each user in the log in the array $gg |
7161 | */ |
7162 | if($transgroups) { |
7163 | $loggroups = $loguser->getGroups(); |
7164 | foreach($loggroups as $loggroup) { |
7165 | if(!isset($gg[$loggroup->getID()])) |
7166 | $gg[$loggroup->getID()] = 1; |
7167 | else |
7168 | $gg[$loggroup->getID()]++; |
7169 | } |
7170 | } |
7171 | } |
7172 | /* If there are allowed users left, then there some users still |
7173 | * need to trigger the transition. |
7174 | */ |
7175 | if($allowedusers) |
7176 | return false; |
7177 | |
7178 | if($transgroups) { |
7179 | foreach($transgroups as $transgroup) { |
7180 | $group = $transgroup->getGroup(); |
7181 | $minusers = $transgroup->getNumOfUsers(); |
7182 | if(!isset($gg[$group->getID()])) |
7183 | return false; |
7184 | if($gg[$group->getID()] < $minusers) |
7185 | return false; |
7186 | } |
7187 | } |
7188 | return true; |
7189 | } /* }}} */ |
7190 | |
7191 | /** |
7192 | * Trigger transition |
7193 | * |
7194 | * This method will be deprecated |
7195 | * |
7196 | * The method will first check if the user is allowed to trigger the |
7197 | * transition. If the user is allowed, an entry in the workflow log |
7198 | * will be added, which is later used to check if the transition |
7199 | * can actually be processed. The method will finally call |
7200 | * executeWorkflowTransitionIsAllowed() which checks all log entries |
7201 | * and does the transitions post function if all users and groups have |
7202 | * triggered the transition. Finally enterNextState() is called which |
7203 | * will try to enter the next state. |
7204 | * |
7205 | * @param object $user |
7206 | * @param object $transition |
7207 | * @param string $comment user comment |
7208 | * @return boolean/object next state if transition could be triggered and |
7209 | * then next state could be entered, |
7210 | * true if the transition could just be triggered or |
7211 | * false in case of an error |
7212 | */ |
7213 | function triggerWorkflowTransition($user, $transition, $comment='') { /* {{{ */ |
7214 | $db = $this->_document->getDMS()->getDB(); |
7215 | |
7216 | if(!$this->_workflow) |
7217 | $this->getWorkflow(); |
7218 | |
7219 | if(!$this->_workflow) |
7220 | return false; |
7221 | |
7222 | if(!$this->_workflowState) |
7223 | $this->getWorkflowState(); |
7224 | |
7225 | if(!$this->_workflowState) |
7226 | return false; |
7227 | |
7228 | /* Check if the user is allowed to trigger the transition. |
7229 | */ |
7230 | if(!$this->triggerWorkflowTransitionIsAllowed($user, $transition)) |
7231 | return false; |
7232 | |
7233 | $queryStr = "INSERT INTO `tblWorkflowLog` (`workflowdocumentcontent`, `userid`, `transition`, `date`, `comment`) VALUES (".$this->_workflow['id'].", ".(int) $user->getID(). ", ".(int) $transition->getID().", ".$db->getCurrentDatetime().", ".$db->qstr($comment).")"; |
7234 | if (!$db->getResult($queryStr)) |
7235 | return false; |
7236 | |
7237 | /* Check if this transition is processed. Run the post function in |
7238 | * that case. A transition is processed when all users and groups |
7239 | * have triggered it. |
7240 | */ |
7241 | if($this->executeWorkflowTransitionIsAllowed($transition)) { |
7242 | /* run post function of transition */ |
7243 | // echo "run post function of transition ".$transition->getID()."<br />"; |
7244 | } |
7245 | |
7246 | /* Go into the next state. This will only succeed if the pre condition |
7247 | * function of that states succeeds. |
7248 | */ |
7249 | $nextstate = $transition->getNextState(); |
7250 | if($this->enterNextState($user, $nextstate)) { |
7251 | return $nextstate; |
7252 | } |
7253 | return true; |
7254 | |
7255 | } /* }}} */ |
7256 | |
7257 | /** |
7258 | * Enter next state of workflow if possible |
7259 | * |
7260 | * The method will check if one of the following states in the workflow |
7261 | * can be reached. |
7262 | * It does it by running |
7263 | * the precondition function of that state. The precondition function |
7264 | * gets a list of all transitions leading to the state. It will |
7265 | * determine, whether the transitions has been triggered and if that |
7266 | * is sufficient to enter the next state. If no pre condition function |
7267 | * is set, then 1 of n transtions are enough to enter the next state. |
7268 | * |
7269 | * If moving in the next state is possible and this state has a |
7270 | * corresponding document state, then the document state will be |
7271 | * updated and the workflow will be detached from the document. |
7272 | * |
7273 | * @param object $user |
7274 | * @param object $nextstate |
7275 | * @return boolean true if the state could be reached |
7276 | * false if not |
7277 | */ |
7278 | function enterNextState($user, $nextstate) { /* {{{ */ |
7279 | |
7280 | /* run the pre condition of the next state. If it is not set |
7281 | * the next state will be reached if one of the transitions |
7282 | * leading to the given state can be processed. |
7283 | */ |
7284 | if($nextstate->getPreCondFunc() == '') { |
7285 | $workflow = $this->_workflow['workflow']; |
7286 | $transitions = $workflow->getPreviousTransitions($nextstate); |
7287 | foreach($transitions as $transition) { |
7288 | // echo "transition ".$transition->getID()." led to state ".$nextstate->getName()."<br />"; |
7289 | if($this->executeWorkflowTransitionIsAllowed($transition)) { |
7290 | // echo "stepping into next state<br />"; |
7291 | $this->setWorkflowState($nextstate); |
7292 | |
7293 | /* Check if the new workflow state has a mapping into a |
7294 | * document state. If yes, set the document state will |
7295 | * be updated and the workflow will be removed from the |
7296 | * document. |
7297 | */ |
7298 | $docstate = $nextstate->getDocumentStatus(); |
7299 | if($docstate == S_RELEASED || $docstate == S_REJECTED) { |
7300 | $this->setStatus($docstate, "Workflow has ended", $user); |
7301 | /* Detach the workflow from the document, but keep the |
7302 | * workflow log |
7303 | */ |
7304 | $this->removeWorkflow($user, true); |
7305 | return true ; |
7306 | } |
7307 | |
7308 | /* make sure the users and groups allowed to trigger the next |
7309 | * transitions are also allowed to read the document |
7310 | */ |
7311 | $transitions = $workflow->getNextTransitions($nextstate); |
7312 | foreach($transitions as $tran) { |
7313 | // echo "checking access for users/groups allowed to trigger transition ".$tran->getID()."<br />"; |
7314 | $transusers = $tran->getUsers(); |
7315 | foreach($transusers as $transuser) { |
7316 | $u = $transuser->getUser(); |
7317 | // echo $u->getFullName()."<br />"; |
7318 | if($this->_document->getAccessMode($u) < M_READ) { |
7319 | $this->_document->addAccess(M_READ, $u->getID(), 1); |
7320 | // echo "granted read access<br />"; |
7321 | } else { |
7322 | // echo "has already access<br />"; |
7323 | } |
7324 | } |
7325 | $transgroups = $tran->getGroups(); |
7326 | foreach($transgroups as $transgroup) { |
7327 | $g = $transgroup->getGroup(); |
7328 | // echo $g->getName()."<br />"; |
7329 | if ($this->_document->getGroupAccessMode($g) < M_READ) { |
7330 | $this->_document->addAccess(M_READ, $g->getID(), 0); |
7331 | // echo "granted read access<br />"; |
7332 | } else { |
7333 | // echo "has already access<br />"; |
7334 | } |
7335 | } |
7336 | } |
7337 | return(true); |
7338 | } else { |
7339 | // echo "transition not ready for process now<br />"; |
7340 | } |
7341 | } |
7342 | return false; |
7343 | } else { |
7344 | } |
7345 | |
7346 | } /* }}} */ |
7347 | |
7348 | /** |
7349 | * Get the so far logged operations on the document content within the |
7350 | * workflow. If the document content is currently in a workflow and |
7351 | * a transition is passed, then the |
7352 | * log entries will be restricted on the workflow and returned as a one |
7353 | * dimensional list. Without a running workflow the log entries of |
7354 | * all workflows in the past are returned grouped by workflow. |
7355 | * This result is a two dimensional array. The keys of the first |
7356 | * dimension are the ids used in table tblWorkflowDocumentContent. |
7357 | * If only the logs of last workflow run are of interesst, then just |
7358 | * take the last element of the returned array. |
7359 | * |
7360 | * Example: A workflow was started for a document content. |
7361 | * This will add an entry in tblWorkflowDocumentContent whose state is set |
7362 | * to the initial state of the workflow and a new autoinc id, e.g. with id 45 |
7363 | * Once any step in the workflow was triggered, the table tblWorkflowLog will |
7364 | * have an entry for workflowdocumentcontent=45. |
7365 | * Retrieving the workflow log as long the document is still in the workflow |
7366 | * will return the log entries for the current workflow. In this particular |
7367 | * case it will be an array with one log entry. |
7368 | * Once the workflow has ended this method will still return the log entries |
7369 | * but in a 2-dimensional array with the first dimension set to 45. |
7370 | * |
7371 | * The same document version can be run through the same or a different |
7372 | * workflow again which will lead to a new entry in |
7373 | * tblWorkflowDocumentContent, e.g. with id 46. Getting the log entries |
7374 | * while the content is still in the workflow will return only those entries |
7375 | * for the current workflow. Once the workflow has ended, this methods |
7376 | * returns a 2-dimensional array with two elements in the first dimension. |
7377 | * One for key 45 and another one for key 46. |
7378 | * |
7379 | * @return array list of objects |
7380 | */ |
7381 | function getWorkflowLog($transition = null) { /* {{{ */ |
7382 | $db = $this->_document->getDMS()->getDB(); |
7383 | |
7384 | if(!$this->_workflow) |
7385 | $this->getWorkflow(); |
7386 | |
7387 | $queryStr= |
7388 | "SELECT `a`.`id`, `a`.`userid`, `a`.`transition`, `a`.`date`, `a`.`comment`, `a`.`workflowdocumentcontent`, `b`.`version`, `b`.`document`, `b`.`workflow` FROM `tblWorkflowLog` `a` LEFT JOIN `tblWorkflowDocumentContent` `b` ON `a`.`workflowdocumentcontent` = `b`.`id` WHERE `b`.`version`='".$this->_version ."' AND `b`.`document` = '". $this->_document->getID() ."'"; // AND `workflow` = ". $this->_workflow->getID(); |
7389 | if($transition) { |
7390 | $queryStr .= " AND `a`.`transition` = ".$transition->getID(); |
7391 | } |
7392 | if($this->_workflow) |
7393 | $queryStr .= " AND `a`.`workflowdocumentcontent` = ".$this->_workflow['id']; |
7394 | $queryStr .= " ORDER BY `a`.`date`"; |
7395 | $resArr = $db->getResultArray($queryStr); |
7396 | if (is_bool($resArr) && !$resArr) |
7397 | return false; |
7398 | |
7399 | $workflowlogs = array(); |
7400 | for ($i = 0; $i < count($resArr); $i++) { |
7401 | $workflow = $this->_document->getDMS()->getWorkflow($resArr[$i]["workflow"]); |
7402 | $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"]); |
7403 | $workflowlog->setDMS($this); |
7404 | if($this->_workflow) |
7405 | $workflowlogs[] = $workflowlog; |
7406 | else |
7407 | $workflowlogs[$resArr[$i]["workflowdocumentcontent"]][] = $workflowlog; |
7408 | } |
7409 | |
7410 | return $workflowlogs; |
7411 | } /* }}} */ |
7412 | |
7413 | /** |
7414 | * Get the latest workflow log entry for the document content within the |
7415 | * workflow. Even after finishing the workflow (when the document content |
7416 | * does not have a workflow set anymore) this function returns the last |
7417 | * log entry. |
7418 | * |
7419 | * @return object |
7420 | */ |
7421 | function getLastWorkflowLog() { /* {{{ */ |
7422 | $db = $this->_document->getDMS()->getDB(); |
7423 | |
7424 | /* |
7425 | if(!$this->_workflow) |
7426 | $this->getWorkflow(); |
7427 | |
7428 | if(!$this->_workflow) |
7429 | return false; |
7430 | */ |
7431 | $queryStr= |
7432 | "SELECT `a`.*, `b`.`workflow`, `b`.`document`, `b`.`version` FROM `tblWorkflowLog` `a` LEFT JOIN `tblWorkflowDocumentContent` `b` ON `a`.`workflowdocumentcontent` = `b`.`id` WHERE `b`.`version`='".$this->_version ."' AND `b`.`document` = '". $this->_document->getID() ."'"; |
7433 | $queryStr .= " ORDER BY `id` DESC LIMIT 1"; |
7434 | $resArr = $db->getResultArray($queryStr); |
7435 | if (is_bool($resArr) && !$resArr) |
7436 | return false; |
7437 | |
7438 | $i = 0; |
7439 | $workflow = $this->_document->getDMS()->getWorkflow($resArr[$i]["workflow"]); |
7440 | $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"]); |
7441 | $workflowlog->setDMS($this); |
7442 | |
7443 | return $workflowlog; |
7444 | } /* }}} */ |
7445 | |
7446 | /** |
7447 | * Check if the document content needs an action by a user |
7448 | * |
7449 | * This method will return true if document content is in a transition |
7450 | * which can be triggered by the given user. |
7451 | * |
7452 | * @param SeedDMS_Core_User $user |
7453 | * @return boolean true is action is needed |
7454 | */ |
7455 | function needsWorkflowAction($user) { /* {{{ */ |
7456 | $needwkflaction = false; |
7457 | if($this->_workflow) { |
7458 | $workflow = $this->_workflow['workflow']; |
7459 | if (!$this->_workflowState) |
7460 | $this->getWorkflowState(); |
7461 | $workflowstate = $this->_workflowState; |
7462 | if($transitions = $workflow->getNextTransitions($workflowstate)) { |
7463 | foreach($transitions as $transition) { |
7464 | if($this->triggerWorkflowTransitionIsAllowed($user, $transition)) { |
7465 | $needwkflaction = true; |
7466 | } |
7467 | } |
7468 | } |
7469 | } |
7470 | return $needwkflaction; |
7471 | } /* }}} */ |
7472 | |
7473 | /** |
7474 | * Checks the internal data of the document version and repairs it. |
7475 | * Currently, this function only repairs a missing filetype |
7476 | * |
7477 | * @return boolean true on success, otherwise false |
7478 | */ |
7479 | function repair() { /* {{{ */ |
7480 | $dms = $this->_document->getDMS(); |
7481 | $db = $this->_dms->getDB(); |
7482 | |
7483 | if(SeedDMS_Core_File::file_exists($this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType)) { |
7484 | if(strlen($this->_fileType) < 2) { |
7485 | switch($this->_mimeType) { |
7486 | case "application/pdf": |
7487 | case "image/png": |
7488 | case "image/gif": |
7489 | case "image/jpg": |
7490 | $expect = substr($this->_mimeType, -3, 3); |
7491 | if($this->_fileType != '.'.$expect) { |
7492 | $db->startTransaction(); |
7493 | $queryStr = "UPDATE `tblDocumentContent` SET `fileType`='.".$expect."' WHERE `id` = ". $this->_id; |
7494 | $res = $db->getResult($queryStr); |
7495 | if ($res) { |
7496 | if(!SeedDMS_Core_File::renameFile($this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType, $this->_dms->contentDir.$this->_document->getDir() . $this->_version . '.' . $expect)) { |
7497 | $db->rollbackTransaction(); |
7498 | } else { |
7499 | $db->commitTransaction(); |
7500 | } |
7501 | } else { |
7502 | $db->rollbackTransaction(); |
7503 | } |
7504 | } |
7505 | break; |
7506 | } |
7507 | } |
7508 | } elseif(SeedDMS_Core_File::file_exists($this->_document->getDir() . $this->_version . '.')) { |
7509 | echo "no file"; |
7510 | } else { |
7511 | echo $this->_dms->contentDir.$this->_document->getDir() . $this->_version . $this->_fileType; |
7512 | } |
7513 | return true; |
7514 | } /* }}} */ |
7515 | |
7516 | } /* }}} */ |
7517 | |
7518 | |
7519 | /** |
7520 | * Class to represent a link between two document |
7521 | * |
7522 | * Document links are to establish a reference from one document to |
7523 | * another document. The owner of the document link may not be the same |
7524 | * as the owner of one of the documents. |
7525 | * Use {@link SeedDMS_Core_Document::addDocumentLink()} to add a reference |
7526 | * to another document. |
7527 | * |
7528 | * @category DMS |
7529 | * @package SeedDMS_Core |
7530 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli, |
7531 | * Uwe Steinmann <uwe@steinmann.cx> |
7532 | * @copyright Copyright (C) 2002-2005 Markus Westphal, |
7533 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli, |
7534 | * 2010-2022 Uwe Steinmann |
7535 | * @version Release: @package_version@ |
7536 | */ |
7537 | class SeedDMS_Core_DocumentLink { /* {{{ */ |
7538 | /** |
7539 | * @var integer internal id of document link |
7540 | */ |
7541 | protected $_id; |
7542 | |
7543 | /** |
7544 | * @var SeedDMS_Core_Document reference to document this link belongs to |
7545 | */ |
7546 | protected $_document; |
7547 | |
7548 | /** |
7549 | * @var object reference to target document this link points to |
7550 | */ |
7551 | protected $_target; |
7552 | |
7553 | /** |
7554 | * @var integer id of user who is the owner of this link |
7555 | */ |
7556 | protected $_userID; |
7557 | |
7558 | /** |
7559 | * @var object $_user user who is the owner of this link |
7560 | */ |
7561 | protected $_user; |
7562 | |
7563 | /** |
7564 | * @var integer 1 if this link is public, or 0 if is only visible to the owner |
7565 | */ |
7566 | protected $_public; |
7567 | |
7568 | /** |
7569 | * SeedDMS_Core_DocumentLink constructor. |
7570 | * @param $id |
7571 | * @param $document |
7572 | * @param $target |
7573 | * @param $userID |
7574 | * @param $public |
7575 | */ |
7576 | function __construct($id, $document, $target, $userID, $public) { |
7577 | $this->_id = $id; |
7578 | $this->_document = $document; |
7579 | $this->_target = $target; |
7580 | $this->_userID = $userID; |
7581 | $this->_user = null; |
7582 | $this->_public = $public ? true : false; |
7583 | } |
7584 | |
7585 | /** |
7586 | * Check if this object is of type 'documentlink'. |
7587 | * |
7588 | * @param string $type type of object |
7589 | */ |
7590 | public function isType($type) { /* {{{ */ |
7591 | return $type == 'documentlink'; |
7592 | } /* }}} */ |
7593 | |
7594 | /** |
7595 | * @return int |
7596 | */ |
7597 | function getID() { return $this->_id; } |
7598 | |
7599 | /** |
7600 | * @return SeedDMS_Core_Document |
7601 | */ |
7602 | function getDocument() { |
7603 | return $this->_document; |
7604 | } |
7605 | |
7606 | /** |
7607 | * @return object |
7608 | */ |
7609 | function getTarget() { |
7610 | return $this->_target; |
7611 | } |
7612 | |
7613 | /** |
7614 | * @return bool|SeedDMS_Core_User |
7615 | */ |
7616 | function getUser() { |
7617 | if (!isset($this->_user)) { |
7618 | $this->_user = $this->_document->getDMS()->getUser($this->_userID); |
7619 | } |
7620 | return $this->_user; |
7621 | } |
7622 | |
7623 | /** |
7624 | * @return int |
7625 | */ |
7626 | function isPublic() { return $this->_public; } |
7627 | |
7628 | /** |
7629 | * Returns the access mode similar to a document |
7630 | * |
7631 | * There is no real access mode for document links, so this is just |
7632 | * another way to add more access restrictions than the default restrictions. |
7633 | * It is only called for public document links, not accessed by the owner |
7634 | * or the administrator. |
7635 | * |
7636 | * @param SeedDMS_Core_User $u user |
7637 | * @param $source |
7638 | * @param $target |
7639 | * @return int either M_NONE or M_READ |
7640 | */ |
7641 | function getAccessMode($u, $source, $target) { /* {{{ */ |
7642 | $dms = $this->_document->getDMS(); |
7643 | |
7644 | /* Check if 'onCheckAccessDocumentLink' callback is set */ |
7645 | if(isset($dms->callbacks['onCheckAccessDocumentLink'])) { |
7646 | foreach($dms->callbacks['onCheckAccessDocumentLink'] as $callback) { |
7647 | if(($ret = call_user_func($callback[0], $callback[1], $this, $u, $source, $target)) > 0) { |
7648 | return $ret; |
7649 | } |
7650 | } |
7651 | } |
7652 | |
7653 | return M_READ; |
7654 | } /* }}} */ |
7655 | |
7656 | } /* }}} */ |
7657 | |
7658 | /** |
7659 | * Class to represent a file attached to a document |
7660 | * |
7661 | * Beside the regular document content arbitrary files can be attached |
7662 | * to a document. This is a similar concept as attaching files to emails. |
7663 | * The owner of the attached file and the document may not be the same. |
7664 | * Use {@link SeedDMS_Core_Document::addDocumentFile()} to attach a file. |
7665 | * |
7666 | * @category DMS |
7667 | * @package SeedDMS_Core |
7668 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli, |
7669 | * Uwe Steinmann <uwe@steinmann.cx> |
7670 | * @copyright Copyright (C) 2002-2005 Markus Westphal, |
7671 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli, |
7672 | * 2010-2022 Uwe Steinmann |
7673 | * @version Release: @package_version@ |
7674 | */ |
7675 | class SeedDMS_Core_DocumentFile { /* {{{ */ |
7676 | /** |
7677 | * @var integer internal id of document file |
7678 | */ |
7679 | protected $_id; |
7680 | |
7681 | /** |
7682 | * @var SeedDMS_Core_Document reference to document this file belongs to |
7683 | */ |
7684 | protected $_document; |
7685 | |
7686 | /** |
7687 | * @var integer id of user who is the owner of this link |
7688 | */ |
7689 | protected $_userID; |
7690 | |
7691 | /** |
7692 | * @var string comment for the attached file |
7693 | */ |
7694 | protected $_comment; |
7695 | |
7696 | /** |
7697 | * @var string date when the file was attached |
7698 | */ |
7699 | protected $_date; |
7700 | |
7701 | /** |
7702 | * @var integer version of document this file is attached to |
7703 | */ |
7704 | protected $_version; |
7705 | |
7706 | /** |
7707 | * @var integer 1 if this link is public, or 0 if is only visible to the owner |
7708 | */ |
7709 | protected $_public; |
7710 | |
7711 | /** |
7712 | * @var string directory where the file is stored. This is the |
7713 | * document id with a proceding '/'. |
7714 | * FIXME: looks like this isn't used anymore. The file path is |
7715 | * constructed by getPath() |
7716 | */ |
7717 | protected $_dir; |
7718 | |
7719 | /** |
7720 | * @var string extension of the original file name with a leading '.' |
7721 | */ |
7722 | protected $_fileType; |
7723 | |
7724 | /** |
7725 | * @var string mime type of the file |
7726 | */ |
7727 | protected $_mimeType; |
7728 | |
7729 | /** |
7730 | * @var string name of the file that was originally uploaded |
7731 | */ |
7732 | protected $_orgFileName; |
7733 | |
7734 | /** |
7735 | * @var string name of the file as given by the user |
7736 | */ |
7737 | protected $_name; |
7738 | |
7739 | /** |
7740 | * SeedDMS_Core_DocumentFile constructor. |
7741 | * @param $id |
7742 | * @param $document |
7743 | * @param $userID |
7744 | * @param $comment |
7745 | * @param $date |
7746 | * @param $dir |
7747 | * @param $fileType |
7748 | * @param $mimeType |
7749 | * @param $orgFileName |
7750 | * @param $name |
7751 | * @param $version |
7752 | * @param $public |
7753 | */ |
7754 | function __construct($id, $document, $userID, $comment, $date, $dir, $fileType, $mimeType, $orgFileName,$name,$version,$public) { |
7755 | $this->_id = $id; |
7756 | $this->_document = $document; |
7757 | $this->_userID = $userID; |
7758 | $this->_comment = $comment; |
7759 | $this->_date = $date; |
7760 | $this->_dir = $dir; |
7761 | $this->_fileType = $fileType; |
7762 | $this->_mimeType = $mimeType; |
7763 | $this->_orgFileName = $orgFileName; |
7764 | $this->_name = $name; |
7765 | $this->_version = $version; |
7766 | $this->_public = $public ? true : false; |
7767 | } |
7768 | |
7769 | /** |
7770 | * Check if this object is of type 'documentfile'. |
7771 | * |
7772 | * @param string $type type of object |
7773 | */ |
7774 | public function isType($type) { /* {{{ */ |
7775 | return $type == 'documentfile'; |
7776 | } /* }}} */ |
7777 | |
7778 | /** |
7779 | * @return int |
7780 | */ |
7781 | function getID() { return $this->_id; } |
7782 | |
7783 | /** |
7784 | * @return SeedDMS_Core_Document |
7785 | */ |
7786 | function getDocument() { return $this->_document; } |
7787 | |
7788 | /** |
7789 | * @return int |
7790 | */ |
7791 | function getUserID() { return $this->_userID; } |
7792 | |
7793 | /** |
7794 | * @return string |
7795 | */ |
7796 | function getComment() { return $this->_comment; } |
7797 | |
7798 | /* |
7799 | * Set the comment of the document file |
7800 | * |
7801 | * @param string $newComment string new comment of document |
7802 | */ |
7803 | function setComment($newComment) { /* {{{ */ |
7804 | $db = $this->_document->getDMS()->getDB(); |
7805 | |
7806 | $queryStr = "UPDATE `tblDocumentFiles` SET `comment` = ".$db->qstr($newComment)." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id; |
7807 | if (!$db->getResult($queryStr)) |
7808 | return false; |
7809 | |
7810 | $this->_comment = $newComment; |
7811 | return true; |
7812 | } /* }}} */ |
7813 | |
7814 | /** |
7815 | * @return string |
7816 | */ |
7817 | function getDate() { return $this->_date; } |
7818 | |
7819 | /** |
7820 | * Set creation date of the document file |
7821 | * |
7822 | * @param integer $date timestamp of creation date. If false then set it |
7823 | * to the current timestamp |
7824 | * @return boolean true on success |
7825 | */ |
7826 | function setDate($date=null) { /* {{{ */ |
7827 | $db = $this->_document->getDMS()->getDB(); |
7828 | |
7829 | if(!$date) |
7830 | $date = time(); |
7831 | else { |
7832 | if(!is_numeric($date)) |
7833 | return false; |
7834 | } |
7835 | |
7836 | $queryStr = "UPDATE `tblDocumentFiles` SET `date` = " . (int) $date . " WHERE `id` = ". $this->_id; |
7837 | if (!$db->getResult($queryStr)) |
7838 | return false; |
7839 | $this->_date = $date; |
7840 | return true; |
7841 | } /* }}} */ |
7842 | |
7843 | /** |
7844 | * @return string |
7845 | */ |
7846 | function getDir() { return $this->_dir; } |
7847 | |
7848 | /** |
7849 | * @return string |
7850 | */ |
7851 | function getFileType() { return $this->_fileType; } |
7852 | |
7853 | /** |
7854 | * @return string |
7855 | */ |
7856 | function getMimeType() { return $this->_mimeType; } |
7857 | |
7858 | /** |
7859 | * @return string |
7860 | */ |
7861 | function getOriginalFileName() { return $this->_orgFileName; } |
7862 | |
7863 | /** |
7864 | * @return string |
7865 | */ |
7866 | function getName() { return $this->_name; } |
7867 | |
7868 | /* |
7869 | * Set the name of the document file |
7870 | * |
7871 | * @param $newComment string new name of document |
7872 | */ |
7873 | function setName($newName) { /* {{{ */ |
7874 | $db = $this->_document->getDMS()->getDB(); |
7875 | |
7876 | $queryStr = "UPDATE `tblDocumentFiles` SET `name` = ".$db->qstr($newName)." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id; |
7877 | if (!$db->getResult($queryStr)) |
7878 | return false; |
7879 | |
7880 | $this->_name = $newName; |
7881 | |
7882 | return true; |
7883 | } /* }}} */ |
7884 | |
7885 | /** |
7886 | * @return bool|SeedDMS_Core_User |
7887 | */ |
7888 | function getUser() { |
7889 | if (!isset($this->_user)) |
7890 | $this->_user = $this->_document->getDMS()->getUser($this->_userID); |
7891 | return $this->_user; |
7892 | } |
7893 | |
7894 | /** |
7895 | * @return string |
7896 | */ |
7897 | function getPath() { |
7898 | return $this->_document->getDir() . "f" .$this->_id . $this->_fileType; |
7899 | } |
7900 | |
7901 | /** |
7902 | * @return int |
7903 | */ |
7904 | function getVersion() { return $this->_version; } |
7905 | |
7906 | /* |
7907 | * Set the version of the document file |
7908 | * |
7909 | * @param $newComment string new version of document |
7910 | */ |
7911 | function setVersion($newVersion) { /* {{{ */ |
7912 | $db = $this->_document->getDMS()->getDB(); |
7913 | |
7914 | if(!is_numeric($newVersion) && $newVersion != '') |
7915 | return false; |
7916 | |
7917 | $queryStr = "UPDATE `tblDocumentFiles` SET `version` = ".(int) $newVersion." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id; |
7918 | if (!$db->getResult($queryStr)) |
7919 | return false; |
7920 | |
7921 | $this->_version = (int) $newVersion; |
7922 | return true; |
7923 | } /* }}} */ |
7924 | |
7925 | /** |
7926 | * @return int |
7927 | */ |
7928 | function isPublic() { return $this->_public; } |
7929 | |
7930 | /* |
7931 | * Set the public flag of the document file |
7932 | * |
7933 | * @param $newComment string new comment of document |
7934 | */ |
7935 | function setPublic($newPublic) { /* {{{ */ |
7936 | $db = $this->_document->getDMS()->getDB(); |
7937 | |
7938 | $queryStr = "UPDATE `tblDocumentFiles` SET `public` = ".($newPublic ? 1 : 0)." WHERE `document` = ".$this->_document->getId()." AND `id` = ". $this->_id; |
7939 | if (!$db->getResult($queryStr)) |
7940 | return false; |
7941 | |
7942 | $this->_public = $newPublic ? true : false; |
7943 | return true; |
7944 | } /* }}} */ |
7945 | |
7946 | /** |
7947 | * Returns the access mode similar to a document |
7948 | * |
7949 | * There is no real access mode for document files, so this is just |
7950 | * another way to add more access restrictions than the default restrictions. |
7951 | * It is only called for public document files, not accessed by the owner |
7952 | * or the administrator. |
7953 | * |
7954 | * @param object $u user |
7955 | * @return integer either M_NONE or M_READ |
7956 | */ |
7957 | function getAccessMode($u) { /* {{{ */ |
7958 | $dms = $this->_document->getDMS(); |
7959 | |
7960 | /* Check if 'onCheckAccessDocumentLink' callback is set */ |
7961 | if(isset($this->_dms->callbacks['onCheckAccessDocumentFile'])) { |
7962 | foreach($this->_dms->callbacks['onCheckAccessDocumentFile'] as $callback) { |
7963 | if(($ret = call_user_func($callback[0], $callback[1], $this, $u)) > 0) { |
7964 | return $ret; |
7965 | } |
7966 | } |
7967 | } |
7968 | |
7969 | return M_READ; |
7970 | } /* }}} */ |
7971 | |
7972 | } /* }}} */ |
7973 | |
7974 | // |
7975 | // Perhaps not the cleanest object ever devised, it exists to encapsulate all |
7976 | // of the data generated during the addition of new content to the database. |
7977 | // The object stores a copy of the new DocumentContent object, the newly assigned |
7978 | // reviewers and approvers and the status. |
7979 | // |
7980 | /** |
7981 | * Class to represent a list of document contents |
7982 | * |
7983 | * @category DMS |
7984 | * @package SeedDMS_Core |
7985 | * @author Markus Westphal, Malcolm Cowe, Matteo Lucarelli, |
7986 | * Uwe Steinmann <uwe@steinmann.cx> |
7987 | * @copyright Copyright (C) 2002-2005 Markus Westphal, |
7988 | * 2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli, |
7989 | * 2010-2022 Uwe Steinmann |
7990 | * @version Release: @package_version@ |
7991 | */ |
7992 | class SeedDMS_Core_AddContentResultSet { /* {{{ */ |
7993 | |
7994 | /** |
7995 | * @var null |
7996 | */ |
7997 | protected $_indReviewers; |
7998 | |
7999 | /** |
8000 | * @var null |
8001 | */ |
8002 | protected $_grpReviewers; |
8003 | |
8004 | /** |
8005 | * @var null |
8006 | */ |
8007 | protected $_indApprovers; |
8008 | |
8009 | /** |
8010 | * @var null |
8011 | */ |
8012 | protected $_grpApprovers; |
8013 | |
8014 | /** |
8015 | * @var |
8016 | */ |
8017 | protected $_content; |
8018 | |
8019 | /** |
8020 | * @var null |
8021 | */ |
8022 | protected $_status; |
8023 | |
8024 | /** |
8025 | * @var SeedDMS_Core_DMS back reference to document management system |
8026 | */ |
8027 | protected $_dms; |
8028 | |
8029 | /** |
8030 | * SeedDMS_Core_AddContentResultSet constructor. |
8031 | * @param $content |
8032 | */ |
8033 | function __construct($content) { /* {{{ */ |
8034 | $this->_content = $content; |
8035 | $this->_indReviewers = null; |
8036 | $this->_grpReviewers = null; |
8037 | $this->_indApprovers = null; |
8038 | $this->_grpApprovers = null; |
8039 | $this->_status = null; |
8040 | $this->_dms = null; |
8041 | } /* }}} */ |
8042 | |
8043 | /** |
8044 | * Set dms this object belongs to. |
8045 | * |
8046 | * Each object needs a reference to the dms it belongs to. It will be |
8047 | * set when the object is created. |
8048 | * The dms has a references to the currently logged in user |
8049 | * and the database connection. |
8050 | * |
8051 | * @param SeedDMS_Core_DMS $dms reference to dms |
8052 | */ |
8053 | function setDMS($dms) { /* {{{ */ |
8054 | $this->_dms = $dms; |
8055 | } /* }}} */ |
8056 | |
8057 | /** |
8058 | * @param $reviewer |
8059 | * @param $type |
8060 | * @param $status |
8061 | * @return bool |
8062 | */ |
8063 | function addReviewer($reviewer, $type, $status) { /* {{{ */ |
8064 | $dms = $this->_dms; |
8065 | |
8066 | if (!is_object($reviewer) || (strcasecmp($type, "i") && strcasecmp($type, "g")) && !is_integer($status)){ |
8067 | return false; |
8068 | } |
8069 | if (!strcasecmp($type, "i")) { |
8070 | if (strcasecmp(get_class($reviewer), $dms->getClassname("user"))) { |
8071 | return false; |
8072 | } |
8073 | if ($this->_indReviewers == null) { |
8074 | $this->_indReviewers = array(); |
8075 | } |
8076 | $this->_indReviewers[$status][] = $reviewer; |
8077 | } |
8078 | if (!strcasecmp($type, "g")) { |
8079 | if (strcasecmp(get_class($reviewer), $dms->getClassname("group"))) { |
8080 | return false; |
8081 | } |
8082 | if ($this->_grpReviewers == null) { |
8083 | $this->_grpReviewers = array(); |
8084 | } |
8085 | $this->_grpReviewers[$status][] = $reviewer; |
8086 | } |
8087 | return true; |
8088 | } /* }}} */ |
8089 | |
8090 | /** |
8091 | * @param $approver |
8092 | * @param $type |
8093 | * @param $status |
8094 | * @return bool |
8095 | */ |
8096 | function addApprover($approver, $type, $status) { /* {{{ */ |
8097 | $dms = $this->_dms; |
8098 | |
8099 | if (!is_object($approver) || (strcasecmp($type, "i") && strcasecmp($type, "g")) && !is_integer($status)){ |
8100 | return false; |
8101 | } |
8102 | if (!strcasecmp($type, "i")) { |
8103 | if (strcasecmp(get_class($approver), $dms->getClassname("user"))) { |
8104 | return false; |
8105 | } |
8106 | if ($this->_indApprovers == null) { |
8107 | $this->_indApprovers = array(); |
8108 | } |
8109 | $this->_indApprovers[$status][] = $approver; |
8110 | } |
8111 | if (!strcasecmp($type, "g")) { |
8112 | if (strcasecmp(get_class($approver), $dms->getClassname("group"))) { |
8113 | return false; |
8114 | } |
8115 | if ($this->_grpApprovers == null) { |
8116 | $this->_grpApprovers = array(); |
8117 | } |
8118 | $this->_grpApprovers[$status][] = $approver; |
8119 | } |
8120 | return true; |
8121 | } /* }}} */ |
8122 | |
8123 | /** |
8124 | * @param $status |
8125 | * @return bool |
8126 | */ |
8127 | function setStatus($status) { /* {{{ */ |
8128 | if (!is_integer($status)) { |
8129 | return false; |
8130 | } |
8131 | if ($status<-3 || $status>3) { |
8132 | return false; |
8133 | } |
8134 | $this->_status = $status; |
8135 | return true; |
8136 | } /* }}} */ |
8137 | |
8138 | /** |
8139 | * @return null |
8140 | */ |
8141 | function getStatus() { /* {{{ */ |
8142 | return $this->_status; |
8143 | } /* }}} */ |
8144 | |
8145 | /** |
8146 | * @return mixed |
8147 | */ |
8148 | function getContent() { /* {{{ */ |
8149 | return $this->_content; |
8150 | } /* }}} */ |
8151 | |
8152 | /** |
8153 | * @param $type |
8154 | * @return array|bool|null |
8155 | */ |
8156 | function getReviewers($type) { /* {{{ */ |
8157 | if (strcasecmp($type, "i") && strcasecmp($type, "g")) { |
8158 | return false; |
8159 | } |
8160 | if (!strcasecmp($type, "i")) { |
8161 | return ($this->_indReviewers == null ? array() : $this->_indReviewers); |
8162 | } |
8163 | else { |
8164 | return ($this->_grpReviewers == null ? array() : $this->_grpReviewers); |
8165 | } |
8166 | } /* }}} */ |
8167 | |
8168 | /** |
8169 | * @param $type |
8170 | * @return array|bool|null |
8171 | */ |
8172 | function getApprovers($type) { /* {{{ */ |
8173 | if (strcasecmp($type, "i") && strcasecmp($type, "g")) { |
8174 | return false; |
8175 | } |
8176 | if (!strcasecmp($type, "i")) { |
8177 | return ($this->_indApprovers == null ? array() : $this->_indApprovers); |
8178 | } |
8179 | else { |
8180 | return ($this->_grpApprovers == null ? array() : $this->_grpApprovers); |
8181 | } |
8182 | } /* }}} */ |
8183 | } /* }}} */ |