FOSSology  4.4.0
Open Source License Compliance by Open Source Software
CopyrightController.php
Go to the documentation of this file.
1 <?php
2 /*
3  Author: Soham Banerjee <sohambanerjee4abc@hotmail.com>
4  SPDX-FileCopyrightText: © 2023 Soham Banerjee <sohambanerjee4abc@hotmail.com>
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 
13 namespace Fossology\UI\Api\Controllers;
14 
22 use Psr\Http\Message\ServerRequestInterface;
23 
24 
26 {
30  const COPYRIGHT_PARAM = "status";
31 
35  const LIMIT_PARAM = "limit";
36 
40  const PAGE_PARAM = "page";
41 
45  const COPYRIGHT_FETCH_LIMIT = 100;
46 
51  private $copyrightHist;
52 
57  private $copyrightDao;
58  const TYPE_COPYRIGHT = 1;
59  const TYPE_EMAIL = 2;
60  const TYPE_URL = 4;
61  const TYPE_AUTHOR = 8;
62  const TYPE_ECC = 16;
63  const TYPE_KEYWORD = 32;
64  const TYPE_IPRA = 64;
65  const TYPE_COPYRIGHT_USERFINDINGS = 128;
66  const TYPE_COPYRIGHT_SCANCODE = 256;
67  const TYPE_EMAIL_SCANCODE = 512;
68  const TYPE_URL_SCANCODE = 1024;
69  const TYPE_AUTHOR_SCANCODE = 2048;
70 
71  public function __construct($container)
72  {
73  parent::__construct($container);
74  $this->copyrightDao = $this->container->get('dao.copyright');
75  $this->copyrightHist = $this->restHelper->getPlugin('ajax-copyright-hist');
76  }
77 
86  public function getFileCopyrights($request, $response, $args)
87  {
88  return $this->getFileCX($request, $response, $args, self::TYPE_COPYRIGHT);
89  }
90 
99  public function getFileUserCopyrights($request, $response, $args)
100  {
101  return $this->getFileCX($request, $response, $args, self::TYPE_COPYRIGHT_USERFINDINGS);
102  }
103 
112  public function getFileScanCodeCopyrights($request, $response, $args)
113  {
114  return $this->getFileCX($request, $response, $args, self::TYPE_COPYRIGHT_SCANCODE);
115  }
116 
125  public function getFileEmail($request, $response, $args)
126  {
127  return $this->getFileCX($request, $response, $args, self::TYPE_EMAIL);
128  }
129 
138  public function getFileScanCodeEmail($request, $response, $args)
139  {
140  return $this->getFileCX($request, $response, $args, self::TYPE_EMAIL_SCANCODE);
141  }
142 
151  public function getFileUrl($request, $response, $args)
152  {
153  return $this->getFileCX($request, $response, $args, self::TYPE_URL);
154  }
155 
164  public function getFileScanCodeUrl($request, $response, $args)
165  {
166  return $this->getFileCX($request, $response, $args, self::TYPE_URL_SCANCODE);
167  }
168 
177  public function getFileAuthor($request, $response, $args)
178  {
179  return $this->getFileCX($request, $response, $args, self::TYPE_AUTHOR);
180  }
181 
190  public function getFileScanCodeAuthor($request, $response, $args)
191  {
192  return $this->getFileCX($request, $response, $args, self::TYPE_AUTHOR_SCANCODE);
193  }
194 
203  public function getFileEcc($request, $response, $args)
204  {
205  return $this->getFileCX($request, $response, $args, self::TYPE_ECC);
206  }
207 
216  public function getFileKeyword($request, $response, $args)
217  {
218  return $this->getFileCX($request, $response, $args, self::TYPE_KEYWORD);
219  }
220 
229  public function getFileIpra($request, $response, $args)
230  {
231  return $this->getFileCX($request, $response, $args, self::TYPE_IPRA);
232  }
233 
242  public function deleteFileCopyright($request, $response, $args)
243  {
244  return $this->deleteFileCX($args, $response, self::TYPE_COPYRIGHT);
245  }
246 
255  public function deleteFileUserCopyright($request, $response, $args)
256  {
257  return $this->deleteFileCX($args, $response, self::TYPE_COPYRIGHT_USERFINDINGS);
258  }
259 
268  public function deleteFileScanCodeCopyright($request, $response, $args)
269  {
270  return $this->deleteFileCX($args, $response, self::TYPE_COPYRIGHT_SCANCODE);
271  }
272 
281  public function deleteFileEmail($request, $response, $args)
282  {
283  return $this->deleteFileCX($args, $response, self::TYPE_EMAIL);
284  }
285 
294  public function deleteFileScanCodeEmail($request, $response, $args)
295  {
296  return $this->deleteFileCX($args, $response, self::TYPE_EMAIL_SCANCODE);
297  }
306  public function deleteFileUrl($request, $response, $args)
307  {
308  return $this->deleteFileCX($args, $response, self::TYPE_URL);
309  }
310 
319  public function deleteFileScanCodeUrl($request, $response, $args)
320  {
321  return $this->deleteFileCX($args, $response, self::TYPE_URL_SCANCODE);
322  }
323 
332  public function deleteFileAuthor($request, $response, $args)
333  {
334  return $this->deleteFileCX($args, $response, self::TYPE_AUTHOR);
335  }
336 
345  public function deleteFileScanCodeAuthor($request, $response, $args)
346  {
347  return $this->deleteFileCX($args, $response, self::TYPE_AUTHOR_SCANCODE);
348  }
349 
358  public function deleteFileEcc($request, $response, $args)
359  {
360  return $this->deleteFileCX($args, $response, self::TYPE_ECC);
361  }
362 
371  public function deleteFileKeyword($request, $response, $args)
372  {
373  return $this->deleteFileCX($args, $response, self::TYPE_KEYWORD);
374  }
375 
384  public function deleteFileIpra($request, $response, $args)
385  {
386  return $this->deleteFileCX($args, $response, self::TYPE_IPRA);
387  }
388 
397  public function updateFileCopyright($request, $response, $args)
398  {
399  return $this->updateFileCx($request, $response, $args, self::TYPE_COPYRIGHT);
400  }
401 
410  public function updateFileUserCopyright($request, $response, $args)
411  {
412  return $this->updateFileCx($request, $response, $args, self::TYPE_COPYRIGHT_USERFINDINGS);
413  }
414 
423  public function updateFileScanCodeCopyright($request, $response, $args)
424  {
425  return $this->updateFileCx($request, $response, $args, self::TYPE_COPYRIGHT_SCANCODE);
426  }
427 
436  public function updateFileEmail($request, $response, $args)
437  {
438  return $this->updateFileCx($request, $response, $args, self::TYPE_EMAIL);
439  }
440 
449  public function updateFileScanCodeEmail($request, $response, $args)
450  {
451  return $this->updateFileCx($request, $response, $args, self::TYPE_EMAIL_SCANCODE);
452  }
453 
462  public function updateFileUrl($request, $response, $args)
463  {
464  return $this->updateFileCx($request, $response, $args, self::TYPE_URL);
465  }
466 
475  public function updateFileScanCodeUrl($request, $response, $args)
476  {
477  return $this->updateFileCx($request, $response, $args, self::TYPE_URL_SCANCODE);
478  }
479 
488  public function updateFileAuthor($request, $response, $args)
489  {
490  return $this->updateFileCx($request, $response, $args, self::TYPE_AUTHOR);
491  }
492 
501  public function updateFileScanCodeAuthor($request, $response, $args)
502  {
503  return $this->updateFileCx($request, $response, $args, self::TYPE_AUTHOR_SCANCODE);
504  }
505 
514  public function updateFileEcc($request, $response, $args)
515  {
516  return $this->updateFileCx($request, $response, $args, self::TYPE_ECC);
517  }
518 
527  public function updateFileKeyword($request, $response, $args)
528  {
529  return $this->updateFileCx($request, $response, $args, self::TYPE_KEYWORD);
530  }
531 
540  public function updateFileIpra($request, $response, $args)
541  {
542  return $this->updateFileCx($request, $response, $args, self::TYPE_IPRA);
543  }
544 
553  public function restoreFileCopyright($request, $response, $args)
554  {
555  return $this->restoreFileCx($args, $response, self::TYPE_COPYRIGHT);
556  }
557 
566  public function restoreFileUserCopyright($request, $response, $args)
567  {
568  return $this->restoreFileCx($args, $response, self::TYPE_COPYRIGHT_USERFINDINGS);
569  }
570 
579  public function restoreFileScanCodeCopyright($request, $response, $args)
580  {
581  return $this->restoreFileCx($args, $response, self::TYPE_COPYRIGHT_SCANCODE);
582  }
583 
592  public function restoreFileEmail($request, $response, $args)
593  {
594  return $this->restoreFileCx($args, $response, self::TYPE_EMAIL);
595  }
596 
605  public function restoreFileScanCodeEmail($request, $response, $args)
606  {
607  return $this->restoreFileCx($args, $response, self::TYPE_EMAIL_SCANCODE);
608  }
609 
618  public function restoreFileUrl($request, $response, $args)
619  {
620  return $this->restoreFileCx($args, $response, self::TYPE_URL);
621  }
622 
631  public function restoreFileScanCodeUrl($request, $response, $args)
632  {
633  return $this->restoreFileCx($args, $response, self::TYPE_URL_SCANCODE);
634  }
635 
644  public function restoreFileAuthor($request, $response, $args)
645  {
646  return $this->restoreFileCx($args, $response, self::TYPE_AUTHOR);
647  }
648 
657  public function restoreFileScanCodeAuthor($request, $response, $args)
658  {
659  return $this->restoreFileCx($args, $response, self::TYPE_AUTHOR_SCANCODE);
660  }
661 
670  public function restoreFileEcc($request, $response, $args)
671  {
672  return $this->restoreFileCx($args, $response, self::TYPE_ECC);
673  }
674 
683  public function restoreFileKeyword($request, $response, $args)
684  {
685  return $this->restoreFileCx($args, $response, self::TYPE_KEYWORD);
686  }
687 
696  public function restoreFileIpra($request, $response, $args)
697  {
698  return $this->restoreFileCx($args, $response, self::TYPE_IPRA);
699  }
700 
710  public function getTotalFileCopyrights($request, $response, $args)
711  {
712  return $this->getTotalCX($request, $response, $args, self::TYPE_COPYRIGHT);
713  }
714 
724  public function getTotalFileUserCopyrights($request, $response, $args)
725  {
726  return $this->getTotalCX($request, $response, $args, self::TYPE_COPYRIGHT_USERFINDINGS);
727  }
728 
738  private function getTotalCX($request, $response, $args, $cxType)
739  {
740  $version = ApiVersion::getVersion($request);
741  $uploadPk = $args["id"];
742  $uploadTreeId = $args["itemId"];
743  $query = $request->getQueryParams();
744  $statusVal = true;
745 
746  $this->uploadAccessible($uploadPk);
747  $this->isItemExists($uploadPk, $uploadTreeId);
748 
749  if (!array_key_exists(self::COPYRIGHT_PARAM, $query)) {
750  throw new HttpBadRequestException("Bad Request. 'status' is a " .
751  "required query param with expected values 'active' or 'inactive");
752  }
753  $status = $query[self::COPYRIGHT_PARAM];
754  if ($status == "active") {
755  $statusVal = true;
756  } else if ($status == "inactive") {
757  $statusVal = false;
758  } else {
759  throw new HttpBadRequestException("Bad Request. Invalid query " .
760  "parameter, expected values 'active' or 'inactive");
761  }
762  $uploadTreeTableName = $this->restHelper->getUploadDao()->getUploadtreeTableName($uploadPk);
763 
764  if ($cxType == self::TYPE_COPYRIGHT) {
765  $agentId = $this->copyrightHist->getAgentId($uploadPk, 'copyright_ars');
766  $returnVal = $this->copyrightDao->getTotalCopyrights($uploadPk, $uploadTreeId, $uploadTreeTableName, $agentId, 'statement', $statusVal);
767  } else if ($cxType == self::TYPE_COPYRIGHT_USERFINDINGS) {
768  $copyrightData = $this->copyrightDao->getUserCopyrights($uploadPk, $uploadTreeId, $uploadTreeTableName, 'userfindingcopyright', $statusVal);
769  $returnVal = $copyrightData[1];
770  }
771  return $response->withJson(array($version == ApiVersion::V2 ? "totalCopyrights" : "total_copyrights" => $returnVal), 200);
772  }
773 
784  private function getFileCX($request, $response, $args, $cxType)
785  {
786  switch ($cxType) {
787  case self::TYPE_COPYRIGHT:
788  $dataType = 'statement';
789  $agentArs = 'copyright_ars';
790  break;
791  case self::TYPE_COPYRIGHT_USERFINDINGS:
792  $dataType = 'userfindingcopyright';
793  $agentArs = 'copyright_ars';
794  break;
795  case self::TYPE_COPYRIGHT_SCANCODE:
796  $dataType = 'scancode_statement';
797  $agentArs = 'scancode_ars';
798  break;
799  case self::TYPE_EMAIL:
800  $dataType = 'email';
801  $agentArs = 'copyright_ars';
802  break;
803  case self::TYPE_EMAIL_SCANCODE:
804  $dataType = 'scancode_email';
805  $agentArs = 'scancode_ars';
806  break;
807  case self::TYPE_URL:
808  $dataType = 'url';
809  $agentArs = 'copyright_ars';
810  break;
811  case self::TYPE_URL_SCANCODE:
812  $dataType = 'scancode_url';
813  $agentArs = 'scancode_ars';
814  break;
815  case self::TYPE_AUTHOR:
816  $dataType = 'author';
817  $agentArs = 'copyright_ars';
818  break;
819  case self::TYPE_AUTHOR_SCANCODE:
820  $dataType = 'scancode_author';
821  $agentArs = 'scancode_ars';
822  break;
823  case self::TYPE_ECC:
824  $dataType = 'ecc';
825  $agentArs = 'ecc_ars';
826  break;
827  case self::TYPE_KEYWORD:
828  $dataType = 'keyword';
829  $agentArs = 'keyword_ars';
830  break;
831  case self::TYPE_IPRA:
832  $dataType = 'ipra';
833  $agentArs = 'ipra_ars';
834  break;
835  default:
836  $dataType = 'statement';
837  $agentArs = 'copyright_ars';
838  }
839  $apiVersion = ApiVersion::getVersion($request);
840  $uploadPk = $args["id"];
841  $uploadTreeId = $args["itemId"];
842  $query = $request->getQueryParams();
843  if ($apiVersion == ApiVersion::V2) {
844  $limit = $query[self::LIMIT_PARAM] ?? "";
845  } else {
846  $limit = $request->getHeaderLine(self::LIMIT_PARAM);
847  }
848  $finalVal = [];
849  if (!empty($limit)) {
850  $limit = filter_var($limit, FILTER_VALIDATE_INT);
851  if ($limit < 1) {
852  throw new HttpBadRequestException(
853  "limit should be positive integer > 1");
854  }
855  } else {
857  }
858  if (!array_key_exists(self::COPYRIGHT_PARAM, $query)) {
859  throw new HttpBadRequestException("Bad Request. 'status' is a " .
860  "required query param with expected values 'active' or 'inactive");
861  }
862  $status = $query[self::COPYRIGHT_PARAM];
863  if ($status == "active") {
864  $statusVal = true;
865  } else if ($status == "inactive") {
866  $statusVal = false;
867  } else {
868  throw new HttpBadRequestException("Bad Request. Invalid query " .
869  "parameter, expected values 'active' or 'inactive");
870  }
871 
872  $this->uploadAccessible($uploadPk);
873  $this->isItemExists($uploadPk, $uploadTreeId);
874 
875  $agentId = $this->copyrightHist->getAgentId($uploadPk, $agentArs);
876  $uploadTreeTableName = $this->restHelper->getUploadDao()->getuploadTreeTableName($uploadPk);
877  if ($apiVersion == ApiVersion::V2) {
878  $page = $query[self::PAGE_PARAM] ?? "";
879  } else {
880  $page = $request->getHeaderLine(self::PAGE_PARAM);
881  }
882  if (empty($page) && $page != "0") {
883  $page = 1;
884  }
885  if (!empty($page) || $page == "0") {
886  $page = filter_var($page, FILTER_VALIDATE_INT);
887  if ($page <= 0) {
888  throw new HttpBadRequestException(
889  "page should be positive integer > 0");
890  }
891  }
892  $offset = $limit * ($page - 1);
893  if (self::TYPE_COPYRIGHT_USERFINDINGS == $cxType) {
894  list($rows, $iTotalRecords) = $this->copyrightDao
895  ->getUserCopyrights($uploadPk, $uploadTreeId, $uploadTreeTableName,
896  $dataType, $statusVal, $offset, $limit);
897  } else {
898  list($rows, $iTotalDisplayRecords, $iTotalRecords) = $this->copyrightHist
899  ->getCopyrights($uploadPk, $uploadTreeId, $uploadTreeTableName,
900  $agentId, $dataType, 'active', $statusVal, $offset, $limit);
901  }
902  foreach ($rows as $row) {
903  $row['count'] = intval($row['copyright_count']);
904  unset($row['copyright_count']);
905  $finalVal[] = $row;
906  }
907  $totalPages = intval(ceil($iTotalRecords / $limit));
908  if ($totalPages != 0 && $page > $totalPages) {
909  throw (new HttpBadRequestException(
910  "Can not exceed total pages: $totalPages"))
911  ->setHeaders(["X-Total-Pages" => $totalPages]);
912  }
913  return $response->withHeader("X-Total-Pages", $totalPages)->withJson($finalVal, 200);
914  }
915 
925  private function deleteFileCX($args, $response, $cxType)
926  {
927  list($dataType, $delName) = $this->convertTypeToTable($cxType);
928 
929  $uploadDao = $this->restHelper->getUploadDao();
930  $uploadPk = intval($args['id']);
931  $uploadTreeId = intval($args['itemId']);
932  $copyrightHash = $args['hash'];
933  $userId = $this->restHelper->getUserId();
934  $cpTable = $this->copyrightHist->getTableName($dataType);
935 
936  $this->uploadAccessible($uploadPk);
937  $this->isItemExists($uploadPk, $uploadTreeId);
938 
939  $uploadTreeTableName = $uploadDao->getUploadTreeTableName($uploadTreeId);
940  if (self::TYPE_COPYRIGHT_USERFINDINGS == $cxType) {
941  $tableName = $cpTable."_decision";
942  $decisions = $this->copyrightDao->getDecisionsFromHash($tableName, $copyrightHash,
943  $uploadPk, $uploadTreeTableName);
944  foreach ($decisions as $decision) {
945  $this->copyrightDao->removeDecision($tableName, $decision['pfile_fk'],
946  $decision[$tableName . '_pk']);
947  }
948  } else {
949  $item = $uploadDao->getItemTreeBounds($uploadTreeId, $uploadTreeTableName);
950  $this->copyrightDao->updateTable($item, $copyrightHash, '', $userId, $cpTable, 'delete');
951  }
952  $returnVal = new Info(200, "Successfully removed $delName.", InfoType::INFO);
953  return $response->withJson($returnVal->getArray(), $returnVal->getCode());
954  }
955 
965  private function restoreFileCx($args, $response, $cxType)
966  {
967  list($dataType, $resName) = $this->convertTypeToTable($cxType);
968  $uploadPk = intval($args['id']);
969  $uploadTreeId = intval($args['itemId']);
970  $copyrightHash = ($args['hash']);
971  $userId = $this->restHelper->getUserId();
972  $cpTable = $this->copyrightHist->getTableName($dataType);
973 
974  $this->uploadAccessible($uploadPk);
975  $this->isItemExists($uploadPk, $uploadTreeId);
976 
977  $uploadTreeTableName = $this->restHelper->getUploadDao()->getuploadTreeTableName($uploadTreeId);
978  if (self::TYPE_COPYRIGHT_USERFINDINGS == $cxType) {
979  $tableName = $cpTable."_decision";
980  $decisions = $this->copyrightDao->getDecisionsFromHash($tableName, $copyrightHash,
981  $uploadPk, $uploadTreeTableName);
982  foreach ($decisions as $decision) {
983  $this->copyrightDao->undoDecision($tableName, $decision['pfile_fk'],
984  $decision[$tableName . '_pk']);
985  }
986  } else {
987  $item = $this->restHelper->getUploadDao()->getItemTreeBounds($uploadTreeId, $uploadTreeTableName);
988  $this->copyrightDao->updateTable($item, $copyrightHash, '', $userId, $cpTable, 'rollback');
989  }
990  $returnVal = new Info(200, "Successfully restored $resName.", InfoType::INFO);
991  return $response->withJson($returnVal->getArray(), 200);
992  }
993 
1004  private function updateFileCx($request, $response, $args, $cxType)
1005  {
1006  list($dataType, $resName) = $this->convertTypeToTable($cxType);
1007  $uploadTreeId = intval($args["itemId"]);
1008  $uploadPk = intval($args["id"]);
1009  $copyrightHash = $args["hash"];
1010  $userId = $this->restHelper->getUserId();
1011  $cpTable = $this->copyrightHist->getTableName($dataType);
1012  $body = $this->getParsedBody($request);
1013  $content = $body['content'];
1014 
1015  $this->uploadAccessible($uploadPk);
1016  $this->isItemExists($uploadPk, $uploadTreeId);
1017 
1018  $uploadTreeTableName = $this->restHelper->getUploadDao()->getuploadTreeTableName($uploadTreeId);
1019  if (self::TYPE_COPYRIGHT_USERFINDINGS == $cxType) {
1020  $tableName = $cpTable."_decision";
1021  $decisions = $this->copyrightDao->getDecisionsFromHash($tableName, $copyrightHash,
1022  $uploadPk, $uploadTreeTableName);
1023  foreach ($decisions as $decision) {
1024  $this->copyrightDao->saveDecision($tableName, $decision['pfile_fk'], $decision['user_fk'],
1025  $decision['clearing_decision_type_fk'], $decision['description'],
1026  $content, $decision['comment'], $decision[$tableName . '_pk']);
1027  }
1028  } else {
1029  $item = $this->restHelper->getUploadDao()->getItemTreeBounds($uploadTreeId, $uploadTreeTableName);
1030  $this->copyrightDao->updateTable($item, $copyrightHash, $content, $userId, $cpTable);
1031  }
1032  $returnVal = new Info(200, "Successfully Updated $resName.", InfoType::INFO);
1033  return $response->withJson($returnVal->getArray(), 200);
1034  }
1035 
1042  private function convertTypeToTable(int $cxType): array
1043  {
1044  switch ($cxType) {
1045  case self::TYPE_COPYRIGHT:
1046  $dataType = 'statement';
1047  $dispName = 'copyright';
1048  break;
1049  case self::TYPE_COPYRIGHT_USERFINDINGS:
1050  $dataType = 'statement';
1051  $dispName = 'user-copyright';
1052  break;
1053  case self::TYPE_COPYRIGHT_SCANCODE:
1054  $dataType = 'scancode_statement';
1055  $dispName = 'scancode-copyright';
1056  break;
1057  case self::TYPE_EMAIL:
1058  $dispName = $dataType = 'email';
1059  break;
1060  case self::TYPE_EMAIL_SCANCODE:
1061  $dataType = 'scancode_email';
1062  $dispName = 'scancode-email';
1063  break;
1064  case self::TYPE_URL:
1065  $dispName = $dataType = 'url';
1066  break;
1067  case self::TYPE_URL_SCANCODE:
1068  $dataType = 'scancode_url';
1069  $dispName = 'scancode-url';
1070  break;
1071  case self::TYPE_AUTHOR:
1072  $dispName = $dataType = 'author';
1073  break;
1074  case self::TYPE_AUTHOR_SCANCODE:
1075  $dataType = 'scancode_author';
1076  $dispName = 'scancode-author';
1077  break;
1078  case self::TYPE_ECC:
1079  $dispName = $dataType = 'ecc';
1080  break;
1081  case self::TYPE_KEYWORD:
1082  $dispName = $dataType = 'keyword';
1083  break;
1084  case self::TYPE_IPRA:
1085  $dispName = $dataType = 'ipra';
1086  break;
1087  default:
1088  $dataType = 'statement';
1089  $dispName = 'copyright';
1090  }
1091  return array($dataType, $dispName);
1092  }
1093 }
getFileCX($request, $response, $args, $cxType)
updateFileCx($request, $response, $args, $cxType)
getTotalCX($request, $response, $args, $cxType)
Base controller for REST calls.
isItemExists(int $uploadId, int $itemId)
getParsedBody(ServerRequestInterface $request)
Parse request body as JSON and return associative PHP array.
Override Slim response for withJson function.
static getVersion(ServerRequestInterface $request)
Definition: ApiVersion.php:29
Different type of infos provided by REST.
Definition: InfoType.php:16
Info model to contain general error and return values.
Definition: Info.php:19
list_t type structure used to keep various lists. (e.g. there are multiple lists).
Definition: nomos.h:308