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