| Code Coverage
| Lines | Functions and Methods | Classes and Traits
Total | 60.81% covered (warning) | 60.81% | 1046 / 1720 | 58.95% covered (warning) | 58.95% | 56 / 95 | CRAP | 0.00% covered (danger) | 0.00% | 0 / 1
SeedDMS_Core_DMS | 60.52% covered (warning) | 60.52% | 1033 / 1707 | 58.95% covered (warning) | 58.95% | 56 / 95 | 38913.65 | 0.00% covered (danger) | 0.00% | 0 / 1
checkIfEqual | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
inList | 0.00% covered (danger) | 0.00% | 0 / 4 | 0.00% covered (danger) | 0.00% | 0 / 1 | 20 |
checkDate | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
filterAccess | 0.00% covered (danger) | 0.00% | 0 / 7 | 0.00% covered (danger) | 0.00% | 0 / 1 | 20 |
filterUsersByAccess | 0.00% covered (danger) | 0.00% | 0 / 5 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12 |
filterDocumentLinks | 0.00% covered (danger) | 0.00% | 0 / 13 | 0.00% covered (danger) | 0.00% | 0 / 1 | 90 |
filterDocumentFiles | 0.00% covered (danger) | 0.00% | 0 / 6 | 0.00% covered (danger) | 0.00% | 0 / 1 | 56 |
__construct | 93.75% covered (success) | 93.75% | 30 / 32 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.00 |
getClassname | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
setClassname | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
getDecorators | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
addDecorator | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDB | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getStorage | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDBVersion | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
checkVersion | 100.00% covered (success) | 100.00% | 15 / 15 | 100.00% covered (success) | 100.00% | 1 / 1 | 7 |
setMemcache | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
setRootFolderID | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
setMaxDirID | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
getRootFolder | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
setForceRename | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
setForceLink | 0.00% covered (danger) | 0.00% | 0 / 1 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
setUser | 100.00% covered (success) | 100.00% | 9 / 9 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
getLoggedInUser | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDocument | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDocumentsByUser | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDocumentsLockedByUser | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getDocumentsExpired | 64.71% covered (warning) | 64.71% | 44 / 68 | 0.00% covered (danger) | 0.00% | 0 / 1 | 59.05 |
getDocumentByName | 100.00% covered (success) | 100.00% | 20 / 20 | 100.00% covered (success) | 100.00% | 1 / 1 | 7 |
getDocumentByOriginalFilename | 95.45% covered (success) | 95.45% | 21 / 22 | 0.00% covered (danger) | 0.00% | 0 / 1 | 7 |
getDocumentContent | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
countTasks | 0.00% covered (danger) | 0.00% | 0 / 67 | 0.00% covered (danger) | 0.00% | 0 / 1 | 420 |
getDocumentList | 27.06% covered (danger) | 27.06% | 69 / 255 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3597.40 |
makeTimeStamp | 100.00% covered (success) | 100.00% | 15 / 15 | 100.00% covered (success) | 100.00% | 1 / 1 | 23 |
getSqlForAttribute | 0.00% covered (danger) | 0.00% | 0 / 66 | 0.00% covered (danger) | 0.00% | 0 / 1 | 1482 |
search | 60.95% covered (warning) | 60.95% | 192 / 315 | 0.00% covered (danger) | 0.00% | 0 / 1 | 1585.37 |
getFolder | 71.43% covered (warning) | 71.43% | 5 / 7 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.37 |
getFolderByName | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
checkFolders | 96.43% covered (success) | 96.43% | 27 / 28 | 0.00% covered (danger) | 0.00% | 0 / 1 | 13 |
checkDocuments | 97.06% covered (success) | 97.06% | 33 / 34 | 0.00% covered (danger) | 0.00% | 0 / 1 | 16 |
getUser | 71.43% covered (warning) | 71.43% | 5 / 7 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.37 |
getUserByLogin | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getUserByEmail | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getAllUsers | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
addUser | 100.00% covered (success) | 100.00% | 19 / 19 | 100.00% covered (success) | 100.00% | 1 / 1 | 11 |
getGroup | 71.43% covered (warning) | 71.43% | 5 / 7 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.37 |
getGroupByName | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getAllGroups | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
addGroup | 100.00% covered (success) | 100.00% | 11 / 11 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getKeywordCategory | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getKeywordCategoryByName | 100.00% covered (success) | 100.00% | 13 / 13 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getAllKeywordCategories | 100.00% covered (success) | 100.00% | 13 / 13 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
getAllUserKeywordCategories | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
addKeywordCategory | 100.00% covered (success) | 100.00% | 15 / 15 | 100.00% covered (success) | 100.00% | 1 / 1 | 9 |
getDocumentCategory | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getDocumentCategories | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
getDocumentCategoryByName | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |
addDocumentCategory | 100.00% covered (success) | 100.00% | 13 / 13 | 100.00% covered (success) | 100.00% | 1 / 1 | 7 |
getNotificationsByGroup | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
getNotificationsByUser | 100.00% covered (success) | 100.00% | 1 / 1 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
createPasswordRequest | 72.73% covered (warning) | 72.73% | 8 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 5.51 |
checkPasswordRequest | 100.00% covered (success) | 100.00% | 8 / 8 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
deletePasswordRequest | 100.00% covered (success) | 100.00% | 4 / 4 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |
getAttributeDefinition | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getAttributeDefinitionByName | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
getAllAttributeDefinitions | 82.61% covered (warning) | 82.61% | 19 / 23 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12.76 |
addAttributeDefinition | 94.44% covered (success) | 94.44% | 17 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 8.01 |
getAllWorkflows | 93.75% covered (success) | 93.75% | 15 / 16 | 0.00% covered (danger) | 0.00% | 0 / 1 | 7.01 |
getWorkflow | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getWorkflowByName | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
addWorkflow | 90.91% covered (success) | 90.91% | 10 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.01 |
getWorkflowState | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getWorkflowStateByName | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
getAllWorkflowStates | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
addWorkflowState | 90.91% covered (success) | 90.91% | 10 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.01 |
getWorkflowAction | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 6 |
getWorkflowActionByName | 100.00% covered (success) | 100.00% | 12 / 12 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
getAllWorkflowActions | 100.00% covered (success) | 100.00% | 10 / 10 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
addWorkflowAction | 90.91% covered (success) | 90.91% | 10 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.01 |
getWorkflowTransition | 0.00% covered (danger) | 0.00% | 0 / 10 | 0.00% covered (danger) | 0.00% | 0 / 1 | 30 |
getUnlinkedDocumentContent | 54.55% covered (warning) | 54.55% | 6 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 3.85 |
getNoFileSizeDocumentContent | 100.00% covered (success) | 100.00% | 11 / 11 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
getNoChecksumDocumentContent | 100.00% covered (success) | 100.00% | 11 / 11 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
getDuplicateDocumentContent | 100.00% covered (success) | 100.00% | 14 / 14 | 100.00% covered (success) | 100.00% | 1 / 1 | 5 |
getDuplicateSequenceNo | 0.00% covered (danger) | 0.00% | 0 / 10 | 0.00% covered (danger) | 0.00% | 0 / 1 | 20 |
getLinksToItself | 0.00% covered (danger) | 0.00% | 0 / 10 | 0.00% covered (danger) | 0.00% | 0 / 1 | 20 |
getProcessWithoutUserGroup | 0.00% covered (danger) | 0.00% | 0 / 14 | 0.00% covered (danger) | 0.00% | 0 / 1 | 30 |
removeProcessWithoutUserGroup | 0.00% covered (danger) | 0.00% | 0 / 20 | 0.00% covered (danger) | 0.00% | 0 / 1 | 56 |
getStatisticalData | 70.89% covered (warning) | 70.89% | 56 / 79 | 0.00% covered (danger) | 0.00% | 0 / 1 | 76.53 |
getTimeline | 76.47% covered (warning) | 76.47% | 13 / 17 | 0.00% covered (danger) | 0.00% | 0 / 1 | 6.47 |
getLatestChanges | 0.00% covered (danger) | 0.00% | 0 / 44 | 0.00% covered (danger) | 0.00% | 0 / 1 | 380 |
getMimeTypes | 0.00% covered (danger) | 0.00% | 0 / 5 | 0.00% covered (danger) | 0.00% | 0 / 1 | 12 |
setCallback | 75.00% covered (warning) | 75.00% | 3 / 4 | 0.00% covered (danger) | 0.00% | 0 / 1 | 4.25 |
addCallback | 100.00% covered (success) | 100.00% | 4 / 4 | 100.00% covered (success) | 100.00% | 1 / 1 | 4 |
hasCallback | 100.00% covered (success) | 100.00% | 3 / 3 | 100.00% covered (success) | 100.00% | 1 / 1 | 3 |

1 | <?php
2 | declare(strict_types=1);
3 |
4 | /**
5 | * Implementation of the document management system
6 | *
7 | * @category DMS
8 | * @package SeedDMS_Core
9 | * @license GPL 2
10 | * @author Uwe Steinmann <uwe@steinmann.cx>
11 | * @copyright Copyright (C) 2010-2024 Uwe Steinmann
12 | */
13 |
14 | /**
15 | * Include some files
16 | */
17 | require_once("inc.AccessUtils.php");
18 | require_once("inc.FileUtils.php");
19 | require_once("inc.ClassAccess.php");
20 | require_once("inc.ClassObject.php");
21 | require_once("inc.ClassFolder.php");
22 | require_once("inc.ClassDocument.php");
23 | require_once("inc.ClassGroup.php");
24 | require_once("inc.ClassUser.php");
25 | require_once("inc.ClassKeywords.php");
26 | require_once("inc.ClassNotification.php");
27 | require_once("inc.ClassAttribute.php");
28 | require_once("inc.ClassStorage.php");
29 | require_once("inc.ClassStorageFile.php");
30 |
31 | /**
32 | * Class to represent the complete document management system.
33 | * This class is needed to do most of the dms operations. It needs
34 | * an instance of {@see SeedDMS_Core_DatabaseAccess} to access the
35 | * underlying database. Many methods are factory functions which create
36 | * objects representing the entities in the dms, like folders, documents,
37 | * users, or groups.
38 | *
39 | * Each dms has its own database for meta data and a data store for document
40 | * content. Both must be specified when creating a new instance of this class.
41 | * All folders and documents are organized in a hierachy like
42 | * a regular file system starting with a {@see SeedDMS_Core_DMS::rootFolderID}
43 | *
44 | * This class does not enforce any access rights on documents and folders
45 | * by design. It is up to the calling application to use the methods
46 | * {@see SeedDMS_Core_Folder::getAccessMode()} and
47 | * {@see SeedDMS_Core_Document::getAccessMode()} and interpret them as desired.
48 | * Though, there are two convenient functions to filter a list of
49 | * documents/folders for which users have access rights for. See
50 | * {@see SeedDMS_Core_DMS::filterAccess()}
51 | * and {@see SeedDMS_Core_DMS::filterUsersByAccess()}
52 | *
53 | * Though, this class has a method to set the currently logged in user
54 | * ({@see SeedDMS_Core_DMS::setUser()}), it does not have to be called, because
55 | * there is currently no class within the SeedDMS core which needs the logged
56 | * in user. {@see SeedDMS_Core_DMS} itself does not do any user authentication.
57 | * It is up to the application using this class.
58 | *
59 | * ```php
60 | * <?php
61 | * include("inc/inc.ClassDMS.php");
62 | * $db = new SeedDMS_Core_DatabaseAccess($type, $hostname, $user, $passwd, $name);
63 | * $db->connect() or die ("Could not connect to db-server");
64 | * $dms = new SeedDMS_Core_DMS($db, $contentDir);
65 | * $dms->setRootFolderID(1);
66 | * ...
67 | * ?>
68 | * ```
69 | *
70 | * @category DMS
71 | * @package SeedDMS_Core
72 | * @author Uwe Steinmann <uwe@steinmann.cx>
73 | * @copyright Copyright (C) 2010-2024 Uwe Steinmann
74 | */
75 | class SeedDMS_Core_DMS {
76 | /**
77 | * @var SeedDMS_Core_DatabaseAccess $db reference to database object. This must be an instance
78 | * of {@see SeedDMS_Core_DatabaseAccess}.
79 | * @access protected
80 | */
81 | protected $db;
82 |
83 | /**
84 | * @var SeedDMS_Core_Storage $storage reference to storage object.
85 | * This must be an instance {@see SeedDMS_Core_Storage_File}.
86 | * @access protected
87 | */
88 | protected $storage;
89 |
90 | /**
91 | * @var object $memcache reference to memcache.
92 | * @access protected
93 | */
94 | public $memcache;
95 |
96 | /**
97 | * @var array $classnames list of classnames for objects being instanciate
98 | * by the dms
99 | * @access protected
100 | */
101 | protected $classnames;
102 |
103 | /**
104 | * @var array $decorators list of decorators for objects being instanciate
105 | * by the dms
106 | * @access protected
107 | */
108 | protected $decorators;
109 |
110 | /**
111 | * @var SeedDMS_Core_User $user reference to currently logged in user. This must be
112 | * an instance of {@see SeedDMS_Core_User}. This variable is currently not
113 | * used. It is set by {@see SeedDMS_Core_DMS::setUser()}.
114 | * @access private
115 | */
116 | private $user;
117 |
118 | /**
119 | * @var string $contentDir location in the file system where all the
120 | * document data is located. This should be an absolute path.
121 | * @access public
122 | */
123 | public $contentDir;
124 |
125 | /**
126 | * @var integer $rootFolderID ID of root folder
127 | * @access public
128 | */
129 | public $rootFolderID;
130 |
131 | /**
132 | * @var integer $maxDirID maximum number of documents per folder on the
133 | * filesystem. If this variable is set to a value != 0, the content
134 | * directory will have a two level hierarchy for document storage.
135 | * @access public
136 | */
137 | public $maxDirID;
138 |
139 | /**
140 | * @var boolean $forceRename use renameFile() instead of copyFile() when
141 | * copying the document content into the data store. The default is
142 | * to copy the file. This parameter only affects the methods
143 | * SeedDMS_Core_Document::addDocument() and
144 | * SeedDMS_Core_Document::addDocumentFile(). Setting this to true
145 | * may save resources especially for large files.
146 | * @access public
147 | */
148 | public $forceRename;
149 |
150 | /**
151 | * @var boolean $forceLink use linkFile() instead of copyFile() when
152 | * copying the document content into the data store. The default is
153 | * to copy the file. This parameter only affects the method
154 | * SeedDMS_Core_Document::addDocument(). Use this with care,
155 | * because it will leave the original document at its place.
156 | * @access public
157 | */
158 | public $forceLink;
159 |
160 | /**
161 | * @var array $noReadForStatus list of status without read right
162 | * online.
163 | * @access public
164 | */
165 | public $noReadForStatus;
166 |
167 | /**
168 | * @var boolean $checkWithinRootDir check if folder/document being accessed
169 | * is within the rootdir
170 | * @access public
171 | */
172 | public $checkWithinRootDir;
173 |
174 | /**
175 | * @var string $version version of pear package
176 | * @access public
177 | */
178 | public $version;
179 |
180 | /**
181 | * @var boolean $usecache true if internal cache shall be used
182 | * @access public
183 | */
184 | public $usecache;
185 |
186 | /**
187 | * @var array $cache cache for various objects
188 | * @access public
189 | */
190 | protected $cache;
191 |
192 | /**
193 | * @var array $callbacks list of methods called when certain operations,
194 | * like removing a document, are executed. Set a callback with
195 | * {@see SeedDMS_Core_DMS::setCallback()}.
196 | * The key of the array is the internal callback function name. Each
197 | * array element is an array with two elements: the function name
198 | * and the parameter passed to the function.
199 | *
200 | * Currently implemented callbacks are:
201 | *
202 | * onPreRemoveDocument($user_param, $document);
203 | * called before deleting a document. If this function returns false
204 | * the document will not be deleted.
205 | *
206 | * onPostRemoveDocument($user_param, $document_id);
207 | * called after the successful deletion of a document.
208 | *
209 | * @access public
210 | */
211 | public $callbacks;
212 |
213 | /**
214 | * @var string last error message. This can be set by hooks to pass an
215 | * error message from the hook to the application which has called the
216 | * method containing the hook. For example SeedDMS_Core_Document::remove()
217 | * calls the hook 'onPreRemoveDocument'. The hook function can set $dms->lasterror
218 | * which can than be read when SeedDMS_Core_Document::remove() fails.
219 | * This variable could be set in any SeedDMS_Core class, but is currently
220 | * only set by hooks.
221 | * @access public
222 | */
223 | public $lasterror;
224 |
225 | /**
226 | * @var SeedDMS_Core_DMS
227 | */
228 | // public $_dms;
229 |
230 |
231 | /**
232 | * Checks if two objects are equal by comparing their IDs
233 | *
234 | * The regular php check done by '==' compares all attributes of
235 | * two objects, which is often not required. This method will first check
236 | * if the objects are instances of the same class and than if they
237 | * have the same id.
238 | *
239 | * @param object $object1 first object to be compared
240 | * @param object $object2 second object to be compared
241 | * @return boolean true if objects are equal, otherwise false
242 | */
243 | public static function checkIfEqual($object1, $object2) { /* {{{ */
244 | if (get_class($object1) != get_class($object2))
245 | return false;
246 | if ($object1->getID() != $object2->getID())
247 | return false;
248 | return true;
249 | } /* }}} */
250 |
251 | /**
252 | * Checks if a list of objects contains a single object by comparing their IDs
253 | *
254 | * This method is only applicable on list containing objects which have
255 | * a method getID() because it is used to check if two objects are equal.
256 | * The regular php check on objects done by '==' compares all attributes of
257 | * two objects, which often isn't required. The method will first check
258 | * if the objects are instances of the same class.
259 | *
260 | * The result of the function can be 0 which happens if the first element
261 | * of an indexed array matches.
262 | *
263 | * @param object $object object to look for (needle)
264 | * @param array $list list of objects (haystack)
265 | * @return boolean|integer index in array if object was found, otherwise false
266 | */
267 | public static function inList($object, $list) { /* {{{ */
268 | foreach ($list as $i => $item) {
269 | if (get_class($item) == get_class($object) && $item->getID() == $object->getID())
270 | return $i;
271 | }
272 | return false;
273 | } /* }}} */
274 |
275 | /**
276 | * Checks if date conforms to a given format
277 | *
278 | * @param string $date date to be checked
279 | * @param string $format format of date. Will default to 'Y-m-d H:i:s' if
280 | * format is not given.
281 | * @return boolean true if date is in propper format, otherwise false
282 | */
283 | public static function checkDate($date, $format = 'Y-m-d H:i:s') { /* {{{ */
284 | $d = DateTime::createFromFormat($format, $date);
285 | return $d && $d->format($format) == $date;
286 | } /* }}} */
287 |
288 | /**
289 | * Filter out objects which are not accessible in a given mode by a user.
290 | *
291 | * The list of objects to be checked can be of any class, but has to have
292 | * a method getAccessMode($user) which checks if the given user has at
293 | * least the access right on the object as passed in $minMode.
294 | * Hence, passing a group instead of a user is possible.
295 | *
296 | * @param array $objArr list of objects (either documents or folders)
297 | * @param object $user user for which access is checked
298 | * @param integer $minMode minimum access mode required (M_ANY, M_NONE,
299 | * M_READ, M_READWRITE, M_ALL)
300 | * @return array filtered list of objects
301 | */
302 | public static function filterAccess($objArr, $user, $minMode) { /* {{{ */
303 | if (!is_array($objArr)) {
304 | return array();
305 | }
306 | $newArr = array();
307 | foreach ($objArr as $obj) {
308 | if ($obj->getAccessMode($user) >= $minMode)
309 | array_push($newArr, $obj);
310 | }
311 | return $newArr;
312 | } /* }}} */
313 |
314 | /**
315 | * Filter out users which cannot access an object in a given mode.
316 | *
317 | * The list of users to be checked can be of any class, but has to have
318 | * a method getAccessMode($user) which checks if a user has at least the
319 | * access right as passed in $minMode. Hence, passing a list of groups
320 | * instead of users is possible.
321 | *
322 | * @param object $obj object that shall be accessed
323 | * @param array $users list of users/groups which are to check for sufficient
324 | * access rights
325 | * @param integer $minMode minimum access right on the object for each user
326 | * (M_ANY, M_NONE, M_READ, M_READWRITE, M_ALL)
327 | * @return array filtered list of users
328 | */
329 | public static function filterUsersByAccess($obj, $users, $minMode) { /* {{{ */
330 | $newArr = array();
331 | foreach ($users as $currUser) {
332 | if ($obj->getAccessMode($currUser) >= $minMode)
333 | array_push($newArr, $currUser);
334 | }
335 | return $newArr;
336 | } /* }}} */
337 |
338 | /**
339 | * Filter out document links which can not be accessed by a given user
340 | *
341 | * Returns a filtered list of links which are accessible by the
342 | * given user. A link is only accessible, if it is publically visible,
343 | * owned by the user, or the accessing user is an administrator.
344 | *
345 | * @param SeedDMS_Core_DocumentLink[] $links list of objects of type SeedDMS_Core_DocumentLink
346 | * @param object $user user for which access is being checked
347 | * @param string $access set if source or target of link shall be checked
348 | * for sufficient access rights. Set to 'source' if the source document
349 | * of a link is to be checked, set to 'target' for the target document.
350 | * If not set, then access rights will not be checked at all.
351 | * @return array filtered list of links
352 | */
353 | public static function filterDocumentLinks($user, $links, $access = '') { /* {{{ */
354 | $tmp = array();
355 | foreach ($links as $link) {
356 | if ($link->isPublic() || ($link->getUser()->getID() == $user->getID()) || $user->isAdmin()){
357 | if ($access == 'source') {
358 | $obj = $link->getDocument();
359 | if ($obj->getAccessMode($user) >= M_READ)
360 | array_push($tmp, $link);
361 | } elseif ($access == 'target') {
362 | $obj = $link->getTarget();
363 | if ($obj->getAccessMode($user) >= M_READ)
364 | array_push($tmp, $link);
365 | } else {
366 | array_push($tmp, $link);
367 | }
368 | }
369 | }
370 | return $tmp;
371 | } /* }}} */
372 |
373 | /**
374 | * Filter out document attachments which can not be accessed by a given user
375 | *
376 | * Returns a filtered list of files which are accessible by the
377 | * given user. A file is only accessible, if it is publically visible,
378 | * owned by the user, or the accessing user is an administrator.
379 | *
380 | * @param array $files list of objects of type SeedDMS_Core_DocumentFile
381 | * @param object $user user for which access is being checked
382 | * @return array filtered list of files
383 | */
384 | public static function filterDocumentFiles($user, $files) { /* {{{ */
385 | $tmp = array();
386 | if ($files) {
387 | foreach ($files as $file)
388 | if ($file->isPublic() || ($file->getUser()->getID() == $user->getID()) || $user->isAdmin() || ($file->getDocument()->getOwner()->getID() == $user->getID()))
389 | array_push($tmp, $file);
390 | }
391 | return $tmp;
392 | } /* }}} */
393 |
394 | /** @noinspection PhpUndefinedClassInspection */
395 | /**
396 | * Create a new instance of the dms
397 | *
398 | * @param SeedDMS_Core_DatabaseAccess $db object of class {@see SeedDMS_Core_DatabaseAccess}
399 | * to access the underlying database
400 | * @param string $contentDir path in filesystem containing the data store
401 | * all document contents is stored
402 | */
403 | public function __construct($db, $contentDir) { /* {{{ */
404 | $this->db = $db;
405 | if (is_object($contentDir)) {
406 | $this->storage = $contentDir;
407 | } else {
408 | $this->storage = null;
409 | if (substr($contentDir, -1) == DIRECTORY_SEPARATOR)
410 | $this->contentDir = $contentDir;
411 | else
412 | $this->contentDir = $contentDir.DIRECTORY_SEPARATOR;
413 | }
414 | $this->memcache = null;
415 | $this->rootFolderID = 1;
416 | $this->user = null;
417 | $this->maxDirID = 0; //31998;
418 | $this->forceRename = false;
419 | $this->forceLink = false;
420 | $this->checkWithinRootDir = false;
421 | $this->noReadForStatus = array();
422 | $this->user = null;
423 | $this->classnames = array();
424 | $this->classnames['folder'] = 'SeedDMS_Core_Folder';
425 | $this->classnames['document'] = 'SeedDMS_Core_Document';
426 | $this->classnames['documentcontent'] = 'SeedDMS_Core_DocumentContent';
427 | $this->classnames['documentfile'] = 'SeedDMS_Core_DocumentFile';
428 | $this->classnames['user'] = 'SeedDMS_Core_User';
429 | $this->classnames['group'] = 'SeedDMS_Core_Group';
430 | $this->usecache = false;
431 | $this->cache['users'] = [];
432 | $this->cache['groups'] = [];
433 | $this->cache['folders'] = [];
434 | $this->callbacks = array();
435 | $this->lasterror = '';
436 | $this->version = '@package_version@';
437 | if ($this->version[0] == '@')
438 | $this->version = '5.1.36';
439 | } /* }}} */
440 |
441 | /**
442 | * Return class name of classes instanciated by SeedDMS_Core
443 | *
444 | * This method returns the class name of those objects being instantiated
445 | * by the dms. Each class has an internal place holder, which must be
446 | * passed to function.
447 | *
448 | * @param string $objectname placeholder (can be one of 'folder', 'document',
449 | * 'documentcontent', 'user', 'group')
450 | *
451 | * @return string/boolean name of class or false if object name is invalid
452 | */
453 | public function getClassname($objectname) { /* {{{ */
454 | if (isset($this->classnames[$objectname]))
455 | return $this->classnames[$objectname];
456 | else
457 | return false;
458 | } /* }}} */
459 |
460 | /**
461 | * Set class name of instantiated objects
462 | *
463 | * This method sets the class name of those objects being instatiated
464 | * by the dms. It is mainly used to create a new class (possible
465 | * inherited from one of the available classes) implementing new
466 | * features. The method should be called in the postInitDMS hook.
467 | *
468 | * @param string $objectname placeholder (can be one of 'folder', 'document',
469 | * 'documentcontent', 'user', 'group'
470 | * @param string $classname name of class
471 | *
472 | * @return string/boolean name of old class or false if not set
473 | */
474 | public function setClassname($objectname, $classname) { /* {{{ */
475 | if (isset($this->classnames[$objectname]))
476 | $oldclass = $this->classnames[$objectname];
477 | else
478 | $oldclass = false;
479 | $this->classnames[$objectname] = $classname;
480 | return $oldclass;
481 | } /* }}} */
482 |
483 | /**
484 | * Return list of decorators
485 | *
486 | * This method returns the list of decorator class names of those objects
487 | * being instantiated
488 | * by the dms. Each class has an internal place holder, which must be
489 | * passed to function.
490 | *
491 | * @param string $objectname placeholder (can be one of 'folder', 'document',
492 | * 'documentcontent', 'user', 'group')
493 | *
494 | * @return array/boolean list of class names or false if object name is invalid
495 | */
496 | public function getDecorators($objectname) { /* {{{ */
497 | if (isset($this->decorators[$objectname]))
498 | return $this->decorators[$objectname];
499 | else
500 | return false;
501 | } /* }}} */
502 |
503 | /**
504 | * Add a decorator
505 | *
506 | * This method adds a single decorator class name to the list of decorators
507 | * of those objects being instantiated
508 | * by the dms. Each class has an internal place holder, which must be
509 | * passed to function.
510 | *
511 | * @param string $objectname placeholder (can be one of 'folder', 'document',
512 | * 'documentcontent', 'user', 'group')
513 | *
514 | * @return boolean true if decorator could be added, otherwise false
515 | */
516 | public function addDecorator($objectname, $decorator) { /* {{{ */
517 | $this->decorators[$objectname][] = $decorator;
518 | return true;
519 | } /* }}} */
520 |
521 | /**
522 | * Return database where meta data is stored
523 | *
524 | * This method returns the database object as it was set by the first
525 | * parameter of the constructor.
526 | *
527 | * @return SeedDMS_Core_DatabaseAccess database
528 | */
529 | public function getDB() { /* {{{ */
530 | return $this->db;
531 | } /* }}} */
532 |
533 | /**
534 | * Return storage where files are stored
535 | *
536 | * This method returns the storage object as it was set by the second
537 | * parameter of the constructor.
538 | *
539 | * @return SeedDMS_Core_Storage
540 | */
541 | public function getStorage() { /* {{{ */
542 | return $this->storage;
543 | } /* }}} */
544 |
545 | /**
546 | * Return the database version
547 | *
548 | * @return array|bool
549 | */
550 | public function getDBVersion() { /* {{{ */
551 | $tbllist = $this->db->TableList();
552 | $tbllist = explode(',', strtolower(join(',', $tbllist)));
553 | if (!in_array('tblversion', $tbllist))
554 | return false;
555 | $queryStr = "SELECT * FROM `tblVersion` ORDER BY `major`,`minor`,`subminor` LIMIT 1";
556 | $resArr = $this->db->getResultArray($queryStr);
557 | if (is_bool($resArr) && $resArr == false)
558 | return false;
559 | if (count($resArr) != 1)
560 | return false;
561 | $resArr = $resArr[0];
562 | return $resArr;
563 | } /* }}} */
564 |
565 | /**
566 | * Check if the version in the database is the same as of this package
567 | * Only the major and minor version number will be checked.
568 | *
569 | * @return boolean returns false if versions do not match, but returns
570 | * true if version matches or table tblVersion does not exists.
571 | */
572 | public function checkVersion() { /* {{{ */
573 | $tbllist = $this->db->TableList();
574 | $tbllist = explode(',', strtolower(join(',', $tbllist)));
575 | if (!in_array('tblversion', $tbllist))
576 | return true;
577 | $queryStr = "SELECT * FROM `tblVersion` ORDER BY `major`,`minor`,`subminor` LIMIT 1";
578 | $resArr = $this->db->getResultArray($queryStr);
579 | if (is_bool($resArr) && $resArr == false)
580 | return false;
581 | if (count($resArr) != 1)
582 | return false;
583 | $resArr = $resArr[0];
584 | $ver = explode('.', $this->version);
585 | if (($resArr['major'] != $ver[0]) || ($resArr['minor'] != $ver[1]))
586 | return false;
587 | return true;
588 | } /* }}} */
589 |
590 | /**
591 | * Set memcache server
592 | *
593 | * This method must be called right after creating an instance of
594 | * {@see SeedDMS_Core_DMS}
595 | *
596 | * If the memcache server is set, SeedDMS_Core_DMS will make use of
597 | * it if possible.
598 | *
599 | * @param object $memcache memcache object created with new Memcached()
600 | * @return void
601 | */
602 | public function setMemcache($memcache) { /* {{{ */
603 | $this->memcache = $memcache;
604 | } /* }}} */
605 |
606 | /**
607 | * Set id of root folder
608 | *
609 | * This method must be called right after creating an instance of
610 | * {@see SeedDMS_Core_DMS}
611 | *
612 | * The new root folder id will only be set if the folder actually
613 | * exists. In that case the old root folder id will be returned.
614 | * If it does not exists, the method will return false;
615 | * @param integer $id id of root folder
616 | * @return boolean/int old root folder id if new root folder exists, otherwise false
617 | */
618 | public function setRootFolderID($id) { /* {{{ */
619 | if ($this->getFolder($id)) {
620 | $oldid = $this->rootFolderID;
621 | $this->rootFolderID = $id;
622 | return $oldid;
623 | }
624 | return false;
625 | } /* }}} */
626 |
627 | /**
628 | * Set maximum number of subdirectories per directory
629 | *
630 | * The value of maxDirID is quite crucial, because each document is
631 | * stored within a directory in the filesystem. Consequently, there can be
632 | * a maximum number of documents, because depending on the file system
633 | * the maximum number of subdirectories is limited. Since version 3.3.0 of
634 | * SeedDMS an additional directory level has been introduced, which
635 | * will be created when maxDirID is not 0. All documents
636 | * from 1 to maxDirID-1 will be saved in 1/<docid>, documents from maxDirID
637 | * to 2*maxDirID-1 are stored in 2/<docid> and so on.
638 | *
639 | * Modern file systems like ext4 do not have any restrictions on the number
640 | * of subdirectories anymore. Therefore it is best if this parameter is
641 | * set to 0. Never change this parameter if documents has already been
642 | * created.
643 | *
644 | * This method must be called right after creating an instance of
645 | * {@see SeedDMS_Core_DMS}
646 | *
647 | * @param integer $id id of root folder
648 | */
649 | public function setMaxDirID($id) { /* {{{ */
650 | $this->maxDirID = $id;
651 | } /* }}} */
652 |
653 | /**
654 | * Get root folder
655 | *
656 | * @return SeedDMS_Core_Folder|boolean return the object of the root folder or false if
657 | * the root folder id was not set before with {@see SeedDMS_Core_DMS::setRootFolderID()}.
658 | */
659 | public function getRootFolder() { /* {{{ */
660 | if (!$this->rootFolderID) return false;
661 | return $this->getFolder($this->rootFolderID);
662 | } /* }}} */
663 |
664 | public function setForceRename($enable) { /* {{{ */
665 | $this->forceRename = $enable;
666 | } /* }}} */
667 |
668 | public function setForceLink($enable) { /* {{{ */
669 | $this->forceLink = $enable;
670 | } /* }}} */
671 |
672 | /**
673 | * Set the logged in user
674 | *
675 | * This method tells SeeDMS_Core_DMS the currently logged in user. It must be
676 | * called right after instanciating the class, because some methods in
677 | * SeedDMS_Core_Document() require the currently logged in user.
678 | *
679 | * @param object $user this muss not be empty and an instance of SeedDMS_Core_User
680 | * @return bool|object returns the old user object or null on success, otherwise false
681 | *
682 | */
683 | public function setUser($user) { /* {{{ */
684 | if (!$user) {
685 | $olduser = $this->user;
686 | $this->user = null;
687 | return $olduser;
688 | }
689 | if (is_object($user) && (get_class($user) == $this->getClassname('user'))) {
690 | $olduser = $this->user;
691 | $this->user = $user;
692 | return $olduser;
693 | }
694 | return false;
695 | } /* }}} */
696 |
697 | /**
698 | * Get the logged in user
699 | *
700 | * Returns the currently logged in user, previously set by {@see SeedDMS_Core_DMS::setUser()}
701 | *
702 | * @return SeedDMS_Core_User $user
703 | *
704 | */
705 | public function getLoggedInUser() { /* {{{ */
706 | return $this->user;
707 | } /* }}} */
708 |
709 | /**
710 | * Return a document by its id
711 | *
712 | * This method retrieves a document from the database by its id.
713 | *
714 | * @param integer $id internal id of document
715 | * @return SeedDMS_Core_Document instance of {@see SeedDMS_Core_Document}, null or false
716 | */
717 | public function getDocument($id) { /* {{{ */
718 | $classname = $this->classnames['document'];
719 | return $classname::getInstance($id, $this);
720 | } /* }}} */
721 |
722 | /**
723 | * Returns all documents of a given user
724 | *
725 | * @param object $user
726 | * @return array list of documents
727 | */
728 | public function getDocumentsByUser($user) { /* {{{ */
729 | return $user->getDocuments();
730 | } /* }}} */
731 |
732 | /**
733 | * Returns all documents locked by a given user
734 | *
735 | * @param object $user
736 | * @return array list of documents
737 | */
738 | public function getDocumentsLockedByUser($user) { /* {{{ */
739 | return $user->getDocumentsLocked();
740 | } /* }}} */
741 |
742 | /**
743 | * Returns all documents which already expired or will expire in the future
744 | *
745 | * The parameter $date will be relative to the start of the day. It can
746 | * be either a number of days (if an integer is passed) or a date string
747 | * in the format 'YYYY-MM-DD'.
748 | * If the parameter $date is a negative number or a date in the past, then
749 | * all documents from the start of that date till the end of the current
750 | * day will be returned. If $date is a positive integer or $date is a
751 | * date in the future, then all documents from the start of the current
752 | * day till the end of the day of the given date will be returned.
753 | * Passing 0 or the
754 | * current date in $date, will return all documents expiring the current
755 | * day.
756 | * @param string $date date in format YYYY-MM-DD or an integer with the number
757 | * of days. A negative value will cover the days in the past.
758 | * @param SeedDMS_Core_User $user limits the documents on those owned
759 | * by this user
760 | * @param string $orderby n=name, e=expired
761 | * @param string $orderdir d=desc or a=asc
762 | * @param bool $update update status of document if set to true
763 | * @return bool|SeedDMS_Core_Document[]
764 | */
765 | public function getDocumentsExpired($date, $user = null, $orderby = 'e', $orderdir = 'desc', $update = true) { /* {{{ */
766 | $db = $this->getDB();
767 |
768 | if (!$db->createTemporaryTable("ttstatid") || !$db->createTemporaryTable("ttcontentid")) {
769 | return false;
770 | }
771 |
772 | $tsnow = mktime(0, 0, 0); /* Start of today */
773 | if (is_int($date) || is_string($date)) {
774 | if (is_int($date)) {
775 | $ts = $tsnow + $date * 86400;
776 | } else {
777 | $tmp = explode('-', $date, 3);
778 | if (count($tmp) != 3)
779 | return false;
780 | if (!self::checkDate($date, 'Y-m-d'))
781 | return false;
782 | $ts = mktime(0, 0, 0, (int) $tmp[1], (int) $tmp[2], (int) $tmp[0]);
783 | }
784 | if ($ts < $tsnow) { /* Check for docs expired in the past */
785 | $startts = $ts;
786 | $endts = $tsnow+86400; /* Use end of day */
787 | $updatestatus = $update;
788 | } else { /* Check for docs which will expire in the future */
789 | $startts = $tsnow;
790 | $endts = $ts+86400; /* Use end of day */
791 | $updatestatus = false;
792 | }
793 | } elseif (is_array($date)) { // start and end date
794 | if (!empty($date['start'])) {
795 | if (is_int($date['start']))
796 | $startts = $date['start'];
797 | else {
798 | $tmp = explode('-', $date['start'], 3);
799 | if (count($tmp) != 3)
800 | return false;
801 | if (!self::checkDate($date, 'Y-m-d'))
802 | return false;
803 | $startts = mktime(0, 0, 0, (int) $tmp[1], (int) $tmp[2], (int) $tmp[0]);
804 | }
805 | } else {
806 | $startts = time();
807 | }
808 | if (!empty($date['end'])) {
809 | if (is_int($date['end']))
810 | $endts = $date['end'];
811 | else {
812 | $tmp = explode('-', $date['end'], 3);
813 | if (count($tmp) != 3)
814 | return false;
815 | if (!self::checkDate($date, 'Y-m-d'))
816 | return false;
817 | $endts = mktime(24, 0, 0, (int) $tmp[1], (int) $tmp[2], (int) $tmp[0]);
818 | }
819 | } else {
820 | $endts = time() + 365*86400;
821 | }
822 | if (($startts < $tsnow) && ($endts < $tsnow))
823 | $updatestatus = $update;
824 | else
825 | $updatestatus = false;
826 | } else
827 | return false;
828 |
829 | /* Get all documents which have an expiration date. It doesn't check for
830 | * the latest status which should be S_EXPIRED, but doesn't have to, because
831 | * status may have not been updated after the expiration date has been reached.
832 | **/
833 | $queryStr = "SELECT `tblDocuments`.`id`, `tblDocumentStatusLog`.`status` FROM `tblDocuments` ".
834 | "LEFT JOIN `ttcontentid` ON `ttcontentid`.`document` = `tblDocuments`.`id` ".
835 | "LEFT JOIN `tblDocumentContent` ON `tblDocuments`.`id` = `tblDocumentContent`.`document` AND `tblDocumentContent`.`version` = `ttcontentid`.`maxVersion` ".
836 | "LEFT JOIN `tblDocumentStatus` ON `tblDocumentStatus`.`documentID` = `tblDocumentContent`.`document` AND `tblDocumentContent`.`version` = `tblDocumentStatus`.`version` ".
837 | "LEFT JOIN `ttstatid` ON `ttstatid`.`statusID` = `tblDocumentStatus`.`statusID` ".
838 | "LEFT JOIN `tblDocumentStatusLog` ON `tblDocumentStatusLog`.`statusLogID` = `ttstatid`.`maxLogID`";
839 | $queryStr .=
840 | " WHERE `tblDocuments`.`expires` >= ".$startts." AND `tblDocuments`.`expires` < ".$endts;
841 | if ($user)
842 | $queryStr .=
843 | " AND `tblDocuments`.`owner` = '".$user->getID()."' ";
844 | $queryStr .=
845 | " ORDER BY ".($orderby == 'e' ? "`expires`" : "`name`")." ".($orderdir == 'd' ? "DESC" : "ASC");
846 |
847 | $resArr = $db->getResultArray($queryStr);
848 | if (is_bool($resArr) && !$resArr)
849 | return false;
850 |
851 | /** @var SeedDMS_Core_Document[] $documents */
852 | $documents = array();
853 | foreach ($resArr as $row) {
854 | $document = $this->getDocument($row["id"]);
855 | if ($updatestatus) {
856 | $document->verifyLastestContentExpriry();
857 | }
858 | $documents[] = $document;
859 | }
860 | return $documents;
861 | } /* }}} */
862 |
863 | /**
864 | * Returns a document by its name
865 | *
866 | * This method searches a document by its name and restricts the search
867 | * to the given folder if passed as the second parameter.
868 | * If there are more than one document with that name, then only the
869 | * one with the highest id will be returned.
870 | *
871 | * @param string $name Name of the document
872 | * @param object $folder parent folder of document
873 | * @return SeedDMS_Core_Document|null|boolean found document or null if not document was found or false in case of an error
874 | */
875 | public function getDocumentByName($name, $folder = null) { /* {{{ */
876 | $name = trim($name);
877 | if (!$name) return false;
878 |
879 | $queryStr = "SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lockUser` ".
880 | "FROM `tblDocuments` ".
881 | "LEFT JOIN `tblDocumentLocks` ON `tblDocuments`.`id`=`tblDocumentLocks`.`document` ".
882 | "WHERE `tblDocuments`.`name` = " . $this->db->qstr($name);
883 | if ($folder)
884 | $queryStr .= " AND `tblDocuments`.`folder` = ". $folder->getID();
885 | if ($this->checkWithinRootDir)
886 | $queryStr .= " AND `tblDocuments`.`folderList` LIKE '%:".$this->rootFolderID.":%'";
887 | $queryStr .= " ORDER BY `tblDocuments`.`id` DESC LIMIT 1";
888 |
889 | $resArr = $this->db->getResultArray($queryStr);
890 | if (is_bool($resArr) && !$resArr)
891 | return false;
892 |
893 | if (!$resArr)
894 | return null;
895 |
896 | $row = $resArr[0];
897 | /** @var SeedDMS_Core_Document $document */
898 | $document = new $this->classnames['document']($row["id"], $row["name"], $row["comment"], $row["date"], $row["expires"], $row["owner"], $row["folder"], $row["inheritAccess"], $row["defaultAccess"], $row["lockUser"], $row["keywords"], $row["sequence"]);
899 | $document->setDMS($this);
900 | return $document;
901 | } /* }}} */
902 |
903 | /**
904 | * Returns a document by the original file name of the last version
905 | *
906 | * This method searches a document by the name of the last document
907 | * version and restricts the search
908 | * to given folder if passed as the second parameter.
909 | * If there are more than one document with that name, then only the
910 | * one with the highest id will be returned.
911 | *
912 | * @param string $name Name of the original file
913 | * @param object $folder parent folder of document
914 | * @return SeedDMS_Core_Document|null|boolean found document or null if not document was found or false in case of an error
915 | */
916 | public function getDocumentByOriginalFilename($name, $folder = null) { /* {{{ */
917 | $name = trim($name);
918 | if (!$name) return false;
919 |
920 | if (!$this->db->createTemporaryTable("ttcontentid")) {
921 | return false;
922 | }
923 | $queryStr = "SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lockUser` ".
924 | "FROM `tblDocuments` ".
925 | "LEFT JOIN `ttcontentid` ON `ttcontentid`.`document` = `tblDocuments`.`id` ".
926 | "LEFT JOIN `tblDocumentContent` ON `tblDocumentContent`.`document` = `tblDocuments`.`id` AND `tblDocumentContent`.`version` = `ttcontentid`.`maxVersion` ".
927 | "LEFT JOIN `tblDocumentLocks` ON `tblDocuments`.`id`=`tblDocumentLocks`.`document` ".
928 | "WHERE `tblDocumentContent`.`orgFileName` = " . $this->db->qstr($name);
929 | if ($folder)
930 | $queryStr .= " AND `tblDocuments`.`folder` = ". $folder->getID();
931 | $queryStr .= " ORDER BY `tblDocuments`.`id` DESC LIMIT 1";
932 |
933 | $resArr = $this->db->getResultArray($queryStr);
934 | if (is_bool($resArr) && !$resArr)
935 | return false;
936 |
937 | if (!$resArr)
938 | return null;
939 |
940 | $row = $resArr[0];
941 | /** @var SeedDMS_Core_Document $document */
942 | $document = new $this->classnames['document']($row["id"], $row["name"], $row["comment"], $row["date"], $row["expires"], $row["owner"], $row["folder"], $row["inheritAccess"], $row["defaultAccess"], $row["lockUser"], $row["keywords"], $row["sequence"]);
943 | $document->setDMS($this);
944 | return $document;
945 | } /* }}} */
946 |
947 | /**
948 | * Return a document content by its id
949 | *
950 | * This method retrieves a document content from the database by its id.
951 | *
952 | * @param integer $id internal id of document content
953 | * @return bool|null|SeedDMS_Core_DocumentContent found document content or null if not document content was found or false in case of an error
954 |
955 | */
956 | public function getDocumentContent($id) { /* {{{ */
957 | if (!is_numeric($id)) return false;
958 | if ($id < 1) return false;
959 |
960 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `id` = ".(int) $id;
961 | $resArr = $this->db->getResultArray($queryStr);
962 | if (is_bool($resArr) && $resArr == false)
963 | return false;
964 | if (count($resArr) != 1)
965 | return null;
966 | $row = $resArr[0];
967 |
968 | $document = $this->getDocument($row['document']);
969 | $version = new $this->classnames['documentcontent']($row['id'], $document, $row['version'], $row['comment'], $row['date'], $row['createdBy'], $row['dir'], $row['orgFileName'], $row['fileType'], $row['mimeType'], $row['fileSize'], $row['checksum']);
970 | return $version;
971 | } /* }}} */
972 |
973 | /**
974 | * Returns number of documents with a given task
975 | *
976 | * @param string $listtype type of document list, can be 'AppRevByMe',
977 | * 'AppRevOwner', 'WorkflowByMe'
978 | * @param object $user user
979 | * @return array number of tasks
980 | */
981 | public function countTasks($listtype, $user = null, $param5 = true) { /* {{{ */
982 | if (!$this->db->createTemporaryTable("ttstatid") || !$this->db->createTemporaryTable("ttcontentid")) {
983 | return false;
984 | }
985 | $groups = array();
986 | if ($user) {
987 | $tmp = $user->getGroups();
988 | foreach ($tmp as $group)
989 | $groups[] = $group->getID();
990 | }
991 | $selectStr = "count(distinct ttcontentid.document) c ";
992 | $queryStr =
993 | "FROM `ttcontentid` ".
994 | "LEFT JOIN `tblDocumentStatus` ON `tblDocumentStatus`.`documentID`=`ttcontentid`.`document` AND `tblDocumentStatus`.`version`=`ttcontentid`.`maxVersion` ".
995 | "LEFT JOIN `ttstatid` ON `ttstatid`.`statusID` = `tblDocumentStatus`.`statusID` ".
996 | "LEFT JOIN `tblDocumentStatusLog` ON `ttstatid`.`statusID` = `tblDocumentStatusLog`.`statusID` AND `ttstatid`.`maxLogID` = `tblDocumentStatusLog`.`statusLogID` ";
997 | switch ($listtype) {
998 | case 'ReviewByMe': // Documents I have to review {{{
999 | if (!$this->db->createTemporaryTable("ttreviewid")) {
1000 | return false;
1001 | }
1002 | $queryStr .=
1003 | "LEFT JOIN `tblDocumentReviewers` on `ttcontentid`.`document`=`tblDocumentReviewers`.`documentID` AND `ttcontentid`.`maxVersion`=`tblDocumentReviewers`.`version` ".
1004 | "LEFT JOIN `ttreviewid` ON `ttreviewid`.`reviewID` = `tblDocumentReviewers`.`reviewID` ".
1005 | "LEFT JOIN `tblDocumentReviewLog` ON `tblDocumentReviewLog`.`reviewLogID`=`ttreviewid`.`maxLogID` ";
1006 |
1007 | $queryStr .= "WHERE (`tblDocumentReviewers`.`type` = 0 AND `tblDocumentReviewers`.`required` = ".$user->getID()." ";
1008 | if ($groups)
1009 | $queryStr .= "OR `tblDocumentReviewers`.`type` = 1 AND `tblDocumentReviewers`.`required` IN (".implode(',', $groups).") ";
1010 | $queryStr .= ") ";
1011 | $docstatarr = array(S_DRAFT_REV);
1012 | if ($param5)
1013 | $docstatarr[] = S_EXPIRED;
1014 | $queryStr .= "AND `tblDocumentStatusLog`.`status` IN (".implode(',', $docstatarr).") ";
1015 | $queryStr .= "AND `tblDocumentReviewLog`.`status` = 0 ";
1016 | break; /* }}} */
1017 | case 'ApproveByMe': // Documents I have to approve {{{
1018 | if (!$this->db->createTemporaryTable("ttapproveid")) {
1019 | return false;
1020 | }
1021 | $queryStr .=
1022 | "LEFT JOIN `tblDocumentApprovers` on `ttcontentid`.`document`=`tblDocumentApprovers`.`documentID` AND `ttcontentid`.`maxVersion`=`tblDocumentApprovers`.`version` ".
1023 | "LEFT JOIN `ttapproveid` ON `ttapproveid`.`approveID` = `tblDocumentApprovers`.`approveID` ".
1024 | "LEFT JOIN `tblDocumentApproveLog` ON `tblDocumentApproveLog`.`approveLogID`=`ttapproveid`.`maxLogID` ";
1025 |
1026 | if ($user) {
1027 | $queryStr .= "WHERE (`tblDocumentApprovers`.`type` = 0 AND `tblDocumentApprovers`.`required` = ".$user->getID()." ";
1028 | if ($groups)
1029 | $queryStr .= "OR `tblDocumentApprovers`.`type` = 1 AND `tblDocumentApprovers`.`required` IN (".implode(',', $groups).") ";
1030 | $queryStr .= ") ";
1031 | }
1032 | $docstatarr = array(S_DRAFT_APP);
1033 | if ($param5)
1034 | $docstatarr[] = S_EXPIRED;
1035 | $queryStr .= "AND `tblDocumentStatusLog`.`status` IN (".implode(',', $docstatarr).") ";
1036 | $queryStr .= "AND `tblDocumentApproveLog`.`status` = 0 ";
1037 | break; /* }}} */
1038 | case 'WorkflowByMe': // Documents which need my workflow action {{{
1039 |
1040 | $queryStr .=
1041 | "LEFT JOIN `tblWorkflowDocumentContent` on `ttcontentid`.`document`=`tblWorkflowDocumentContent`.`document` AND `ttcontentid`.`maxVersion`=`tblWorkflowDocumentContent`.`version` ".
1042 | "LEFT JOIN `tblWorkflowTransitions` on `tblWorkflowDocumentContent`.`workflow`=`tblWorkflowTransitions`.`workflow` AND `tblWorkflowDocumentContent`.`state`=`tblWorkflowTransitions`.`state` ".
1043 | "LEFT JOIN `tblWorkflowTransitionUsers` on `tblWorkflowTransitionUsers`.`transition` = `tblWorkflowTransitions`.`id` ".
1044 | "LEFT JOIN `tblWorkflowTransitionGroups` on `tblWorkflowTransitionGroups`.`transition` = `tblWorkflowTransitions`.`id` ";
1045 |
1046 | if ($user) {
1047 | $queryStr .= "WHERE (`tblWorkflowTransitionUsers`.`userid` = ".$user->getID()." ";
1048 | if ($groups)
1049 | $queryStr .= "OR `tblWorkflowTransitionGroups`.`groupid` IN (".implode(',', $groups).")";
1050 | $queryStr .= ") ";
1051 | }
1052 | $queryStr .= "AND `tblDocumentStatusLog`.`status` = ".S_IN_WORKFLOW." ";
1053 | break; // }}}
1054 | }
1055 | if ($queryStr) {
1056 | $resArr = $this->db->getResultArray('SELECT '.$selectStr.$queryStr);
1057 | if (is_bool($resArr) && !$resArr) {
1058 | return false;
1059 | }
1060 | } else {
1061 | return false;
1062 | }
1063 | return $resArr[0]['c'];
1064 | } /* }}} */
1065 |
1066 | /**
1067 | * Returns all documents with a predefined search criteria
1068 | *
1069 | * The records return have the following elements
1070 | *
1071 | * From Table tblDocuments
1072 | * [id] => id of document
1073 | * [name] => name of document
1074 | * [comment] => comment of document
1075 | * [date] => timestamp of creation date of document
1076 | * [expires] => timestamp of expiration date of document
1077 | * [owner] => user id of owner
1078 | * [folder] => id of parent folder
1079 | * [folderList] => column separated list of folder ids, e.g. :1:41:
1080 | * [inheritAccess] => 1 if access is inherited
1081 | * [defaultAccess] => default access mode
1082 | * [locked] => always -1 (TODO: is this field still used?)
1083 | * [keywords] => keywords of document
1084 | * [sequence] => sequence of document
1085 | *
1086 | * From Table tblDocumentLocks
1087 | * [lockUser] => id of user locking the document
1088 | *
1089 | * From Table tblDocumentStatusLog
1090 | * [version] => latest version of document
1091 | * [statusID] => id of latest status log
1092 | * [documentID] => id of document
1093 | * [status] => current status of document
1094 | * [statusComment] => comment of current status
1095 | * [statusDate] => datetime when the status was entered, e.g. 2014-04-17 21:35:51
1096 | * [userID] => id of user who has initiated the status change
1097 | *
1098 | * From Table tblUsers
1099 | * [ownerName] => name of owner of document
1100 | * [statusName] => name of user who has initiated the status change
1101 | *
1102 | * @param string $listtype type of document list, can be 'AppRevByMe',
1103 | * 'AppRevOwner', 'ReceiptByMe', 'ReviseByMe', 'LockedByMe', 'MyDocs'
1104 | * @param SeedDMS_Core_User $param1 user
1105 | * @param bool|integer|string $param2 if set to true
1106 | * 'ReviewByMe', 'ApproveByMe', 'AppRevByMe', 'ReviseByMe', 'ReceiptByMe'
1107 | * will also return documents which the reviewer, approver, etc.
1108 | * has already taken care of. If set to false only
1109 | * untouched documents will be returned. In case of 'ExpiredOwner' this
1110 | * parameter contains the number of days (a negative number is allowed)
1111 | * relativ to the current date or a date in format 'yyyy-mm-dd'
1112 | * (even in the past).
1113 | * @param string $param3 sort list by this field
1114 | * @param string $param4 order direction
1115 | * @param bool $param5 set to false if expired documents shall not be considered
1116 | * @return array|bool
1117 | */
1118 | public function getDocumentList($listtype, $param1 = null, $param2 = false, $param3 = '', $param4 = '', $param5 = true) { /* {{{ */
1119 | /* The following query will get all documents and lots of additional
1120 | * information. It requires the two temporary tables ttcontentid and
1121 | * ttstatid.
1122 | */
1123 | if (!$this->db->createTemporaryTable("ttstatid") || !$this->db->createTemporaryTable("ttcontentid")) {
1124 | return false;
1125 | }
1126 | /* The following statement retrieves the status of the last version of all
1127 | * documents. It must be restricted by further where clauses.
1128 | */
1129 | /*
1130 | $queryStr = "SELECT `tblDocuments`.*, `tblDocumentLocks`.`userID` as `lockUser`, ".
1131 | "`tblDocumentContent`.`version`, `tblDocumentStatus`.*, `tblDocumentStatusLog`.`status`, ".
1132 | "`tblDocumentStatusLog`.`comment` AS `statusComment`, `tblDocumentStatusLog`.`date` as `statusDate`, ".
1133 | "`tblDocumentStatusLog`.`userID`, `oTbl`.`fullName` AS `ownerName`, `sTbl`.`fullName` AS `statusName` ".
1134 | "FROM `tblDocumentContent` ".
1135 | "LEFT JOIN `tblDocuments` ON `tblDocuments`.`id` = `tblDocumentContent`.`document` ".
1136 | "LEFT JOIN `tblDocumentStatus` ON `tblDocumentStatus`.`documentID` = `tblDocumentContent`.`document` ".
1137 | "LEFT JOIN `tblDocumentStatusLog` ON `tblDocumentStatusLog`.`statusID` = `tblDocumentStatus`.`statusID` ".
1138 | "LEFT JOIN `ttstatid` ON `ttstatid`.`maxLogID` = `tblDocumentStatusLog`.`statusLogID` ".
1139 | "LEFT JOIN `ttcontentid` ON `ttcontentid`.`maxVersion` = `tblDocumentStatus`.`version` AND `ttcontentid`.`document` = `tblDocumentStatus`.`documentID` ".
1140 | "LEFT JOIN `tblDocumentLocks` ON `tblDocuments`.`id`=`tblDocumentLocks`.`document` ".
1141 | "LEFT JOIN `tblUsers` AS `oTbl` on `oTbl`.`id` = `tblDocuments`.`owner` ".
1142 | "LEFT JOIN `tblUsers` AS `sTbl` on `sTbl`.`id` = `tblDocumentStatusLog`.`userID` ".
1143 | "WHERE `ttstatid`.`maxLogID`=`tblDocumentStatusLog`.`statusLogID` ".
1144 | "AND `ttcontentid`.`maxVersion` = `tblDocumentContent`.`version` ";
1145 | */
1146 | /* New sql statement which retrieves all documents, its latest version and
1147 | * status, the owner and user initiating the latest status.
1148 | * It doesn't need the where clause anymore. Hence the statement could be
1149 | * extended with further left joins.
1150 | */
1151 | $selectStr = "`tblDocuments`.*, `tblDocumentLocks`.`userID` as `lockUser`, ".
1152 | "`tblDocumentContent`.`version`, `tblDocumentStatus`.*, `tblDocumentStatusLog`.`status`, ".
1153 | "`tblDocumentStatusLog`.`comment` AS `statusComment`, `tblDocumentStatusLog`.`date` as `statusDate`, ".
1154 | "`tblDocumentStatusLog`.`userID`, `oTbl`.`fullName` AS `ownerName`, `sTbl`.`fullName` AS `statusName` ";
1155 | $queryStr =
1156 | "FROM `ttcontentid` ".
1157 | "LEFT JOIN `tblDocuments` ON `tblDocuments`.`id` = `ttcontentid`.`document` ".
1158 | "LEFT JOIN `tblDocumentContent` ON `tblDocumentContent`.`document` = `ttcontentid`.`document` AND `tblDocumentContent`.`version` = `ttcontentid`.`maxVersion` ".
1159 | "LEFT JOIN `tblDocumentStatus` ON `tblDocumentStatus`.`documentID`=`ttcontentid`.`document` AND `tblDocumentStatus`.`version`=`ttcontentid`.`maxVersion` ".
1160 | "LEFT JOIN `ttstatid` ON `ttstatid`.`statusID` = `tblDocumentStatus`.`statusID` ".
1161 | "LEFT JOIN `tblDocumentStatusLog` ON `ttstatid`.`statusID` = `tblDocumentStatusLog`.`statusID` AND `ttstatid`.`maxLogID` = `tblDocumentStatusLog`.`statusLogID` ".
1162 | "LEFT JOIN `tblDocumentLocks` ON `ttcontentid`.`document`=`tblDocumentLocks`.`document` ".
1163 | "LEFT JOIN `tblUsers` `oTbl` ON `oTbl`.`id` = `tblDocuments`.`owner` ".
1164 | "LEFT JOIN `tblUsers` `sTbl` ON `sTbl`.`id` = `tblDocumentStatusLog`.`userID` ";
1165 |
1166 | // echo $queryStr;
1167 |
1168 | switch ($listtype) {
1169 | case 'AppRevByMe': // Documents I have to review/approve {{{
1170 | $queryStr .= "WHERE 1=1 ";
1171 |
1172 | $user = $param1;
1173 | // Get document list for the current user.
1174 | $reviewStatus = $user->getReviewStatus();
1175 | $approvalStatus = $user->getApprovalStatus();
1176 |
1177 | // Create a comma separated list of all the documentIDs whose information is
1178 | // required.
1179 | // Take only those documents into account which hasn't be touched by the user
1180 | $dList = array();
1181 | foreach ($reviewStatus["indstatus"] as $st) {
1182 | if (($st["status"]==0 || $param2) && !in_array($st["documentID"], $dList)) {
1183 | $dList[] = $st["documentID"];
1184 | }
1185 | }
1186 | foreach ($reviewStatus["grpstatus"] as $st) {
1187 | if (($st["status"]==0 || $param2) && !in_array($st["documentID"], $dList)) {
1188 | $dList[] = $st["documentID"];
1189 | }
1190 | }
1191 | foreach ($approvalStatus["indstatus"] as $st) {
1192 | if (($st["status"]==0 || $param2) && !in_array($st["documentID"], $dList)) {
1193 | $dList[] = $st["documentID"];
1194 | }
1195 | }
1196 | foreach ($approvalStatus["grpstatus"] as $st) {
1197 | if (($st["status"]==0 || $param2) && !in_array($st["documentID"], $dList)) {
1198 | $dList[] = $st["documentID"];
1199 | }
1200 | }
1201 | $docCSV = "";
1202 | foreach ($dList as $d) {
1203 | $docCSV .= (strlen($docCSV)==0 ? "" : ", ")."'".$d."'";
1204 | }
1205 |
1206 | if (strlen($docCSV)>0) {
1207 | $docstatarr = array(S_DRAFT_REV, S_DRAFT_APP);
1208 | if ($param5)
1209 | $docstatarr[] = S_EXPIRED;
1210 | $queryStr .= "AND `tblDocumentStatusLog`.`status` IN (".implode(',', $docstatarr).") ".
1211 | "AND `tblDocuments`.`id` IN (" . $docCSV . ") ".
1212 | "ORDER BY `statusDate` DESC";
1213 | } else {
1214 | $queryStr = '';
1215 | }
1216 | break; // }}}
1217 | case 'ReviewByMe': // Documents I have to review {{{
1218 | if (!$this->db->createTemporaryTable("ttreviewid")) {
1219 | return false;
1220 | }
1221 | $user = $param1;
1222 | $orderby = $param3;
1223 | if ($param4 == 'desc')
1224 | $orderdir = 'DESC';
1225 | else
1226 | $orderdir = 'ASC';
1227 |
1228 | $groups = array();
1229 | if ($user) {
1230 | $tmp = $user->getGroups();
1231 | foreach ($tmp as $group)
1232 | $groups[] = $group->getID();
1233 | }
1234 |
1235 | $selectStr .= ", `tblDocumentReviewLog`.`date` as `duedate` ";
1236 | $queryStr .=
1237 | "LEFT JOIN `tblDocumentReviewers` ON `ttcontentid`.`document`=`tblDocumentReviewers`.`documentID` AND `ttcontentid`.`maxVersion`=`tblDocumentReviewers`.`version` ".
1238 | "LEFT JOIN `ttreviewid` ON `ttreviewid`.`reviewID` = `tblDocumentReviewers`.`reviewID` ".
1239 | "LEFT JOIN `tblDocumentReviewLog` ON `tblDocumentReviewLog`.`reviewLogID`=`ttreviewid`.`maxLogID` ";
1240 |
1241 | if ($user) {
1242 | $queryStr .= "WHERE (`tblDocumentReviewers`.`type` = 0 AND `tblDocumentReviewers`.`required` = ".$user->getID()." ";
1243 | if ($groups)
1244 | $queryStr .= "OR `tblDocumentReviewers`.`type` = 1 AND `tblDocumentReviewers`.`required` IN (".implode(',', $groups).") ";
1245 | $queryStr .= ") ";
1246 | }
1247 | $docstatarr = array(S_DRAFT_REV);
1248 | if ($param5)
1249 | $docstatarr[] = S_EXPIRED;
1250 | $queryStr .= "AND `tblDocumentStatusLog`.`status` IN (".implode(',', $docstatarr).") ";
1251 | if (!$param2)
1252 | $queryStr .= " AND `tblDocumentReviewLog`.`status` = 0 ";
1253 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1254 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1255 | elseif ($orderby == 's') $queryStr .= "ORDER BY `tblDocumentStatusLog`.`status`";
1256 | else $queryStr .= "ORDER BY `name`";
1257 | $queryStr .= " ".$orderdir;
1258 | break; // }}}
1259 | case 'ApproveByMe': // Documents I have to approve {{{
1260 | if (!$this->db->createTemporaryTable("ttapproveid")) {
1261 | return false;
1262 | }
1263 | $user = $param1;
1264 | $orderby = $param3;
1265 | if ($param4 == 'desc')
1266 | $orderdir = 'DESC';
1267 | else
1268 | $orderdir = 'ASC';
1269 |
1270 | $groups = array();
1271 | if ($user) {
1272 | $tmp = $user->getGroups();
1273 | foreach ($tmp as $group)
1274 | $groups[] = $group->getID();
1275 | }
1276 |
1277 | $selectStr .= ", `tblDocumentApproveLog`.`date` as `duedate` ";
1278 | $queryStr .=
1279 | "LEFT JOIN `tblDocumentApprovers` ON `ttcontentid`.`document`=`tblDocumentApprovers`.`documentID` AND `ttcontentid`.`maxVersion`=`tblDocumentApprovers`.`version` ".
1280 | "LEFT JOIN `ttapproveid` ON `ttapproveid`.`approveID` = `tblDocumentApprovers`.`approveID` ".
1281 | "LEFT JOIN `tblDocumentApproveLog` ON `tblDocumentApproveLog`.`approveLogID`=`ttapproveid`.`maxLogID` ";
1282 |
1283 | if ($user) {
1284 | $queryStr .= "WHERE (`tblDocumentApprovers`.`type` = 0 AND `tblDocumentApprovers`.`required` = ".$user->getID()." ";
1285 | if ($groups)
1286 | $queryStr .= "OR `tblDocumentApprovers`.`type` = 1 AND `tblDocumentApprovers`.`required` IN (".implode(',', $groups).")";
1287 | $queryStr .= ") ";
1288 | }
1289 | $docstatarr = array(S_DRAFT_APP);
1290 | if ($param5)
1291 | $docstatarr[] = S_EXPIRED;
1292 | $queryStr .= "AND `tblDocumentStatusLog`.`status` IN (".implode(',', $docstatarr).") ";
1293 | if (!$param2)
1294 | $queryStr .= " AND `tblDocumentApproveLog`.`status` = 0 ";
1295 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1296 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1297 | elseif ($orderby == 's') $queryStr .= "ORDER BY `tblDocumentStatusLog`.`status`";
1298 | else $queryStr .= "ORDER BY `name`";
1299 | $queryStr .= " ".$orderdir;
1300 | break; // }}}
1301 | case 'WorkflowByMe': // Documents I to trigger in Worklflow {{{
1302 | $user = $param1;
1303 | $orderby = $param3;
1304 | if ($param4 == 'desc')
1305 | $orderdir = 'DESC';
1306 | else
1307 | $orderdir = 'ASC';
1308 |
1309 | $groups = array();
1310 | if ($user) {
1311 | $tmp = $user->getGroups();
1312 | foreach ($tmp as $group)
1313 | $groups[] = $group->getID();
1314 | }
1315 | $selectStr = 'distinct '.$selectStr;
1316 | $queryStr .=
1317 | "LEFT JOIN `tblWorkflowDocumentContent` ON `ttcontentid`.`document`=`tblWorkflowDocumentContent`.`document` AND `ttcontentid`.`maxVersion`=`tblWorkflowDocumentContent`.`version` ".
1318 | "LEFT JOIN `tblWorkflowTransitions` ON `tblWorkflowDocumentContent`.`workflow`=`tblWorkflowTransitions`.`workflow` AND `tblWorkflowDocumentContent`.`state`=`tblWorkflowTransitions`.`state` ".
1319 | "LEFT JOIN `tblWorkflowTransitionUsers` ON `tblWorkflowTransitionUsers`.`transition` = `tblWorkflowTransitions`.`id` ".
1320 | "LEFT JOIN `tblWorkflowTransitionGroups` ON `tblWorkflowTransitionGroups`.`transition` = `tblWorkflowTransitions`.`id` ";
1321 |
1322 | if ($user) {
1323 | $queryStr .= "WHERE (`tblWorkflowTransitionUsers`.`userid` = ".$user->getID()." ";
1324 | if ($groups)
1325 | $queryStr .= "OR `tblWorkflowTransitionGroups`.`groupid` IN (".implode(',', $groups).")";
1326 | $queryStr .= ") ";
1327 | }
1328 | $queryStr .= "AND `tblDocumentStatusLog`.`status` = ".S_IN_WORKFLOW." ";
1329 | // echo 'SELECT '.$selectStr." ".$queryStr;
1330 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1331 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1332 | else $queryStr .= "ORDER BY `name`";
1333 | break; // }}}
1334 | case 'AppRevOwner': // Documents waiting for review/approval/revision I'm owning {{{
1335 | $queryStr .= "WHERE 1=1 ";
1336 |
1337 | $user = $param1;
1338 | $orderby = $param3;
1339 | if ($param4 == 'desc')
1340 | $orderdir = 'DESC';
1341 | else
1342 | $orderdir = 'ASC';
1343 | /** @noinspection PhpUndefinedConstantInspection */
1344 | $queryStr .= "AND `tblDocuments`.`owner` = '".$user->getID()."' ".
1345 | "AND `tblDocumentStatusLog`.`status` IN (".S_DRAFT_REV.", ".S_DRAFT_APP.") ";
1346 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1347 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1348 | elseif ($orderby == 's') $queryStr .= "ORDER BY `status`";
1349 | else $queryStr .= "ORDER BY `name`";
1350 | $queryStr .= " ".$orderdir;
1351 | // $queryStr .= "AND `tblDocuments`.`owner` = '".$user->getID()."' ".
1352 | // "AND `tblDocumentStatusLog`.`status` IN (".S_DRAFT_REV.", ".S_DRAFT_APP.") ".
1353 | // "ORDER BY `statusDate` DESC";
1354 | break; // }}}
1355 | case 'RejectOwner': // Documents that has been rejected and I'm owning {{{
1356 | $queryStr .= "WHERE 1=1 ";
1357 |
1358 | $user = $param1;
1359 | $orderby = $param3;
1360 | if ($param4 == 'desc')
1361 | $orderdir = 'DESC';
1362 | else
1363 | $orderdir = 'ASC';
1364 | $queryStr .= "AND `tblDocuments`.`owner` = '".$user->getID()."' ";
1365 | $queryStr .= "AND `tblDocumentStatusLog`.`status` IN (".S_REJECTED.") ";
1366 | //$queryStr .= "ORDER BY `statusDate` DESC";
1367 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1368 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1369 | elseif ($orderby == 's') $queryStr .= "ORDER BY `status`";
1370 | else $queryStr .= "ORDER BY `name`";
1371 | $queryStr .= " ".$orderdir;
1372 | break; // }}}
1373 | case 'LockedByMe': // Documents locked by me {{{
1374 | $queryStr .= "WHERE 1=1 ";
1375 |
1376 | $user = $param1;
1377 | $orderby = $param3;
1378 | if ($param4 == 'desc')
1379 | $orderdir = 'DESC';
1380 | else
1381 | $orderdir = 'ASC';
1382 |
1383 | $qs = 'SELECT `document` FROM `tblDocumentLocks` WHERE `userID`='.$user->getID();
1384 | $ra = $this->db->getResultArray($qs);
1385 | if (is_bool($ra) && !$ra) {
1386 | return false;
1387 | }
1388 | $docs = array();
1389 | foreach ($ra as $d) {
1390 | $docs[] = $d['document'];
1391 | }
1392 |
1393 | if ($docs) {
1394 | $queryStr .= "AND `tblDocuments`.`id` IN (" . implode(',', $docs) . ") ";
1395 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1396 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1397 | elseif ($orderby == 's') $queryStr .= "ORDER BY `status`";
1398 | else $queryStr .= "ORDER BY `name`";
1399 | $queryStr .= " ".$orderdir;
1400 | } else {
1401 | $queryStr = '';
1402 | }
1403 | break; // }}}
1404 | case 'ExpiredOwner': // Documents expired and owned by me {{{
1405 | if (is_int($param2)) {
1406 | $ts = mktime(0, 0, 0) + $param2 * 86400;
1407 | } elseif (is_string($param2)) {
1408 | $tmp = explode('-', $param2, 3);
1409 | if (count($tmp) != 3)
1410 | return false;
1411 | if (!self::checkDate($param2, 'Y-m-d'))
1412 | return false;
1413 | $ts = mktime(0, 0, 0, (int) $tmp[1], (int) $tmp[2], (int) $tmp[0]);
1414 | } else
1415 | $ts = mktime(0, 0, 0)-365*86400; /* Start of today - 1 year */
1416 |
1417 | $tsnow = mktime(0, 0, 0); /* Start of today */
1418 | if ($ts < $tsnow) { /* Check for docs expired in the past */
1419 | $startts = $ts;
1420 | $endts = $tsnow+86400; /* Use end of day */
1421 | } else { /* Check for docs which will expire in the future */
1422 | $startts = $tsnow;
1423 | $endts = $ts+86400; /* Use end of day */
1424 | }
1425 |
1426 | $queryStr .=
1427 | "WHERE `tblDocuments`.`expires` >= ".$startts." AND `tblDocuments`.`expires` <= ".$endts." ";
1428 |
1429 | $user = $param1;
1430 | $orderby = $param3;
1431 | if ($param4 == 'desc')
1432 | $orderdir = 'DESC';
1433 | else
1434 | $orderdir = 'ASC';
1435 | $queryStr .= "AND `tblDocuments`.`owner` = '".$user->getID()."' ";
1436 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1437 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1438 | elseif ($orderby == 's') $queryStr .= "ORDER BY `status`";
1439 | else $queryStr .= "ORDER BY `name`";
1440 | $queryStr .= " ".$orderdir;
1441 | break; // }}}
1442 | case 'WorkflowOwner': // Documents waiting for workflow trigger I'm owning {{{
1443 | $queryStr .= "WHERE 1=1 ";
1444 |
1445 | $user = $param1;
1446 | $queryStr .= "AND `tblDocuments`.`owner` = '".$user->getID()."' ".
1447 | "AND `tblDocumentStatusLog`.`status` IN (".S_IN_WORKFLOW.") ".
1448 | "ORDER BY `statusDate` DESC";
1449 | break; // }}}
1450 | case 'MyDocs': // Documents owned by me {{{
1451 | $queryStr .= "WHERE 1=1 ";
1452 |
1453 | $user = $param1;
1454 | $orderby = $param3;
1455 | if ($param4 == 'desc')
1456 | $orderdir = 'DESC';
1457 | else
1458 | $orderdir = 'ASC';
1459 | $queryStr .= "AND `tblDocuments`.`owner` = '".$user->getID()."' ";
1460 | if ($orderby == 'e') $queryStr .= "ORDER BY `expires`";
1461 | elseif ($orderby == 'u') $queryStr .= "ORDER BY `statusDate`";
1462 | elseif ($orderby == 's') $queryStr .= "ORDER BY `status`";
1463 | else $queryStr .= "ORDER BY `name`";
1464 | $queryStr .= " ".$orderdir;
1465 | break; // }}}
1466 | default: // {{{
1467 | return false;
1468 | break; // }}}
1469 | }
1470 |
1471 | if ($queryStr) {
1472 | $resArr = $this->db->getResultArray('SELECT '.$selectStr.$queryStr);
1473 | if (is_bool($resArr) && !$resArr) {
1474 | return false;
1475 | }
1476 | /*
1477 | $documents = array();
1478 | foreach ($resArr as $row)
1479 | $documents[] = $this->getDocument($row["id"]);
1480 | */
1481 | } else {
1482 | return array();
1483 | }
1484 |
1485 | return $resArr;
1486 | } /* }}} */
1487 |
1488 | /**
1489 | * Create a unix time stamp
1490 | *
1491 | * This method is much like `mktime()` but does some range checks
1492 | * on the passed values.
1493 | *
1494 | * @param int $hour hour
1495 | * @param int $min minute
1496 | * @param int $sec second
1497 | * @param int $year year
1498 | * @param int $month month
1499 | * @param int $day day
1500 | * @return int|boolean unix time stamp or false if range check failed
1501 | */
1502 | public function makeTimeStamp($hour, $min, $sec, $year, $month, $day) { /* {{{ */
1503 | $thirtyone = array (1, 3, 5, 7, 8, 10, 12);
1504 | $thirty = array (4, 6, 9, 11);
1505 |
1506 | // Very basic check that the terms are valid. Does not fail for illegal
1507 | // dates such as 31 Feb.
1508 | if (!is_numeric($hour) || !is_numeric($min) || !is_numeric($sec) || !is_numeric($year) || !is_numeric($month) || !is_numeric($day) || $month<1 || $month>12 || $day<1 || $day>31 || $hour<0 || $hour>23 || $min<0 || $min>59 || $sec<0 || $sec>59) {
1509 | return false;
1510 | }
1511 | $year = (int) $year;
1512 | $month = (int) $month;
1513 | $day = (int) $day;
1514 |
1515 | if (in_array($month, $thirtyone)) {
1516 | $max = 31;
1517 | } elseif (in_array($month, $thirty)) {
1518 | $max = 30;
1519 | } else {
1520 | $max = (($year % 4 == 0) && ($year % 100 != 0 || $year % 400 == 0)) ? 29 : 28;
1521 | }
1522 |
1523 | // Check again if day of month is valid in the given month
1524 | if ($day>$max) {
1525 | return false;
1526 | }
1527 |
1528 | return mktime($hour, $min, $sec, $month, $day, $year);
1529 | } /* }}} */
1530 |
1531 | protected function getSqlForAttribute($attrdef, $attribute, $table, $field) { /* {{{ */
1532 |
1533 | $attrdefid = $attrdef->getId();
1534 | $sql = '';
1535 | /* The only differenc between Document, Folder and DocumentContent is
1536 | * the name of the tables. The tables for documents and folders have a
1537 | * trailing 's' (tblDocuments, tblFolders), but the table for document
1538 | * content doesn't have it (tblDocumentContent).
1539 | * The sql statements are equal.
1540 | */
1541 | if($table == 'DocumentContent') {
1542 | if ($valueset = $attrdef->getValueSet()) {
1543 | if (is_string($attribute))
1544 | $attribute = array($attribute);
1545 | foreach ($attribute as &$v)
1546 | $v = trim($this->db->qstr($v), "'");
1547 | if ($attrdef->getMultipleValues()) {
1548 | $sql = "EXISTS (SELECT NULL FROM `tblDocumentContentAttributes` WHERE `tblDocumentContentAttributes`.`attrdef`=".$attrdefid." AND (`tblDocumentContentAttributes`.`value` like '%".$valueset[0].implode("%' OR `tblDocumentContentAttributes`.`value` like '%".$valueset[0], $attribute)."%') AND `tblDocumentContentAttributes`.`content` = `tblDocumentContent`.`id`)";
1549 | } else {
1550 | $sql = "EXISTS (SELECT NULL FROM `tblDocumentContentAttributes` WHERE `tblDocumentContentAttributes`.`attrdef`=".$attrdefid." AND (`tblDocumentContentAttributes`.`value`='".(is_array($attribute) ? implode("' OR `tblDocumentContentAttributes`.`value` = '", $attribute) : $attribute)."') AND `tblDocumentContentAttributes`.content = `tblDocumentContent`.`id`)";
1551 | }
1552 | } else {
1553 | if (in_array($attrdef->getType(), [SeedDMS_Core_AttributeDefinition::type_date, SeedDMS_Core_AttributeDefinition::type_int, SeedDMS_Core_AttributeDefinition::type_float]) && is_array($attribute)) {
1554 | $kkll = [];
1555 | if (!empty($attribute['from'])) {
1556 | if ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_int)
1557 | $kkll[] = "CAST(`tblDocumentContentAttributes`.`value` AS INTEGER)>=".(int) $attribute['from'];
1558 | elseif ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_float)
1559 | $kkll[] = "CAST(`tblDocumentContentAttributes`.`value` AS DECIMAL)>=".(float) $attribute['from'];
1560 | else
1561 | $kkll[] = "`tblDocumentContentAttributes`.`value`>=".$this->db->qstr($attribute['from']);
1562 | }
1563 | if (!empty($attribute['to'])) {
1564 | if ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_int)
1565 | $kkll[] = "CAST(`tblDocumentContentAttributes`.`value` AS INTEGER)<=".(int) $attribute['to'];
1566 | elseif ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_float)
1567 | $kkll[] = "CAST(`tblDocumentContentAttributes`.`value` AS DECIMAL)<=".(float) $attribute['to'];
1568 | else
1569 | $kkll[] = "`tblDocumentContentAttributes`.`value`<=".$this->db->qstr($attribute['to']);
1570 | }
1571 | if ($kkll)
1572 | $sql = "EXISTS (SELECT NULL FROM `tblDocumentContentAttributes` WHERE `tblDocumentContentAttributes`.`attrdef`=".$attrdefid." AND ".implode(' AND ', $kkll)." AND `tblDocumentContentAttributes`.`content`=`tblDocumentContent`.`id`)";
1573 | } elseif($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_string) {
1574 | $sql = "EXISTS (SELECT NULL FROM `tblDocumentContentAttributes` WHERE `tblDocumentContentAttributes`.`attrdef`=".$attrdefid." AND `tblDocumentContentAttributes`.`value` like ".$this->db->qstr("%".$attribute."%")." AND `tblDocumentContentAttributes`.`content` = `tblDocumentContent`.`id`)";
1575 | } elseif (in_array($attrdef->getType(), [SeedDMS_Core_AttributeDefinition::type_user, SeedDMS_Core_AttributeDefinition::type_group, SeedDMS_Core_AttributeDefinition::type_document, SeedDMS_Core_AttributeDefinition::type_folder])) {
1576 | if ($attrdef->getMultipleValues()) {
1577 | $sql = "EXISTS (SELECT NULL FROM `tblDocumentContentAttributes` WHERE `tblDocumentContentAttributes`.`attrdef`=".$attrdefid." AND ((`tblDocumentContentAttributes`.`value` like '%,".implode(",%' OR `tblDocumentContentAttributes`.`value` like '%,", $attribute).",%') OR (`tblDocumentContentAttributes`.`value` like '%,".implode("' OR `tblDocumentContentAttributes`.`value` like '%,", $attribute)."') ) AND `tblDocumentContentAttributes`.`content` = `tblDocumentContent`.`id`)";
1578 | } else {
1579 | $sql = "EXISTS (SELECT NULL FROM `tblDocumentContentAttributes` WHERE `tblDocumentContentAttributes`.`attrdef`=".$attrdefid." AND (`tblDocumentContentAttributes`.`value`='".(is_array($attribute) ? implode("' OR `tblDocumentContentAttributes`.`value`='", $attribute) : $attribute)."') AND `tblDocumentContentAttributes`.`content`=`tblDocumentContent`.`id`)";
1580 | }
1581 | } else {
1582 | $sql = "EXISTS (SELECT NULL FROM `tblDocumentContentAttributes` WHERE `tblDocumentContentAttributes`.`attrdef`=".$attrdefid." AND `tblDocumentContentAttributes`.`value`=".$this->db->qstr($attribute)." AND `tblDocumentContentAttributes`.`content` = `tblDocumentContent`.`id`)";
1583 | }
1584 | }
1585 | } else {
1586 | if ($valueset = $attrdef->getValueSet()) {
1587 | if (is_string($attribute))
1588 | $attribute = array($attribute);
1589 | foreach ($attribute as &$v)
1590 | $v = trim($this->db->qstr($v), "'");
1591 | if ($attrdef->getMultipleValues()) {
1592 | $sql = "EXISTS (SELECT NULL FROM `tbl".$table."Attributes` WHERE `tbl".$table."Attributes`.`attrdef`=".$attrdefid." AND (`tbl".$table."Attributes`.`value` like '%".$valueset[0].implode("%' OR `tbl".$table."Attributes`.`value` like '%".$valueset[0], $attribute)."%') AND `tbl".$table."Attributes`.`".$field."`=`tbl".$table."s`.`id`)";
1593 | } else {
1594 | $sql = "EXISTS (SELECT NULL FROM `tbl".$table."Attributes` WHERE `tbl".$table."Attributes`.`attrdef`=".$attrdefid." AND (`tbl".$table."Attributes`.`value`='".(is_array($attribute) ? implode("' OR `tbl".$table."Attributes`.`value` = '", $attribute) : $attribute)."') AND `tbl".$table."Attributes`.`".$field."`=`tbl".$table."s`.`id`)";
1595 | }
1596 | } else {
1597 | if (in_array($attrdef->getType(), [SeedDMS_Core_AttributeDefinition::type_date, SeedDMS_Core_AttributeDefinition::type_int, SeedDMS_Core_AttributeDefinition::type_float]) && is_array($attribute)) {
1598 | $kkll = [];
1599 | if (!empty($attribute['from'])) {
1600 | if ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_int)
1601 | $kkll[] = "CAST(`tbl".$table."Attributes`.`value` AS INTEGER)>=".(int) $attribute['from'];
1602 | elseif ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_float)
1603 | $kkll[] = "CAST(`tbl".$table."Attributes`.`value` AS DECIMAL)>=".(float) $attribute['from'];
1604 | else
1605 | $kkll[] = "`tbl".$table."Attributes`.`value`>=".$this->db->qstr($attribute['from']);
1606 | }
1607 | if (!empty($attribute['to'])) {
1608 | if ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_int)
1609 | $kkll[] = "CAST(`tbl".$table."Attributes`.`value` AS INTEGER)<=".(int) $attribute['to'];
1610 | elseif ($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_float)
1611 | $kkll[] = "CAST(`tbl".$table."Attributes`.`value` AS DECIMAL)<=".(float) $attribute['to'];
1612 | else
1613 | $kkll[] = "`tbl".$table."Attributes`.`value`<=".$this->db->qstr($attribute['to']);
1614 | }
1615 | if ($kkll)
1616 | $sql = "EXISTS (SELECT NULL FROM `tbl".$table."Attributes` WHERE `tbl".$table."Attributes`.`attrdef`=".$attrdefid." AND ".implode(' AND ', $kkll)." AND `tbl".$table."Attributes`.`".$field."`=`tbl".$table."s`.`id`)";
1617 | } elseif($attrdef->getType() == SeedDMS_Core_AttributeDefinition::type_string) {
1618 | $sql = "EXISTS (SELECT NULL FROM `tbl".$table."Attributes` WHERE `tbl".$table."Attributes`.`attrdef`=".$attrdefid." AND `tbl".$table."Attributes`.`value` like ".$this->db->qstr("%".$attribute."%")." AND `tbl".$table."Attributes`.`".$field."`=`tbl".$table."s`.`id`)";
1619 | } elseif (in_array($attrdef->getType(), [SeedDMS_Core_AttributeDefinition::type_user, SeedDMS_Core_AttributeDefinition::type_group, SeedDMS_Core_AttributeDefinition::type_document, SeedDMS_Core_AttributeDefinition::type_folder])) {
1620 | if ($attrdef->getMultipleValues()) {
1621 | $sql = "EXISTS (SELECT NULL FROM `tbl".$table."Attributes` WHERE `tbl".$table."Attributes`.`attrdef`=".$attrdefid." AND ((`tbl".$table."Attributes`.`value` like '%,".implode(",%' OR `tbl".$table."Attributes`.`value` like '%,", $attribute).",%') OR (`tbl".$table."Attributes`.`value` like '%,".implode("' OR `tbl".$table."Attributes`.`value` like '%,", $attribute)."') ) AND `tbl".$table."Attributes`.`".$field."` = `tbl".$table."s`.`id`)";
1622 | } else {
1623 | $sql = "EXISTS (SELECT NULL FROM `tbl".$table."Attributes` WHERE `tbl".$table."Attributes`.`attrdef`=".$attrdefid." AND (`tbl".$table."Attributes`.`value`='".(is_array($attribute) ? implode("' OR `tbl".$table."Attributes`.`value`='", $attribute) : $attribute)."') AND `tbl".$table."Attributes`.`".$field."`=`tbl".$table."s`.`id`)";
1624 | }
1625 | } else {
1626 | $sql = "EXISTS (SELECT NULL FROM `tbl".$table."Attributes` WHERE `tbl".$table."Attributes`.`attrdef`=".$attrdefid." AND `tbl".$table."Attributes`.`value`=".$this->db->qstr($attribute)." AND `tbl".$table."Attributes`.`".$field."`=`tbl".$table."s`.`id`)";
1627 | }
1628 | }
1629 | }
1630 | return $sql;
1631 | } /* }}} /
1632 |
1633 | /**
1634 | * Search the database for documents
1635 | *
1636 | * Note: the creation date will be used to check againts the
1637 | * date saved with the document
1638 | * or folder. The modification date will only be used for documents. It
1639 | * is checked against the creation date of the document content. This
1640 | * meanѕ that updateѕ of a document will only result in a searchable
1641 | * modification if a new version is uploaded.
1642 | *
1643 | * If the search is filtered by an expiration date, only documents with
1644 | * an expiration date will be found. Even if just an end date is given.
1645 | *
1646 | * dates, integers and floats fields are treated as ranges (expecting a 'from'
1647 | * and 'to' value) unless they have a value set.
1648 | *
1649 | * @param string $query seach query with space separated words
1650 | * @param integer $limit number of items in result set
1651 | * @param integer $offset index of first item in result set
1652 | * @param string $logicalmode either AND or OR
1653 | * @param array $searchin list of fields to search in
1654 | * 1 = keywords, 2=name, 3=comment, 4=attributes, 5=id
1655 | * @param SeedDMS_Core_Folder|null $startFolder search in the folder only (null for root folder)
1656 | * @param SeedDMS_Core_User $owner search for documents owned by this user
1657 | * @param array $status list of status
1658 | * @param array $creationstartdate search for documents created after this date
1659 | * @param array $creationenddate search for documents created before this date
1660 | * @param array $modificationstartdate search for documents modified after this date
1661 | * @param array $modificationenddate search for documents modified before this date
1662 | * @param array $categories list of categories the documents must have assigned
1663 | * @param array $attributes list of attributes. The key of this array is the
1664 | * attribute definition id. The value of the array is the value of the
1665 | * attribute. If the attribute may have multiple values it must be an array.
1666 | * attributes with a range must have the elements 'from' and 'to'
1667 | * @param integer $mode decide whether to search for documents/folders
1668 | * 0x1 = documents only
1669 | * 0x2 = folders only
1670 | * 0x3 = both
1671 | * @param array $expirationstartdate search for documents expiring after and on this date
1672 | * @param array $expirationenddate search for documents expiring before and on this date
1673 | * @return array|bool
1674 | */
1675 | public function search($query, $limit = 0, $offset = 0, $logicalmode = 'AND', $searchin = array(), $startFolder = null, $owner = null, $status = array(), $creationstartdate = array(), $creationenddate = array(), $modificationstartdate = array(), $modificationenddate = array(), $categories = array(), $attributes = array(), $mode = 0x3, $expirationstartdate = array(), $expirationenddate = array()) { /* {{{ */
1676 | $orderby = '';
1677 | $statusstartdate = array();
1678 | $statusenddate = array();
1679 | $mimetype = '';
1680 | if (is_array($query)) {
1681 | foreach (array('limit', 'offset', 'logicalmode', 'searchin', 'startFolder', 'owner', 'status', 'mimetype', 'creationstartdate', 'creationenddate', 'modificationstartdate', 'modificationenddate', 'categories', 'attributes', 'mode', 'expirationstartdate', 'expirationenddate') as $paramname)
1682 | ${$paramname} = isset($query[$paramname]) ? $query[$paramname] : ${$paramname};
1683 | foreach (array('orderby', 'statusstartdate', 'statusenddate') as $paramname)
1684 | ${$paramname} = isset($query[$paramname]) ? $query[$paramname] : '';
1685 | $query = isset($query['query']) ? $query['query'] : '';
1686 | }
1687 | /* Ensure $logicalmode has a valid value */
1688 | if ($logicalmode != 'OR')
1689 | $logicalmode = 'AND';
1690 |
1691 | // Split the search string into constituent keywords.
1692 | $tkeys = array();
1693 | if (strlen($query)>0) {
1694 | $tkeys = preg_split("/[\t\r\n ,]+/", $query);
1695 | }
1696 |
1697 | // if none is checkd search all
1698 | if (count($searchin)==0)
1699 | $searchin = array(1, 2, 3, 4, 5);
1700 |
1701 | /*--------- Do it all over again for folders -------------*/
1702 | $totalFolders = 0;
1703 | if ($mode & 0x2) {
1704 | $searchKey = "";
1705 |
1706 | $classname = $this->classnames['folder'];
1707 | $searchFields = $classname::getSearchFields($this, $searchin);
1708 |
1709 | if (count($searchFields)>0) {
1710 | foreach ($tkeys as $key) {
1711 | $key = trim($key);
1712 | if (strlen($key)>0) {
1713 | $searchKey = (strlen($searchKey)==0 ? "" : $searchKey." ".$logicalmode." ")."(".implode(" like ".$this->db->qstr("%".$key."%")." OR ", $searchFields)." like ".$this->db->qstr("%".$key."%").")";
1714 | }
1715 | }
1716 | }
1717 |
1718 | // Check to see if the search has been restricted to a particular sub-tree in
1719 | // the folder hierarchy.
1720 | $searchFolder = "";
1721 | if ($startFolder) {
1722 | $searchFolder = "`tblFolders`.`folderList` LIKE '%:".$startFolder->getID().":%'";
1723 | if ($this->checkWithinRootDir)
1724 | $searchFolder = '('.$searchFolder." AND `tblFolders`.`folderList` LIKE '%:".$this->rootFolderID.":%')";
1725 | } elseif ($this->checkWithinRootDir) {
1726 | $searchFolder = "`tblFolders`.`folderList` LIKE '%:".$this->rootFolderID.":%'";
1727 | }
1728 |
1729 | // Check to see if the search has been restricted to a particular
1730 | // document owner.
1731 | $searchOwner = "";
1732 | if ($owner) {
1733 | if (is_array($owner)) {
1734 | $ownerids = array();
1735 | foreach ($owner as $o)
1736 | $ownerids[] = $o->getID();
1737 | if ($ownerids)
1738 | $searchOwner = "`tblFolders`.`owner` IN (".implode(',', $ownerids).")";
1739 | } else {
1740 | $searchOwner = "`tblFolders`.`owner` = '".$owner->getId()."'";
1741 | }
1742 | }
1743 |
1744 | // Check to see if the search has been restricted to a particular
1745 | // attribute.
1746 | $searchAttributes = array();
1747 | if ($attributes) {
1748 | foreach ($attributes as $attrdefid => $attribute) {
1749 | if ($attribute) {
1750 | $attrdef = $this->getAttributeDefinition($attrdefid);
1751 | if ($attrdef->getObjType() == SeedDMS_Core_AttributeDefinition::objtype_folder || $attrdef->getObjType() == SeedDMS_Core_AttributeDefinition::objtype_all) {
1752 | if($sql = $this->getSqlForAttribute($attrdef, $attribute, 'Folder', 'folder'))
1753 | $searchAttributes[] = $sql;
1754 | }
1755 | }
1756 | }
1757 | }
1758 |
1759 | // Is the search restricted to documents created between two specific dates?
1760 | $searchCreateDate = "";
1761 | if ($creationstartdate) {
1762 | if (is_numeric($creationstartdate))
1763 | $startdate = $creationstartdate;
1764 | else
1765 | $startdate = SeedDMS_Core_DMS::makeTimeStamp($creationstartdate['hour'], $creationstartdate['minute'], $creationstartdate['second'], $creationstartdate['year'], $creationstartdate["month"], $creationstartdate["day"]);
1766 | if ($startdate) {
1767 | $searchCreateDate .= "`tblFolders`.`date` >= ".(int) $startdate;
1768 | }
1769 | }
1770 | if ($creationenddate) {
1771 | if (is_numeric($creationenddate))
1772 | $stopdate = $creationenddate;
1773 | else
1774 | $stopdate = SeedDMS_Core_DMS::makeTimeStamp($creationenddate['hour'], $creationenddate['minute'], $creationenddate['second'], $creationenddate["year"], $creationenddate["month"], $creationenddate["day"]);
1775 | if ($stopdate) {
1776 | /** @noinspection PhpUndefinedVariableInspection */
1777 | if ($startdate)
1778 | $searchCreateDate .= " AND ";
1779 | $searchCreateDate .= "`tblFolders`.`date` <= ".(int) $stopdate;
1780 | }
1781 | }
1782 |
1783 | $searchQuery = "FROM ".$classname::getSearchTables()." WHERE 1=1";
1784 |
1785 | if (strlen($searchKey)>0) {
1786 | $searchQuery .= " AND (".$searchKey.")";
1787 | }
1788 | if (strlen($searchFolder)>0) {
1789 | $searchQuery .= " AND ".$searchFolder;
1790 | }
1791 | if (strlen($searchOwner)>0) {
1792 | $searchQuery .= " AND (".$searchOwner.")";
1793 | }
1794 | if (strlen($searchCreateDate)>0) {
1795 | $searchQuery .= " AND (".$searchCreateDate.")";
1796 | }
1797 | if ($searchAttributes) {
1798 | $searchQuery .= " AND (".implode(" AND ", $searchAttributes).")";
1799 | }
1800 |
1801 | /* Do not search for folders if not at least a search for a key,
1802 | * an owner, or creation date is requested.
1803 | */
1804 | if ($searchKey || $searchOwner || $searchCreateDate || $searchAttributes) {
1805 | // Count the number of rows that the search will produce.
1806 | $resArr = $this->db->getResultArray("SELECT COUNT(*) AS num FROM (SELECT DISTINCT `tblFolders`.id ".$searchQuery.") a");
1807 | if ($resArr && isset($resArr[0]) && is_numeric($resArr[0]["num"]) && $resArr[0]["num"]>0) {
1808 | $totalFolders = (integer)$resArr[0]["num"];
1809 | }
1810 |
1811 | // If there are no results from the count query, then there is no real need
1812 | // to run the full query. TODO: re-structure code to by-pass additional
1813 | // queries when no initial results are found.
1814 |
1815 | // Only search if the offset is not beyond the number of folders
1816 | if ($totalFolders > $offset) {
1817 | // Prepare the complete search query, including the LIMIT clause.
1818 | $searchQuery = "SELECT DISTINCT `tblFolders`.`id` ".$searchQuery." GROUP BY `tblFolders`.`id`";
1819 |
1820 | switch ($orderby) {
1821 | case 'dd':
1822 | $searchQuery .= " ORDER BY `tblFolders`.`date` DESC";
1823 | break;
1824 | case 'da':
1825 | case 'd':
1826 | $searchQuery .= " ORDER BY `tblFolders`.`date`";
1827 | break;
1828 | case 'nd':
1829 | $searchQuery .= " ORDER BY `tblFolders`.`name` DESC";
1830 | break;
1831 | case 'na':
1832 | case 'n':
1833 | $searchQuery .= " ORDER BY `tblFolders`.`name`";
1834 | break;
1835 | case 'id':
1836 | $searchQuery .= " ORDER BY `tblFolders`.`id` DESC";
1837 | break;
1838 | case 'ia':
1839 | case 'i':
1840 | $searchQuery .= " ORDER BY `tblFolders`.`id`";
1841 | break;
1842 | default:
1843 | break;
1844 | }
1845 |
1846 | if ($limit) {
1847 | $searchQuery .= " LIMIT ".$limit." OFFSET ".$offset;
1848 | }
1849 |
1850 | // Send the complete search query to the database.
1851 | $resArr = $this->db->getResultArray($searchQuery);
1852 | } else {
1853 | $resArr = array();
1854 | }
1855 |
1856 | // ------------------- Ausgabe der Ergebnisse ----------------------------
1857 | $numResults = count($resArr);
1858 | if ($numResults == 0) {
1859 | $folderresult = array('totalFolders'=>$totalFolders, 'folders'=>array());
1860 | } else {
1861 | foreach ($resArr as $folderArr) {
1862 | $folders[] = $this->getFolder($folderArr['id']);
1863 | }
1864 | /** @noinspection PhpUndefinedVariableInspection */
1865 | $folderresult = array('totalFolders'=>$totalFolders, 'folders'=>$folders);
1866 | }
1867 | } else {
1868 | $folderresult = array('totalFolders'=>0, 'folders'=>array());
1869 | }
1870 | } else {
1871 | $folderresult = array('totalFolders'=>0, 'folders'=>array());
1872 | }
1873 |
1874 | /*--------- Do it all over again for documents -------------*/
1875 |
1876 | $totalDocs = 0;
1877 | if ($mode & 0x1) {
1878 | $searchKey = "";
1879 |
1880 | $classname = $this->classnames['document'];
1881 | $searchFields = $classname::getSearchFields($this, $searchin);
1882 |
1883 | if (count($searchFields)>0) {
1884 | foreach ($tkeys as $key) {
1885 | $key = trim($key);
1886 | if (strlen($key)>0) {
1887 | $searchKey = (strlen($searchKey)==0 ? "" : $searchKey." ".$logicalmode." ")."(".implode(" like ".$this->db->qstr("%".$key."%")." OR ", $searchFields)." like ".$this->db->qstr("%".$key."%").")";
1888 | }
1889 | }
1890 | }
1891 |
1892 | // Check to see if the search has been restricted to a particular sub-tree in
1893 | // the folder hierarchy.
1894 | $searchFolder = "";
1895 | if ($startFolder) {
1896 | $searchFolder = "`tblDocuments`.`folderList` LIKE '%:".$startFolder->getID().":%'";
1897 | if ($this->checkWithinRootDir)
1898 | $searchFolder = '('.$searchFolder." AND `tblDocuments`.`folderList` LIKE '%:".$this->rootFolderID.":%')";
1899 | } elseif ($this->checkWithinRootDir) {
1900 | $searchFolder = "`tblDocuments`.`folderList` LIKE '%:".$this->rootFolderID.":%'";
1901 | }
1902 |
1903 | // Check to see if the search has been restricted to a particular
1904 | // document owner.
1905 | $searchOwner = "";
1906 | if ($owner) {
1907 | if (is_array($owner)) {
1908 | $ownerids = array();
1909 | foreach ($owner as $o)
1910 | $ownerids[] = $o->getID();
1911 | if ($ownerids)
1912 | $searchOwner = "`tblDocuments`.`owner` IN (".implode(',', $ownerids).")";
1913 | } else {
1914 | $searchOwner = "`tblDocuments`.`owner` = '".$owner->getId()."'";
1915 | }
1916 | }
1917 |
1918 | // Check to see if the search has been restricted to a particular
1919 | // document category.
1920 | $searchCategories = "";
1921 | if ($categories) {
1922 | $catids = array();
1923 | foreach ($categories as $category)
1924 | $catids[] = $category->getId();
1925 | $searchCategories = "`tblDocumentCategory`.`categoryID` in (".implode(',', $catids).")";
1926 | }
1927 |
1928 | // Check to see if the search has been restricted to a particular
1929 | // attribute.
1930 | $searchAttributes = array();
1931 | if ($attributes) {
1932 | foreach ($attributes as $attrdefid => $attribute) {
1933 | if ($attribute) {
1934 | $lsearchAttributes = [];
1935 | $attrdef = $this->getAttributeDefinition($attrdefid);
1936 | if ($attrdef->getObjType() == SeedDMS_Core_AttributeDefinition::objtype_document || $attrdef->getObjType() == SeedDMS_Core_AttributeDefinition::objtype_all) {
1937 | if($sql = $this->getSqlForAttribute($attrdef, $attribute, 'Document', 'document'))
1938 | $lsearchAttributes[] = $sql;
1939 | }
1940 | if ($attrdef->getObjType() == SeedDMS_Core_AttributeDefinition::objtype_documentcontent || $attrdef->getObjType() == SeedDMS_Core_AttributeDefinition::objtype_all) {
1941 | if($sql = $this->getSqlForAttribute($attrdef, $attribute, 'DocumentContent', 'content'))
1942 | $lsearchAttributes[] = $sql;
1943 | }
1944 | if ($lsearchAttributes)
1945 | $searchAttributes[] = "(".implode(" OR ", $lsearchAttributes).")";
1946 | }
1947 | }
1948 | }
1949 |
1950 | // Is the search restricted to documents created between two specific dates?
1951 | $searchCreateDate = "";
1952 | if ($creationstartdate) {
1953 | if (is_numeric($creationstartdate))
1954 | $startdate = $creationstartdate;
1955 | else
1956 | $startdate = SeedDMS_Core_DMS::makeTimeStamp($creationstartdate['hour'], $creationstartdate['minute'], $creationstartdate['second'], $creationstartdate['year'], $creationstartdate["month"], $creationstartdate["day"]);
1957 | if ($startdate) {
1958 | $searchCreateDate .= "`tblDocuments`.`date` >= ".(int) $startdate;
1959 | }
1960 | }
1961 | if ($creationenddate) {
1962 | if (is_numeric($creationenddate))
1963 | $stopdate = $creationenddate;
1964 | else
1965 | $stopdate = SeedDMS_Core_DMS::makeTimeStamp($creationenddate['hour'], $creationenddate['minute'], $creationenddate['second'], $creationenddate["year"], $creationenddate["month"], $creationenddate["day"]);
1966 | if ($stopdate) {
1967 | if ($searchCreateDate)
1968 | $searchCreateDate .= " AND ";
1969 | $searchCreateDate .= "`tblDocuments`.`date` <= ".(int) $stopdate;
1970 | }
1971 | }
1972 |
1973 | if ($modificationstartdate) {
1974 | if (is_numeric($modificationstartdate))
1975 | $startdate = $modificationstartdate;
1976 | else
1977 | $startdate = SeedDMS_Core_DMS::makeTimeStamp($modificationstartdate['hour'], $modificationstartdate['minute'], $modificationstartdate['second'], $modificationstartdate['year'], $modificationstartdate["month"], $modificationstartdate["day"]);
1978 | if ($startdate) {
1979 | if ($searchCreateDate)
1980 | $searchCreateDate .= " AND ";
1981 | $searchCreateDate .= "`tblDocumentContent`.`date` >= ".(int) $startdate;
1982 | }
1983 | }
1984 | if ($modificationenddate) {
1985 | if (is_numeric($modificationenddate))
1986 | $stopdate = $modificationenddate;
1987 | else
1988 | $stopdate = SeedDMS_Core_DMS::makeTimeStamp($modificationenddate['hour'], $modificationenddate['minute'], $modificationenddate['second'], $modificationenddate["year"], $modificationenddate["month"], $modificationenddate["day"]);
1989 | if ($stopdate) {
1990 | if ($searchCreateDate)
1991 | $searchCreateDate .= " AND ";
1992 | $searchCreateDate .= "`tblDocumentContent`.`date` <= ".(int) $stopdate;
1993 | }
1994 | }
1995 | $searchExpirationDate = '';
1996 | if ($expirationstartdate) {
1997 | $startdate = SeedDMS_Core_DMS::makeTimeStamp($expirationstartdate['hour'], $expirationstartdate['minute'], $expirationstartdate['second'], $expirationstartdate['year'], $expirationstartdate["month"], $expirationstartdate["day"]);
1998 | if ($startdate) {
1999 | $searchExpirationDate .= "`tblDocuments`.`expires` >= ".(int) $startdate;
2000 | }
2001 | }
2002 | if ($expirationenddate) {
2003 | $stopdate = SeedDMS_Core_DMS::makeTimeStamp($expirationenddate['hour'], $expirationenddate['minute'], $expirationenddate['second'], $expirationenddate["year"], $expirationenddate["month"], $expirationenddate["day"]);
2004 | if ($stopdate) {
2005 | if ($searchExpirationDate)
2006 | $searchExpirationDate .= " AND ";
2007 | else // do not find documents without an expiration date
2008 | $searchExpirationDate .= "`tblDocuments`.`expires` != 0 AND ";
2009 | $searchExpirationDate .= "`tblDocuments`.`expires` <= ".(int) $stopdate;
2010 | }
2011 | }
2012 | $searchStatusDate = '';
2013 | if ($statusstartdate) {
2014 | $startdate = $statusstartdate['year'].'-'.$statusstartdate["month"].'-'.$statusstartdate["day"].' '.$statusstartdate['hour'].':'.$statusstartdate['minute'].':'.$statusstartdate['second'];
2015 | if ($startdate) {
2016 | if ($searchStatusDate)
2017 | $searchStatusDate .= " AND ";
2018 | $searchStatusDate .= "`tblDocumentStatusLog`.`date` >= ".$this->db->qstr($startdate);
2019 | }
2020 | }
2021 | if ($statusenddate) {
2022 | $stopdate = $statusenddate['year'].'-'.$statusenddate["month"].'-'.$statusenddate["day"].' '.$statusenddate['hour'].':'.$statusenddate['minute'].':'.$statusenddate['second'];
2023 | if ($stopdate) {
2024 | if ($searchStatusDate)
2025 | $searchStatusDate .= " AND ";
2026 | $searchStatusDate .= "`tblDocumentStatusLog`.`date` <= ".$this->db->qstr($stopdate);
2027 | }
2028 | }
2029 |
2030 | // ---------------------- Suche starten ----------------------------------
2031 |
2032 | //
2033 | // Construct the SQL query that will be used to search the database.
2034 | //
2035 |
2036 | if (!$this->db->createTemporaryTable("ttcontentid") || !$this->db->createTemporaryTable("ttstatid")) {
2037 | return false;
2038 | }
2039 |
2040 | $searchQuery = "FROM `tblDocuments` ".
2041 | "LEFT JOIN `tblDocumentContent` ON `tblDocuments`.`id` = `tblDocumentContent`.`document` ".
2042 | "LEFT JOIN `tblDocumentAttributes` ON `tblDocuments`.`id` = `tblDocumentAttributes`.`document` ".
2043 | "LEFT JOIN `tblDocumentContentAttributes` ON `tblDocumentContent`.`id` = `tblDocumentContentAttributes`.`content` ".
2044 | "LEFT JOIN `tblDocumentStatus` ON `tblDocumentStatus`.`documentID` = `tblDocumentContent`.`document` ".
2045 | "LEFT JOIN `ttstatid` ON `ttstatid`.`statusID` = `tblDocumentStatus`.`statusID` ".
2046 | "LEFT JOIN `tblDocumentStatusLog` ON `tblDocumentStatusLog`.`statusLogID` = `ttstatid`.`maxLogID` ".
2047 | "LEFT JOIN `ttcontentid` ON `ttcontentid`.`maxVersion` = `tblDocumentStatus`.`version` AND `ttcontentid`.`document` = `tblDocumentStatus`.`documentID` ".
2048 | "LEFT JOIN `tblDocumentLocks` ON `tblDocuments`.`id`=`tblDocumentLocks`.`document` ".
2049 | "LEFT JOIN `tblDocumentCategory` ON `tblDocuments`.`id`=`tblDocumentCategory`.`documentID` ".
2050 | "WHERE ".
2051 | // "`ttstatid`.`maxLogID`=`tblDocumentStatusLog`.`statusLogID` AND ".
2052 | "`ttcontentid`.`maxVersion` = `tblDocumentContent`.`version`";
2053 |
2054 | if (strlen($searchKey)>0) {
2055 | $searchQuery .= " AND (".$searchKey.")";
2056 | }
2057 | if (strlen($searchFolder)>0) {
2058 | $searchQuery .= " AND ".$searchFolder;
2059 | }
2060 | if (strlen($searchOwner)>0) {
2061 | $searchQuery .= " AND (".$searchOwner.")";
2062 | }
2063 | if (strlen($searchCategories)>0) {
2064 | $searchQuery .= " AND (".$searchCategories.")";
2065 | }
2066 | if (strlen($searchCreateDate)>0) {
2067 | $searchQuery .= " AND (".$searchCreateDate.")";
2068 | }
2069 | if (strlen($searchExpirationDate)>0) {
2070 | $searchQuery .= " AND (".$searchExpirationDate.")";
2071 | }
2072 | if (strlen($searchStatusDate)>0) {
2073 | $searchQuery .= " AND (".$searchStatusDate.")";
2074 | }
2075 | if ($searchAttributes) {
2076 | $searchQuery .= " AND (".implode(" AND ", $searchAttributes).")";
2077 | }
2078 |
2079 | // status
2080 | if ($status) {
2081 | $searchQuery .= " AND `tblDocumentStatusLog`.`status` IN (".implode(',', $status).")";
2082 | }
2083 |
2084 | // mime type
2085 | if ($mimetype) {
2086 | $searchQuery .= " AND `tblDocumentContent`.`mimeType` IN ('".implode("','", $mimetype)."')";
2087 | }
2088 | if ($searchKey || $searchOwner || $searchCategories || $searchCreateDate || $searchExpirationDate || $searchStatusDate || $searchAttributes || $status || $mimetype) {
2089 | // Count the number of rows that the search will produce.
2090 | $resArr = $this->db->getResultArray("SELECT COUNT(*) AS num FROM (SELECT DISTINCT `tblDocuments`.`id` ".$searchQuery.") a");
2091 | $totalDocs = 0;
2092 | if (is_numeric($resArr[0]["num"]) && $resArr[0]["num"]>0) {
2093 | $totalDocs = (integer)$resArr[0]["num"];
2094 | }
2095 |
2096 | // If there are no results from the count query, then there is no real need
2097 | // to run the full query. TODO: re-structure code to by-pass additional
2098 | // queries when no initial results are found.
2099 |
2100 | // Prepare the complete search query, including the LIMIT clause.
2101 | $searchQuery = "SELECT DISTINCT `tblDocuments`.*, ".
2102 | "`tblDocumentContent`.`version`, ".
2103 | "`tblDocumentStatusLog`.`status`, `tblDocumentLocks`.`userID` as `lockUser` ".$searchQuery;
2104 |
2105 | switch ($orderby) {
2106 | case 'dd':
2107 | $orderbyQuery = " ORDER BY `tblDocuments`.`date` DESC";
2108 | break;
2109 | case 'da':
2110 | case 'd':
2111 | $orderbyQuery = " ORDER BY `tblDocuments`.`date`";
2112 | break;
2113 | case 'nd':
2114 | $orderbyQuery = " ORDER BY `tblDocuments`.`name` DESC";
2115 | break;
2116 | case 'na':
2117 | case 'n':
2118 | $orderbyQuery = " ORDER BY `tblDocuments`.`name`";
2119 | break;
2120 | case 'id':
2121 | $orderbyQuery = " ORDER BY `tblDocuments`.`id` DESC";
2122 | break;
2123 | case 'ia':
2124 | case 'i':
2125 | $orderbyQuery = " ORDER BY `tblDocuments`.`id`";
2126 | break;
2127 | default:
2128 | $orderbyQuery = "";
2129 | break;
2130 | }
2131 |
2132 | // calculate the remaining entrїes of the current page
2133 | // If page is not full yet, get remaining entries
2134 | if ($limit) {
2135 | $remain = $limit - count($folderresult['folders']);
2136 | if ($remain) {
2137 | if ($remain == $limit)
2138 | $offset -= $totalFolders;
2139 | else
2140 | $offset = 0;
2141 |
2142 | $searchQuery .= $orderbyQuery;
2143 |
2144 | if ($limit)
2145 | $searchQuery .= " LIMIT ".$limit." OFFSET ".$offset;
2146 |
2147 | // Send the complete search query to the database.
2148 | $resArr = $this->db->getResultArray($searchQuery);
2149 | if ($resArr === false)
2150 | return false;
2151 | } else {
2152 | $resArr = array();
2153 | }
2154 | } else {
2155 | $searchQuery .= $orderbyQuery;
2156 |
2157 | // Send the complete search query to the database.
2158 | $resArr = $this->db->getResultArray($searchQuery);
2159 | if ($resArr === false)
2160 | return false;
2161 | }
2162 |
2163 | // ------------------- Ausgabe der Ergebnisse ----------------------------
2164 | $numResults = count($resArr);
2165 | if ($numResults == 0) {
2166 | $docresult = array('totalDocs'=>$totalDocs, 'docs'=>array());
2167 | } else {
2168 | foreach ($resArr as $docArr) {
2169 | $docs[] = $this->getDocument($docArr['id']);
2170 | }
2171 | /** @noinspection PhpUndefinedVariableInspection */
2172 | $docresult = array('totalDocs'=>$totalDocs, 'docs'=>$docs);
2173 | }
2174 | } else {
2175 | $docresult = array('totalDocs'=>0, 'docs'=>array());
2176 | }
2177 | } else {
2178 | $docresult = array('totalDocs'=>0, 'docs'=>array());
2179 | }
2180 |
2181 | if ($limit) {
2182 | $totalPages = (integer)(($totalDocs+$totalFolders)/$limit);
2183 | if ((($totalDocs+$totalFolders)%$limit) > 0) {
2184 | $totalPages++;
2185 | }
2186 | } else {
2187 | $totalPages = 1;
2188 | }
2189 |
2190 | return array_merge($docresult, $folderresult, array('totalPages'=>$totalPages));
2191 | } /* }}} */
2192 |
2193 | /**
2194 | * Return a folder by its id
2195 | *
2196 | * This method retrieves a folder from the database by its id.
2197 | *
2198 | * @param integer $id internal id of folder
2199 | * @return SeedDMS_Core_Folder instance of SeedDMS_Core_Folder or false
2200 | */
2201 | public function getFolder($id) { /* {{{ */
2202 | if ($this->usecache && isset($this->cache['folders'][$id])) {
2203 | return $this->cache['folders'][$id];
2204 | }
2205 | $classname = $this->classnames['folder'];
2206 | $folder = $classname::getInstance($id, $this);
2207 | if ($this->usecache)
2208 | $this->cache['folders'][$id] = $folder;
2209 | return $folder;
2210 | } /* }}} */
2211 |
2212 | /**
2213 | * Return a folder by its name
2214 | *
2215 | * This method retrieves a folder from the database by its name. The
2216 | * search covers the whole database. If
2217 | * the parameter $folder is not null, it will search for the name
2218 | * only within this parent folder. It will not be done recursively.
2219 | *
2220 | * @param string $name name of the folder
2221 | * @param SeedDMS_Core_Folder $folder parent folder
2222 | * @return SeedDMS_Core_Folder|boolean found folder or false
2223 | */
2224 | public function getFolderByName($name, $folder = null) { /* {{{ */
2225 | $name = trim($name);
2226 | $classname = $this->classnames['folder'];
2227 | return $classname::getInstanceByName($name, $folder, $this);
2228 | } /* }}} */
2229 |
2230 | /**
2231 | * Returns a list of folders and error message not linked in the tree
2232 | *
2233 | * This method checks all folders in the database.
2234 | *
2235 | * @return array|bool
2236 | */
2237 | public function checkFolders() { /* {{{ */
2238 | $queryStr = "SELECT * FROM `tblFolders`";
2239 | $resArr = $this->db->getResultArray($queryStr);
2240 |
2241 | if (is_bool($resArr) && $resArr === false)
2242 | return false;
2243 |
2244 | $cache = array();
2245 | foreach ($resArr as $rec) {
2246 | $cache[$rec['id']] = array('name'=>$rec['name'], 'parent'=>$rec['parent'], 'folderList'=>$rec['folderList']);
2247 | }
2248 | $errors = array();
2249 | foreach ($cache as $id => $rec) {
2250 | if (!array_key_exists($rec['parent'], $cache) && $rec['parent'] != 0) {
2251 | $errors[$id] = array('id'=>$id, 'name'=>$rec['name'], 'parent'=>$rec['parent'], 'msg'=>'Missing parent');
2252 | }
2253 | if (!isset($errors[$id])) {
2254 | /* Create the real folderList and compare it with the stored folderList */
2255 | $parent = $rec['parent'];
2256 | $fl = [];
2257 | while($parent) {
2258 | array_unshift($fl, $parent);
2259 | $parent = $cache[$parent]['parent'];
2260 | }
2261 | if ($fl)
2262 | $flstr = ':'.implode(':', $fl).':';
2263 | else
2264 | $flstr = '';
2265 | if ($flstr != $rec['folderList'])
2266 | $errors[$id] = array('id'=>$id, 'name'=>$rec['name'], 'parent'=>$rec['parent'], 'msg'=>'Wrong folder list '.$flstr.'!='.$rec['folderList']);
2267 | }
2268 | if (!isset($errors[$id])) {
2269 | /* This is the old insufficient test which will most likely not be called
2270 | * anymore, because the check for a wrong folder list will cache a folder
2271 | * list problem anyway.
2272 | */
2273 | $tmparr = explode(':', $rec['folderList']);
2274 | array_shift($tmparr);
2275 | if (count($tmparr) != count(array_unique($tmparr))) {
2276 | $errors[$id] = array('id'=>$id, 'name'=>$rec['name'], 'parent'=>$rec['parent'], 'msg'=>'Duplicate entry in folder list ('.$rec['folderList'].')');
2277 | }
2278 | }
2279 | }
2280 |
2281 | return $errors;
2282 | } /* }}} */
2283 |
2284 | /**
2285 | * Returns a list of documents and error message not linked in the tree
2286 | *
2287 | * This method checks all documents in the database.
2288 | *
2289 | * @return array|bool
2290 | */
2291 | public function checkDocuments() { /* {{{ */
2292 | $queryStr = "SELECT * FROM `tblFolders`";
2293 | $resArr = $this->db->getResultArray($queryStr);
2294 |
2295 | if (is_bool($resArr) && $resArr === false)
2296 | return false;
2297 |
2298 | $fcache = array();
2299 | foreach ($resArr as $rec) {
2300 | $fcache[$rec['id']] = array('name'=>$rec['name'], 'parent'=>$rec['parent'], 'folderList'=>$rec['folderList']);
2301 | }
2302 |
2303 | $queryStr = "SELECT * FROM `tblDocuments`";
2304 | $resArr = $this->db->getResultArray($queryStr);
2305 |
2306 | if (is_bool($resArr) && $resArr === false)
2307 | return false;
2308 |
2309 | $dcache = array();
2310 | foreach ($resArr as $rec) {
2311 | $dcache[$rec['id']] = array('name'=>$rec['name'], 'parent'=>$rec['folder'], 'folderList'=>$rec['folderList']);
2312 | }
2313 | $errors = array();
2314 | foreach ($dcache as $id => $rec) {
2315 | if (!array_key_exists($rec['parent'], $fcache) && $rec['parent'] != 0) {
2316 | $errors[$id] = array('id'=>$id, 'name'=>$rec['name'], 'parent'=>$rec['parent'], 'msg'=>'Missing parent');
2317 | }
2318 | if (!isset($errors[$id])) {
2319 | /* Create the real folderList and compare it with the stored folderList */
2320 | $parent = $rec['parent'];
2321 | $fl = [];
2322 | while($parent) {
2323 | array_unshift($fl, $parent);
2324 | $parent = $fcache[$parent]['parent'];
2325 | }
2326 | if ($fl)
2327 | $flstr = ':'.implode(':', $fl).':';
2328 | if ($flstr != $rec['folderList'])
2329 | $errors[$id] = array('id'=>$id, 'name'=>$rec['name'], 'parent'=>$rec['parent'], 'msg'=>'Wrong folder list '.$flstr.'!='.$rec['folderList']);
2330 | }
2331 | if (!isset($errors[$id])) {
2332 | $tmparr = explode(':', $rec['folderList']);
2333 | array_shift($tmparr);
2334 | if (count($tmparr) != count(array_unique($tmparr))) {
2335 | $errors[$id] = array('id'=>$id, 'name'=>$rec['name'], 'parent'=>$rec['parent'], 'msg'=>'Duplicate entry in folder list ('.$rec['folderList'].'');
2336 | }
2337 | }
2338 | }
2339 |
2340 | return $errors;
2341 | } /* }}} */
2342 |
2343 | /**
2344 | * Return a user by its id
2345 | *
2346 | * This method retrieves a user from the database by its id.
2347 | *
2348 | * @param integer $id internal id of user
2349 | * @return SeedDMS_Core_User|boolean instance of {@see SeedDMS_Core_User} or false
2350 | */
2351 | public function getUser($id) { /* {{{ */
2352 | if ($this->usecache && isset($this->cache['users'][$id])) {
2353 | return $this->cache['users'][$id];
2354 | }
2355 | $classname = $this->classnames['user'];
2356 | $user = $classname::getInstance($id, $this);
2357 | if ($this->usecache)
2358 | $this->cache['users'][$id] = $user;
2359 | return $user;
2360 | } /* }}} */
2361 |
2362 | /**
2363 | * Return a user by its login
2364 | *
2365 | * This method retrieves a user from the database by its login.
2366 | * If the second optional parameter $email is not empty, the user must
2367 | * also have the given email.
2368 | *
2369 | * @param string $login internal login of user
2370 | * @param string $email email of user
2371 | * @return object instance of {@see SeedDMS_Core_User} or false
2372 | */
2373 | public function getUserByLogin($login, $email = '') { /* {{{ */
2374 | $classname = $this->classnames['user'];
2375 | return $classname::getInstance($login, $this, 'name', $email);
2376 | } /* }}} */
2377 |
2378 | /**
2379 | * Return a user by its email
2380 | *
2381 | * This method retrieves a user from the database by its email.
2382 | * It is needed when the user requests a new password.
2383 | *
2384 | * @param integer $email email address of user
2385 | * @return object instance of {@see SeedDMS_Core_User} or false in case of an error
2386 | */
2387 | public function getUserByEmail($email) { /* {{{ */
2388 | $classname = $this->classnames['user'];
2389 | return $classname::getInstance($email, $this, 'email');
2390 | } /* }}} */
2391 |
2392 | /**
2393 | * Return list of all users
2394 | *
2395 | * @param string $orderby
2396 | * @return array list of instances of {@see SeedDMS_Core_User} or false in case of an error
2397 | */
2398 | public function getAllUsers($orderby = '') { /* {{{ */
2399 | $classname = $this->classnames['user'];
2400 | return $classname::getAllInstances($orderby, $this);
2401 | } /* }}} */
2402 |
2403 | /**
2404 | * Add a new user
2405 | *
2406 | * This method calls the hook `onPostAddUser` after the user has been
2407 | * added successfully.
2408 | *
2409 | * @param string $login login name
2410 | * @param string $pwd hashed password of new user
2411 | * @param string $fullName full name of user
2412 | * @param string $email Email of new user
2413 | * @param string $language language of new user
2414 | * @param string $theme theme
2415 | * @param string $comment comment of new user
2416 | * @param int|string $role role of new user (can be 0=normal, 1=admin, 2=guest)
2417 | * @param integer $isHidden hide user in all lists, if this is set login
2418 | * is still allowed
2419 | * @param integer $isDisabled disable user and prevent login
2420 | * @param string $pwdexpiration
2421 | * @param int $quota
2422 | * @param null $homefolder
2423 | * @return bool|SeedDMS_Core_User or false if the user already exists or in case of an error
2424 | */
2425 | public function addUser($login, $pwd, $fullName, $email, $language, $theme, $comment, $role = '0', $isHidden = 0, $isDisabled = 0, $pwdexpiration = '', $quota = 0, $homefolder = null) { /* {{{ */
2426 | $db = $this->db;
2427 | if (is_object($this->getUserByLogin($login))) {
2428 | return false;
2429 | }
2430 | if ($role == '')
2431 | $role = '0';
2432 | if (trim($pwdexpiration) == '' || trim($pwdexpiration) == 'never') {
2433 | $pwdexpiration = 'NULL';
2434 | } elseif (trim($pwdexpiration) == 'now') {
2435 | $pwdexpiration = $db->qstr(date('Y-m-d H:i:s'));
2436 | } else {
2437 | $pwdexpiration = $db->qstr($pwdexpiration);
2438 | }
2439 | $queryStr = "INSERT INTO `tblUsers` (`login`, `pwd`, `fullName`, `email`, `language`, `theme`, `comment`, `role`, `hidden`, `disabled`, `pwdExpiration`, `quota`, `homefolder`) VALUES (".$db->qstr($login).", ".$db->qstr($pwd).", ".$db->qstr($fullName).", ".$db->qstr($email).", '".$language."', '".$theme."', ".$db->qstr($comment).", '".intval($role)."', '".intval($isHidden)."', '".intval($isDisabled)."', ".$pwdexpiration.", '".intval($quota)."', ".($homefolder ? intval($homefolder) : "NULL").")";
2440 | $res = $this->db->getResult($queryStr);
2441 | if (!$res)
2442 | return false;
2443 |
2444 | $user = $this->getUser($this->db->getInsertID('tblUsers'));
2445 |
2446 | /* Check if 'onPostAddUser' callback is set */
2447 | if (isset($this->callbacks['onPostAddUser'])) {
2448 | foreach ($this->callbacks['onPostAddUser'] as $callback) {
2449 | /** @noinspection PhpStatementHasEmptyBodyInspection */
2450 | if (!call_user_func($callback[0], $callback[1], $user)) {
2451 | }
2452 | }
2453 | }
2454 |
2455 | return $user;
2456 | } /* }}} */
2457 |
2458 | /**
2459 | * Get a group by its id
2460 | *
2461 | * @param integer $id id of group
2462 | * @return SeedDMS_Core_Group|boolean group or false if no group was found
2463 | */
2464 | public function getGroup($id) { /* {{{ */
2465 | if ($this->usecache && isset($this->cache['groups'][$id])) {
2466 | return $this->cache['groups'][$id];
2467 | }
2468 | $classname = $this->classnames['group'];
2469 | $group = $classname::getInstance($id, $this, '');
2470 | if ($this->usecache)
2471 | $this->cache['groups'][$id] = $group;
2472 | return $group;
2473 | } /* }}} */
2474 |
2475 | /**
2476 | * Get a group by its name
2477 | *
2478 | * @param string $name name of group
2479 | * @return SeedDMS_Core_Group|boolean group or false if no group was found
2480 | */
2481 | public function getGroupByName($name) { /* {{{ */
2482 | $name = trim($name);
2483 | $classname = $this->classnames['group'];
2484 | return $classname::getInstance($name, $this, 'name');
2485 | } /* }}} */
2486 |
2487 | /**
2488 | * Get a list of all groups
2489 | *
2490 | * @return SeedDMS_Core_Group[] array of instances of {@see SeedDMS_Core_Group}
2491 | */
2492 | public function getAllGroups() { /* {{{ */
2493 | $classname = $this->classnames['group'];
2494 | return $classname::getAllInstances('name', $this);
2495 | } /* }}} */
2496 |
2497 | /**
2498 | * Create a new user group
2499 | *
2500 | * @param string $name name of group
2501 | * @param string $comment comment of group
2502 | * @return SeedDMS_Core_Group|boolean instance of {@see SeedDMS_Core_Group} or false in
2503 | * case of an error.
2504 | */
2505 | public function addGroup($name, $comment) { /* {{{ */
2506 | $name = trim($name);
2507 | if (is_object($this->getGroupByName($name))) {
2508 | return false;
2509 | }
2510 |
2511 | $queryStr = "INSERT INTO `tblGroups` (`name`, `comment`) VALUES (".$this->db->qstr($name).", ".$this->db->qstr($comment).")";
2512 | if (!$this->db->getResult($queryStr))
2513 | return false;
2514 |
2515 | $group = $this->getGroup($this->db->getInsertID('tblGroups'));
2516 |
2517 | /* Check if 'onPostAddGroup' callback is set */
2518 | if (isset($this->callbacks['onPostAddGroup'])) {
2519 | foreach ($this->callbacks['onPostAddGroup'] as $callback) {
2520 | /** @noinspection PhpStatementHasEmptyBodyInspection */
2521 | if (!call_user_func($callback[0], $callback[1], $group)) {
2522 | }
2523 | }
2524 | }
2525 |
2526 | return $group;
2527 | } /* }}} */
2528 |
2529 | public function getKeywordCategory($id) { /* {{{ */
2530 | if (!is_numeric($id) || $id < 1)
2531 | return false;
2532 |
2533 | $queryStr = "SELECT * FROM `tblKeywordCategories` WHERE `id` = " . (int) $id;
2534 | $resArr = $this->db->getResultArray($queryStr);
2535 | if (is_bool($resArr) && !$resArr)
2536 | return false;
2537 | if (count($resArr) != 1)
2538 | return null;
2539 |
2540 | $resArr = $resArr[0];
2541 | $cat = new SeedDMS_Core_KeywordCategory($resArr["id"], $resArr["owner"], $resArr["name"]);
2542 | $cat->setDMS($this);
2543 | return $cat;
2544 | } /* }}} */
2545 |
2546 | public function getKeywordCategoryByName($name, $userID) { /* {{{ */
2547 | if (!is_numeric($userID) || $userID < 1)
2548 | return false;
2549 | $name = trim($name);
2550 | $queryStr = "SELECT * FROM `tblKeywordCategories` WHERE `name` = " . $this->db->qstr($name) . " AND `owner` = " . (int) $userID;
2551 | $resArr = $this->db->getResultArray($queryStr);
2552 | if (is_bool($resArr) && !$resArr)
2553 | return false;
2554 | if (count($resArr) != 1)
2555 | return null;
2556 |
2557 | $resArr = $resArr[0];
2558 | $cat = new SeedDMS_Core_KeywordCategory($resArr["id"], $resArr["owner"], $resArr["name"]);
2559 | $cat->setDMS($this);
2560 | return $cat;
2561 | } /* }}} */
2562 |
2563 | public function getAllKeywordCategories($userIDs = array()) { /* {{{ */
2564 | $queryStr = "SELECT * FROM `tblKeywordCategories`";
2565 | /* Ensure $userIDs() will only contain integers > 0 */
2566 | $userIDs = array_filter(array_unique(array_map('intval', $userIDs)), function($a) {return $a > 0;});
2567 | if ($userIDs) {
2568 | $queryStr .= " WHERE `owner` IN (".implode(',', $userIDs).")";
2569 | }
2570 |
2571 | $resArr = $this->db->getResultArray($queryStr);
2572 | if (is_bool($resArr) && !$resArr)
2573 | return false;
2574 |
2575 | $categories = array();
2576 | foreach ($resArr as $row) {
2577 | $cat = new SeedDMS_Core_KeywordCategory($row["id"], $row["owner"], $row["name"]);
2578 | $cat->setDMS($this);
2579 | array_push($categories, $cat);
2580 | }
2581 |
2582 | return $categories;
2583 | } /* }}} */
2584 |
2585 | /**
2586 | * This method should be replaced by getAllKeywordCategories()
2587 | *
2588 | * @param $userID
2589 | * @return SeedDMS_Core_KeywordCategory[]|bool
2590 | */
2591 | public function getAllUserKeywordCategories($userID) { /* {{{ */
2592 | if (!is_numeric($userID) || $userID < 1)
2593 | return false;
2594 | return self::getAllKeywordCategories([$userID]);
2595 | } /* }}} */
2596 |
2597 | public function addKeywordCategory($userID, $name) { /* {{{ */
2598 | if (!is_numeric($userID) || $userID < 1)
2599 | return false;
2600 | $name = trim($name);
2601 | if (!$name)
2602 | return false;
2603 | if (is_object($this->getKeywordCategoryByName($name, $userID))) {
2604 | return false;
2605 | }
2606 | $queryStr = "INSERT INTO `tblKeywordCategories` (`owner`, `name`) VALUES (".(int) $userID.", ".$this->db->qstr($name).")";
2607 | if (!$this->db->getResult($queryStr))
2608 | return false;
2609 |
2610 | $category = $this->getKeywordCategory($this->db->getInsertID('tblKeywordCategories'));
2611 |
2612 | /* Check if 'onPostAddKeywordCategory' callback is set */
2613 | if (isset($this->callbacks['onPostAddKeywordCategory'])) {
2614 | foreach ($this->callbacks['onPostAddKeywordCategory'] as $callback) {
2615 | /** @noinspection PhpStatementHasEmptyBodyInspection */
2616 | if (!call_user_func($callback[0], $callback[1], $category)) {
2617 | }
2618 | }
2619 | }
2620 |
2621 | return $category;
2622 | } /* }}} */
2623 |
2624 | public function getDocumentCategory($id) { /* {{{ */
2625 | if (!is_numeric($id) || $id < 1)
2626 | return false;
2627 |
2628 | $queryStr = "SELECT * FROM `tblCategory` WHERE `id` = " . (int) $id;
2629 | $resArr = $this->db->getResultArray($queryStr);
2630 | if (is_bool($resArr) && !$resArr)
2631 | return false;
2632 | if (count($resArr) != 1)
2633 | return null;
2634 |
2635 | $resArr = $resArr[0];
2636 | $cat = new SeedDMS_Core_DocumentCategory($resArr["id"], $resArr["name"]);
2637 | $cat->setDMS($this);
2638 | return $cat;
2639 | } /* }}} */
2640 |
2641 | public function getDocumentCategories() { /* {{{ */
2642 | $queryStr = "SELECT * FROM `tblCategory` order by `name`";
2643 |
2644 | $resArr = $this->db->getResultArray($queryStr);
2645 | if (is_bool($resArr) && !$resArr)
2646 | return false;
2647 |
2648 | $categories = array();
2649 | foreach ($resArr as $row) {
2650 | $cat = new SeedDMS_Core_DocumentCategory($row["id"], $row["name"]);
2651 | $cat->setDMS($this);
2652 | array_push($categories, $cat);
2653 | }
2654 |
2655 | return $categories;
2656 | } /* }}} */
2657 |
2658 | /**
2659 | * Get a category by its name
2660 | *
2661 | * The name of a category is by default unique.
2662 | *
2663 | * @param string $name human readable name of category
2664 | * @return SeedDMS_Core_DocumentCategory|boolean instance of {@see SeedDMS_Core_DocumentCategory}
2665 | */
2666 | public function getDocumentCategoryByName($name) { /* {{{ */
2667 | $name = trim($name);
2668 | if (!$name) return false;
2669 |
2670 | $queryStr = "SELECT * FROM `tblCategory` WHERE `name`=".$this->db->qstr($name);
2671 | $resArr = $this->db->getResultArray($queryStr);
2672 | if (!$resArr)
2673 | return false;
2674 |
2675 | $row = $resArr[0];
2676 | $cat = new SeedDMS_Core_DocumentCategory($row["id"], $row["name"]);
2677 | $cat->setDMS($this);
2678 |
2679 | return $cat;
2680 | } /* }}} */
2681 |
2682 | /**
2683 | * Add a new document category
2684 | *
2685 | * This method calls the hook `onPostAddDocumentCategory` if the new
2686 | * category was added successfully.
2687 | *
2688 | * @param string $name name of category
2689 | * @return SeedDMS_Core_DocumentCategory|boolean instance of {@see SeedDMS_Core_DocumentCategory} or false if the category already exists or in case of an error.
2690 | */
2691 | public function addDocumentCategory($name) { /* {{{ */
2692 | $name = trim($name);
2693 | if (!$name)
2694 | return false;
2695 | if (is_object($this->getDocumentCategoryByName($name))) {
2696 | return false;
2697 | }
2698 | $queryStr = "INSERT INTO `tblCategory` (`name`) VALUES (".$this->db->qstr($name).")";
2699 | if (!$this->db->getResult($queryStr))
2700 | return false;
2701 |
2702 | $category = $this->getDocumentCategory($this->db->getInsertID('tblCategory'));
2703 |
2704 | /* Check if 'onPostAddDocumentCategory' callback is set */
2705 | if (isset($this->callbacks['onPostAddDocumentCategory'])) {
2706 | foreach ($this->callbacks['onPostAddDocumentCategory'] as $callback) {
2707 | /** @noinspection PhpStatementHasEmptyBodyInspection */
2708 | if (!call_user_func($callback[0], $callback[1], $category)) {
2709 | }
2710 | }
2711 | }
2712 |
2713 | return $category;
2714 | } /* }}} */
2715 |
2716 | /**
2717 | * Get all notifications for a group
2718 | *
2719 | * deprecated: User {@see SeedDMS_Core_Group::getNotifications()}
2720 | *
2721 | * @param object $group group for which notifications are to be retrieved
2722 | * @param integer $type type of item (T_DOCUMENT or T_FOLDER)
2723 | * @return array array of notifications
2724 | */
2725 | public function getNotificationsByGroup($group, $type = 0) { /* {{{ */
2726 | return $group->getNotifications($type);
2727 | } /* }}} */
2728 |
2729 | /**
2730 | * Get all notifications for a user
2731 | *
2732 | * deprecated: User {@see SeedDMS_Core_User::getNotifications()}
2733 | *
2734 | * @param object $user user for which notifications are to be retrieved
2735 | * @param integer $type type of item (T_DOCUMENT or T_FOLDER)
2736 | * @return array array of notifications
2737 | */
2738 | public function getNotificationsByUser($user, $type = 0) { /* {{{ */
2739 | return $user->getNotifications($type);
2740 | } /* }}} */
2741 |
2742 | /**
2743 | * Create a token to request a new password.
2744 | *
2745 | * This method will not delete the password but just creates an entry
2746 | * in `tblUserRequestPassword` indicating a password request.
2747 | *
2748 | * @param SeedDMS_Core_User $user
2749 | * @return string|boolean hash value of false in case of an error
2750 | */
2751 | public function createPasswordRequest($user) { /* {{{ */
2752 | $lenght = 32;
2753 | if (function_exists("random_bytes")) {
2754 | $bytes = random_bytes((int) ceil($lenght / 2));
2755 | } elseif (function_exists("openssl_random_pseudo_bytes")) {
2756 | $bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
2757 | } else {
2758 | return false;
2759 | }
2760 | $hash = bin2hex($bytes);
2761 | $queryStr = "INSERT INTO `tblUserPasswordRequest` (`userID`, `hash`, `date`) VALUES (" . $user->getId() . ", " . $this->db->qstr($hash) .", ".$this->db->getCurrentDatetime().")";
2762 | $resArr = $this->db->getResult($queryStr);
2763 | if (is_bool($resArr) && !$resArr) return false;
2764 | return $hash;
2765 | } /* }}} */
2766 |
2767 | /**
2768 | * Check if hash for a password request is valid.
2769 | *
2770 | * This method searches a previously created password request and
2771 | * returns the user.
2772 | *
2773 | * @param string $hash
2774 | * @return bool|SeedDMS_Core_User
2775 | */
2776 | public function checkPasswordRequest($hash) { /* {{{ */
2777 | /* Get the password request from the database */
2778 | $queryStr = "SELECT * FROM `tblUserPasswordRequest` WHERE `hash`=".$this->db->qstr($hash);
2779 | $resArr = $this->db->getResultArray($queryStr);
2780 | if (is_bool($resArr) && !$resArr)
2781 | return false;
2782 |
2783 | if (count($resArr) != 1)
2784 | return false;
2785 | $resArr = $resArr[0];
2786 |
2787 | return $this->getUser($resArr['userID']);
2788 |
2789 | } /* }}} */
2790 |
2791 | /**
2792 | * Delete a password request
2793 | *
2794 | * @param string $hash
2795 | * @return bool
2796 | */
2797 | public function deletePasswordRequest($hash) { /* {{{ */
2798 | /* Delete the request, so nobody can use it a second time */
2799 | $queryStr = "DELETE FROM `tblUserPasswordRequest` WHERE `hash`=".$this->db->qstr($hash);
2800 | if (!$this->db->getResult($queryStr))
2801 | return false;
2802 | return true;
2803 | } /* }}} */
2804 |
2805 | /**
2806 | * Return a attribute definition by its id
2807 | *
2808 | * This method retrieves a attribute definitionr from the database by
2809 | * its id.
2810 | *
2811 | * @param integer $id internal id of attribute defintion
2812 | * @return bool|SeedDMS_Core_AttributeDefinition or false
2813 | */
2814 | public function getAttributeDefinition($id) { /* {{{ */
2815 | if (!is_numeric($id) || $id < 1)
2816 | return false;
2817 |
2818 | $queryStr = "SELECT * FROM `tblAttributeDefinitions` WHERE `id` = " . (int) $id;
2819 | $resArr = $this->db->getResultArray($queryStr);
2820 |
2821 | if (is_bool($resArr) && $resArr == false)
2822 | return false;
2823 | if (count($resArr) != 1)
2824 | return null;
2825 |
2826 | $resArr = $resArr[0];
2827 |
2828 | $attrdef = new SeedDMS_Core_AttributeDefinition($resArr["id"], $resArr["name"], (int) $resArr["objtype"], (int) $resArr["type"], $resArr["multiple"], $resArr["minvalues"], $resArr["maxvalues"], $resArr["valueset"], $resArr["regex"]);
2829 | $attrdef->setDMS($this);
2830 | return $attrdef;
2831 | } /* }}} */
2832 |
2833 | /**
2834 | * Return a attribute definition by its name
2835 | *
2836 | * This method retrieves an attribute def. from the database by its name.
2837 | *
2838 | * @param string $name internal name of attribute def.
2839 | * @return SeedDMS_Core_AttributeDefinition|boolean instance of {@see SeedDMS_Core_AttributeDefinition} or false
2840 | */
2841 | public function getAttributeDefinitionByName($name) { /* {{{ */
2842 | $name = trim($name);
2843 | if (!$name) return false;
2844 |
2845 | $queryStr = "SELECT * FROM `tblAttributeDefinitions` WHERE `name` = " . $this->db->qstr($name);
2846 | $resArr = $this->db->getResultArray($queryStr);
2847 |
2848 | if (is_bool($resArr) && $resArr == false)
2849 | return false;
2850 | if (count($resArr) != 1)
2851 | return null;
2852 |
2853 | $resArr = $resArr[0];
2854 |
2855 | $attrdef = new SeedDMS_Core_AttributeDefinition($resArr["id"], $resArr["name"], (int) $resArr["objtype"], (int) $resArr["type"], $resArr["multiple"], $resArr["minvalues"], $resArr["maxvalues"], $resArr["valueset"], $resArr["regex"]);
2856 | $attrdef->setDMS($this);
2857 | return $attrdef;
2858 | } /* }}} */
2859 |
2860 | /**
2861 | * Return list of all attribute definitions
2862 | *
2863 | * @param integer|array $objtype select those attribute definitions defined for an object type
2864 | * @param integer|array $type select those attribute definitions defined for a type
2865 | * @return bool|SeedDMS_Core_AttributeDefinition[] of instances of {@see SeedDMS_Core_AttributeDefinition} or false
2866 | * or false
2867 | */
2868 | public function getAllAttributeDefinitions($objtype = 0, $type = 0) { /* {{{ */
2869 | $queryStr = "SELECT * FROM `tblAttributeDefinitions`";
2870 | if ($objtype || $type) {
2871 | $queryStr .= ' WHERE ';
2872 | if ($objtype) {
2873 | if (is_array($objtype))
2874 | $queryStr .= '`objtype` in (\''.implode("','", $objtype).'\')';
2875 | else
2876 | $queryStr .= '`objtype`='.intval($objtype);
2877 | }
2878 | if ($objtype && $type) {
2879 | $queryStr .= ' AND ';
2880 | }
2881 | if ($type) {
2882 | if (is_array($type))
2883 | $queryStr .= '`type` in (\''.implode("','", $type).'\')';
2884 | else
2885 | $queryStr .= '`type`='.intval($type);
2886 | }
2887 | }
2888 | $queryStr .= ' ORDER BY `name`';
2889 | $resArr = $this->db->getResultArray($queryStr);
2890 |
2891 | if (is_bool($resArr) && $resArr == false)
2892 | return false;
2893 |
2894 | /** @var SeedDMS_Core_AttributeDefinition[] $attrdefs */
2895 | $attrdefs = array();
2896 |
2897 | for ($i = 0; $i < count($resArr); $i++) {
2898 | $attrdef = new SeedDMS_Core_AttributeDefinition($resArr[$i]["id"], $resArr[$i]["name"], (int) $resArr[$i]["objtype"], (int) $resArr[$i]["type"], $resArr[$i]["multiple"], $resArr[$i]["minvalues"], $resArr[$i]["maxvalues"], $resArr[$i]["valueset"], $resArr[$i]["regex"]);
2899 | $attrdef->setDMS($this);
2900 | $attrdefs[$i] = $attrdef;
2901 | }
2902 |
2903 | return $attrdefs;
2904 | } /* }}} */
2905 |
2906 | /**
2907 | * Add a new attribute definition
2908 | *
2909 | * @param string $name name of attribute
2910 | * @param $objtype
2911 | * @param string $type type of attribute
2912 | * @param bool|int $multiple set to 1 if attribute has multiple attributes
2913 | * @param integer $minvalues minimum number of values
2914 | * @param integer $maxvalues maximum number of values if multiple is set
2915 | * @param string $valueset list of allowed values (csv format)
2916 | * @param string $regex
2917 | * @return bool|SeedDMS_Core_User
2918 | */
2919 | public function addAttributeDefinition($name, $objtype, $type, $multiple = 0, $minvalues = 0, $maxvalues = 1, $valueset = '', $regex = '') { /* {{{ */
2920 | $name = trim($name);
2921 | if (!$name)
2922 | return false;
2923 | if (is_object($this->getAttributeDefinitionByName($name))) {
2924 | return false;
2925 | }
2926 | if ($objtype < SeedDMS_Core_AttributeDefinition::objtype_all || $objtype > SeedDMS_Core_AttributeDefinition::objtype_documentcontent)
2927 | return false;
2928 | if (!$type)
2929 | return false;
2930 | if (trim($valueset)) {
2931 | $valuesetarr = array_map('trim', explode($valueset[0], substr($valueset, 1)));
2932 | $valueset = $valueset[0].implode($valueset[0], $valuesetarr);
2933 | } else {
2934 | $valueset = '';
2935 | }
2936 | $queryStr = "INSERT INTO `tblAttributeDefinitions` (`name`, `objtype`, `type`, `multiple`, `minvalues`, `maxvalues`, `valueset`, `regex`) VALUES (".$this->db->qstr($name).", ".intval($objtype).", ".intval($type).", ".intval($multiple).", ".intval($minvalues).", ".intval($maxvalues).", ".$this->db->qstr($valueset).", ".$this->db->qstr($regex).")";
2937 | $res = $this->db->getResult($queryStr);
2938 | if (!$res)
2939 | return false;
2940 |
2941 | return $this->getAttributeDefinition($this->db->getInsertID('tblAttributeDefinitions'));
2942 | } /* }}} */
2943 |
2944 | /**
2945 | * Return list of all workflows
2946 | *
2947 | * @return SeedDMS_Core_Workflow[]|bool of instances of {@see SeedDMS_Core_Workflow} or false
2948 | */
2949 | public function getAllWorkflows() { /* {{{ */
2950 | $queryStr = "SELECT * FROM `tblWorkflows` ORDER BY `name`";
2951 | $resArr = $this->db->getResultArray($queryStr);
2952 |
2953 | if (is_bool($resArr) && $resArr == false)
2954 | return false;
2955 |
2956 | $queryStr = "SELECT * FROM `tblWorkflowStates` ORDER BY `name`";
2957 | $ressArr = $this->db->getResultArray($queryStr);
2958 |
2959 | if (is_bool($ressArr) && $ressArr == false)
2960 | return false;
2961 |
2962 | for ($i = 0; $i < count($ressArr); $i++) {
2963 | $wkfstates[$ressArr[$i]["id"]] = new SeedDMS_Core_Workflow_State($ressArr[$i]["id"], $ressArr[$i]["name"], $ressArr[$i]["maxtime"], $ressArr[$i]["precondfunc"], $ressArr[$i]["documentstatus"]);
2964 | }
2965 |
2966 | /** @var SeedDMS_Core_Workflow[] $workflows */
2967 | $workflows = array();
2968 | for ($i = 0; $i < count($resArr); $i++) {
2969 | /** @noinspection PhpUndefinedVariableInspection */
2970 | $workflow = new SeedDMS_Core_Workflow($resArr[$i]["id"], $resArr[$i]["name"], $wkfstates[$resArr[$i]["initstate"]]);
2971 | $workflow->setDMS($this);
2972 | $workflows[$i] = $workflow;
2973 | }
2974 |
2975 | return $workflows;
2976 | } /* }}} */
2977 |
2978 | /**
2979 | * Return workflow by its Id
2980 | *
2981 | * @param integer $id internal id of workflow
2982 | * @return SeedDMS_Core_Workflow|bool of instances of {@see SeedDMS_Core_Workflow}, null if no workflow was found or false
2983 | */
2984 | public function getWorkflow($id) { /* {{{ */
2985 | if (!is_numeric($id) || $id < 1)
2986 | return false;
2987 |
2988 | $queryStr = "SELECT * FROM `tblWorkflows` WHERE `id`=".intval($id);
2989 | $resArr = $this->db->getResultArray($queryStr);
2990 |
2991 | if (is_bool($resArr) && $resArr == false)
2992 | return false;
2993 |
2994 | if (!$resArr)
2995 | return null;
2996 |
2997 | $initstate = $this->getWorkflowState($resArr[0]['initstate']);
2998 |
2999 | $workflow = new SeedDMS_Core_Workflow($resArr[0]["id"], $resArr[0]["name"], $initstate);
3000 | $workflow->setDMS($this);
3001 |
3002 | return $workflow;
3003 | } /* }}} */
3004 |
3005 | /**
3006 | * Return workflow by its name
3007 | *
3008 | * @param string $name name of workflow
3009 | * @return SeedDMS_Core_Workflow|bool of instances of {@see SeedDMS_Core_Workflow} or null if no workflow was found or false
3010 | */
3011 | public function getWorkflowByName($name) { /* {{{ */
3012 | $name = trim($name);
3013 | if (!$name) return false;
3014 |
3015 | $queryStr = "SELECT * FROM `tblWorkflows` WHERE `name`=".$this->db->qstr($name);
3016 | $resArr = $this->db->getResultArray($queryStr);
3017 |
3018 | if (is_bool($resArr) && $resArr == false)
3019 | return false;
3020 |
3021 | if (!$resArr)
3022 | return null;
3023 |
3024 | $initstate = $this->getWorkflowState($resArr[0]['initstate']);
3025 |
3026 | $workflow = new SeedDMS_Core_Workflow($resArr[0]["id"], $resArr[0]["name"], $initstate);
3027 | $workflow->setDMS($this);
3028 |
3029 | return $workflow;
3030 | } /* }}} */
3031 |
3032 | /**
3033 | * Add a new workflow
3034 | *
3035 | * @param string $name name of workflow
3036 | * @param SeedDMS_Core_Workflow_State $initstate initial state of workflow
3037 | * @return bool|SeedDMS_Core_Workflow
3038 | */
3039 | public function addWorkflow($name, $initstate) { /* {{{ */
3040 | $db = $this->db;
3041 | $name = trim($name);
3042 | if (!$name)
3043 | return false;
3044 | if (is_object($this->getWorkflowByName($name))) {
3045 | return false;
3046 | }
3047 | $queryStr = "INSERT INTO `tblWorkflows` (`name`, `initstate`) VALUES (".$db->qstr($name).", ".$initstate->getID().")";
3048 | $res = $db->getResult($queryStr);
3049 | if (!$res)
3050 | return false;
3051 |
3052 | return $this->getWorkflow($db->getInsertID('tblWorkflows'));
3053 | } /* }}} */
3054 |
3055 | /**
3056 | * Return a workflow state by its id
3057 | *
3058 | * This method retrieves a workflow state from the database by its id.
3059 | *
3060 | * @param integer $id internal id of workflow state
3061 | * @return bool|SeedDMS_Core_Workflow_State or false
3062 | */
3063 | public function getWorkflowState($id) { /* {{{ */
3064 | if (!is_numeric($id) || $id < 1)
3065 | return false;
3066 |
3067 | $queryStr = "SELECT * FROM `tblWorkflowStates` WHERE `id` = " . (int) $id;
3068 | $resArr = $this->db->getResultArray($queryStr);
3069 |
3070 | if (is_bool($resArr) && $resArr == false)
3071 | return false;
3072 |
3073 | if (count($resArr) != 1)
3074 | return null;
3075 |
3076 | $resArr = $resArr[0];
3077 |
3078 | $state = new SeedDMS_Core_Workflow_State($resArr["id"], $resArr["name"], $resArr["maxtime"], $resArr["precondfunc"], $resArr["documentstatus"]);
3079 | $state->setDMS($this);
3080 | return $state;
3081 | } /* }}} */
3082 |
3083 | /**
3084 | * Return workflow state by its name
3085 | *
3086 | * @param string $name name of workflow state
3087 | * @return bool|SeedDMS_Core_Workflow_State or false
3088 | */
3089 | public function getWorkflowStateByName($name) { /* {{{ */
3090 | $name = trim($name);
3091 | if (!$name) return false;
3092 |
3093 | $queryStr = "SELECT * FROM `tblWorkflowStates` WHERE `name`=".$this->db->qstr($name);
3094 | $resArr = $this->db->getResultArray($queryStr);
3095 |
3096 | if (is_bool($resArr) && $resArr == false)
3097 | return false;
3098 |
3099 | if (!$resArr)
3100 | return null;
3101 |
3102 | $resArr = $resArr[0];
3103 |
3104 | $state = new SeedDMS_Core_Workflow_State($resArr["id"], $resArr["name"], $resArr["maxtime"], $resArr["precondfunc"], $resArr["documentstatus"]);
3105 | $state->setDMS($this);
3106 |
3107 | return $state;
3108 | } /* }}} */
3109 |
3110 | /**
3111 | * Return list of all workflow states
3112 | *
3113 | * @return SeedDMS_Core_Workflow_State[]|bool of instances of {@see SeedDMS_Core_Workflow_State} or false
3114 | */
3115 | public function getAllWorkflowStates() { /* {{{ */
3116 | $queryStr = "SELECT * FROM `tblWorkflowStates` ORDER BY `name`";
3117 | $ressArr = $this->db->getResultArray($queryStr);
3118 |
3119 | if (is_bool($ressArr) && $ressArr == false)
3120 | return false;
3121 |
3122 | $wkfstates = array();
3123 | for ($i = 0; $i < count($ressArr); $i++) {
3124 | $wkfstate = new SeedDMS_Core_Workflow_State($ressArr[$i]["id"], $ressArr[$i]["name"], $ressArr[$i]["maxtime"], $ressArr[$i]["precondfunc"], $ressArr[$i]["documentstatus"]);
3125 | $wkfstate->setDMS($this);
3126 | $wkfstates[$i] = $wkfstate;
3127 | }
3128 |
3129 | return $wkfstates;
3130 | } /* }}} */
3131 |
3132 | /**
3133 | * Add new workflow state
3134 | *
3135 | * @param string $name name of workflow state
3136 | * @param integer $docstatus document status when this state is reached
3137 | * @return bool|SeedDMS_Core_Workflow_State
3138 | */
3139 | public function addWorkflowState($name, $docstatus) { /* {{{ */
3140 | $db = $this->db;
3141 | $name = trim($name);
3142 | if (!$name)
3143 | return false;
3144 | if (is_object($this->getWorkflowStateByName($name))) {
3145 | return false;
3146 | }
3147 | $queryStr = "INSERT INTO `tblWorkflowStates` (`name`, `documentstatus`) VALUES (".$db->qstr($name).", ".(int) $docstatus.")";
3148 | $res = $db->getResult($queryStr);
3149 | if (!$res)
3150 | return false;
3151 |
3152 | return $this->getWorkflowState($db->getInsertID('tblWorkflowStates'));
3153 | } /* }}} */
3154 |
3155 | /**
3156 | * Return a workflow action by its id
3157 | *
3158 | * This method retrieves a workflow action from the database by its id.
3159 | *
3160 | * @param integer $id internal id of workflow action
3161 | * @return SeedDMS_Core_Workflow_Action|bool instance of {@see SeedDMS_Core_Workflow_Action} or false
3162 | */
3163 | public function getWorkflowAction($id) { /* {{{ */
3164 | if (!is_numeric($id) || $id < 1)
3165 | return false;
3166 |
3167 | $queryStr = "SELECT * FROM `tblWorkflowActions` WHERE `id` = " . (int) $id;
3168 | $resArr = $this->db->getResultArray($queryStr);
3169 |
3170 | if (is_bool($resArr) && $resArr == false)
3171 | return false;
3172 |
3173 | if (count($resArr) != 1)
3174 | return null;
3175 |
3176 | $resArr = $resArr[0];
3177 |
3178 | $action = new SeedDMS_Core_Workflow_Action($resArr["id"], $resArr["name"]);
3179 | $action->setDMS($this);
3180 | return $action;
3181 | } /* }}} */
3182 |
3183 | /**
3184 | * Return a workflow action by its name
3185 | *
3186 | * This method retrieves a workflow action from the database by its name.
3187 | *
3188 | * @param string $name name of workflow action
3189 | * @return SeedDMS_Core_Workflow_Action|bool instance of {@see SeedDMS_Core_Workflow_Action} or false
3190 | */
3191 | public function getWorkflowActionByName($name) { /* {{{ */
3192 | $name = trim($name);
3193 | if (!$name) return false;
3194 |
3195 | $queryStr = "SELECT * FROM `tblWorkflowActions` WHERE `name` = " . $this->db->qstr($name);
3196 | $resArr = $this->db->getResultArray($queryStr);
3197 |
3198 | if (is_bool($resArr) && $resArr == false)
3199 | return false;
3200 |
3201 | if (count($resArr) != 1)
3202 | return null;
3203 |
3204 | $resArr = $resArr[0];
3205 |
3206 | $action = new SeedDMS_Core_Workflow_Action($resArr["id"], $resArr["name"]);
3207 | $action->setDMS($this);
3208 | return $action;
3209 | } /* }}} */
3210 |
3211 | /**
3212 | * Return list of workflow action
3213 | *
3214 | * @return SeedDMS_Core_Workflow_Action[]|bool list of instances of {@see SeedDMS_Core_Workflow_Action} or false
3215 | */
3216 | public function getAllWorkflowActions() { /* {{{ */
3217 | $queryStr = "SELECT * FROM `tblWorkflowActions`";
3218 | $resArr = $this->db->getResultArray($queryStr);
3219 |
3220 | if (is_bool($resArr) && $resArr == false)
3221 | return false;
3222 |
3223 | /** @var SeedDMS_Core_Workflow_Action[] $wkfactions */
3224 | $wkfactions = array();
3225 | for ($i = 0; $i < count($resArr); $i++) {
3226 | $action = new SeedDMS_Core_Workflow_Action($resArr[$i]["id"], $resArr[$i]["name"]);
3227 | $action->setDMS($this);
3228 | $wkfactions[$i] = $action;
3229 | }
3230 |
3231 | return $wkfactions;
3232 | } /* }}} */
3233 |
3234 | /**
3235 | * Add new workflow action
3236 | *
3237 | * @param string $name name of workflow action
3238 | * @return SeedDMS_Core_Workflow_Action|bool
3239 | */
3240 | public function addWorkflowAction($name) { /* {{{ */
3241 | $db = $this->db;
3242 | $name = trim($name);
3243 | if (!$name)
3244 | return false;
3245 | if (is_object($this->getWorkflowActionByName($name))) {
3246 | return false;
3247 | }
3248 | $queryStr = "INSERT INTO `tblWorkflowActions` (`name`) VALUES (".$db->qstr($name).")";
3249 | $res = $db->getResult($queryStr);
3250 | if (!$res)
3251 | return false;
3252 |
3253 | return $this->getWorkflowAction($db->getInsertID('tblWorkflowActions'));
3254 | } /* }}} */
3255 |
3256 | /**
3257 | * Return a workflow transition by its id
3258 | *
3259 | * This method retrieves a workflow transition from the database by its id.
3260 | *
3261 | * @param integer $id internal id of workflow transition
3262 | * @return SeedDMS_Core_Workflow_Transition|bool instance of {@see SeedDMS_Core_Workflow_Transition} or false
3263 | */
3264 | public function getWorkflowTransition($id) { /* {{{ */
3265 | if (!is_numeric($id))
3266 | return false;
3267 |
3268 | $queryStr = "SELECT * FROM `tblWorkflowTransitions` WHERE `id` = " . (int) $id;
3269 | $resArr = $this->db->getResultArray($queryStr);
3270 |
3271 | if (is_bool($resArr) && $resArr == false) return false;
3272 | if (count($resArr) != 1) return false;
3273 |
3274 | $resArr = $resArr[0];
3275 |
3276 | $transition = new SeedDMS_Core_Workflow_Transition($resArr["id"], $this->getWorkflow($resArr["workflow"]), $this->getWorkflowState($resArr["state"]), $this->getWorkflowAction($resArr["action"]), $this->getWorkflowState($resArr["nextstate"]), $resArr["maxtime"]);
3277 | $transition->setDMS($this);
3278 | return $transition;
3279 | } /* }}} */
3280 |
3281 | /**
3282 | * Returns document content which is not linked to a document
3283 | *
3284 | * This method is for finding straying document content without
3285 | * a parent document. In normal operation this should not happen
3286 | * but little checks for database consistency and possible errors
3287 | * in the application may have left over document content though
3288 | * the document is gone already.
3289 | *
3290 | * @return array|bool
3291 | */
3292 | public function getUnlinkedDocumentContent() { /* {{{ */
3293 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `document` NOT IN (SELECT id FROM `tblDocuments`)";
3294 | $resArr = $this->db->getResultArray($queryStr);
3295 | if ($resArr === false)
3296 | return false;
3297 |
3298 | $versions = array();
3299 | foreach ($resArr as $row) {
3300 | /** @var SeedDMS_Core_Document $document */
3301 | $document = new $this->classnames['document']($row['document'], '', '', '', '', '', '', '', '', '', '', '');
3302 | $document->setDMS($this);
3303 | $version = new $this->classnames['documentcontent']($row['id'], $document, $row['version'], $row['comment'], $row['date'], $row['createdBy'], $row['dir'], $row['orgFileName'], $row['fileType'], $row['mimeType'], $row['fileSize'], $row['checksum']);
3304 | $versions[] = $version;
3305 | }
3306 | return $versions;
3307 |
3308 | } /* }}} */
3309 |
3310 | /**
3311 | * Returns document content which has no file size set
3312 | *
3313 | * This method is for finding document content without a file size
3314 | * set in the database. The file size of a document content was introduced
3315 | * in version 4.0.0 of SeedDMS for implementation of user quotas.
3316 | *
3317 | * @return SeedDMS_Core_Document[]|bool
3318 | */
3319 | public function getNoFileSizeDocumentContent() { /* {{{ */
3320 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `fileSize` = 0 OR `fileSize` is null";
3321 | $resArr = $this->db->getResultArray($queryStr);
3322 | if ($resArr === false)
3323 | return false;
3324 |
3325 | /** @var SeedDMS_Core_Document[] $versions */
3326 | $versions = array();
3327 | foreach ($resArr as $row) {
3328 | $document = $this->getDocument($row['document']);
3329 | /* getting the document can fail if it is outside the root folder
3330 | * and checkWithinRootDir is enabled.
3331 | */
3332 | if ($document) {
3333 | $version = new $this->classnames['documentcontent']($row['id'], $document, $row['version'], $row['comment'], $row['date'], $row['createdBy'], $row['dir'], $row['orgFileName'], $row['fileType'], $row['mimeType'], $row['fileSize'], $row['checksum'], $row['fileSize'], $row['checksum']);
3334 | $versions[] = $version;
3335 | }
3336 | }
3337 | return $versions;
3338 |
3339 | } /* }}} */
3340 |
3341 | /**
3342 | * Returns document content which has no checksum set
3343 | *
3344 | * This method is for finding document content without a checksum
3345 | * set in the database. The checksum of a document content was introduced
3346 | * in version 4.0.0 of SeedDMS for finding duplicates.
3347 | * @return bool|SeedDMS_Core_Document[]
3348 | */
3349 | public function getNoChecksumDocumentContent() { /* {{{ */
3350 | $queryStr = "SELECT * FROM `tblDocumentContent` WHERE `checksum` = '' OR `checksum` is null";
3351 | $resArr = $this->db->getResultArray($queryStr);
3352 | if ($resArr === false)
3353 | return false;
3354 |
3355 | /** @var SeedDMS_Core_Document[] $versions */
3356 | $versions = array();
3357 | foreach ($resArr as $row) {
3358 | $document = $this->getDocument($row['document']);
3359 | /* getting the document can fail if it is outside the root folder
3360 | * and checkWithinRootDir is enabled.
3361 | */
3362 | if ($document) {
3363 | $version = new $this->classnames['documentcontent']($row['id'], $document, $row['version'], $row['comment'], $row['date'], $row['createdBy'], $row['dir'], $row['orgFileName'], $row['fileType'], $row['mimeType'], $row['fileSize'], $row['checksum']);
3364 | $versions[] = $version;
3365 | }
3366 | }
3367 | return $versions;
3368 |
3369 | } /* }}} */
3370 |
3371 | /**
3372 | * Returns document content which is duplicated
3373 | *
3374 | * This method is for finding document content which is available twice
3375 | * in the database. The checksum of a document content was introduced
3376 | * in version 4.0.0 of SeedDMS for finding duplicates.
3377 | * @return array|bool
3378 | */
3379 | public function getDuplicateDocumentContent() { /* {{{ */
3380 | $queryStr = "SELECT a.*, b.`id` as dupid FROM `tblDocumentContent` a LEFT JOIN `tblDocumentContent` b ON a.`checksum`=b.`checksum` WHERE a.`id`!=b.`id` ORDER BY a.`id` LIMIT 1000";
3381 | $resArr = $this->db->getResultArray($queryStr);
3382 | if ($resArr === false)
3383 | return false;
3384 |
3385 | /** @var SeedDMS_Core_Document[] $versions */
3386 | $versions = array();
3387 | foreach ($resArr as $row) {
3388 | $document = $this->getDocument($row['document']);
3389 | /* getting the document can fail if it is outside the root folder
3390 | * and checkWithinRootDir is enabled.
3391 | */
3392 | if ($document) {
3393 | $version = new $this->classnames['documentcontent']($row['id'], $document, $row['version'], $row['comment'], $row['date'], $row['createdBy'], $row['dir'], $row['orgFileName'], $row['fileType'], $row['mimeType'], $row['fileSize'], $row['checksum']);
3394 | if (!isset($versions[$row['dupid']])) {
3395 | $versions[$row['id']]['content'] = $version;
3396 | $versions[$row['id']]['duplicates'] = array();
3397 | } else
3398 | $versions[$row['dupid']]['duplicates'][] = $version;
3399 | }
3400 | }
3401 | return $versions;
3402 |
3403 | } /* }}} */
3404 |
3405 | /**
3406 | * Returns folders which contain documents with none unique sequence number
3407 | *
3408 | * This method is for finding folders with documents not having a
3409 | * unique sequence number. Those documents cannot propperly be sorted
3410 | * by sequence and changing their position is impossible if more than
3411 | * two documents with the same sequence number exists, e.g.
3412 | * doc 1: 3
3413 | * doc 2: 5
3414 | * doc 3: 5
3415 | * doc 4: 5
3416 | * doc 5: 7
3417 | * If document 4 was to be moved between doc 1 and 2 it get sequence
3418 | * number 4 ((5+3)/2).
3419 | * But if document 4 was to be moved between doc 2 and 3 it will again
3420 | * have sequence number 5.
3421 | *
3422 | * @return array|bool
3423 | */
3424 | public function getDuplicateSequenceNo() { /* {{{ */
3425 | $queryStr = "SELECT DISTINCT `folder` FROM (SELECT `folder`, `sequence` FROM `tblDocuments` GROUP BY `folder`, `sequence` HAVING count(*) > 1) a";
3426 | $resArr = $this->db->getResultArray($queryStr);
3427 | if ($resArr === false)
3428 | return false;
3429 |
3430 | $folders = array();
3431 | foreach ($resArr as $row) {
3432 | $folder = $this->getFolder($row['folder']);
3433 | if ($folder)
3434 | $folders[] = $folder;
3435 | }
3436 | return $folders;
3437 |
3438 | } /* }}} */
3439 |
3440 | /**
3441 | * Returns documents which have link to themselves
3442 | *
3443 | * @return array|bool
3444 | */
3445 | public function getLinksToItself() { /* {{{ */
3446 | $queryStr = "SELECT * FROM `tblDocumentLinks` WHERE `document`=`target`";
3447 | $resArr = $this->db->getResultArray($queryStr);
3448 | if ($resArr === false)
3449 | return false;
3450 |
3451 | $documents = array();
3452 | foreach ($resArr as $row) {
3453 | $document = $this->getDocument($row['document']);
3454 | if ($document)
3455 | $documents[] = $document;
3456 | }
3457 | return $documents;
3458 |
3459 | } /* }}} */
3460 |
3461 | /**
3462 | * Returns a list of reviews, approvals, receipts, revisions which are not
3463 | * linked to a user, group anymore
3464 | *
3465 | * This method is for finding reviews or approvals whose user
3466 | * or group was deleted and not just removed from the process.
3467 | *
3468 | * @param string $process
3469 | * @param string $usergroup
3470 | * @return array
3471 | */
3472 | public function getProcessWithoutUserGroup($process, $usergroup) { /* {{{ */
3473 | switch ($process) {
3474 | case 'review':
3475 | $queryStr = "SELECT a.*, b.`name` FROM `tblDocumentReviewers`";
3476 | break;
3477 | case 'approval':
3478 | $queryStr = "SELECT a.*, b.`name` FROM `tblDocumentApprovers`";
3479 | break;
3480 | }
3481 | /** @noinspection PhpUndefinedVariableInspection */
3482 | $queryStr .= " a LEFT JOIN `tblDocuments` b ON a.`documentID`=b.`id` WHERE";
3483 | switch ($usergroup) {
3484 | case 'user':
3485 | $queryStr .= " a.`type`=0 and a.`required` not in (SELECT `id` FROM `tblUsers`) ORDER BY b.`id`";
3486 | break;
3487 | case 'group':
3488 | $queryStr .= " a.`type`=1 and a.`required` not in (SELECT `id` FROM `tblGroups`) ORDER BY b.`id`";
3489 | break;
3490 | }
3491 | return $this->db->getResultArray($queryStr);
3492 | } /* }}} */
3493 |
3494 | /**
3495 | * Removes all reviews, approvals which are not linked
3496 | * to a user, group anymore
3497 | *
3498 | * This method is for removing all reviews or approvals whose user
3499 | * or group was deleted and not just removed from the process.
3500 | * If the optional parameter $id is set, only this user/group id is removed.
3501 | * @param string $process
3502 | * @param string $usergroup
3503 | * @param int $id
3504 | * @return array
3505 | */
3506 | public function removeProcessWithoutUserGroup($process, $usergroup, $id = 0) { /* {{{ */
3507 | /* Entries of tblDocumentReviewLog or tblDocumentApproveLog are deleted
3508 | * because of CASCADE ON
3509 | */
3510 | switch ($process) {
3511 | case 'review':
3512 | $queryStr = "DELETE FROM tblDocumentReviewers";
3513 | break;
3514 | case 'approval':
3515 | $queryStr = "DELETE FROM tblDocumentApprovers";
3516 | break;
3517 | }
3518 | /** @noinspection PhpUndefinedVariableInspection */
3519 | $queryStr .= " WHERE";
3520 | switch ($usergroup) {
3521 | case 'user':
3522 | $queryStr .= " type=0 AND";
3523 | if ($id)
3524 | $queryStr .= " required=".((int) $id)." AND";
3525 | $queryStr .= " required NOT IN (SELECT id FROM tblUsers)";
3526 | break;
3527 | case 'group':
3528 | $queryStr .= " type=1 AND";
3529 | if ($id)
3530 | $queryStr .= " required=".((int) $id)." AND";
3531 | $queryStr .= " required NOT IN (SELECT id FROM tblGroups)";
3532 | break;
3533 | }
3534 | return $this->db->getResultArray($queryStr);
3535 | } /* }}} */
3536 |
3537 | /**
3538 | * Returns statitical information
3539 | *
3540 | * This method returns all kind of statistical information like
3541 | * documents or used space per user, recent activity, etc.
3542 | *
3543 | * @param string $type type of statistic
3544 | * @return array|bool returns false if the sql statement fails, returns an empty
3545 | * array if no documents or folder where found, otherwise returns a non empty
3546 | * array with statistical data
3547 | */
3548 | public function getStatisticalData($type = '') { /* {{{ */
3549 | switch ($type) {
3550 | case 'docsperuser':
3551 | $queryStr = "SELECT ".$this->db->concat(array('b.`fullName`', "' ('", 'b.`login`', "')'"))." AS `key`, count(`owner`) AS total, `b`.`id` AS res FROM `tblDocuments` a LEFT JOIN `tblUsers` b ON a.`owner`=b.`id` GROUP BY `owner`, `key`";
3552 | $resArr = $this->db->getResultArray($queryStr);
3553 | if (is_bool($resArr) && $resArr == false)
3554 | return false;
3555 |
3556 | return $resArr;
3557 | case 'foldersperuser':
3558 | $queryStr = "SELECT ".$this->db->concat(array('b.`fullName`', "' ('", 'b.`login`', "')'"))." AS `key`, count(`owner`) AS total, `b`.`id` AS res FROM `tblFolders` a LEFT JOIN `tblUsers` b ON a.`owner`=b.`id` GROUP BY `owner`, `key`";
3559 | $resArr = $this->db->getResultArray($queryStr);
3560 | if (is_bool($resArr) && $resArr == false)
3561 | return false;
3562 |
3563 | return $resArr;
3564 | case 'docspermimetype':
3565 | $queryStr = "SELECT b.`mimeType` AS `key`, count(`mimeType`) AS total FROM `tblDocuments` a LEFT JOIN `tblDocumentContent` b ON a.`id`=b.`document` GROUP BY b.`mimeType`";
3566 | $resArr = $this->db->getResultArray($queryStr);
3567 | if (is_bool($resArr) && $resArr == false)
3568 | return false;
3569 |
3570 | return $resArr;
3571 | case 'docspercategory':
3572 | $queryStr = "SELECT b.`name` AS `key`, count(a.`categoryID`) AS total, `b`.`id` AS res FROM `tblDocumentCategory` a LEFT JOIN `tblCategory` b ON a.`categoryID`=b.id GROUP BY a.`categoryID`, b.`name`";
3573 | $resArr = $this->db->getResultArray($queryStr);
3574 | if (is_bool($resArr) && $resArr == false)
3575 | return false;
3576 |
3577 | return $resArr;
3578 | case 'docsperstatus':
3579 | /** @noinspection PhpUnusedLocalVariableInspection */
3580 | $queryStr = "SELECT b.`status` AS `key`, count(b.`status`) AS total FROM (SELECT a.id, max(b.version), max(c.`statusLogID`) AS maxlog FROM `tblDocuments` a LEFT JOIN `tblDocumentStatus` b ON a.id=b.`documentID` LEFT JOIN `tblDocumentStatusLog` c ON b.`statusID`=c.`statusID` GROUP BY a.`id`, b.`version` ORDER BY a.`id`, b.`statusID`) a LEFT JOIN `tblDocumentStatusLog` b ON a.`maxlog`=b.`statusLogID` GROUP BY b.`status`";
3581 | $queryStr = "SELECT b.`status` AS `key`, count(b.`status`) AS total, b.`status` AS `res` FROM (SELECT a.`id`, max(c.`statusLogID`) AS maxlog FROM `tblDocuments` a LEFT JOIN `tblDocumentStatus` b ON a.id=b.`documentID` LEFT JOIN `tblDocumentStatusLog` c ON b.`statusID`=c.`statusID` GROUP BY a.`id` ORDER BY a.id) a LEFT JOIN `tblDocumentStatusLog` b ON a.maxlog=b.`statusLogID` GROUP BY b.`status`";
3582 | $resArr = $this->db->getResultArray($queryStr);
3583 | if (is_bool($resArr) && $resArr == false)
3584 | return false;
3585 |
3586 | return $resArr;
3587 | case 'docspermonth':
3588 | $queryStr = "SELECT *, count(`key`) AS total FROM (SELECT ".$this->db->getDateExtract("date", '%Y-%m')." AS `key` FROM `tblDocuments`) a GROUP BY `key` ORDER BY `key`";
3589 | $resArr = $this->db->getResultArray($queryStr);
3590 | if (is_bool($resArr) && $resArr == false)
3591 | return false;
3592 |
3593 | return $resArr;
3594 | case 'docsaccumulated':
3595 | $queryStr = "SELECT *, count(`key`) AS total FROM (SELECT ".$this->db->getDateExtract("date")." AS `key` FROM `tblDocuments`) a GROUP BY `key` ORDER BY `key`";
3596 | $resArr = $this->db->getResultArray($queryStr);
3597 | if (is_bool($resArr) && $resArr == false)
3598 | return false;
3599 |
3600 | $sum = 0;
3601 | foreach ($resArr as &$res) {
3602 | $sum += $res['total'];
3603 | /* auxially variable $key is need because sqlite returns
3604 | * a key '`key`'
3605 | */
3606 | $res['key'] = mktime(12, 0, 0, (int) substr($res['key'], 5, 2), (int) substr($res['key'], 8, 2), (int) substr($res['key'], 0, 4)) * 1000;
3607 | $res['total'] = $sum;
3608 | }
3609 | return $resArr;
3610 | case 'docstotal':
3611 | $queryStr = "SELECT count(*) AS total FROM `tblDocuments`";
3612 | $resArr = $this->db->getResultArray($queryStr);
3613 | if (is_bool($resArr) && $resArr == false)
3614 | return false;
3615 | return (int) $resArr[0]['total'];
3616 | case 'folderstotal':
3617 | $queryStr = "SELECT count(*) AS total FROM `tblFolders`";
3618 | $resArr = $this->db->getResultArray($queryStr);
3619 | if (is_bool($resArr) && $resArr == false)
3620 | return false;
3621 | return (int) $resArr[0]['total'];
3622 | case 'userstotal':
3623 | $queryStr = "SELECT count(*) AS total FROM `tblUsers`";
3624 | $resArr = $this->db->getResultArray($queryStr);
3625 | if (is_bool($resArr) && $resArr == false)
3626 | return false;
3627 | return (int) $resArr[0]['total'];
3628 | case 'sizeperuser':
3629 | $queryStr = "SELECT ".$this->db->concat(array('c.`fullName`', "' ('", 'c.`login`', "')'"))." AS `key`, sum(`fileSize`) AS total, `c`.`id` AS res FROM `tblDocuments` a LEFT JOIN `tblDocumentContent` b ON a.id=b.`document` LEFT JOIN `tblUsers` c ON a.`owner`=c.`id` GROUP BY a.`owner`, `key`";
3630 | $resArr = $this->db->getResultArray($queryStr);
3631 | if (is_bool($resArr) && $resArr == false)
3632 | return false;
3633 |
3634 | return $resArr;
3635 | case 'sizepermonth':
3636 | $queryStr = "SELECT *, sum(`fileSize`) AS total FROM (SELECT ".$this->db->getDateExtract("date", '%Y-%m')." AS `key`, `fileSize` FROM `tblDocumentContent`) a GROUP BY `key` ORDER BY `key`";
3637 | $resArr = $this->db->getResultArray($queryStr);
3638 | if (is_bool($resArr) && $resArr == false)
3639 | return false;
3640 |
3641 | return $resArr;
3642 | default:
3643 | return array();
3644 | }
3645 | } /* }}} */
3646 |
3647 | /**
3648 | * Returns changes with a period of time
3649 | *
3650 | * This method returns a list of all changes happened in the database
3651 | * within a given period of time. It currently just checks for
3652 | * entries in the database tables tblDocumentContent, tblDocumentFiles,
3653 | * and tblDocumentStatusLog
3654 | *
3655 | * @param string $startts
3656 | * @param string $endts
3657 | * @return array|bool
3658 | * @internal param string $start start date, defaults to start of current day
3659 | * @internal param string $end end date, defaults to end of start day
3660 | */
3661 | public function getTimeline($startts = '', $endts = '') { /* {{{ */
3662 | if (!$startts)
3663 | $startts = mktime(0, 0, 0);
3664 | if (!$endts)
3665 | $endts = $startts+86400;
3666 |
3667 | /** @var SeedDMS_Core_Document[] $timeline */
3668 | $timeline = array();
3669 |
3670 | if (0) {
3671 | $queryStr = "SELECT DISTINCT `document` FROM `tblDocumentContent` WHERE `date` > ".$startts." AND `date` < ".$endts." UNION SELECT DISTINCT `document` FROM `tblDocumentFiles` WHERE `date` > ".$startts." AND `date` < ".$endts;
3672 | } else {
3673 | $startdate = date('Y-m-d H:i:s', $startts);
3674 | $enddate = date('Y-m-d H:i:s', $endts);
3675 | $queryStr = "SELECT DISTINCT `documentID` AS `document` FROM `tblDocumentStatus` LEFT JOIN `tblDocumentStatusLog` ON `tblDocumentStatus`.`statusID`=`tblDocumentStatusLog`.`statusID` WHERE `date` > ".$this->db->qstr($startdate)." AND `date` < ".$this->db->qstr($enddate)." UNION SELECT DISTINCT document FROM `tblDocumentFiles` WHERE `date` > ".$this->db->qstr($startdate)." AND `date` < ".$this->db->qstr($enddate)." UNION SELECT DISTINCT `document` FROM `tblDocumentFiles` WHERE `date` > ".$startts." AND `date` < ".$endts;
3676 | }
3677 | $resArr = $this->db->getResultArray($queryStr);
3678 | if ($resArr === false)
3679 | return false;
3680 | foreach ($resArr as $rec) {
3681 | $document = $this->getDocument($rec['document']);
3682 | $timeline = array_merge($timeline, $document->getTimeline());
3683 | }
3684 | return $timeline;
3685 |
3686 | } /* }}} */
3687 |
3688 | /**
3689 | * Returns changes with a period of time
3690 | *
3691 | * This method is similar to getTimeline() but returns more dedicated lists
3692 | * of documents or folders which has change in various ways.
3693 | *
3694 | * @param string $mode
3695 | * @param string $startts
3696 | * @param string $endts
3697 | * @return array|bool
3698 | * @internal param string $start start date, defaults to start of current day
3699 | * @internal param string $end end date, defaults to end of start day
3700 | */
3701 | public function getLatestChanges($mode, $startts = '', $endts = '') { /* {{{ */
3702 | if (!$startts)
3703 | $startts = mktime(0, 0, 0);
3704 | if (!$endts)
3705 | $endts = $startts+86400;
3706 |
3707 | $startdate = date('Y-m-d H:i:s', $startts);
3708 | $enddate = date('Y-m-d H:i:s', $endts);
3709 |
3710 | $objects = [];
3711 | switch ($mode) {
3712 | case 'statuschange':
3713 | /* Count entries in tblDocumentStatusLog for each tblDocumentStatus and
3714 | * take only those into account with at least 2 log entries. For the
3715 | * document id do a left join with tblDocumentStatus
3716 | * This is similar to ttstatid + the count + the join
3717 | * c > 1 is required to find only those documents with a changed status
3718 | * This sql statement appears to be much to complicated.
3719 | */
3720 | //$queryStr = "SELECT `a`.*, `tblDocumentStatus`.`documentID` as `document` FROM (SELECT `tblDocumentStatusLog`.`statusID` AS `statusID`, MAX(`tblDocumentStatusLog`.`statusLogID`) AS `maxLogID`, COUNT(`tblDocumentStatusLog`.`statusLogID`) AS `c`, `tblDocumentStatusLog`.`date` FROM `tblDocumentStatusLog` GROUP BY `tblDocumentStatusLog`.`statusID` HAVING `c` > 1 ORDER BY `tblDocumentStatusLog`.`date` DESC) `a` LEFT JOIN `tblDocumentStatus` ON `a`.`statusID`=`tblDocumentStatus`.`statusID` WHERE `a`.`date` > ".$this->db->qstr($startdate)." AND `a`.`date` < ".$this->db->qstr($enddate)." ";
3721 | $queryStr = "SELECT DISTINCT `tblDocumentStatus`.`documentID` as `document` FROM `tblDocumentStatusLog` LEFT JOIN `tblDocumentStatus` ON `tblDocumentStatusLog`.`statusID` = `tblDocumentStatus`.`statusID` WHERE `tblDocumentStatusLog`.`date` > ".$this->db->qstr($startdate)." AND `tblDocumentStatusLog`.`date` < ".$this->db->qstr($enddate)." ORDER BY `tblDocumentStatusLog`.`date` DESC";
3722 | $resArr = $this->db->getResultArray($queryStr);
3723 | if ($resArr === false)
3724 | return false;
3725 | foreach ($resArr as $rec) {
3726 | if ($object = $this->getDocument($rec['document']))
3727 | $objects[] = $object;
3728 | }
3729 | break;
3730 | case 'newdocuments':
3731 | $queryStr = "SELECT `id` AS `document` FROM `tblDocuments` WHERE `date` > ".$startts." AND `date` < ".$endts." ORDER BY `date` DESC";
3732 | $resArr = $this->db->getResultArray($queryStr);
3733 | if ($resArr === false)
3734 | return false;
3735 | foreach ($resArr as $rec) {
3736 | if ($object = $this->getDocument($rec['document']))
3737 | $objects[] = $object;
3738 | }
3739 | break;
3740 | case 'updateddocuments':
3741 | /* DISTINCT is need if there is more than 1 update of the document in the
3742 | * given period of time. Without it, the query will return the document
3743 | * more than once.
3744 | */
3745 | $queryStr = "SELECT DISTINCT `document` AS `document` FROM `tblDocumentContent` LEFT JOIN `tblDocuments` ON `tblDocumentContent`.`document`=`tblDocuments`.`id` WHERE `tblDocumentContent`.`date` > ".$startts." AND `tblDocumentContent`.`date` < ".$endts." AND `tblDocumentContent`.`date` > `tblDocuments`.`date` ORDER BY `tblDocumentContent`.`date` DESC";
3746 | $resArr = $this->db->getResultArray($queryStr);
3747 | if ($resArr === false)
3748 | return false;
3749 | foreach ($resArr as $rec) {
3750 | if ($object = $this->getDocument($rec['document']))
3751 | $objects[] = $object;
3752 | }
3753 | break;
3754 | case 'newfolders':
3755 | $queryStr = "SELECT `id` AS `folder` FROM `tblFolders` WHERE `date` > ".$startts." AND `date` < ".$endts." ORDER BY `date` DESC";
3756 | $resArr = $this->db->getResultArray($queryStr);
3757 | if ($resArr === false)
3758 | return false;
3759 | foreach ($resArr as $rec) {
3760 | if ($object = $this->getFolder($rec['folder']))
3761 | $objects[] = $object;
3762 | }
3763 | break;
3764 | }
3765 | return $objects;
3766 | } /* }}} */
3767 |
3768 | public function getMimeTypes() { /* {{{ */
3769 | $queryStr = "SELECT `mimeType`, COUNT(`mimeType`) AS `c` FROM `tblDocumentContent` GROUP BY `mimeType` ORDER BY `mimeType`";
3770 |
3771 | $resArr = $this->db->getResultArray($queryStr);
3772 | if (is_bool($resArr) && !$resArr)
3773 | return false;
3774 |
3775 | return $resArr;
3776 | } /* }}} */
3777 |
3778 | /**
3779 | * Set a callback function
3780 | *
3781 | * The function passed in $func must be a callable and $name must not be empty.
3782 | *
3783 | * Setting a callback with this method will remove all previously
3784 | * set callbacks. Use {@see SeedDMS_Core_DMS::addCallback()} to register
3785 | * additional callbacks.
3786 | * This method does not check if there is a callback with the given name.
3787 | *
3788 | * @param string $name internal name of callback
3789 | * @param mixed $func function name as expected by {call_user_method}
3790 | * @param mixed $params parameter passed as the first argument to the
3791 | * callback
3792 | * @return bool true if adding the callback succeeds otherwise false
3793 | */
3794 | public function setCallback($name, $func, $params = null) { /* {{{ */
3795 | if ($name && $func && is_callable($func)) {
3796 | $this->callbacks[$name] = array(array($func, $params));
3797 | return true;
3798 | } else {
3799 | return false;
3800 | }
3801 | } /* }}} */
3802 |
3803 | /**
3804 | * Add a callback function
3805 | *
3806 | * The function passed in $func must be a callable and $name must not be empty.
3807 | * This method does not check if there is a callback with the given name.
3808 | *
3809 | * @param string $name internal name of callback
3810 | * @param mixed $func function name as expected by {call_user_method}
3811 | * @param mixed $params parameter passed as the first argument to the
3812 | * callback
3813 | * @return bool true if adding the callback succeeds otherwise false
3814 | */
3815 | public function addCallback($name, $func, $params = null) { /* {{{ */
3816 | if ($name && $func && is_callable($func)) {
3817 | $this->callbacks[$name][] = array($func, $params);
3818 | return true;
3819 | } else {
3820 | return false;
3821 | }
3822 | } /* }}} */
3823 |
3824 | /**
3825 | * Check if a callback with the given name has been set
3826 | *
3827 | * @param string $name internal name of callback
3828 | * @return bool true if callback exists otherwise false
3829 | */
3830 | public function hasCallback($name) { /* {{{ */
3831 | if ($name && !empty($this->callbacks[$name]))
3832 | return true;
3833 | return false;
3834 | } /* }}} */
3835 |
3836 | }