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