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