FOSSology  4.7.1
Open Source License Compliance by Open Source Software
ClearingDao.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014-2018, 2020-2022 Siemens AG
4  SPDX-FileCopyrightText: © Fossology contributors
5  Author: Johannes Najjar
6 
7  SPDX-License-Identifier: GPL-2.0-only
8 */
9 
10 namespace Fossology\Lib\Dao;
11 
25 use Monolog\Logger;
26 
28 {
30  private $dbManager;
32  private $logger;
34  private $uploadDao;
36  private $copyrightDao;
38  private $licenseRefCache;
39 
44  function __construct(DbManager $dbManager, UploadDao $uploadDao)
45  {
46  $this->dbManager = $dbManager;
47  $this->logger = new Logger(self::class);
48  $this->uploadDao = $uploadDao;
49  $this->licenseRefCache = array();
50  global $container;
51  $this->copyrightDao = $container->get('dao.copyright');
52  }
53 
54  private function getRelevantDecisionsCte(ItemTreeBounds $itemTreeBounds, $groupId, $onlyCurrent, &$statementName, &$params, $condition="")
55  {
56  $uploadTreeTable = $itemTreeBounds->getUploadTreeTableName();
57  $uploadId = $itemTreeBounds->getUploadId();
58 
59  $params[] = DecisionTypes::WIP; $p1 = "$". count($params);
60  $params[] = $groupId; $p2 = "$". count($params);
61 
62  $sql_upload = "";
63  if ('uploadtree' === $uploadTreeTable || 'uploadtree_a' === $uploadTreeTable) {
64  $params[] = $uploadId; $p = "$". count($params);
65  $sql_upload = " AND ut.upload_fk=$p";
66  }
67  if (!empty($condition)) {
68  $statementName .= ".(".$condition.")";
69  $condition = " AND $condition";
70  }
71 
72  $filterClause = $onlyCurrent ? "DISTINCT ON(itemid)" : "";
73  $sortClause = $onlyCurrent ? "ORDER BY itemid, id DESC" : "";
74 
75  $statementName .= "." . $uploadTreeTable . ($onlyCurrent ? ".current": "");
76 
77  $globalScope = DecisionScopes::REPO;
78  $localScope = DecisionScopes::ITEM;
79 
80  $applyGlobal = $this->uploadDao->getGlobalDecisionSettingsFromInfo($uploadId);
81  if (!empty($applyGlobal)) {
82  $applyGlobal = "(ut.pfile_fk = cd.pfile_fk AND cd.scope = $globalScope) OR
83  (ut.uploadtree_pk = cd.uploadtree_fk
84  AND cd.scope = $localScope AND cd.group_fk = $p2)";
85  $statementName .= "WithGlobal";
86  } else {
87  $applyGlobal = "(ut.uploadtree_pk = cd.uploadtree_fk
88  AND cd.group_fk = $p2)";
89  $statementName .= "WithoutGlobal";
90  }
91 
92  return "WITH decision AS (
93  SELECT
94  $filterClause
95  cd.clearing_decision_pk AS id,
96  cd.pfile_fk AS pfile_id,
97  ut.uploadtree_pk AS itemid,
98  cd.user_fk AS user_id,
99  cd.decision_type AS type_id,
100  cd.scope AS scope,
101  EXTRACT(EPOCH FROM cd.date_added) AS ts_added
102  FROM clearing_decision cd
103  INNER JOIN $uploadTreeTable ut
104  ON ( $applyGlobal )
105  $sql_upload $condition
106  WHERE cd.decision_type != $p1
107  $sortClause
108  )";
109  }
110 
116  function getClearedLicenses(ItemTreeBounds $itemTreeBounds, $groupId)
117  {
118  $statementName = __METHOD__;
119 
120  $params = array($itemTreeBounds->getLeft(), $itemTreeBounds->getRight());
121  $condition = "ut.lft BETWEEN $1 AND $2";
122 
123  $decisionsCte = $this->getRelevantDecisionsCte($itemTreeBounds, $groupId, $onlyCurrent=true, $statementName, $params, $condition);
124  $params[] = DecisionTypes::IRRELEVANT;
125  $sql = "$decisionsCte
126  SELECT
127  lr.rf_pk AS license_id,
128  lr.rf_shortname AS shortname,
129  lr.rf_spdx_id AS spdx_id,
130  lr.rf_fullname AS fullname
131  FROM decision
132  INNER JOIN clearing_decision_event cde ON cde.clearing_decision_fk = decision.id
133  INNER JOIN clearing_event ce ON
134  (ce.clearing_event_pk = cde.clearing_event_fk AND NOT ce.removed)
135  INNER JOIN license_ref lr ON lr.rf_pk = ce.rf_fk
136  WHERE type_id != $".count($params)."
137  GROUP BY license_id,shortname,fullname,spdx_id";
138 
139  $this->dbManager->prepare($statementName, $sql);
140 
141  $res = $this->dbManager->execute($statementName, $params);
142 
143  $licenses = array();
144  while ($row = $this->dbManager->fetchArray($res)) {
145  $licenses[] = new LicenseRef($row['license_id'], $row['shortname'], $row['fullname'], $row['spdx_id']);
146  }
147  $this->dbManager->freeResult($res);
148 
149  return $licenses;
150  }
151 
152 
160  function getFileClearings(ItemTreeBounds $itemTreeBounds, $groupId, $onlyCurrent=true, $forClearingHistory=false)
161  {
162  $this->dbManager->begin();
163 
164  $statementName = __METHOD__;
165 
166  $params = array($itemTreeBounds->getItemId());
167  $condition = "ut.uploadtree_pk = $1";
168 
169  $decisionsCte = $this->getRelevantDecisionsCte($itemTreeBounds, $groupId, $onlyCurrent, $statementName, $params, $condition);
170 
171  $clearingsWithLicensesArray = $this->getDecisionsFromCte($decisionsCte, $statementName, $params, $forClearingHistory);
172 
173  $this->dbManager->commit();
174  return $clearingsWithLicensesArray;
175  }
176 
184  function getFileClearingsFolder(ItemTreeBounds $itemTreeBounds, $groupId, $includeSubFolders=true, $onlyCurrent=true)
185  {
186  $this->dbManager->begin();
187 
188  $statementName = __METHOD__ . ($includeSubFolders ? ".subfolders" : ".direct");
189 
190  if (!$includeSubFolders) {
191  $params = array($itemTreeBounds->getItemId());
192  $condition = "ut.realparent = $1";
193  } else {
194  $params = array($itemTreeBounds->getLeft(), $itemTreeBounds->getRight());
195  $condition = "ut.lft BETWEEN $1 AND $2";
196  }
197 
198  $decisionsCte = $this->getRelevantDecisionsCte($itemTreeBounds, $groupId, $onlyCurrent, $statementName, $params, $condition);
199 
200  $clearingsWithLicensesArray = $this->getDecisionsFromCte($decisionsCte, $statementName, $params);
201 
202  $this->dbManager->commit();
203  return $clearingsWithLicensesArray;
204  }
205 
212  private function getDecisionsFromCte($decisionsCte, $statementName, $params, $forClearingHistory=false)
213  {
214  $sql = "$decisionsCte
215  SELECT
216  decision.*,
217  users.user_name AS user_name,
218  ce.clearing_event_pk as event_id,
219  ce.user_fk as event_user_id,
220  ce.group_fk as event_group_id,
221  lr.rf_pk AS license_id,
222  lr.rf_spdx_id AS spdx_id,
223  lr.rf_shortname AS shortname,
224  lr.rf_fullname AS fullname,
225  ce.removed AS removed,
226  ce.type_fk AS event_type_id,
227  ce.reportinfo AS reportinfo,
228  ce.comment AS comment,
229  ce.acknowledgement AS acknowledgement
230  FROM decision
231  LEFT JOIN users ON decision.user_id = users.user_pk
232  LEFT JOIN clearing_decision_event cde ON cde.clearing_decision_fk = decision.id
233  LEFT JOIN clearing_event ce ON ce.clearing_event_pk = cde.clearing_event_fk
234  LEFT JOIN license_ref lr ON lr.rf_pk = ce.rf_fk
235  ORDER BY decision.id DESC, itemid, event_id ASC";
236 
237  $this->dbManager->prepare($statementName, $sql);
238 
239  $result = $this->dbManager->execute($statementName, $params);
240  $clearingsWithLicensesArray = array();
241 
242  $previousClearingId = -1;
243  $previousItemId = -1;
244  $clearingEvents = array();
245  $clearingEventCache = array();
246  $clearingDecisionBuilder = ClearingDecisionBuilder::create();
247  $firstMatch = true;
248  while ($row = $this->dbManager->fetchArray($result)) {
249  $clearingId = $row['id'];
250  $itemId = $row['itemid'];
251  $licenseId = $row['license_id'];
252  $eventId = $row['event_id'];
253  $licenseSpdxId = $row['spdx_id'];
254  $licenseShortName = $row['shortname'];
255  $licenseName = $row['fullname'];
256  $licenseIsRemoved = $row['removed'];
257 
258  $eventType = $row['event_type_id'];
259  $eventUserId = $row['event_user_id'];
260  $eventGroupId = $row['event_group_id'];
261  $comment = $row['comment'];
262  $reportInfo = $row['reportinfo'];
263  $acknowledgement = $row['acknowledgement'];
264 
265  if ($clearingId !== $previousClearingId || $itemId !== $previousItemId) {
266  //store the old one
267  if (!$firstMatch) {
268  $clearingsWithLicensesArray[] = $clearingDecisionBuilder->setClearingEvents($clearingEvents)->build();
269  }
270 
271  $firstMatch = false;
272  //prepare the new one
273  $previousClearingId = $clearingId;
274  $previousItemId = $itemId;
275  $clearingEvents = array();
276  $clearingDecisionBuilder = ClearingDecisionBuilder::create()
277  ->setClearingId($row['id'])
278  ->setUploadTreeId($itemId)
279  ->setPfileId($row['pfile_id'])
280  ->setUserName($row['user_name'])
281  ->setUserId($row['user_id'])
282  ->setType(intval($row['type_id']))
283  ->setScope(intval($row['scope']))
284  ->setTimeStamp($row['ts_added']);
285  }
286 
287  if ($licenseId !== null) {
288  if (!array_key_exists($eventId, $clearingEventCache)) {
289  if (!array_key_exists($licenseId, $this->licenseRefCache)) {
290  $this->licenseRefCache[$licenseId] = new LicenseRef($licenseId, $licenseShortName, $licenseName, $licenseSpdxId);
291  }
292  $licenseRef = $this->licenseRefCache[$licenseId];
293  $clearingEventCache[$eventId] = $this->buildClearingEvent($eventId, $eventUserId, $eventGroupId, $licenseRef, $licenseIsRemoved, $eventType, $reportInfo, $comment, $acknowledgement);
294  }
295  $clearingEvents[] = $clearingEventCache[$eventId];
296  }
297  }
298 
300  if (!$firstMatch) {
301  $clearingsWithLicensesArray[] = $clearingDecisionBuilder->setClearingEvents($clearingEvents)->build();
302  }
303  $this->dbManager->freeResult($result);
304 
305  return $clearingsWithLicensesArray;
306  }
312  public function getRelevantClearingDecision(ItemTreeBounds $itemTreeBounds, $groupId)
313  {
314  $clearingDecisions = $this->getFileClearings($itemTreeBounds, $groupId);
315  if (count($clearingDecisions) > 0) {
316  return $clearingDecisions[0];
317  }
318  return null;
319  }
320 
325  public function removeWipClearingDecision($uploadTreeId, $groupId)
326  {
327  $sql = "DELETE FROM clearing_decision WHERE uploadtree_fk=$1 AND group_fk=$2 AND decision_type=$3";
328  $this->dbManager->prepare($stmt = __METHOD__, $sql);
329  $this->dbManager->freeResult($this->dbManager->execute($stmt, array($uploadTreeId, $groupId, DecisionTypes::WIP)));
330  }
331 
339  public function createDecisionFromEvents($uploadTreeId, $userId, $groupId, $decType, $scope, $eventIds)
340  {
341  if ( ($scope == DecisionScopes::REPO) &&
342  !empty($this->getCandidateLicenseCountForCurrentDecisions($uploadTreeId))) {
343  throw new \Exception( _("Cannot add candidate license as global decision\n") );
344  }
345 
346  $itemTreeBounds = $this->uploadDao->getItemTreeBounds($uploadTreeId);
347  $uploadId = $itemTreeBounds->getUploadId();
348  $uploadTreeTable = $this->uploadDao->getUploadtreeTableName($uploadId);
349  $itemTreeBounds = $this->uploadDao->getItemTreeBounds($uploadTreeId, $uploadTreeTable);
350 
351  if ($this->isDecisionCheck($uploadTreeId, $groupId, DecisionTypes::IRRELEVANT)) {
352  $this->copyrightDao->updateTable($itemTreeBounds, '', '', $userId, 'copyright', 'rollback');
353  } else if ($decType == DecisionTypes::IRRELEVANT) {
354  $this->copyrightDao->updateTable($itemTreeBounds, '', '', $userId, 'copyright', 'delete', '2');
355  }
356 
357  $this->dbManager->begin();
358 
359  $this->removeWipClearingDecision($uploadTreeId, $groupId);
360 
361  $statementName = __METHOD__;
362  $this->dbManager->prepare($statementName,
363  "
364 INSERT INTO clearing_decision (
365  uploadtree_fk,
366  pfile_fk,
367  user_fk,
368  group_fk,
369  decision_type,
370  scope
371 ) VALUES (
372  $1,
373  (SELECT pfile_fk FROM ". $uploadTreeTable ." WHERE uploadtree_pk=$1),
374  $2,
375  $3,
376  $4,
377  $5) RETURNING clearing_decision_pk
378  ");
379  $res = $this->dbManager->execute($statementName,
380  array($uploadTreeId, $userId, $groupId, $decType, $scope));
381  $result = $this->dbManager->fetchArray($res);
382  $clearingDecisionId = $result['clearing_decision_pk'];
383  $this->dbManager->freeResult($res);
384 
385  $statementNameClearingDecisionEventInsert = __METHOD__ . ".insertClearingDecisionEvent";
386  $this->dbManager->prepare($statementNameClearingDecisionEventInsert,
387  "INSERT INTO clearing_decision_event (clearing_decision_fk, clearing_event_fk) VALUES($1, $2)"
388  );
389 
390  foreach ($eventIds as $eventId) {
391  $this->dbManager->freeResult($this->dbManager->execute($statementNameClearingDecisionEventInsert, array($clearingDecisionId, $eventId)));
392  }
393 
394  $this->dbManager->commit();
395  }
396 
402  public function getRelevantClearingEvents($itemTreeBounds, $groupId, $includeSubFolders=true)
403  {
404  $decision = $this->getFileClearingsFolder($itemTreeBounds, $groupId, $includeSubFolders, $onlyCurrent=true);
405  $events = array();
406  $date = 0;
407 
408  if (count($decision)) {
409  foreach ($decision[0]->getClearingEvents() as $event) {
410  $events[$event->getLicenseId()] = $event;
411  }
412  $date = $decision[0]->getTimeStamp();
413  }
414 
415  $stmt = __METHOD__;
416  $sql = 'SELECT rf_fk,rf_shortname,rf_spdx_id,rf_fullname,clearing_event_pk,comment,type_fk,removed,reportinfo,acknowledgement, EXTRACT(EPOCH FROM date_added) AS ts_added
417  FROM clearing_event LEFT JOIN license_ref ON rf_fk=rf_pk
418  WHERE uploadtree_fk=$1 AND group_fk=$2 AND date_added>to_timestamp($3)
419  ORDER BY clearing_event_pk ASC';
420  $this->dbManager->prepare($stmt, $sql);
421  $res = $this->dbManager->execute($stmt,array($itemTreeBounds->getItemId(),$groupId,$date));
422 
423  while ($row = $this->dbManager->fetchArray($res)) {
424  $licenseRef = new LicenseRef($row['rf_fk'],$row['rf_shortname'],$row['rf_fullname'],$row['rf_spdx_id']);
425  $events[$row['rf_fk']] = ClearingEventBuilder::create()
426  ->setEventId($row['clearing_event_pk'])
427  ->setComment($row['comment'])
428  ->setTimeStamp($row['ts_added'])
429  ->setEventType($row['type_fk'])
430  ->setLicenseRef($licenseRef)
431  ->setRemoved($this->dbManager->booleanFromDb($row['removed']))
432  ->setReportinfo($row['reportinfo'])
433  ->setAcknowledgement($row['acknowledgement'])
434  ->setUploadTreeId($itemTreeBounds->getItemId())
435  ->build();
436  }
437  $this->dbManager->freeResult($res);
438  return $events;
439  }
440 
449  public function updateClearingEvent($uploadTreeId, $userId, $groupId, $licenseId, $what, $changeTo)
450  {
451  $this->dbManager->begin();
452 
453  $statementGetOldata = "SELECT * FROM clearing_event WHERE uploadtree_fk=$1 AND rf_fk=$2 AND group_fk=$3 ORDER BY clearing_event_pk DESC LIMIT 1";
454  $statementName = __METHOD__ . 'getOld';
455  $params = array($uploadTreeId, $licenseId, $groupId);
456  $row = $this->dbManager->getSingleRow($statementGetOldata, $params, $statementName);
457 
458  if (!$row) { //The license was not added as user decision yet -> we promote it here
459  $type = ClearingEventTypes::USER;
460  $row['type_fk'] = $type;
461  $row['comment'] = "";
462  $row['reportinfo'] = "";
463  $row['acknowledgement'] = "";
464  }
465 
466  $changeTo = StringOperation::replaceUnicodeControlChar($changeTo, false);
467  if ($what == 'reportinfo') {
468  $reportInfo = $changeTo;
469  $comment = $row['comment'];
470  $acknowledgement = $row['acknowledgement'];
471  } elseif ($what == 'comment') {
472  $reportInfo = $row['reportinfo'];
473  $comment = $changeTo;
474  $acknowledgement = $row['acknowledgement'];
475  } else {
476  $reportInfo = $row['reportinfo'];
477  $comment = $row['comment'];
478  $acknowledgement = $changeTo;
479  }
480  $this->insertClearingEvent($uploadTreeId, $userId, $groupId, $licenseId, false, $row['type_fk'], $reportInfo, $comment, $acknowledgement);
481 
482  $this->dbManager->commit();
483 
484  }
485 
486  public function copyEventIdTo($eventId, $itemId, $userId, $groupId)
487  {
488  $stmt = __METHOD__;
489  $this->dbManager->prepare($stmt,
490  "INSERT INTO clearing_event(uploadtree_fk, user_fk, group_fk, type_fk, rf_fk, removed, reportinfo, comment, acknowledgement)
491  SELECT $2, $3, $4, type_fk, rf_fk, removed, reportinfo, comment, acknowledgement FROM clearing_event WHERE clearing_event_pk = $1"
492  );
493 
494  $this->dbManager->freeResult($this->dbManager->execute($stmt, array($eventId, $itemId, $userId, $groupId)));
495  }
496 
509  public function insertClearingEvent($uploadTreeId, $userId, $groupId, $licenseId, $isRemoved, $type = ClearingEventTypes::USER, $reportInfo = '', $comment = '', $acknowledgement = '', $jobId=0)
510  {
511  $insertIsRemoved = $this->dbManager->booleanToDb($isRemoved);
512 
513  $reportInfo = StringOperation::replaceUnicodeControlChar($reportInfo);
514  $comment = StringOperation::replaceUnicodeControlChar($comment);
515  $acknowledgement = StringOperation::replaceUnicodeControlChar($acknowledgement);
516 
517  $stmt = __METHOD__;
518  $params = array($uploadTreeId, $userId, $groupId, $type, $licenseId, $insertIsRemoved, $reportInfo, $comment, $acknowledgement);
519  $columns = "uploadtree_fk, user_fk, group_fk, type_fk, rf_fk, removed, reportinfo, comment, acknowledgement";
520  $values = "$1,$2,$3,$4,$5,$6,$7,$8,$9";
521 
522  if ($jobId > 0) {
523  $stmt.= ".jobId";
524  $params[] = $jobId;
525  $columns .= ", job_fk";
526  $values .= ",$".count($params);
527  } else {
528  $this->markDecisionAsWip($uploadTreeId, $userId, $groupId);
529  }
530 
531  $this->dbManager->prepare($stmt, "INSERT INTO clearing_event ($columns) VALUES($values) RETURNING clearing_event_pk");
532  $res = $this->dbManager->execute($stmt, $params);
533 
534  $row = $this->dbManager->fetchArray($res);
535  $this->dbManager->freeResult($res);
536 
537  return intval($row['clearing_event_pk']);
538  }
539 
544  public function getEventIdsOfJob($jobId)
545  {
546  $statementName = __METHOD__;
547  $this->dbManager->prepare(
548  $statementName,
549  "SELECT uploadtree_fk, clearing_event_pk, rf_fk FROM clearing_event WHERE job_fk = $1"
550  );
551 
552  $res = $this->dbManager->execute($statementName, array($jobId));
553 
554  $events = array();
555  while ($row = $this->dbManager->fetchArray($res)) {
556  $itemId = intval($row['uploadtree_fk']);
557  $eventId = intval($row['clearing_event_pk']);
558  $licenseId = intval($row['rf_fk']);
559 
560  $events[$itemId][$licenseId] = $eventId;
561  }
562  $this->dbManager->freeResult($res);
563 
564  return $events;
565  }
566 
579  protected function buildClearingEvent($eventId, $userId, $groupId, $licenseRef, $licenseIsRemoved, $type, $reportInfo, $comment, $acknowledgement)
580  {
581  $removed = $this->dbManager->booleanFromDb($licenseIsRemoved);
582 
583  return ClearingEventBuilder::create()
584  ->setEventId($eventId)
585  ->setUserId($userId)
586  ->setGroupId($groupId)
587  ->setEventType($type)
588  ->setLicenseRef($licenseRef)
589  ->setRemoved($removed)
590  ->setReportInfo($reportInfo)
591  ->setAcknowledgement($acknowledgement)
592  ->setComment($comment)
593  ->build();
594  }
595 
601  public function markDecisionAsWip($uploadTreeId, $userId, $groupId)
602  {
603  $statementName = __METHOD__;
604 
605  $this->dbManager->prepare($statementName,
606  "INSERT INTO clearing_decision (uploadtree_fk,pfile_fk,user_fk,group_fk,decision_type,scope) VALUES (
607  $1, (SELECT pfile_fk FROM uploadtree WHERE uploadtree_pk=$1), $2, $3, $4, $5)");
608  $res = $this->dbManager->execute($statementName,
609  array($uploadTreeId, $userId, $groupId, DecisionTypes::WIP, DecisionScopes::ITEM));
610  $this->dbManager->freeResult($res);
611  }
612 
618  public function isDecisionCheck($uploadTreeId, $groupId, $decisionType)
619  {
620  $columns = "decision_type";
621  if (!in_array($decisionType,
622  [DecisionTypes::WIP, DecisionTypes::TO_BE_DISCUSSED,
623  DecisionTypes::DO_NOT_USE, DecisionTypes::IRRELEVANT,
624  DecisionTypes::NON_FUNCTIONAL])
625  ) {
626  $columns = "decision_type, scope";
627  }
628  $sql = "SELECT $columns FROM clearing_decision
629  WHERE uploadtree_fk=$1 AND group_fk = $2
630  ORDER BY clearing_decision_pk DESC LIMIT 1";
631  $latestDec = $this->dbManager->getSingleRow($sql,
632  array($uploadTreeId, $groupId), $sqlLog = __METHOD__);
633 
634  if ($latestDec === false) {
635  return false;
636  } else if ($decisionType !== "") {
637  return ($latestDec['decision_type'] == $decisionType);
638  } else {
639  return $latestDec;
640  }
641  }
642 
649  public function getBulkHistory(ItemTreeBounds $itemTreeBound, $groupId, $onlyTried = true)
650  {
651  $uploadTreeTableName = $itemTreeBound->getUploadTreeTableName();
652  $itemId = $itemTreeBound->getItemId();
653  $uploadId = $itemTreeBound->getUploadId();
654  $left = $itemTreeBound->getLeft();
655 
656  $params = array($uploadId, $itemId, $left, $groupId);
657  $stmt = __METHOD__ . "." . $uploadTreeTableName;
658 
659  $triedExpr = "$3 between ut2.lft and ut2.rgt";
660  $triedFilter = "";
661  if ($onlyTried) {
662  $triedFilter = "and " . $triedExpr;
663  $stmt .= ".tried";
664  }
665 
666  $sql = "WITH relevant_bulks AS MATERIALIZED (
667  SELECT lr.lrb_pk, $triedExpr AS tried
668  FROM license_ref_bulk lr
669  INNER JOIN $uploadTreeTableName ut2 ON ut2.uploadtree_pk = lr.uploadtree_fk
670  WHERE ut2.upload_fk = $1 AND lr.group_fk = $4
671  $triedFilter
672  ), alltried AS (
673  SELECT rb.lrb_pk, ce.clearing_event_pk ce_pk, ce.uploadtree_fk, rb.tried
674  FROM relevant_bulks rb
675  LEFT JOIN highlight_bulk h ON h.lrb_fk = rb.lrb_pk
676  LEFT JOIN clearing_event ce ON ce.clearing_event_pk = h.clearing_event_fk
677  ), aggregated_tried AS (
678  SELECT DISTINCT ON(lrb_pk) lrb_pk, ce_pk, tried, matched
679  FROM (
680  SELECT DISTINCT ON(lrb_pk) lrb_pk, ce_pk, tried, true AS matched FROM alltried WHERE uploadtree_fk = $2
681  UNION ALL
682  SELECT DISTINCT ON(lrb_pk) lrb_pk, ce_pk, tried, false AS matched FROM alltried WHERE uploadtree_fk != $2 OR uploadtree_fk IS NULL
683  ) AS result ORDER BY lrb_pk, matched DESC)
684  SELECT a.lrb_pk, lr.rf_text AS text, lrf.rf_shortname, lsb.removing, a.tried, a.ce_pk, a.matched
685  FROM aggregated_tried a
686  INNER JOIN license_set_bulk lsb ON lsb.lrb_fk = a.lrb_pk
687  INNER JOIN license_ref lrf ON lsb.rf_fk = lrf.rf_pk
688  INNER JOIN license_ref_bulk lr ON lr.lrb_pk = a.lrb_pk
689  ORDER BY a.lrb_pk";
690 
691  $this->dbManager->prepare($stmt, $sql);
692  $res = $this->dbManager->execute($stmt, $params);
693 
694  $bulks = array();
695  while ($row = $this->dbManager->fetchArray($res)) {
696  $bulkRun = $row['lrb_pk'];
697  if (!array_key_exists($bulkRun, $bulks)) {
698  $bulks[$bulkRun] = array(
699  "bulkId" => $row['lrb_pk'],
700  "id" => $row['ce_pk'],
701  "text" => $row['text'],
702  "matched" => $this->dbManager->booleanFromDb($row['matched']),
703  "tried" => $this->dbManager->booleanFromDb($row['tried']),
704  "removedLicenses" => array(),
705  "addedLicenses" => array());
706  }
707  $key = $this->dbManager->booleanFromDb($row['removing']) ? 'removedLicenses' : 'addedLicenses';
708  $bulks[$bulkRun][$key][] = $row['rf_shortname'];
709  }
710 
711  $this->dbManager->freeResult($res);
712  return $bulks;
713  }
714 
715 
716  public function getBulkMatches($bulkId, $groupId)
717  {
718  $stmt = __METHOD__;
719  $sql = "SELECT uploadtree_fk AS itemid
720  FROM clearing_event ce
721  INNER JOIN highlight_bulk h
722  ON ce.clearing_event_pk = h.clearing_event_fk
723  WHERE lrb_fk = $1 AND group_fk = $2";
724 
725  $this->dbManager->prepare($stmt, $sql);
726  $res = $this->dbManager->execute($stmt, array($bulkId, $groupId));
727 
728  $result = $this->dbManager->fetchAll($res);
729  $this->dbManager->freeResult($res);
730  return $result;
731  }
732 
738  public function hasKotobaFindings(ItemTreeBounds $itemTreeBound, $groupId)
739  {
740  $uploadTreeTableName = $itemTreeBound->getUploadTreeTableName();
741  $uploadId = $itemTreeBound->getUploadId();
742  $left = $itemTreeBound->getLeft();
743  $right = $itemTreeBound->getRight();
744 
745  $stmt = __METHOD__ . "." . $uploadTreeTableName;
746  $sql = "SELECT COUNT(*) FROM clearing_event ce
747  INNER JOIN $uploadTreeTableName ut ON ut.uploadtree_pk = ce.uploadtree_fk
748  WHERE ce.type_fk = " . ClearingEventTypes::KOTOBA . "
749  AND ce.group_fk = $1
750  AND ut.upload_fk = $2
751  AND ut.lft BETWEEN $3 AND $4";
752 
753  $this->dbManager->prepare($stmt, $sql);
754  $res = $this->dbManager->execute($stmt, array($groupId, $uploadId, $left, $right));
755  $row = $this->dbManager->fetchArray($res);
756  $this->dbManager->freeResult($res);
757 
758  return ($row && intval($row['count']) > 0);
759  }
760 
767  public function getKotobaHistory(ItemTreeBounds $itemTreeBound, $groupId, $onlyTried = true)
768  {
769  $uploadTreeTableName = $itemTreeBound->getUploadTreeTableName();
770  $itemId = $itemTreeBound->getItemId();
771  $uploadId = $itemTreeBound->getUploadId();
772  $left = $itemTreeBound->getLeft();
773  $right = $itemTreeBound->getRight();
774 
775  $params = array($uploadId, $itemId, $left, $right, $groupId);
776  $stmt = __METHOD__ . "." . $uploadTreeTableName;
777 
778  $triedExpr = "ut2.lft BETWEEN $3 AND $4";
779  $triedFilter = "";
780  if ($onlyTried) {
781  $triedFilter = "AND " . $triedExpr;
782  $stmt .= ".tried";
783  }
784 
785  $kotobaType = ClearingEventTypes::KOTOBA;
786  $sql = "WITH alltried AS (
787  SELECT ce.reportinfo, ce.clearing_event_pk ce_pk, ce.uploadtree_fk,
788  $triedExpr AS tried
789  FROM clearing_event ce
790  INNER JOIN $uploadTreeTableName ut ON ut.uploadtree_pk = ce.uploadtree_fk
791  INNER JOIN $uploadTreeTableName ut2 ON ut2.uploadtree_pk = ce.uploadtree_fk
792  WHERE ce.type_fk = $kotobaType
793  AND ce.group_fk = $5
794  AND ut.upload_fk = $1
795  AND ce.reportinfo IS NOT NULL
796  AND ce.reportinfo != ''
797  $triedFilter
798  ORDER BY ce.reportinfo, ce.clearing_event_pk
799  ), aggregated_tried AS (
800  SELECT DISTINCT ON(reportinfo) reportinfo AS text, ce_pk, tried, matched
801  FROM (
802  SELECT DISTINCT ON(reportinfo) reportinfo, ce_pk, tried, true AS matched FROM alltried WHERE uploadtree_fk = $2
803  UNION ALL
804  SELECT DISTINCT ON(reportinfo) reportinfo, ce_pk, tried, false AS matched FROM alltried WHERE uploadtree_fk != $2 OR uploadtree_fk IS NULL
805  ) AS result ORDER BY reportinfo, matched DESC)
806  SELECT aggregated_tried.text, lrf.rf_shortname, ce.removed, aggregated_tried.tried, aggregated_tried.ce_pk, aggregated_tried.matched
807  FROM aggregated_tried
808  INNER JOIN clearing_event ce ON ce.reportinfo = aggregated_tried.text AND ce.type_fk = $kotobaType AND ce.group_fk = $5
809  INNER JOIN license_ref lrf ON ce.rf_fk = lrf.rf_pk
810  INNER JOIN $uploadTreeTableName ut ON ut.uploadtree_pk = ce.uploadtree_fk
811  WHERE ut.upload_fk = $1
812  ORDER BY aggregated_tried.text, ce.clearing_event_pk";
813 
814  $this->dbManager->prepare($stmt, $sql);
815  $res = $this->dbManager->execute($stmt, $params);
816 
817  $phrases = array();
818  while ($row = $this->dbManager->fetchArray($res)) {
819  $phraseText = $row['text'];
820  $phraseId = md5($phraseText); // Use hash of phrase text as ID
821 
822  if (!array_key_exists($phraseId, $phrases)) {
823  $phrases[$phraseId] = array(
824  "phraseId" => $phraseId,
825  "id" => $row['ce_pk'],
826  "text" => $phraseText,
827  "matched" => $this->dbManager->booleanFromDb($row['matched']),
828  "tried" => $this->dbManager->booleanFromDb($row['tried']),
829  "removedLicenses" => array(),
830  "addedLicenses" => array());
831  }
832  $key = $this->dbManager->booleanFromDb($row['removed']) ? 'removedLicenses' : 'addedLicenses';
833  if (!in_array($row['rf_shortname'], $phrases[$phraseId][$key])) {
834  $phrases[$phraseId][$key][] = $row['rf_shortname'];
835  }
836  }
837 
838  $this->dbManager->freeResult($res);
839  return $phrases;
840  }
841 
847  function getClearedLicenseIdAndMultiplicities(ItemTreeBounds $itemTreeBounds, $groupId)
848  {
849  $statementName = __METHOD__;
850 
851  $params = array($itemTreeBounds->getLeft(), $itemTreeBounds->getRight());
852  $condition = "ut.lft BETWEEN $1 AND $2";
853 
854  $decisionsCte = $this->getRelevantDecisionsCte($itemTreeBounds, $groupId, $onlyCurrent=true, $statementName, $params, $condition);
855  $params[] = DecisionTypes::IRRELEVANT;
856  $sql = "$decisionsCte
857  SELECT
858  COUNT(DISTINCT itemid) AS count,
859  lr.rf_shortname AS shortname,
860  lr.rf_spdx_id AS spdx_id,
861  rf_pk
862  FROM decision
863  LEFT JOIN clearing_decision_event cde ON cde.clearing_decision_fk = decision.id
864  LEFT JOIN clearing_event ce ON ce.clearing_event_pk = cde.clearing_event_fk
865  LEFT JOIN license_ref lr ON lr.rf_pk = ce.rf_fk
866  WHERE (NOT ce.removed OR clearing_event_pk IS NULL) AND type_id!=$".count($params)."
867  GROUP BY shortname,rf_pk,spdx_id";
868 
869  $this->dbManager->prepare($statementName, $sql);
870  $res = $this->dbManager->execute($statementName, $params);
871  $multiplicity = array();
872  while ($row = $this->dbManager->fetchArray($res)) {
873  $shortname = empty($row['rf_pk']) ? LicenseDao::NO_LICENSE_FOUND : $row['shortname'];
874  $row['spdx_id'] = LicenseRef::convertToSpdxId($shortname, $row['spdx_id']);
875  $multiplicity[$shortname] = $row;
876  }
877  $this->dbManager->freeResult($res);
878 
879  return $multiplicity;
880  }
881 
886  public function getDecisionType($decisionType)
887  {
888  if ($decisionType == "doNotUse" || $decisionType == "deleteDoNotUse") {
889  return DecisionTypes::DO_NOT_USE;
890  } else if ($decisionType == "irrelevant" || $decisionType == "deleteIrrelevant") {
891  return DecisionTypes::IRRELEVANT;
892  } else {
893  return DecisionTypes::NON_FUNCTIONAL;
894  }
895  }
896 
903  public function markDirectoryAsDecisionType(ItemTreeBounds $itemTreeBounds, $groupId, $userId, $decisionMark)
904  {
905  $decisionMark = $this->getDecisionType($decisionMark);
906  $this->markDirectoryAsDecisionTypeRec($itemTreeBounds, $groupId, $userId, false, $decisionMark);
907  }
908 
915  public function deleteDecisionTypeFromDirectory(ItemTreeBounds $itemTreeBounds, $groupId, $userId, $decisionMark)
916  {
917  $decisionMark = $this->getDecisionType($decisionMark);
918  $this->markDirectoryAsDecisionTypeRec($itemTreeBounds, $groupId, $userId, true, $decisionMark);
919  }
920 
928  protected function markDirectoryAsDecisionTypeRec(ItemTreeBounds $itemTreeBounds, $groupId, $userId, $removeDecision=false, $decisionMark=DecisionTypes::IRRELEVANT)
929  {
930  $params = array($itemTreeBounds->getLeft(), $itemTreeBounds->getRight());
931  $params[] = $groupId;
932  $a = count($params);
933 
934  $options = array(
935  UploadTreeProxy::OPT_SKIP_THESE => 'nolicensenocopyright',
936  UploadTreeProxy::OPT_ITEM_FILTER => ' AND (lft BETWEEN $1 AND $2)',
937  UploadTreeProxy::OPT_GROUP_ID => '$' . $a
938  );
939  $uploadTreeProxy = new UploadTreeProxy($itemTreeBounds->getUploadId(), $options, $itemTreeBounds->getUploadTreeTableName());
940  if (!$removeDecision) {
941  $sql = $uploadTreeProxy->asCTE() .
942  ' SELECT uploadtree_pk, upload_fk, lft, rgt FROM UploadTreeView;';
943  $itemRows = $this->dbManager->getRows($sql, $params,
944  __METHOD__ . ".getRevelantItems");
945  $uploadTreeTableName = $itemTreeBounds->getUploadTreeTableName();
947  $clearingDecisionEventProcessor = $GLOBALS['container']->get(
948  'businessrules.clearing_decision_processor');
949  foreach ($itemRows as $itemRow) {
950  $itemBounds = new ItemTreeBounds(
951  $itemRow['uploadtree_pk'], $uploadTreeTableName,
952  $itemRow['upload_fk'], $itemRow['lft'], $itemRow['rgt']);
953  $clearingDecisionEventProcessor->makeDecisionFromLastEvents(
954  $itemBounds, $userId, $groupId, $decisionMark, DecisionScopes::ITEM);
955  }
956  } else {
957  $this->dbManager->begin();
958  $params[] = $decisionMark;
959  $sql = $uploadTreeProxy->asCTE() .
960  ' DELETE FROM clearing_decision WHERE clearing_decision_pk IN (
961  SELECT clearing_decision_pk FROM clearing_decision cd
962  INNER JOIN (
963  SELECT MAX(date_added) AS date_added, uploadtree_fk
964  FROM clearing_decision WHERE uploadtree_fk IN (
965  SELECT uploadtree_pk FROM UploadTreeView)
966  GROUP BY uploadtree_fk) cd2
967  ON cd.uploadtree_fk = cd2.uploadtree_fk
968  AND cd.date_added = cd2.date_added
969  AND decision_type = $' . ($a + 1) . ')
970  RETURNING clearing_decision_pk;';
971  $clearingDecisionRows = $this->dbManager->getRows($sql, $params,
972  __METHOD__ . ".getRelevantDecisions");
973  $clearingDecisions = array_map(function($x) {
974  return $x['clearing_decision_pk'];
975  }, $clearingDecisionRows);
976  $clearingDecisions = "{" . join(",", $clearingDecisions) . "}";
977 
978  $delEventSql = "DELETE FROM clearing_event WHERE clearing_event_pk IN (" .
979  "SELECT clearing_event_fk FROM clearing_decision_event " .
980  "WHERE clearing_decision_fk = ANY($1::int[]));";
981  $this->dbManager->getSingleRow($delEventSql, array($clearingDecisions),
982  __METHOD__ . ".deleteEvent");
983 
984  $delCdEventSql = "DELETE FROM clearing_decision_event WHERE " .
985  "clearing_decision_fk = ANY($1::int[]);";
986  $this->dbManager->getSingleRow($delCdEventSql, array($clearingDecisions),
987  __METHOD__ . ".deleteCdEvent");
988  $this->dbManager->commit();
989  $this->copyrightDao->updateTable($itemTreeBounds, '', '', $userId,
990  'copyright', 'rollback');
991  }
992  }
993 
999  public function getMainLicenseIds($uploadId, $groupId)
1000  {
1001  $stmt = __METHOD__;
1002  $sql = "SELECT rf_fk FROM upload_clearing_license WHERE upload_fk=$1 AND group_fk=$2";
1003  $this->dbManager->prepare($stmt, $sql);
1004  $res = $this->dbManager->execute($stmt,array($uploadId,$groupId));
1005  $ids = array();
1006  while ($row = $this->dbManager->fetchArray($res)) {
1007  $ids[$row['rf_fk']] = $row['rf_fk'];
1008  }
1009  $this->dbManager->freeResult($res);
1010  return $ids;
1011  }
1012 
1020  public function getMainLicenseReportInfos($uploadId, $groupId)
1021  {
1022  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
1023  $statementName = __METHOD__;
1024  $sql = "SELECT DISTINCT ON (ce.rf_fk)
1025  ce.rf_fk AS license_id,
1026  ce.reportinfo
1027  FROM $uploadTreeTableName ut
1028  INNER JOIN clearing_event ce ON ce.uploadtree_fk = ut.uploadtree_pk
1029  WHERE ut.upload_fk = \$1
1030  AND ce.group_fk = \$2
1031  AND NOT ce.removed
1032  AND ce.reportinfo IS NOT NULL
1033  AND ce.reportinfo <> ''
1034  ORDER BY ce.rf_fk, ce.date_added DESC, ce.clearing_event_pk DESC";
1035  $this->dbManager->prepare($statementName, $sql);
1036  $result = $this->dbManager->execute($statementName, array($uploadId, $groupId));
1037  $reportInfos = array();
1038  while ($row = $this->dbManager->fetchArray($result)) {
1039  $reportInfos[intval($row['license_id'])] = $row['reportinfo'];
1040  }
1041  $this->dbManager->freeResult($result);
1042  return $reportInfos;
1043  }
1044 
1050  public function makeMainLicense($uploadId, $groupId, $licenseId)
1051  {
1052  $this->dbManager->insertTableRow('upload_clearing_license',
1053  array('upload_fk'=>$uploadId,'group_fk'=>$groupId,'rf_fk'=>$licenseId));
1054  }
1055 
1061  public function removeMainLicense($uploadId, $groupId, $licenseId)
1062  {
1063  $this->dbManager->getSingleRow('DELETE FROM upload_clearing_license WHERE upload_fk=$1 AND group_fk=$2 AND rf_fk=$3',
1064  array($uploadId,$groupId,$licenseId));
1065  }
1066 
1074  function getFilesForDecisionTypeFolderLevel(ItemTreeBounds $itemTreeBounds, $groupId, $onlyCurrent=true, $decisionMark="")
1075  {
1076  $decisionMark = $this->getDecisionType($decisionMark);
1077  $statementName = __METHOD__;
1078  $params = array();
1079  $decisionsCte = $this->getRelevantDecisionsCte($itemTreeBounds, $groupId, $onlyCurrent, $statementName, $params);
1080  $params[] = $decisionMark;
1081  $sql = "$decisionsCte
1082  SELECT
1083  itemid as uploadtree_pk,
1084  lr.rf_shortname AS shortname,
1085  lr.rf_spdx_id AS spdx_id,
1086  comment
1087  FROM decision
1088  LEFT JOIN clearing_decision_event cde ON cde.clearing_decision_fk = decision.id
1089  LEFT JOIN clearing_event ce ON ce.clearing_event_pk = cde.clearing_event_fk
1090  LEFT JOIN license_ref lr ON lr.rf_pk = ce.rf_fk
1091  WHERE type_id=$".count($params);
1092  $this->dbManager->prepare($statementName, $sql);
1093  $res = $this->dbManager->execute($statementName, $params);
1094  $irrelevantFiles = $this->dbManager->fetchAll($res);
1095  $this->dbManager->freeResult($res);
1096  return $irrelevantFiles;
1097  }
1098 
1104  public function getPreviousBulkIds($uploadId, $groupId, $userId, $onlyCount=0)
1105  {
1106  $stmt = __METHOD__;
1107  $bulkIds = array();
1108  $sql = "SELECT jq_args FROM upload_reuse, jobqueue, job
1109  WHERE upload_fk=$1 AND group_fk=$2
1110  AND EXISTS(SELECT * FROM group_user_member gum WHERE gum.group_fk=upload_reuse.group_fk AND gum.user_fk=$3)
1111  AND jq_type=$4 AND jq_job_fk=job_pk
1112  AND job_upload_fk=reused_upload_fk AND job_group_fk=reused_group_fk";
1113  $this->dbManager->prepare($stmt, $sql);
1114  $res = $this->dbManager->execute($stmt,array($uploadId, $groupId, $userId,'monkbulk'));
1115  while ($row= $this->dbManager->fetchArray($res)) {
1116  $bulkIds = array_merge($bulkIds,explode("\n", $row['jq_args']));
1117  }
1118  $this->dbManager->freeResult($res);
1119  if (empty($onlyCount)) {
1120  return array_unique($bulkIds);
1121  } else {
1122  return count(array_unique($bulkIds));
1123  }
1124  }
1125 
1130  public function getCandidateLicenseCountForCurrentDecisions($uploadTreeId, $uploadId=0)
1131  {
1132  $params = array();
1133  if (!empty($uploadId)) {
1134  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
1135  $itemTreeBounds = $this->uploadDao->getParentItemBounds($uploadId, $uploadTreeTableName);
1136  $params[] = $itemTreeBounds->getLeft();
1137  $params[] = $itemTreeBounds->getRight();
1138  $condition = "UT.lft BETWEEN $1 AND $2";
1139  $uploadtreeStatement = " uploadtree_fk IN (SELECT uploadtree_pk FROM $uploadTreeTableName UT WHERE $condition)";
1140  } else {
1141  $params = array($uploadTreeId);
1142  $uploadtreeStatement = " uploadtree_fk = $1";
1143  }
1144 
1145  $sql = "WITH latestEvents AS (
1146  SELECT rf_fk, date_added, removed FROM (
1147  SELECT rf_fk, date_added, removed, row_number()
1148  OVER (PARTITION BY rf_fk ORDER BY date_added DESC) AS ROWNUM
1149  FROM clearing_event WHERE $uploadtreeStatement) SORTABLE
1150  WHERE ROWNUM = 1 ORDER BY rf_fk)
1151  SELECT count(*) FROM license_candidate WHERE license_candidate.rf_pk IN
1152  (SELECT rf_fk FROM latestEvents WHERE removed=false);";
1153  $countCandidate = $this->dbManager->getSingleRow($sql,
1154  $params, $sqlLog = __METHOD__);
1155 
1156  return $countCandidate['count'];
1157  }
1158 
1163  public function marklocalDecisionsAsGlobal($uploadId)
1164  {
1165  $statementName = __METHOD__ . $uploadId;
1166 
1167  $sql = "WITH latestDecisions AS (
1168  SELECT clearing_decision_pk FROM (
1169  SELECT clearing_decision_pk, uploadtree_fk, date_added, row_number()
1170  OVER (PARTITION BY uploadtree_fk ORDER BY date_added DESC) AS ROWNUM
1171  FROM clearing_decision WHERE uploadtree_fk IN
1172  (SELECT uploadtree_pk FROM uploadtree WHERE upload_fk = $1)) SORTABLE
1173  WHERE ROWNUM = $2 ORDER BY uploadtree_fk)
1174  UPDATE clearing_decision SET scope = $2 WHERE clearing_decision_pk IN (
1175  SELECT clearing_decision_pk FROM latestDecisions) RETURNING clearing_decision_pk";
1176 
1177  $countUpdated = $this->dbManager->getRows($sql,
1178  array($uploadId, DecisionScopes::REPO), $statementName);
1179 
1180  return count($countUpdated);
1181  }
1182 }
Utility functions to process ClearingDecision.
buildClearingEvent($eventId, $userId, $groupId, $licenseRef, $licenseIsRemoved, $type, $reportInfo, $comment, $acknowledgement)
getFileClearings(ItemTreeBounds $itemTreeBounds, $groupId, $onlyCurrent=true, $forClearingHistory=false)
deleteDecisionTypeFromDirectory(ItemTreeBounds $itemTreeBounds, $groupId, $userId, $decisionMark)
getMainLicenseReportInfos($uploadId, $groupId)
updateClearingEvent($uploadTreeId, $userId, $groupId, $licenseId, $what, $changeTo)
markDirectoryAsDecisionType(ItemTreeBounds $itemTreeBounds, $groupId, $userId, $decisionMark)
createDecisionFromEvents($uploadTreeId, $userId, $groupId, $decType, $scope, $eventIds)
getClearedLicenses(ItemTreeBounds $itemTreeBounds, $groupId)
markDecisionAsWip($uploadTreeId, $userId, $groupId)
removeMainLicense($uploadId, $groupId, $licenseId)
getRelevantClearingDecision(ItemTreeBounds $itemTreeBounds, $groupId)
isDecisionCheck($uploadTreeId, $groupId, $decisionType)
makeMainLicense($uploadId, $groupId, $licenseId)
getRelevantClearingEvents($itemTreeBounds, $groupId, $includeSubFolders=true)
getMainLicenseIds($uploadId, $groupId)
getPreviousBulkIds($uploadId, $groupId, $userId, $onlyCount=0)
__construct(DbManager $dbManager, UploadDao $uploadDao)
Definition: ClearingDao.php:44
getBulkHistory(ItemTreeBounds $itemTreeBound, $groupId, $onlyTried=true)
getFileClearingsFolder(ItemTreeBounds $itemTreeBounds, $groupId, $includeSubFolders=true, $onlyCurrent=true)
getClearedLicenseIdAndMultiplicities(ItemTreeBounds $itemTreeBounds, $groupId)
getDecisionsFromCte($decisionsCte, $statementName, $params, $forClearingHistory=false)
hasKotobaFindings(ItemTreeBounds $itemTreeBound, $groupId)
getCandidateLicenseCountForCurrentDecisions($uploadTreeId, $uploadId=0)
getFilesForDecisionTypeFolderLevel(ItemTreeBounds $itemTreeBounds, $groupId, $onlyCurrent=true, $decisionMark="")
insertClearingEvent($uploadTreeId, $userId, $groupId, $licenseId, $isRemoved, $type=ClearingEventTypes::USER, $reportInfo='', $comment='', $acknowledgement='', $jobId=0)
removeWipClearingDecision($uploadTreeId, $groupId)
getKotobaHistory(ItemTreeBounds $itemTreeBound, $groupId, $onlyTried=true)
static convertToSpdxId($shortname, $spdxId)
Given a license's shortname and spdx id, give out spdx id to use in reports.
Definition: LicenseRef.php:106
static replaceUnicodeControlChar($input, $replace="")
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16