FOSSology  4.6.0
Open Source License Compliance by Open Source Software
FolderDao.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014-2015 Siemens AG
4  Authors: Andreas Würl, Steffen Weber
5 
6  SPDX-License-Identifier: GPL-2.0-only
7 */
8 
9 namespace Fossology\Lib\Dao;
10 
15 use Monolog\Logger;
16 
17 class FolderDao
18 {
19  const FOLDER_KEY = "folder";
20  const DEPTH_KEY = "depth";
21  const REUSE_KEY = 'reuse';
22  const TOP_LEVEL = 1;
23 
24  const MODE_FOLDER = 1;
25  const MODE_UPLOAD = 2;
26  const MODE_ITEM = 4;
27 
29  private $dbManager;
31  private $userDao;
33  private $uploadDao;
35  private $logger;
36 
37  public function __construct(DbManager $dbManager, UserDao $userDao, UploadDao $uploadDao)
38  {
39  $this->dbManager = $dbManager;
40  $this->logger = new Logger(self::class);
41  $this->uploadDao = $uploadDao;
42  $this->userDao = $userDao;
43  }
44 
48  public function hasTopLevelFolder()
49  {
50  $folderInfo = $this->dbManager->getSingleRow("SELECT count(*) cnt FROM folder WHERE folder_pk=$1", array(self::TOP_LEVEL), __METHOD__);
51  $hasFolder = $folderInfo['cnt'] > 0;
52  return $hasFolder;
53  }
54 
55  public function insertFolder($folderName, $folderDescription, $parentFolderId = self::TOP_LEVEL)
56  {
57 
58  $statementName = __METHOD__;
59 
60  $this->dbManager->prepare($statementName,
61  "INSERT INTO folder (folder_name, folder_desc) VALUES ($1, $2) returning folder_pk");
62  $res = $this->dbManager->execute($statementName, array($folderName, $folderDescription));
63  $folderRow = $this->dbManager->fetchArray($res);
64  $folderId = $folderRow["folder_pk"];
65  $this->dbManager->freeResult($res);
66  $this->insertFolderContents($parentFolderId, self::MODE_FOLDER, $folderId);
67 
68  return $folderId;
69  }
70 
71  public function getFolderId($folderName, $parentFolderId = self::TOP_LEVEL)
72  {
73  $statementName = __METHOD__;
74  $this->dbManager->prepare($statementName,
75  "SELECT folder_pk FROM folder, foldercontents fc"
76  ." WHERE LOWER(folder_name)=LOWER($1) AND fc.parent_fk=$2 AND fc.foldercontents_mode=$3 AND folder_pk=child_id");
77  $res = $this->dbManager->execute($statementName, array( $folderName, $parentFolderId, self::MODE_FOLDER));
78  $rows= $this->dbManager->fetchAll($res);
79 
80  $rootFolder = !empty($rows) ? intval($rows[0]['folder_pk']) : null;
81  $this->dbManager->freeResult($res);
82 
83  return $rootFolder;
84  }
85 
86  public function insertFolderContents($parentId, $foldercontentsMode, $childId)
87  {
88  $statementName = __METHOD__;
89  $this->dbManager->prepare($statementName,
90  "INSERT INTO foldercontents (parent_fk, foldercontents_mode, child_id) VALUES ($1, $2, $3)");
91  $res = $this->dbManager->execute($statementName, array($parentId, $foldercontentsMode, $childId));
92  $this->dbManager->freeResult($res);
93  }
94 
95  protected function fixFolderSequence()
96  {
97  $statementName = __METHOD__;
98  $this->dbManager->prepare($statementName,
99  "SELECT setval('folder_folder_pk_seq', (SELECT max(folder_pk) + 1 FROM folder LIMIT 1))");
100  $res = $this->dbManager->execute($statementName);
101  $this->dbManager->freeResult($res);
102  }
103 
108  public function getRootFolder($userId)
109  {
110  $statementName = __METHOD__;
111  $this->dbManager->prepare($statementName,
112  "SELECT f.* FROM folder f INNER JOIN users u ON f.folder_pk = u.root_folder_fk WHERE u.user_pk = $1");
113  $res = $this->dbManager->execute($statementName, array($userId));
114  $row = $this->dbManager->fetchArray($res);
115  $rootFolder = $row ? new Folder(intval($row['folder_pk']), $row['folder_name'], $row['folder_desc'], intval($row['folder_perm'])) : null;
116  $this->dbManager->freeResult($res);
117  return $rootFolder;
118  }
119 
124  public function getDefaultFolder($userId)
125  {
126  $statementName = __METHOD__;
127  $this->dbManager->prepare($statementName,
128  "SELECT f.* FROM folder f INNER JOIN users u ON f.folder_pk = u.default_folder_fk WHERE u.user_pk = $1");
129  $res = $this->dbManager->execute($statementName, array($userId));
130  $row = $this->dbManager->fetchArray($res);
131  $rootFolder = $row ? new Folder(intval($row['folder_pk']), $row['folder_name'], $row['folder_desc'], intval($row['folder_perm'])) : null;
132  $this->dbManager->freeResult($res);
133  return $rootFolder;
134  }
135 
136  public function getFolderTreeCte($parentId = null)
137  {
138  $parentCondition = $parentId ? 'folder_pk=$1' : 'folder_pk=' . self::TOP_LEVEL;
139 
140  return "WITH RECURSIVE folder_tree(folder_pk, parent_fk, folder_name, folder_desc, folder_perm, id_path, name_path, depth, cycle_detected) AS (
141  SELECT
142  f.folder_pk, fc.parent_fk, f.folder_name, f.folder_desc, f.folder_perm,
143  ARRAY [f.folder_pk] AS id_path,
144  ARRAY [f.folder_name] AS name_path,
145  0 AS depth,
146  FALSE AS cycle_detected
147  FROM folder f LEFT JOIN foldercontents fc ON fc.foldercontents_mode=" . self::MODE_FOLDER . " AND f.folder_pk=fc.child_id
148  WHERE $parentCondition
149  UNION ALL
150  SELECT
151  f.folder_pk, fc.parent_fk, f.folder_name, f.folder_desc, f.folder_perm,
152  id_path || f.folder_pk,
153  name_path || f.folder_name,
154  array_length(id_path, 1),
155  f.folder_pk = ANY (id_path)
156  FROM folder f, foldercontents fc, folder_tree ft
157  WHERE f.folder_pk=fc.child_id AND foldercontents_mode=" . self::MODE_FOLDER . " AND fc.parent_fk = ft.folder_pk AND NOT cycle_detected
158 )";
159  }
160 
161  public function getFolderStructure($parentId = null)
162  {
163  $statementName = __METHOD__ . ($parentId ? '.relativeToParent' : '');
164  $parameters = $parentId ? array($parentId) : array();
165  $this->dbManager->prepare($statementName, $this->getFolderTreeCte($parentId)
166  . " SELECT folder_pk, parent_fk, folder_name, folder_desc, folder_perm, depth FROM folder_tree ORDER BY name_path");
167  $res = $this->dbManager->execute($statementName, $parameters);
168 
169  $userGroupMap = $this->userDao->getUserGroupMap(Auth::getUserId());
170 
171  // Collect all rows first
172  $rows = array();
173  while ($row = $this->dbManager->fetchArray($res)) {
174  $rows[] = $row;
175  }
176  $this->dbManager->freeResult($res);
177 
178  $folderIds = [];
179  foreach ($rows as $r) {
180  $folderIds[] = intval($r['folder_pk']);
181  }
182  // Batch-fetch upload counts for ALL folders in one query
183  $allCounts = $this->countFolderUploadsBatch($folderIds, $userGroupMap);
184 
185  $results = array();
186  foreach ($rows as $row) {
187  $folderId = intval($row['folder_pk']);
188  $countUploads = isset($allCounts[$folderId]) ? $allCounts[$folderId] : array();
189 
190  $results[] = array(
191  self::FOLDER_KEY => new Folder(
192  $folderId, $row['folder_name'], $row['folder_desc'], intval($row['folder_perm'])),
193  self::DEPTH_KEY => $row['depth'],
194  self::REUSE_KEY => $countUploads
195  );
196  }
197  return $results;
198  }
199 
205  public function countFolderUploadsBatch($folderIds, $userGroupMap)
206  {
207  if (empty($folderIds)) {
208  return array();
209  }
210 
211  $trustGroupIds = array_keys($userGroupMap);
212  $trustedGroups = '{' . implode(',', $trustGroupIds) . '}';
213  $folderIdList = '{' . implode(',', array_map('intval', $folderIds)) . '}';
214 
215  $statementName = __METHOD__;
216  $this->dbManager->prepare($statementName, "
217  SELECT fc.parent_fk AS folder_pk, uc.group_fk AS group_id, count(*) AS cnt
218  FROM foldercontents fc
219  INNER JOIN upload u ON u.upload_pk = fc.child_id
220  INNER JOIN upload_clearing uc ON u.upload_pk = uc.upload_fk AND uc.group_fk = ANY($1)
221  WHERE fc.parent_fk = ANY($2)
222  AND fc.foldercontents_mode = " . self::MODE_UPLOAD . "
223  AND (u.upload_mode = 100 OR u.upload_mode = 104)
224  GROUP BY fc.parent_fk, uc.group_fk
225  ");
226  $res = $this->dbManager->execute($statementName, array($trustedGroups, $folderIdList));
227 
228  $results = array();
229  while ($row = $this->dbManager->fetchArray($res)) {
230  $folderId = intval($row['folder_pk']);
231  $groupName = $userGroupMap[$row['group_id']];
232  if (!isset($results[$folderId])) {
233  $results[$folderId] = array();
234  }
235  $results[$folderId][$groupName] = array(
236  'group_id' => $row['group_id'],
237  'count' => $row['cnt'],
238  'group_name' => $groupName
239  );
240  }
241  $this->dbManager->freeResult($res);
242  return $results;
243  }
244 
250  public function countFolderUploads($parentId, $userGroupMap)
251  {
252  $trustGroupIds = array_keys($userGroupMap);
253  $statementName = __METHOD__;
254  $trustedGroups = '{' . implode(',', $trustGroupIds) . '}';
255  $parameters = array($parentId, $trustedGroups);
256 
257  $this->dbManager->prepare($statementName, "
258 SELECT group_fk group_id,count(*) FROM foldercontents fc
259  INNER JOIN upload u ON u.upload_pk = fc.child_id
260  INNER JOIN upload_clearing uc ON u.upload_pk=uc.upload_fk AND uc.group_fk=ANY($2)
261 WHERE fc.parent_fk = $1 AND fc.foldercontents_mode = " . self::MODE_UPLOAD . " AND (u.upload_mode = 100 OR u.upload_mode = 104)
262 GROUP BY group_fk
263 ");
264  $res = $this->dbManager->execute($statementName, $parameters);
265  $results = array();
266  while ($row = $this->dbManager->fetchArray($res)) {
267  $row['group_name'] = $userGroupMap[$row['group_id']];
268  $results[$row['group_name']] = $row;
269  }
270  $this->dbManager->freeResult($res);
271  return $results;
272  }
273 
274  public function getAllFolderIds()
275  {
276  $statementName = __METHOD__;
277  $this->dbManager->prepare($statementName, "SELECT folder_pk FROM folder");
278  $res = $this->dbManager->execute($statementName);
279  $results = $this->dbManager->fetchAll($res);
280  $this->dbManager->freeResult($res);
281 
282  return array_map('intval', array_column($results, 'folder_pk'));
283  }
284 
285  public function getFolderChildUploads($parentId, $trustGroupId)
286  {
287  $statementName = __METHOD__;
288  $parameters = array($parentId, $trustGroupId);
289 
290  $this->dbManager->prepare($statementName, $sql = "
291 SELECT u.*, uc.*, fc.foldercontents_pk FROM foldercontents fc
292  INNER JOIN upload u ON u.upload_pk = fc.child_id AND u.upload_mode IN (100, 104)
293  INNER JOIN upload_clearing uc ON uc.upload_fk = u.upload_pk AND uc.group_fk = $2
294 WHERE fc.parent_fk = $1 AND fc.foldercontents_mode = " . self::MODE_UPLOAD . ";");
295  $res = $this->dbManager->execute($statementName, $parameters);
296  $results = $this->dbManager->fetchAll($res);
297  $this->dbManager->freeResult($res);
298  return $results;
299  }
300 
306  public function getFolderUploads($parentId, $trustGroupId = null)
307  {
308  if (empty($trustGroupId)) {
309  $trustGroupId = Auth::getGroupId();
310  }
311  $results = array();
312  foreach ($this->getFolderChildUploads($parentId, $trustGroupId) as $row) {
313  $results[] = UploadProgress::createFromTable($row);
314  }
315  return $results;
316  }
317 
323  public function getAllUploadsForGroup($trustGroupId)
324  {
325  $statementName = __METHOD__;
326  $this->dbManager->prepare($statementName, "
327  SELECT u.upload_pk, u.upload_filename, u.upload_desc,
328  u.uploadtree_tablename, u.upload_ts,
329  uc.group_fk, uc.assignee, uc.status_fk, uc.status_comment
330  FROM upload u
331  INNER JOIN upload_clearing uc ON uc.upload_fk = u.upload_pk AND uc.group_fk = $1
332  WHERE u.upload_mode IN (100, 104)
333  AND u.pfile_fk IS NOT NULL
334  ORDER BY u.upload_filename
335  ");
336  $res = $this->dbManager->execute($statementName, array($trustGroupId));
337  $results = array();
338  while ($row = $this->dbManager->fetchArray($res)) {
339  $results[] = UploadProgress::createFromTable($row);
340  }
341  $this->dbManager->freeResult($res);
342  return $results;
343  }
344 
345  public function createFolder($folderName, $folderDescription, $parentId)
346  {
347  $folderId = $this->dbManager->insertTableRow("folder", array("folder_name" => $folderName, "user_fk" => Auth::getUserId(), "folder_desc" => $folderDescription), null, 'folder_pk');
348  $this->insertFolderContents($parentId, self::MODE_FOLDER, $folderId);
349  return $folderId;
350  }
351 
352 
353  public function ensureTopLevelFolder()
354  {
355  if (!$this->hasTopLevelFolder()) {
356  $this->dbManager->insertTableRow("folder", array("folder_pk" => self::TOP_LEVEL, "folder_name" => "Software Repository", "folder_desc" => "Top Folder"));
357  $this->insertFolderContents(1, 0, 0);
358  $this->fixFolderSequence();
359  }
360  }
361 
362  public function isWithoutReusableFolders($folderStructure)
363  {
364  foreach ($folderStructure as $folder) {
365  $posibilities = array_reduce($folder[self::REUSE_KEY], function($sum,$groupInfo)
366  {
367  return $sum+$groupInfo['count'];
368  }, 0);
369  if ($posibilities > 0) {
370  return false;
371  }
372  }
373  return true;
374  }
375 
376  protected function isInFolderTree($parentId, $folderId)
377  {
378  $cycle = $this->dbManager->getSingleRow(
379  $this->getFolderTreeCte($parentId) . " SELECT depth FROM folder_tree WHERE folder_pk=$2 LIMIT 1",
380  array($parentId, $folderId),
381  __METHOD__);
382  return !empty($cycle);
383  }
384 
385  public function getContent($folderContentId)
386  {
387  $content = $this->dbManager->getSingleRow('SELECT * FROM foldercontents WHERE foldercontents_pk=$1',
388  array($folderContentId),
389  __METHOD__ . '.getContent');
390  if (empty($content)) {
391  throw new \Exception('invalid FolderContentId');
392  }
393  return $content;
394  }
395 
396  protected function isContentMovable($content, $newParentId)
397  {
398  if ($content['parent_fk'] == $newParentId) {
399  return false;
400  }
401  $newParent = $this->dbManager->getSingleRow('SELECT * FROM folder WHERE folder_pk=$1',
402  array($newParentId),
403  __METHOD__ . '.getParent');
404  if (empty($newParent)) {
405  throw new \Exception('invalid parent folder');
406  }
407 
408  if ($content['foldercontents_mode'] == self::MODE_FOLDER) {
409  if ($this->isInFolderTree($content['child_id'], $newParentId)) {
410  throw new \Exception("action would cause a cycle");
411  }
412  } elseif ($content['foldercontents_mode'] == self::MODE_UPLOAD) {
413  $uploadId = $content['child_id'];
414  if (!$this->uploadDao->isEditable($uploadId, Auth::getGroupId())) {
415  throw new \Exception('permission to upload denied');
416  }
417  }
418 
419  return true;
420  }
421 
422  public function moveContent($folderContentId, $newParentId)
423  {
424  $content = $this->getContent($folderContentId);
425  if (!$this->isContentMovable($content, $newParentId)) {
426  return;
427  }
428 
429  $this->dbManager->getSingleRow('UPDATE foldercontents SET parent_fk=$2 WHERE foldercontents_pk=$1',
430  array($folderContentId, $newParentId), __METHOD__ . '.updateFolderParent');
431  }
432 
433  public function copyContent($folderContentId, $newParentId)
434  {
435  $content = $this->getContent($folderContentId);
436  if (!$this->isContentMovable($content, $newParentId)) {
437  return;
438  }
439 
440  $this->insertFolderContents($newParentId, $content['foldercontents_mode'], $content['child_id']);
441  }
442 
443  public function getRemovableContents($folderId)
444  {
445  $sqlChildren = "SELECT child_id,foldercontents_mode
446  FROM foldercontents GROUP BY child_id,foldercontents_mode
447  HAVING count(*)>1 AND bool_or(parent_fk=$1)";
448  $sql = "SELECT fc.* FROM foldercontents fc,($sqlChildren) chi "
449  . "WHERE fc.child_id=chi.child_id AND fc.foldercontents_mode=chi.foldercontents_mode and fc.parent_fk=$1";
450  $this->dbManager->prepare($stmt = __METHOD__, $sql);
451  $res = $this->dbManager->execute($stmt, array($folderId));
452  $contents = array();
453  while ($row = $this->dbManager->fetchArray($res)) {
454  $contents[] = $row['foldercontents_pk'];
455  }
456  $this->dbManager->freeResult($res);
457  return $contents;
458  }
459 
460  public function isRemovableContent($childId, $mode)
461  {
462  $sql = "SELECT count(parent_fk) FROM foldercontents WHERE child_id=$1 AND foldercontents_mode=$2";
463  $parentCounter = $this->dbManager->getSingleRow($sql, array($childId, $mode), __METHOD__);
464  return $parentCounter['count'] > 1;
465  }
466 
467  public function removeContent($folderContentId)
468  {
469  $content = $this->getContent($folderContentId);
470  if ($this->isRemovableContent($content['child_id'], $content['foldercontents_mode'])) {
471  $sql = "DELETE FROM foldercontents WHERE foldercontents_pk=$1";
472  $this->dbManager->getSingleRow($sql, array($folderContentId), __METHOD__);
473  return true;
474  }
475  return false;
476  }
477 
478  public function removeContentById($uploadpk, $folderId)
479  {
480  $sql = "DELETE FROM foldercontents WHERE child_id=$1 AND parent_fk=$2 AND foldercontents_mode=$3";
481  $this->dbManager->getSingleRow($sql,array($uploadpk, $folderId,2),__METHOD__);
482  }
483 
484  public function getFolderChildFolders($folderId)
485  {
486  $results = array();
487  $stmtFolder = __METHOD__;
488  $sqlFolder = "SELECT foldercontents_pk,foldercontents_mode, folder_name FROM foldercontents JOIN folder"
489  . " ON foldercontents.child_id=folder.folder_pk WHERE foldercontents.parent_fk=$1"
490  . " AND foldercontents_mode=" . self::MODE_FOLDER;
491  $this->dbManager->prepare($stmtFolder, $sqlFolder);
492  $res = $this->dbManager->execute($stmtFolder, array($folderId));
493  while ($row = $this->dbManager->fetchArray($res)) {
494  $results[$row['foldercontents_pk']] = $row;
495  }
496  $this->dbManager->freeResult($res);
497  return $results;
498  }
499 
504  public function getFolder($folderId)
505  {
506  $folderRow = $this->dbManager->getSingleRow('SELECT * FROM folder WHERE folder_pk = $1', array($folderId));
507  if (!$folderRow) {
508  return null;
509  }
510  return new Folder($folderRow['folder_pk'], $folderRow['folder_name'], $folderRow['folder_desc'], $folderRow['folder_perm']);
511  }
512 
518  public function isFolderAccessible($folderId, $userId = null)
519  {
520  if ($userId == null) {
521  $userId = Auth::getUserId();
522  }
523  $rootFolder = $this->getRootFolder($userId)->getId();
524  return $this->isInFolderTree($rootFolder, $folderId);
525  }
526 
533  public function getFolderContentsId($childId, $mode)
534  {
535  $folderContentsRow = $this->dbManager->getSingleRow(
536  'SELECT foldercontents_pk FROM foldercontents '.
537  'WHERE child_id = $1 AND foldercontents_mode = $2', [$childId, $mode]);
538  if (!$folderContentsRow) {
539  return null;
540  }
541  return intval($folderContentsRow['foldercontents_pk']);
542  }
543 
549  public function getFolderParentId($folderPk)
550  {
551  $sql = "SELECT parent_fk FROM foldercontents " .
552  "WHERE foldercontents_mode = " . self::MODE_FOLDER .
553  " AND child_id = $1;";
554  $statement = __METHOD__ . ".getParentId";
555  $row = $this->dbManager->getSingleRow($sql, [$folderPk], $statement);
556  return (empty($row)) ? null : $row['parent_fk'];
557  }
558 }
Contains the constants and helpers for authentication of user.
Definition: Auth.php:24
static getUserId()
Get the current user's id.
Definition: Auth.php:68
static getGroupId()
Get the current user's group id.
Definition: Auth.php:80
isFolderAccessible($folderId, $userId=null)
Definition: FolderDao.php:518
getFolderUploads($parentId, $trustGroupId=null)
Definition: FolderDao.php:306
getAllUploadsForGroup($trustGroupId)
Get all uploads accessible to a group in a single query.
Definition: FolderDao.php:323
getFolderContentsId($childId, $mode)
Definition: FolderDao.php:533
countFolderUploads($parentId, $userGroupMap)
Definition: FolderDao.php:250
countFolderUploadsBatch($folderIds, $userGroupMap)
Definition: FolderDao.php:205
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16